Any cone is basically the same as itself::
- sage: K = random_cone(max_dim = 8)
+ sage: K = random_cone(max_ambient_dim = 8)
sage: _basically_the_same(K, K)
True
After applying an invertible matrix to the rows of a cone, the
result should be basically the same as the cone we started with::
- sage: K1 = random_cone(max_dim = 8)
+ sage: K1 = random_cone(max_ambient_dim = 8)
sage: A = random_matrix(QQ, K1.lattice_dim(), algorithm='unimodular')
sage: K2 = Cone( [ A*r for r in K1.rays() ], lattice=K1.lattice())
sage: _basically_the_same(K1, K2)
The projected cone should always be solid::
sage: set_random_seed()
- sage: K = random_cone(max_dim = 8)
+ sage: K = random_cone(max_ambient_dim = 8)
sage: K_S = _rho(K)
sage: K_S.is_solid()
True
dimension as the space we restricted it to::
sage: set_random_seed()
- sage: K = random_cone(max_dim = 8)
+ sage: K = random_cone(max_ambient_dim = 8)
sage: K_S = _rho(K, K.dual() )
sage: K_S.lattice_dim() == K.dual().dim()
True
This function should not affect the dimension of a cone::
sage: set_random_seed()
- sage: K = random_cone(max_dim = 8)
+ sage: K = random_cone(max_ambient_dim = 8)
sage: K.dim() == _rho(K).dim()
True
Nor should it affect the lineality of a cone::
sage: set_random_seed()
- sage: K = random_cone(max_dim = 8)
+ sage: K = random_cone(max_ambient_dim = 8)
sage: K.lineality() == _rho(K).lineality()
True
increase::
sage: set_random_seed()
- sage: K = random_cone(max_dim = 8)
+ sage: K = random_cone(max_ambient_dim = 8)
sage: K.lineality() >= _rho(K).lineality()
True
sage: K.lineality() >= _rho(K, K.dual()).lineality()
If we do this according to our paper, then the result is proper::
sage: set_random_seed()
- sage: K = random_cone(max_dim = 8, strictly_convex=False, solid=False)
+ sage: K = random_cone(max_ambient_dim = 8,
+ ....: strictly_convex=False,
+ ....: solid=False)
sage: K_S = _rho(K)
sage: K_SP = _rho(K_S.dual()).dual()
sage: K_SP.is_proper()
::
sage: set_random_seed()
- sage: K = random_cone(max_dim = 8, strictly_convex=True, solid=False)
+ sage: K = random_cone(max_ambient_dim = 8,
+ ....: strictly_convex=True,
+ ....: solid=False)
sage: K_S = _rho(K)
sage: K_SP = _rho(K_S.dual()).dual()
sage: K_SP.is_proper()
::
sage: set_random_seed()
- sage: K = random_cone(max_dim = 8, strictly_convex=False, solid=True)
+ sage: K = random_cone(max_ambient_dim = 8,
+ ....: strictly_convex=False,
+ ....: solid=True)
sage: K_S = _rho(K)
sage: K_SP = _rho(K_S.dual()).dual()
sage: K_SP.is_proper()
::
sage: set_random_seed()
- sage: K = random_cone(max_dim = 8, strictly_convex=True, solid=True)
+ sage: K = random_cone(max_ambient_dim = 8,
+ ....: strictly_convex=True,
+ ....: solid=True)
sage: K_S = _rho(K)
sage: K_SP = _rho(K_S.dual()).dual()
sage: K_SP.is_proper()
it. The operation of dual-taking should then commute with rho::
sage: set_random_seed()
- sage: J = random_cone(max_dim = 8, solid=False, strictly_convex=False)
+ sage: J = random_cone(max_ambient_dim = 8,
+ ....: solid=False,
+ ....: strictly_convex=False)
sage: K = Cone(random_sublist(J.rays(), 0.5), lattice=J.lattice())
sage: K_W_star = _rho(K, J).dual()
sage: K_star_W = _rho(K.dual(), J)
::
sage: set_random_seed()
- sage: J = random_cone(max_dim = 8, solid=True, strictly_convex=False)
+ sage: J = random_cone(max_ambient_dim = 8,
+ ....: solid=True,
+ ....: strictly_convex=False)
sage: K = Cone(random_sublist(J.rays(), 0.5), lattice=J.lattice())
sage: K_W_star = _rho(K, J).dual()
sage: K_star_W = _rho(K.dual(), J)
::
sage: set_random_seed()
- sage: J = random_cone(max_dim = 8, solid=False, strictly_convex=True)
+ sage: J = random_cone(max_ambient_dim = 8,
+ ....: solid=False,
+ ....: strictly_convex=True)
sage: K = Cone(random_sublist(J.rays(), 0.5), lattice=J.lattice())
sage: K_W_star = _rho(K, J).dual()
sage: K_star_W = _rho(K.dual(), J)
::
sage: set_random_seed()
- sage: J = random_cone(max_dim = 8, solid=True, strictly_convex=True)
+ sage: J = random_cone(max_ambient_dim = 8,
+ ....: solid=True,
+ ....: strictly_convex=True)
sage: K = Cone(random_sublist(J.rays(), 0.5), lattice=J.lattice())
sage: K_W_star = _rho(K, J).dual()
sage: K_star_W = _rho(K.dual(), J)
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.
+ The complementarity set of a cone is the set of all orthogonal pairs
+ `(x,s)` such that `x` is in the cone, and `s` is in its dual. The
+ discrete complementarity set is a subset of the complementarity set
+ where `x` and `s` are required to be generators of their respective
+ cones.
+
+ For polyhedral cones, the discrete complementarity set is always
+ finite.
OUTPUT:
A list of pairs `(x,s)` such that,
+ * Both `x` and `s` are vectors (not rays).
* `x` is a generator of this cone.
* `s` is a generator of this cone's dual.
* `x` and `s` are orthogonal.
+ REFERENCES:
+
+ .. [Orlitzky/Gowda] M. Orlitzky and M. S. Gowda. The Lyapunov Rank of an
+ Improper Cone. Work in-progress.
+
EXAMPLES:
The discrete complementarity set of the nonnegative orthant consists
sage: discrete_complementarity_set(K)
[]
+ Likewise when this cone is trivial (its dual is the entire space)::
+
+ sage: L = ToricLattice(0)
+ sage: K = Cone([], ToricLattice(0))
+ 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: K1 = random_cone(max_ambient_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
+ The pairs in the discrete complementarity set are in fact
+ complementary::
+
+ sage: set_random_seed()
+ sage: K = random_cone(max_ambient_dim=6)
+ sage: dcs = discrete_complementarity_set(K)
+ sage: sum([x.inner_product(s).abs() for (x,s) in dcs])
+ 0
+
"""
V = K.lattice().vector_space()
- # Convert the rays to vectors so that we can compute inner
- # products.
+ # Convert rays to vectors so that we can compute inner products.
xs = [V(x) for x in K.rays()]
+
+ # We also convert the generators of the dual cone so that we
+ # return pairs of vectors and not (vector, ray) pairs.
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]
of the cone::
sage: set_random_seed()
- sage: K = random_cone(max_dim=8)
+ sage: K = random_cone(max_ambient_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))
\right)`
sage: set_random_seed()
- sage: K = random_cone(max_dim=8)
+ sage: K = random_cone(max_ambient_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) ]
[Rudolf et al.]_::
sage: set_random_seed()
- sage: K1 = random_cone(max_dim=8, strictly_convex=True, solid=True)
- sage: K2 = random_cone(max_dim=8, strictly_convex=True, solid=True)
+ sage: K1 = random_cone(max_ambient_dim=8,
+ ....: strictly_convex=True,
+ ....: solid=True)
+ sage: K2 = random_cone(max_ambient_dim=8,
+ ....: strictly_convex=True,
+ ....: solid=True)
sage: K = K1.cartesian_product(K2)
sage: lyapunov_rank(K) == lyapunov_rank(K1) + lyapunov_rank(K2)
True
The Lyapunov rank is invariant under a linear isomorphism
[Orlitzky/Gowda]_::
- sage: K1 = random_cone(max_dim = 8)
+ sage: K1 = random_cone(max_ambient_dim = 8)
sage: A = random_matrix(QQ, K1.lattice_dim(), algorithm='unimodular')
sage: K2 = Cone( [ A*r for r in K1.rays() ], lattice=K1.lattice())
sage: lyapunov_rank(K1) == lyapunov_rank(K2)
Just to be sure, test a few more::
- sage: K1 = random_cone(max_dim=8, strictly_convex=True, solid=True)
+ sage: K1 = random_cone(max_ambient_dim=8,
+ ....: strictly_convex=True,
+ ....: solid=True)
sage: A = random_matrix(QQ, K1.lattice_dim(), algorithm='unimodular')
sage: K2 = Cone( [ A*r for r in K1.rays() ], lattice=K1.lattice())
sage: lyapunov_rank(K1) == lyapunov_rank(K2)
::
- sage: K1 = random_cone(max_dim=8, strictly_convex=True, solid=False)
+ sage: K1 = random_cone(max_ambient_dim=8,
+ ....: strictly_convex=True,
+ ....: solid=False)
sage: A = random_matrix(QQ, K1.lattice_dim(), algorithm='unimodular')
sage: K2 = Cone( [ A*r for r in K1.rays() ], lattice=K1.lattice())
sage: lyapunov_rank(K1) == lyapunov_rank(K2)
::
- sage: K1 = random_cone(max_dim=8, strictly_convex=False, solid=True)
+ sage: K1 = random_cone(max_ambient_dim=8,
+ ....: strictly_convex=False,
+ ....: solid=True)
sage: A = random_matrix(QQ, K1.lattice_dim(), algorithm='unimodular')
sage: K2 = Cone( [ A*r for r in K1.rays() ], lattice=K1.lattice())
sage: lyapunov_rank(K1) == lyapunov_rank(K2)
::
- sage: K1 = random_cone(max_dim=8, strictly_convex=False, solid=False)
+ sage: K1 = random_cone(max_ambient_dim=8,
+ ....: strictly_convex=False,
+ ....: solid=False)
sage: A = random_matrix(QQ, K1.lattice_dim(), algorithm='unimodular')
sage: K2 = Cone( [ A*r for r in K1.rays() ], lattice=K1.lattice())
sage: lyapunov_rank(K1) == lyapunov_rank(K2)
itself [Rudolf et al.]_::
sage: set_random_seed()
- sage: K = random_cone(max_dim=8)
+ sage: K = random_cone(max_ambient_dim=8)
sage: lyapunov_rank(K) == lyapunov_rank(K.dual())
True
Make sure we exercise the non-strictly-convex/non-solid case::
sage: set_random_seed()
- sage: K = random_cone(max_dim=8, strictly_convex=False, solid=False)
+ sage: K = random_cone(max_ambient_dim=8,
+ ....: strictly_convex=False,
+ ....: solid=False)
sage: lyapunov_rank(K) == lyapunov_rank(K.dual())
True
Let's check the other permutations as well, just to be sure::
sage: set_random_seed()
- sage: K = random_cone(max_dim=8, strictly_convex=False, solid=True)
+ sage: K = random_cone(max_ambient_dim=8,
+ ....: strictly_convex=False,
+ ....: solid=True)
sage: lyapunov_rank(K) == lyapunov_rank(K.dual())
True
::
sage: set_random_seed()
- sage: K = random_cone(max_dim=8, strictly_convex=True, solid=False)
+ sage: K = random_cone(max_ambient_dim=8,
+ ....: strictly_convex=True,
+ ....: solid=False)
sage: lyapunov_rank(K) == lyapunov_rank(K.dual())
True
::
sage: set_random_seed()
- sage: K = random_cone(max_dim=8, strictly_convex=True, solid=True)
+ sage: K = random_cone(max_ambient_dim=8,
+ ....: strictly_convex=True,
+ ....: solid=True)
sage: lyapunov_rank(K) == lyapunov_rank(K.dual())
True
the Lyapunov rank of the trivial cone will be zero::
sage: set_random_seed()
- sage: K = random_cone(max_dim=8, strictly_convex=True, solid=True)
+ sage: K = random_cone(max_ambient_dim=8,
+ ....: strictly_convex=True,
+ ....: solid=True)
sage: b = lyapunov_rank(K)
sage: n = K.lattice_dim()
sage: (n == 0 or 1 <= b) and b <= n
Lyapunov rank `n-1` in `n` dimensions::
sage: set_random_seed()
- sage: K = random_cone(max_dim=8)
+ sage: K = random_cone(max_ambient_dim=8)
sage: b = lyapunov_rank(K)
sage: n = K.lattice_dim()
sage: b == n-1
reduced to that of a proper cone [Orlitzky/Gowda]_::
sage: set_random_seed()
- sage: K = random_cone(max_dim=8)
+ sage: K = random_cone(max_ambient_dim=8)
sage: actual = lyapunov_rank(K)
sage: K_S = _rho(K)
sage: K_SP = _rho(K_S.dual()).dual()
The Lyapunov rank of a proper cone is just the dimension of ``LL(K)``::
sage: set_random_seed()
- sage: K = random_cone(max_dim=8, strictly_convex=True, solid=True)
+ sage: K = random_cone(max_ambient_dim=8,
+ ....: strictly_convex=True,
+ ....: solid=True)
sage: lyapunov_rank(K) == len(LL(K))
True
just increase our confidence that the reduction scheme works::
sage: set_random_seed()
- sage: K = random_cone(max_dim=8, strictly_convex=True, solid=False)
+ sage: K = random_cone(max_ambient_dim=8,
+ ....: strictly_convex=True,
+ ....: solid=False)
sage: lyapunov_rank(K) == len(LL(K))
True
::
sage: set_random_seed()
- sage: K = random_cone(max_dim=8, strictly_convex=False, solid=True)
+ sage: K = random_cone(max_ambient_dim=8,
+ ....: strictly_convex=False,
+ ....: solid=True)
sage: lyapunov_rank(K) == len(LL(K))
True
::
sage: set_random_seed()
- sage: K = random_cone(max_dim=8, strictly_convex=False, solid=False)
+ sage: K = random_cone(max_ambient_dim=8,
+ ....: strictly_convex=False,
+ ....: solid=False)
sage: lyapunov_rank(K) == len(LL(K))
True
Test Theorem 3 in [Orlitzky/Gowda]_::
sage: set_random_seed()
- sage: K = random_cone(max_dim=8, strictly_convex=True, solid=True)
+ sage: K = random_cone(max_ambient_dim=8,
+ ....: strictly_convex=True,
+ ....: solid=True)
sage: L = ToricLattice(K.lattice_dim() + 1)
sage: K = Cone([ r.list() + [0] for r in K.rays() ], lattice=L)
sage: lyapunov_rank(K) >= K.lattice_dim()