for (x,s) in K.discrete_complementarity_set()])
-def motzkin_decomposition(K):
- r"""
- Return the pair of components in the Motzkin decomposition of this cone.
-
- Every convex cone is the direct sum of a strictly convex cone and a
- linear subspace [Stoer-Witzgall]_. Return a pair ``(P,S)`` of cones
- such that ``P`` is strictly convex, ``S`` is a subspace, and ``K``
- is the direct sum of ``P`` and ``S``.
-
- .. NOTE::
-
- The name "Motzkin decomposition" is not standard. The result
- is usually stated as the "decomposition theorem", or "cone
- decomposition theorem."
-
- OUTPUT:
-
- An ordered pair ``(P,S)`` of closed convex polyhedral cones where
- ``P`` is strictly convex, ``S`` is a subspace, and ``K`` is the
- direct sum of ``P`` and ``S``.
-
- REFERENCES:
-
- .. [Stoer-Witzgall] J. Stoer and C. Witzgall. Convexity and
- Optimization in Finite Dimensions I. Springer-Verlag, New
- York, 1970.
-
- EXAMPLES:
-
- The nonnegative orthant is strictly convex, so it is its own
- strictly convex component and its subspace component is trivial::
-
- sage: K = Cone([(1,0,0),(0,1,0),(0,0,1)])
- sage: (P,S) = motzkin_decomposition(K)
- sage: K.is_equivalent(P)
- True
- sage: S.is_trivial()
- True
-
- Likewise, full spaces are their own subspace components::
-
- sage: K = Cone([(1,0),(-1,0),(0,1),(0,-1)])
- sage: K.is_full_space()
- True
- sage: (P,S) = motzkin_decomposition(K)
- sage: K.is_equivalent(S)
- True
- sage: P.is_trivial()
- True
-
- TESTS:
-
- A random point in the cone should belong to either the strictly
- convex component or the subspace component. If the point is nonzero,
- it cannot be in both::
-
- sage: set_random_seed()
- sage: K = random_cone(max_ambient_dim=8)
- sage: (P,S) = motzkin_decomposition(K)
- sage: x = K.random_element(ring=QQ)
- sage: P.contains(x) or S.contains(x)
- True
- sage: x.is_zero() or (P.contains(x) != S.contains(x))
- True
-
- The strictly convex component should always be strictly convex, and
- the subspace component should always be a subspace::
-
- sage: set_random_seed()
- sage: K = random_cone(max_ambient_dim=8)
- sage: (P,S) = motzkin_decomposition(K)
- sage: P.is_strictly_convex()
- True
- sage: S.lineality() == S.dim()
- True
-
- A strictly convex cone should be equal to its strictly convex component::
-
- sage: set_random_seed()
- sage: K = random_cone(max_ambient_dim=8, strictly_convex=True)
- sage: (P,_) = motzkin_decomposition(K)
- sage: K.is_equivalent(P)
- True
-
- The generators of the components are obtained from orthogonal
- projections of the original generators [Stoer-Witzgall]_::
-
- sage: set_random_seed()
- sage: K = random_cone(max_ambient_dim=8)
- sage: (P,S) = motzkin_decomposition(K)
- sage: A = S.linear_subspace().complement().matrix()
- sage: proj_S_perp = A.transpose() * (A*A.transpose()).inverse() * A
- sage: expected_P = Cone([ proj_S_perp*g for g in K ], K.lattice())
- sage: P.is_equivalent(expected_P)
- True
- sage: A = S.linear_subspace().matrix()
- sage: proj_S = A.transpose() * (A*A.transpose()).inverse() * A
- sage: expected_S = Cone([ proj_S*g for g in K ], K.lattice())
- sage: S.is_equivalent(expected_S)
- True
- """
- # The lines() method only returns one generator per line. For a true
- # line, we also need a generator pointing in the opposite direction.
- S_gens = [ direction*gen for direction in [1,-1] for gen in K.lines() ]
- S = Cone(S_gens, K.lattice(), check=False)
-
- # Since ``S`` is a subspace, the rays of its dual generate its
- # orthogonal complement.
- S_perp = Cone(S.dual(), K.lattice(), check=False)
- P = K.intersection(S_perp)
-
- return (P,S)
-
-
def positive_operator_gens(K):
r"""
Compute generators of the cone of positive operators on this cone.