X-Git-Url: http://gitweb.michael.orlitzky.com/?a=blobdiff_plain;f=mjo%2Fcone%2Fcone.py;h=b4d2be0d73e10ed9f58d1189c84ed4bec3d0ad94;hb=db016cc07b5f0cb45a96022eb468e3080786078b;hp=1d04084fe2f971978af0ff42e75a03f265fa6690;hpb=d2f0486c20951b059b5d46c67e8a6cff1ce43092;p=sage.d.git diff --git a/mjo/cone/cone.py b/mjo/cone/cone.py index 1d04084..b4d2be0 100644 --- a/mjo/cone/cone.py +++ b/mjo/cone/cone.py @@ -311,8 +311,7 @@ def discrete_complementarity_set(K): def LL(K): r""" - Compute the space `\mathbf{LL}` of all Lyapunov-like transformations - on this cone. + Compute a basis of Lyapunov-like transformations on this cone. OUTPUT: @@ -750,3 +749,154 @@ def is_lyapunov_like(L,K): """ return all([(L*x).inner_product(s) == 0 for (x,s) in discrete_complementarity_set(K)]) + + +def random_element(K): + r""" + Return a random element of ``K`` from its ambient vector space. + + ALGORITHM: + + The cone ``K`` is specified in terms of its generators, so that + ``K`` is equal to the convex conic combination of those generators. + To choose a random element of ``K``, we assign random nonnegative + coefficients to each generator of ``K`` and construct a new vector + from the scaled rays. + + A vector, rather than a ray, is returned so that the element may + have non-integer coordinates. Thus the element may have an + arbitrarily small norm. + + EXAMPLES: + + A random element of the trivial cone is zero:: + + sage: set_random_seed() + sage: K = Cone([], ToricLattice(0)) + sage: random_element(K) + () + sage: K = Cone([(0,)]) + sage: random_element(K) + (0) + sage: K = Cone([(0,0)]) + sage: random_element(K) + (0, 0) + sage: K = Cone([(0,0,0)]) + sage: random_element(K) + (0, 0, 0) + + TESTS: + + Any cone should contain an element of itself:: + + sage: set_random_seed() + sage: K = random_cone(max_rays = 8) + sage: K.contains(random_element(K)) + True + + """ + V = K.lattice().vector_space() + F = V.base_ring() + coefficients = [ F.random_element().abs() for i in range(K.nrays()) ] + vector_gens = map(V, K.rays()) + scaled_gens = [ coefficients[i]*vector_gens[i] + for i in range(len(vector_gens)) ] + + # Make sure we return a vector. Without the coercion, we might + # return ``0`` when ``K`` has no rays. + v = V(sum(scaled_gens)) + return v + + +def positive_operators(K): + r""" + Compute generators of the cone of positive operators on this cone. + + OUTPUT: + + A list of `n`-by-``n`` matrices where ``n == K.lattice_dim()``. + Each matrix ``P`` in the list should have the property that ``P*x`` + is an element of ``K`` whenever ``x`` is an element of + ``K``. Moreover, any nonnegative linear combination of these + matrices shares the same property. + + EXAMPLES: + + The trivial cone in a trivial space has no positive operators:: + + sage: K = Cone([], ToricLattice(0)) + sage: positive_operators(K) + [] + + Positive operators on the nonnegative orthant are nonnegative matrices:: + + sage: K = Cone([(1,)]) + sage: positive_operators(K) + [[1]] + + sage: K = Cone([(1,0),(0,1)]) + sage: positive_operators(K) + [ + [1 0] [0 1] [0 0] [0 0] + [0 0], [0 0], [1 0], [0 1] + ] + + Every operator is positive on the ambient vector space:: + + sage: K = Cone([(1,),(-1,)]) + sage: K.is_full_space() + True + sage: positive_operators(K) + [[1], [-1]] + + sage: K = Cone([(1,0),(-1,0),(0,1),(0,-1)]) + sage: K.is_full_space() + True + sage: positive_operators(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] + ] + + TESTS: + + 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: all([K.contains(p*x) for p in pi_of_k for x in K.rays()]) + True + + """ + V = K.lattice().vector_space() + + # 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) + + G1 = [ V(x) for x in K.rays() ] + G2 = [ V(s) for s in K.dual().rays() ] + + tensor_products = [ s.tensor_product(x) for x in G1 for s in G2 ] + + # Turn our matrices into long vectors... + vectors = [ W(m.list()) for m 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) + + # 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()) + + return [ M(v.list()) for v in pi_cone.rays() ]