-
- return Cone(ws, lattice=L)
-
-
-
-def lineality(K):
- r"""
- Compute the lineality of this cone.
-
- The lineality of a cone is the dimension of the largest linear
- subspace contained in that cone.
-
- OUTPUT:
-
- A nonnegative integer; the dimension of the largest subspace
- contained within this cone.
-
- REFERENCES:
-
- .. [Rockafellar] R.T. Rockafellar. Convex Analysis. Princeton
- University Press, Princeton, 1970.
-
- EXAMPLES:
-
- The lineality of the nonnegative orthant is zero, since it clearly
- contains no lines::
-
- sage: K = Cone([(1,0,0), (0,1,0), (0,0,1)])
- sage: lineality(K)
- 0
-
- However, if we add another ray so that the entire `x`-axis belongs
- to the cone, then the resulting cone will have lineality one::
-
- sage: K = Cone([(1,0,0), (-1,0,0), (0,1,0), (0,0,1)])
- sage: lineality(K)
- 1
-
- If our cone is all of `\mathbb{R}^{2}`, then its lineality is equal
- to the dimension of the ambient space (i.e. two)::
-
- sage: K = Cone([(1,0), (-1,0), (0,1), (0,-1)])
- sage: lineality(K)
- 2
-
- Per the definition, the lineality of the trivial cone in a trivial
- space is zero::
-
- sage: K = Cone([], lattice=ToricLattice(0))
- sage: lineality(K)
- 0
-
- TESTS:
-
- The lineality of a cone should be an integer between zero and the
- dimension of the ambient space, inclusive::
-
- sage: set_random_seed()
- sage: K = random_cone(max_dim = 8)
- sage: l = lineality(K)
- sage: l in ZZ
- True
- sage: (0 <= l) and (l <= K.lattice_dim())
- True
-
- A strictly convex cone should have lineality zero::
-
- sage: set_random_seed()
- sage: K = random_cone(max_dim = 8, strictly_convex = True)
- sage: lineality(K)
- 0
-
- """
- return K.linear_subspace().dimension()
-
-
-def codim(K):
- r"""
- Compute the codimension of this cone.
-
- The codimension of a cone is the dimension of the space of all
- elements perpendicular to every element of the cone. In other words,
- the codimension is the difference between the dimension of the
- ambient space and the dimension of the cone itself.
-
- OUTPUT:
-
- A nonnegative integer representing the dimension of the space of all
- elements perpendicular to this cone.
-
- .. seealso::
-
- :meth:`dim`, :meth:`lattice_dim`
-
- EXAMPLES:
-
- The codimension of the nonnegative orthant is zero, since the span of
- its generators equals the entire ambient space::
-
- sage: K = Cone([(1,0,0), (0,1,0), (0,0,1)])
- sage: codim(K)
- 0
-
- However, if we remove a ray so that the entire cone is contained
- within the `x-y`-plane, then the resulting cone will have
- codimension one, because the `z`-axis is perpendicular to every
- element of the cone::
-
- sage: K = Cone([(1,0,0), (0,1,0)])
- sage: codim(K)
- 1
-
- If our cone is all of `\mathbb{R}^{2}`, then its codimension is zero::
-
- sage: K = Cone([(1,0), (-1,0), (0,1), (0,-1)])
- sage: codim(K)
- 0
-
- And if the cone is trivial in any space, then its codimension is
- equal to the dimension of the ambient space::
-
- sage: K = Cone([], lattice=ToricLattice(0))
- sage: K.lattice_dim()
- 0
- sage: codim(K)
- 0
-
- sage: K = Cone([(0,)])
- sage: K.lattice_dim()
- 1
- sage: codim(K)
- 1
-
- sage: K = Cone([(0,0)])
- sage: K.lattice_dim()
- 2
- sage: codim(K)
- 2
-
- TESTS:
-
- The codimension of a cone should be an integer between zero and
- the dimension of the ambient space, inclusive::
-
- sage: set_random_seed()
- sage: K = random_cone(max_dim = 8)
- sage: c = codim(K)
- sage: c in ZZ
- True
- sage: (0 <= c) and (c <= K.lattice_dim())
- True
-
- A solid cone should have codimension zero::
-
- sage: set_random_seed()
- sage: K = random_cone(max_dim = 8, solid = True)
- sage: codim(K)
- 0
-
- The codimension of a cone is equal to the lineality of its dual::
-
- sage: set_random_seed()
- sage: K = random_cone(max_dim = 8, solid = True)
- sage: codim(K) == lineality(K.dual())
- True
-
- """
- return (K.lattice_dim() - K.dim())
-
-
-def discrete_complementarity_set(K):
- r"""
- Compute the discrete complementarity set of this cone.
-
- The complementarity set of this cone is the set of all orthogonal
- pairs `(x,s)` such that `x` is in this cone, and `s` is in its
- dual. The discrete complementarity set restricts `x` and `s` to be
- generators of their respective cones.
-
- OUTPUT:
-
- A list of pairs `(x,s)` such that,
-
- * `x` is in this cone.
- * `x` is a generator of this cone.
- * `s` is in this cone's dual.
- * `s` is a generator of this cone's dual.
- * `x` and `s` are orthogonal.
-
- EXAMPLES:
-
- The discrete complementarity set of the nonnegative orthant consists
- of pairs of standard basis vectors::
-
- sage: K = Cone([(1,0),(0,1)])
- sage: discrete_complementarity_set(K)
- [((1, 0), (0, 1)), ((0, 1), (1, 0))]
-
- If the cone consists of a single ray, the second components of the
- discrete complementarity set should generate the orthogonal
- complement of that ray::
-
- sage: K = Cone([(1,0)])
- sage: discrete_complementarity_set(K)
- [((1, 0), (0, 1)), ((1, 0), (0, -1))]
- sage: K = Cone([(1,0,0)])
- sage: discrete_complementarity_set(K)
- [((1, 0, 0), (0, 1, 0)),
- ((1, 0, 0), (0, -1, 0)),
- ((1, 0, 0), (0, 0, 1)),
- ((1, 0, 0), (0, 0, -1))]
-
- When the cone is the entire space, its dual is the trivial cone, so
- the discrete complementarity set is empty::
-
- sage: K = Cone([(1,0),(-1,0),(0,1),(0,-1)])
- sage: discrete_complementarity_set(K)
- []
-
- TESTS:
-
- The complementarity set of the dual can be obtained by switching the
- components of the complementarity set of the original cone::
-
- sage: set_random_seed()
- sage: K1 = random_cone(max_dim=6)
- sage: K2 = K1.dual()
- sage: expected = [(x,s) for (s,x) in discrete_complementarity_set(K2)]
- sage: actual = discrete_complementarity_set(K1)
- sage: sorted(actual) == sorted(expected)
- True
-
- """
- V = K.lattice().vector_space()
-
- # Convert the rays to vectors so that we can compute inner
- # products.
- xs = [V(x) for x in K.rays()]
- ss = [V(s) for s in K.dual().rays()]
-
- return [(x,s) for x in xs for s in ss if x.inner_product(s) == 0]
-
-
-def LL(K):
- r"""
- Compute the space `\mathbf{LL}` of all Lyapunov-like transformations
- on this cone.
-
- OUTPUT:
-
- A list of matrices forming a basis for the space of all
- Lyapunov-like transformations on the given cone.
-
- EXAMPLES:
-
- The trivial cone has no Lyapunov-like transformations::
-
- sage: L = ToricLattice(0)
- sage: K = Cone([], lattice=L)
- sage: LL(K)
- []
-
- The Lyapunov-like transformations on the nonnegative orthant are
- simply diagonal matrices::
-
- sage: K = Cone([(1,)])
- sage: LL(K)
- [[1]]
-
- sage: K = Cone([(1,0),(0,1)])
- sage: LL(K)
- [
- [1 0] [0 0]
- [0 0], [0 1]
- ]
-
- sage: K = Cone([(1,0,0),(0,1,0),(0,0,1)])
- sage: LL(K)
- [
- [1 0 0] [0 0 0] [0 0 0]
- [0 0 0] [0 1 0] [0 0 0]
- [0 0 0], [0 0 0], [0 0 1]
- ]
-
- Only the identity matrix is Lyapunov-like on the `L^{3}_{1}` and
- `L^{3}_{\infty}` cones [Rudolf et al.]_::
-
- sage: L31 = Cone([(1,0,1), (0,-1,1), (-1,0,1), (0,1,1)])
- sage: LL(L31)
- [
- [1 0 0]
- [0 1 0]
- [0 0 1]
- ]
-
- sage: L3infty = Cone([(0,1,1), (1,0,1), (0,-1,1), (-1,0,1)])
- sage: LL(L3infty)
- [
- [1 0 0]
- [0 1 0]
- [0 0 1]
- ]
-
- If our cone is the entire space, then every transformation on it is
- Lyapunov-like::
-
- sage: K = Cone([(1,0), (-1,0), (0,1), (0,-1)])
- sage: M = MatrixSpace(QQ,2)
- sage: M.basis() == LL(K)
- True
-
- TESTS:
-
- The inner product `\left< L\left(x\right), s \right>` is zero for
- every pair `\left( x,s \right)` in the discrete complementarity set
- of the cone::
-
- sage: set_random_seed()
- sage: K = random_cone(max_dim=8)
- sage: C_of_K = discrete_complementarity_set(K)
- sage: l = [ (L*x).inner_product(s) for (x,s) in C_of_K for L in LL(K) ]
- sage: sum(map(abs, l))
- 0
-
- The Lyapunov-like transformations on a cone and its dual are related
- by transposition, but we're not guaranteed to compute transposed
- elements of `LL\left( K \right)` as our basis for `LL\left( K^{*}
- \right)`
-
- sage: set_random_seed()
- sage: K = random_cone(max_dim=8)
- sage: LL2 = [ L.transpose() for L in LL(K.dual()) ]
- sage: V = VectorSpace( K.lattice().base_field(), K.lattice_dim()^2)
- sage: LL1_vecs = [ V(m.list()) for m in LL(K) ]
- sage: LL2_vecs = [ V(m.list()) for m in LL2 ]
- sage: V.span(LL1_vecs) == V.span(LL2_vecs)
- True
-
- """
- V = K.lattice().vector_space()
-
- C_of_K = discrete_complementarity_set(K)
-
- tensor_products = [ s.tensor_product(x) for (x,s) in C_of_K ]
-
- # Sage doesn't think matrices are vectors, so we have to convert
- # our matrices to vectors explicitly before we can figure out how
- # many are linearly-indepenedent.
- #
- # The space W has the same base ring as V, but dimension
- # dim(V)^2. So it has the same dimension as the space of linear
- # transformations on V. In other words, it's just the right size
- # to create an isomorphism between it and our matrices.
- W = VectorSpace(V.base_ring(), V.dimension()**2)
-
- # Turn our matrices into long vectors...
- vectors = [ W(m.list()) for m in tensor_products ]
-
- # Vector space representation of Lyapunov-like matrices
- # (i.e. vec(L) where L is Luapunov-like).
- LL_vector = W.span(vectors).complement()
-
- # Now construct an ambient MatrixSpace in which to stick our
- # transformations.
- M = MatrixSpace(V.base_ring(), V.dimension())
-
- matrix_basis = [ M(v.list()) for v in LL_vector.basis() ]
-
- return matrix_basis
-