]>
gitweb.michael.orlitzky.com - sage.d.git/blob - mjo/cone/cone.py
3 def is_lyapunov_like(L
,K
):
5 Determine whether or not ``L`` is Lyapunov-like on ``K``.
7 We say that ``L`` is Lyapunov-like on ``K`` if `\left\langle
8 L\left\lparenx\right\rparen,s\right\rangle = 0` for all pairs
9 `\left\langle x,s \right\rangle` in the complementarity set of
10 ``K``. It is known [Orlitzky]_ that this property need only be
11 checked for generators of ``K`` and its dual.
15 - ``L`` -- A linear transformation or matrix.
17 - ``K`` -- A polyhedral closed convex cone.
21 ``True`` if it can be proven that ``L`` is Lyapunov-like on ``K``,
22 and ``False`` otherwise.
26 If this function returns ``True``, then ``L`` is Lyapunov-like
27 on ``K``. However, if ``False`` is returned, that could mean one
28 of two things. The first is that ``L`` is definitely not
29 Lyapunov-like on ``K``. The second is more of an "I don't know"
30 answer, returned (for example) if we cannot prove that an inner
35 M. Orlitzky. The Lyapunov rank of an improper cone.
36 http://www.optimization-online.org/DB_HTML/2015/10/5135.html
40 The identity is always Lyapunov-like in a nontrivial space::
42 sage: set_random_seed()
43 sage: K = random_cone(min_ambient_dim = 1, max_rays = 8)
44 sage: L = identity_matrix(K.lattice_dim())
45 sage: is_lyapunov_like(L,K)
48 As is the "zero" transformation::
50 sage: K = random_cone(min_ambient_dim = 1, max_rays = 5)
51 sage: R = K.lattice().vector_space().base_ring()
52 sage: L = zero_matrix(R, K.lattice_dim())
53 sage: is_lyapunov_like(L,K)
56 Everything in ``K.lyapunov_like_basis()`` should be Lyapunov-like
59 sage: K = random_cone(min_ambient_dim = 1, max_rays = 5)
60 sage: all([ is_lyapunov_like(L,K) for L in K.lyapunov_like_basis() ])
64 return all([(L
*x
).inner_product(s
) == 0
65 for (x
,s
) in K
.discrete_complementarity_set()])
68 def random_element(K
):
70 Return a random element of ``K`` from its ambient vector space.
74 The cone ``K`` is specified in terms of its generators, so that
75 ``K`` is equal to the convex conic combination of those generators.
76 To choose a random element of ``K``, we assign random nonnegative
77 coefficients to each generator of ``K`` and construct a new vector
80 A vector, rather than a ray, is returned so that the element may
81 have non-integer coordinates. Thus the element may have an
82 arbitrarily small norm.
86 A random element of the trivial cone is zero::
88 sage: set_random_seed()
89 sage: K = Cone([], ToricLattice(0))
90 sage: random_element(K)
92 sage: K = Cone([(0,)])
93 sage: random_element(K)
95 sage: K = Cone([(0,0)])
96 sage: random_element(K)
98 sage: K = Cone([(0,0,0)])
99 sage: random_element(K)
104 Any cone should contain an element of itself::
106 sage: set_random_seed()
107 sage: K = random_cone(max_rays = 8)
108 sage: K.contains(random_element(K))
112 V
= K
.lattice().vector_space()
114 coefficients
= [ F
.random_element().abs() for i
in range(K
.nrays()) ]
115 vector_gens
= map(V
, K
.rays())
116 scaled_gens
= [ coefficients
[i
]*vector_gens
[i
]
117 for i
in range(len(vector_gens
)) ]
119 # Make sure we return a vector. Without the coercion, we might
120 # return ``0`` when ``K`` has no rays.
121 v
= V(sum(scaled_gens
))
125 def positive_operators(K
):
127 Compute generators of the cone of positive operators on this cone.
131 A list of `n`-by-``n`` matrices where ``n == K.lattice_dim()``.
132 Each matrix ``P`` in the list should have the property that ``P*x``
133 is an element of ``K`` whenever ``x`` is an element of
134 ``K``. Moreover, any nonnegative linear combination of these
135 matrices shares the same property.
139 The trivial cone in a trivial space has no positive operators::
141 sage: K = Cone([], ToricLattice(0))
142 sage: positive_operators(K)
145 Positive operators on the nonnegative orthant are nonnegative matrices::
147 sage: K = Cone([(1,)])
148 sage: positive_operators(K)
151 sage: K = Cone([(1,0),(0,1)])
152 sage: positive_operators(K)
154 [1 0] [0 1] [0 0] [0 0]
155 [0 0], [0 0], [1 0], [0 1]
158 Every operator is positive on the ambient vector space::
160 sage: K = Cone([(1,),(-1,)])
161 sage: K.is_full_space()
163 sage: positive_operators(K)
166 sage: K = Cone([(1,0),(-1,0),(0,1),(0,-1)])
167 sage: K.is_full_space()
169 sage: positive_operators(K)
171 [1 0] [-1 0] [0 1] [ 0 -1] [0 0] [ 0 0] [0 0] [ 0 0]
172 [0 0], [ 0 0], [0 0], [ 0 0], [1 0], [-1 0], [0 1], [ 0 -1]
177 A positive operator on a cone should send its generators into the cone::
179 sage: K = random_cone(max_ambient_dim = 6)
180 sage: pi_of_K = positive_operators(K)
181 sage: all([K.contains(p*x) for p in pi_of_K for x in K.rays()])
185 # Sage doesn't think matrices are vectors, so we have to convert
186 # our matrices to vectors explicitly before we can figure out how
187 # many are linearly-indepenedent.
189 # The space W has the same base ring as V, but dimension
190 # dim(V)^2. So it has the same dimension as the space of linear
191 # transformations on V. In other words, it's just the right size
192 # to create an isomorphism between it and our matrices.
193 V
= K
.lattice().vector_space()
194 W
= VectorSpace(V
.base_ring(), V
.dimension()**2)
196 tensor_products
= [ s
.tensor_product(x
) for x
in K
for s
in K
.dual() ]
198 # Turn our matrices into long vectors...
199 vectors
= [ W(m
.list()) for m
in tensor_products
]
201 # Create the *dual* cone of the positive operators, expressed as
203 L
= ToricLattice(W
.dimension())
204 pi_dual
= Cone(vectors
, lattice
=L
)
206 # Now compute the desired cone from its dual...
207 pi_cone
= pi_dual
.dual()
209 # And finally convert its rays back to matrix representations.
210 M
= MatrixSpace(V
.base_ring(), V
.dimension())
212 return [ M(v
.list()) for v
in pi_cone
.rays() ]
215 def Z_transformations(K
):
217 Compute generators of the cone of Z-transformations on this cone.
221 A list of `n`-by-``n`` matrices where ``n == K.lattice_dim()``.
222 Each matrix ``L`` in the list should have the property that
223 ``(L*x).inner_product(s) <= 0`` whenever ``(x,s)`` is an element the
224 discrete complementarity set of ``K``. Moreover, any nonnegative
225 linear combination of these matrices shares the same property.
229 Z-transformations on the nonnegative orthant are just Z-matrices.
230 That is, matrices whose off-diagonal elements are nonnegative::
232 sage: K = Cone([(1,0),(0,1)])
233 sage: Z_transformations(K)
235 [ 0 -1] [ 0 0] [-1 0] [1 0] [ 0 0] [0 0]
236 [ 0 0], [-1 0], [ 0 0], [0 0], [ 0 -1], [0 1]
238 sage: K = Cone([(1,0,0,0),(0,1,0,0),(0,0,1,0),(0,0,0,1)])
239 sage: all([ z[i][j] <= 0 for z in Z_transformations(K)
240 ....: for i in range(z.nrows())
241 ....: for j in range(z.ncols())
245 The trivial cone in a trivial space has no Z-transformations::
247 sage: K = Cone([], ToricLattice(0))
248 sage: Z_transformations(K)
251 Z-transformations on a subspace are Lyapunov-like and vice-versa::
253 sage: K = Cone([(1,0),(-1,0),(0,1),(0,-1)])
254 sage: K.is_full_space()
256 sage: lls = span([ vector(l.list()) for l in K.lyapunov_like_basis() ])
257 sage: zs = span([ vector(z.list()) for z in Z_transformations(K) ])
263 The Z-property is possessed by every Z-transformation::
265 sage: set_random_seed()
266 sage: K = random_cone(max_ambient_dim = 6)
267 sage: Z_of_K = Z_transformations(K)
268 sage: dcs = K.discrete_complementarity_set()
269 sage: all([(z*x).inner_product(s) <= 0 for z in Z_of_K
270 ....: for (x,s) in dcs])
273 The lineality space of Z is LL::
275 sage: set_random_seed()
276 sage: K = random_cone(min_ambient_dim = 1, max_ambient_dim = 6)
277 sage: lls = span([ vector(l.list()) for l in K.lyapunov_like_basis() ])
278 sage: z_cone = Cone([ z.list() for z in Z_transformations(K) ])
279 sage: z_cone.linear_subspace() == lls
283 # Sage doesn't think matrices are vectors, so we have to convert
284 # our matrices to vectors explicitly before we can figure out how
285 # many are linearly-indepenedent.
287 # The space W has the same base ring as V, but dimension
288 # dim(V)^2. So it has the same dimension as the space of linear
289 # transformations on V. In other words, it's just the right size
290 # to create an isomorphism between it and our matrices.
291 V
= K
.lattice().vector_space()
292 W
= VectorSpace(V
.base_ring(), V
.dimension()**2)
294 C_of_K
= K
.discrete_complementarity_set()
295 tensor_products
= [ s
.tensor_product(x
) for (x
,s
) in C_of_K
]
297 # Turn our matrices into long vectors...
298 vectors
= [ W(m
.list()) for m
in tensor_products
]
300 # Create the *dual* cone of the cross-positive operators,
301 # expressed as long vectors..
302 L
= ToricLattice(W
.dimension())
303 Sigma_dual
= Cone(vectors
, lattice
=L
)
305 # Now compute the desired cone from its dual...
306 Sigma_cone
= Sigma_dual
.dual()
308 # And finally convert its rays back to matrix representations.
309 # But first, make them negative, so we get Z-transformations and
310 # not cross-positive ones.
311 M
= MatrixSpace(V
.base_ring(), V
.dimension())
313 return [ -M(v
.list()) for v
in Sigma_cone
.rays() ]