from sage.all import *
-def basically_the_same(K1,K2):
+def _basically_the_same(K1, K2):
r"""
+ Test whether or not ``K1`` and ``K2`` are "basically the same."
+
+ This is a hack to get around the fact that it's difficult to tell
+ when two cones are linearly isomorphic. We have a proposition that
+ equates two cones, but represented over `\mathbb{Q}`, they are
+ merely linearly isomorphic (not equal). So rather than test for
+ equality, we test a list of properties that should be preserved
+ under an invertible linear transformation.
+
+ OUTPUT:
+
``True`` if ``K1`` and ``K2`` are basically the same, and ``False``
- otherwise. This is intended as a lazy way to check whether or not
- ``K1`` and ``K2`` are linearly isomorphic (i.e. ``A(K1) == K2`` for
- some invertible linear transformation ``A``).
+ otherwise.
+
+ EXAMPLES:
+
+ Any proper cone with three generators in `\mathbb{R}^{3}` is
+ basically the same as the nonnegative orthant::
+
+ sage: K1 = Cone([(1,0,0), (0,1,0), (0,0,1)])
+ sage: K2 = Cone([(1,2,3), (3, 18, 4), (66, 51, 0)])
+ sage: _basically_the_same(K1, K2)
+ True
+
+ Negating a cone gives you another cone that is basically the same::
+
+ sage: K = Cone([(0,2,-5), (-6, 2, 4), (0, 51, 0)])
+ sage: _basically_the_same(K, -K)
+ True
+
+ TESTS:
+
+ Any cone is basically the same as itself::
+
+ sage: K = random_cone(max_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: 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)
+ True
+
"""
if K1.lattice_dim() != K2.lattice_dim():
return False
-def rho(K, K2=None):
+def _rho(K, K2=None):
r"""
Restrict ``K`` into its own span, or the span of another cone.
EXAMPLES::
sage: K = Cone([(1,)])
- sage: rho(K) == K
+ sage: _rho(K) == K
True
sage: K2 = Cone([(1,0)])
- sage: rho(K2).rays()
+ sage: _rho(K2).rays()
N(1)
in 1-d lattice N
sage: K3 = Cone([(1,0,0)])
- sage: rho(K3).rays()
+ sage: _rho(K3).rays()
N(1)
in 1-d lattice N
- sage: rho(K2) == rho(K3)
+ sage: _rho(K2) == _rho(K3)
True
TESTS:
sage: set_random_seed()
sage: K = random_cone(max_dim = 8)
- sage: K_S = rho(K)
+ sage: K_S = _rho(K)
sage: K_S.is_solid()
True
sage: set_random_seed()
sage: K = random_cone(max_dim = 8)
- sage: K_S = rho(K, K.dual() )
+ sage: K_S = _rho(K, K.dual() )
sage: K_S.lattice_dim() == K.dual().dim()
True
sage: set_random_seed()
sage: K = random_cone(max_dim = 8)
- sage: K.dim() == rho(K).dim()
+ 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.lineality() == rho(K).lineality()
+ sage: K.lineality() == _rho(K).lineality()
True
No matter which space we restrict to, the lineality should not
sage: set_random_seed()
sage: K = random_cone(max_dim = 8)
- sage: K.lineality() >= rho(K).lineality()
+ sage: K.lineality() >= _rho(K).lineality()
True
- sage: K.lineality() >= rho(K, K.dual()).lineality()
+ sage: K.lineality() >= _rho(K, K.dual()).lineality()
True
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_S = rho(K)
- sage: K_SP = rho(K_S.dual()).dual()
+ sage: K_S = _rho(K)
+ sage: K_SP = _rho(K_S.dual()).dual()
sage: K_SP.is_proper()
True
- sage: K_SP = rho(K_S, K_S.dual())
+ sage: K_SP = _rho(K_S, K_S.dual())
sage: K_SP.is_proper()
True
sage: set_random_seed()
sage: K = random_cone(max_dim = 8, strictly_convex=True, solid=False)
- sage: K_S = rho(K)
- sage: K_SP = rho(K_S.dual()).dual()
+ sage: K_S = _rho(K)
+ sage: K_SP = _rho(K_S.dual()).dual()
sage: K_SP.is_proper()
True
- sage: K_SP = rho(K_S, K_S.dual())
+ sage: K_SP = _rho(K_S, K_S.dual())
sage: K_SP.is_proper()
True
sage: set_random_seed()
sage: K = random_cone(max_dim = 8, strictly_convex=False, solid=True)
- sage: K_S = rho(K)
- sage: K_SP = rho(K_S.dual()).dual()
+ sage: K_S = _rho(K)
+ sage: K_SP = _rho(K_S.dual()).dual()
sage: K_SP.is_proper()
True
- sage: K_SP = rho(K_S, K_S.dual())
+ sage: K_SP = _rho(K_S, K_S.dual())
sage: K_SP.is_proper()
True
sage: set_random_seed()
sage: K = random_cone(max_dim = 8, strictly_convex=True, solid=True)
- sage: K_S = rho(K)
- sage: K_SP = rho(K_S.dual()).dual()
+ sage: K_S = _rho(K)
+ sage: K_SP = _rho(K_S.dual()).dual()
sage: K_SP.is_proper()
True
- sage: K_SP = rho(K_S, K_S.dual())
+ sage: K_SP = _rho(K_S, K_S.dual())
sage: K_SP.is_proper()
True
- Test the proposition in our paper concerning the duals and
+ Test Proposition 7 in our paper concerning the duals and
restrictions. Generate a random cone, then create a subcone of
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: K = Cone(random_sublist(J.rays(), 0.5), lattice=J.lattice())
- sage: K_W = rho(K, J)
- sage: K_star_W_star = rho(K.dual(), J).dual()
- sage: basically_the_same(K_W, K_star_W_star)
+ sage: K_W_star = _rho(K, J).dual()
+ sage: K_star_W = _rho(K.dual(), J)
+ sage: _basically_the_same(K_W_star, K_star_W)
True
::
sage: set_random_seed()
sage: J = random_cone(max_dim = 8, solid=True, strictly_convex=False)
sage: K = Cone(random_sublist(J.rays(), 0.5), lattice=J.lattice())
- sage: K_W = rho(K, J)
- sage: K_star_W_star = rho(K.dual(), J).dual()
- sage: basically_the_same(K_W, K_star_W_star)
+ sage: K_W_star = _rho(K, J).dual()
+ sage: K_star_W = _rho(K.dual(), J)
+ sage: _basically_the_same(K_W_star, K_star_W)
True
::
sage: set_random_seed()
sage: J = random_cone(max_dim = 8, solid=False, strictly_convex=True)
sage: K = Cone(random_sublist(J.rays(), 0.5), lattice=J.lattice())
- sage: K_W = rho(K, J)
- sage: K_star_W_star = rho(K.dual(), J).dual()
- sage: basically_the_same(K_W, K_star_W_star)
+ sage: K_W_star = _rho(K, J).dual()
+ sage: K_star_W = _rho(K.dual(), J)
+ sage: _basically_the_same(K_W_star, K_star_W)
True
::
sage: set_random_seed()
sage: J = random_cone(max_dim = 8, solid=True, strictly_convex=True)
sage: K = Cone(random_sublist(J.rays(), 0.5), lattice=J.lattice())
- sage: K_W = rho(K, J)
- sage: K_star_W_star = rho(K.dual(), J).dual()
- sage: basically_the_same(K_W, K_star_W_star)
+ sage: K_W_star = _rho(K, J).dual()
+ sage: K_star_W = _rho(K.dual(), J)
+ sage: _basically_the_same(K_W_star, K_star_W)
True
"""
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: 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)
+ True
+
+ Just to be sure, test a few more::
+
+ sage: K1 = random_cone(max_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)
+ True
+
+ ::
+
+ sage: K1 = random_cone(max_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)
+ True
+
+ ::
+
+ sage: K1 = random_cone(max_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)
+ True
+
+ ::
+
+ sage: K1 = random_cone(max_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)
+ True
+
The dual cone `K^{*}` of ``K`` should have the same Lyapunov rank as ``K``
itself [Rudolf et al.]_::
sage: set_random_seed()
sage: K = random_cone(max_dim=8)
sage: actual = lyapunov_rank(K)
- sage: K_S = rho(K)
- sage: K_SP = rho(K_S.dual()).dual()
+ sage: K_S = _rho(K)
+ sage: K_SP = _rho(K_S.dual()).dual()
sage: l = K.lineality()
sage: c = K.codim()
sage: expected = lyapunov_rank(K_SP) + K.dim()*(l + c) + c**2
if m < n:
# K is not solid, restrict to its span.
- K = rho(K)
+ K = _rho(K)
# Lemma 2
beta += m*(n - m) + (n - m)**2
if l > 0:
# K is not pointed, restrict to the span of its dual. Uses a
# proposition from our paper, i.e. this is equivalent to K =
- # rho(K.dual()).dual().
- K = rho(K, K.dual())
+ # _rho(K.dual()).dual().
+ K = _rho(K, K.dual())
# Lemma 3
beta += m * l