The identity is always Lyapunov-like in a nontrivial space::
sage: set_random_seed()
- sage: K = random_cone(min_ambient_dim = 1, max_rays = 8)
+ sage: K = random_cone(min_ambient_dim = 1, max_ambient_dim = 8)
sage: L = identity_matrix(K.lattice_dim())
sage: is_lyapunov_like(L,K)
True
As is the "zero" transformation::
- sage: K = random_cone(min_ambient_dim = 1, max_rays = 5)
+ sage: K = random_cone(min_ambient_dim = 1, max_ambient_dim = 8)
sage: R = K.lattice().vector_space().base_ring()
sage: L = zero_matrix(R, K.lattice_dim())
sage: is_lyapunov_like(L,K)
Everything in ``K.lyapunov_like_basis()`` should be Lyapunov-like
on ``K``::
- sage: K = random_cone(min_ambient_dim = 1, max_rays = 5)
+ sage: K = random_cone(min_ambient_dim = 1, max_ambient_dim = 6)
sage: all([ is_lyapunov_like(L,K) for L in K.lyapunov_like_basis() ])
True
return v
-def positive_operators(K):
+def positive_operator_gens(K):
r"""
Compute generators of the cone of positive operators on this cone.
The trivial cone in a trivial space has no positive operators::
sage: K = Cone([], ToricLattice(0))
- sage: positive_operators(K)
+ sage: positive_operator_gens(K)
[]
Positive operators on the nonnegative orthant are nonnegative matrices::
sage: K = Cone([(1,)])
- sage: positive_operators(K)
+ sage: positive_operator_gens(K)
[[1]]
sage: K = Cone([(1,0),(0,1)])
- sage: positive_operators(K)
+ sage: positive_operator_gens(K)
[
[1 0] [0 1] [0 0] [0 0]
[0 0], [0 0], [1 0], [0 1]
sage: K = Cone([(1,),(-1,)])
sage: K.is_full_space()
True
- sage: positive_operators(K)
+ sage: positive_operator_gens(K)
[[1], [-1]]
sage: K = Cone([(1,0),(-1,0),(0,1),(0,-1)])
sage: K.is_full_space()
True
- sage: positive_operators(K)
+ sage: positive_operator_gens(K)
[
[1 0] [-1 0] [0 1] [ 0 -1] [0 0] [ 0 0] [0 0] [ 0 0]
[0 0], [ 0 0], [0 0], [ 0 0], [1 0], [-1 0], [0 1], [ 0 -1]
A positive operator on a cone should send its generators into the cone::
sage: K = random_cone(max_ambient_dim = 6)
- sage: pi_of_K = positive_operators(K)
+ sage: pi_of_K = positive_operator_gens(K)
sage: all([K.contains(p*x) for p in pi_of_K for x in K.rays()])
True
+ The dimension of the cone of positive operators is given by the
+ corollary in my paper::
+
+ sage: K = random_cone(max_ambient_dim = 6)
+ sage: n = K.lattice_dim()
+ sage: m = K.dim()
+ sage: l = K.lineality()
+ sage: pi_of_K = positive_operator_gens(K)
+ sage: actual = Cone([p.list() for p in pi_of_K]).dim()
+ sage: expected = n**2 - l*(n - l) - (n - m)*m
+ sage: actual == expected
+ True
+
"""
- # 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.
- V = K.lattice().vector_space()
- W = VectorSpace(V.base_ring(), V.dimension()**2)
+ # Matrices are not vectors in Sage, so we have to convert them
+ # to vectors explicitly before we can find a basis. We need these
+ # two values to construct the appropriate "long vector" space.
+ F = K.lattice().base_field()
+ n = K.lattice_dim()
tensor_products = [ s.tensor_product(x) for x in K for s in K.dual() ]
- # Turn our matrices into long vectors...
- vectors = [ W(m.list()) for m in tensor_products ]
+ # Convert those tensor products to long vectors.
+ W = VectorSpace(F, n**2)
+ vectors = [ W(tp.list()) for tp in tensor_products ]
# Create the *dual* cone of the positive operators, expressed as
# long vectors..
- L = ToricLattice(W.dimension())
- pi_dual = Cone(vectors, lattice=L)
+ pi_dual = Cone(vectors, ToricLattice(W.dimension()))
# Now compute the desired cone from its dual...
pi_cone = pi_dual.dual()
# And finally convert its rays back to matrix representations.
- M = MatrixSpace(V.base_ring(), V.dimension())
-
+ M = MatrixSpace(F, n)
return [ M(v.list()) for v in pi_cone.rays() ]
-def Z_transformations(K):
+def Z_transformation_gens(K):
r"""
Compute generators of the cone of Z-transformations on this cone.
That is, matrices whose off-diagonal elements are nonnegative::
sage: K = Cone([(1,0),(0,1)])
- sage: Z_transformations(K)
+ sage: Z_transformation_gens(K)
[
[ 0 -1] [ 0 0] [-1 0] [1 0] [ 0 0] [0 0]
[ 0 0], [-1 0], [ 0 0], [0 0], [ 0 -1], [0 1]
]
sage: K = Cone([(1,0,0,0),(0,1,0,0),(0,0,1,0),(0,0,0,1)])
- sage: all([ z[i][j] <= 0 for z in Z_transformations(K)
+ sage: all([ z[i][j] <= 0 for z in Z_transformation_gens(K)
....: for i in range(z.nrows())
....: for j in range(z.ncols())
....: if i != j ])
The trivial cone in a trivial space has no Z-transformations::
sage: K = Cone([], ToricLattice(0))
- sage: Z_transformations(K)
+ sage: Z_transformation_gens(K)
[]
Z-transformations on a subspace are Lyapunov-like and vice-versa::
sage: K.is_full_space()
True
sage: lls = span([ vector(l.list()) for l in K.lyapunov_like_basis() ])
- sage: zs = span([ vector(z.list()) for z in Z_transformations(K) ])
+ sage: zs = span([ vector(z.list()) for z in Z_transformation_gens(K) ])
sage: zs == lls
True
sage: set_random_seed()
sage: K = random_cone(max_ambient_dim = 6)
- sage: Z_of_K = Z_transformations(K)
+ sage: Z_of_K = Z_transformation_gens(K)
sage: dcs = K.discrete_complementarity_set()
sage: all([(z*x).inner_product(s) <= 0 for z in Z_of_K
....: for (x,s) in dcs])
sage: set_random_seed()
sage: K = random_cone(min_ambient_dim = 1, max_ambient_dim = 6)
sage: lls = span([ vector(l.list()) for l in K.lyapunov_like_basis() ])
- sage: z_cone = Cone([ z.list() for z in Z_transformations(K) ])
+ sage: z_cone = Cone([ z.list() for z in Z_transformation_gens(K) ])
sage: z_cone.linear_subspace() == lls
True
"""
- # 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.
- V = K.lattice().vector_space()
- W = VectorSpace(V.base_ring(), V.dimension()**2)
+ # Matrices are not vectors in Sage, so we have to convert them
+ # to vectors explicitly before we can find a basis. We need these
+ # two values to construct the appropriate "long vector" space.
+ F = K.lattice().base_field()
+ n = K.lattice_dim()
- C_of_K = K.discrete_complementarity_set()
- tensor_products = [ s.tensor_product(x) for (x,s) in C_of_K ]
+ # These tensor products contain generators for the dual cone of
+ # the cross-positive transformations.
+ tensor_products = [ s.tensor_product(x)
+ for (x,s) in K.discrete_complementarity_set() ]
# Turn our matrices into long vectors...
+ W = VectorSpace(F, n**2)
vectors = [ W(m.list()) for m in tensor_products ]
# Create the *dual* cone of the cross-positive operators,
# expressed as long vectors..
- L = ToricLattice(W.dimension())
- Sigma_dual = Cone(vectors, lattice=L)
+ Sigma_dual = Cone(vectors, lattice=ToricLattice(W.dimension()))
# Now compute the desired cone from its dual...
Sigma_cone = Sigma_dual.dual()
# And finally convert its rays back to matrix representations.
# But first, make them negative, so we get Z-transformations and
# not cross-positive ones.
- M = MatrixSpace(V.base_ring(), V.dimension())
-
+ M = MatrixSpace(F, n)
return [ -M(v.list()) for v in Sigma_cone.rays() ]