X-Git-Url: http://gitweb.michael.orlitzky.com/?a=blobdiff_plain;f=mjo%2Fcone%2Fcone.py;h=3c582e681a8ea0c6d985cc89d5492a5481578801;hb=826580027d1f9d32a0317c5ad90a9d69cda7795d;hp=48b4061748281e0ebcb3ef19e9872be824666ba5;hpb=af3e2ce56ad6561c5c9b1b6cf3df22d690550618;p=sage.d.git diff --git a/mjo/cone/cone.py b/mjo/cone/cone.py index 48b4061..3c582e6 100644 --- a/mjo/cone/cone.py +++ b/mjo/cone/cone.py @@ -91,34 +91,41 @@ def _basically_the_same(K1, K2): -def _rho(K, K2=None): +def _restrict_to_space(K, W): r""" - Restrict ``K`` into its own span, or the span of another cone. + Restrict this cone a subspace of its ambient space. INPUT: - - ``K2`` -- another cone whose lattice has the same rank as this - cone. + - ``W`` -- The subspace into which this cone will be restricted. OUTPUT: - A new cone in a sublattice. + A new cone in a sublattice corresponding to ``W``. - EXAMPLES:: + EXAMPLES: + + When this cone is solid, restricting it into its own span should do + nothing:: sage: K = Cone([(1,)]) - sage: _rho(K) == K + sage: _restrict_to_space(K, K.span()) == K True + A single ray restricted into its own span gives the same output + regardless of the ambient space:: + sage: K2 = Cone([(1,0)]) - sage: _rho(K2).rays() + sage: K2_S = _restrict_to_space(K2, K2.span()).rays() + sage: K2_S N(1) in 1-d lattice N sage: K3 = Cone([(1,0,0)]) - sage: _rho(K3).rays() + sage: K3_S = _restrict_to_space(K3, K3.span()).rays() + sage: K3_S N(1) in 1-d lattice N - sage: _rho(K2) == _rho(K3) + sage: K2_S == K3_S True TESTS: @@ -127,8 +134,7 @@ def _rho(K, K2=None): sage: set_random_seed() sage: K = random_cone(max_ambient_dim = 8) - sage: K_S = _rho(K) - sage: K_S.is_solid() + sage: _restrict_to_space(K, K.span()).is_solid() True And the resulting cone should live in a space having the same @@ -136,22 +142,22 @@ def _rho(K, K2=None): sage: set_random_seed() sage: K = random_cone(max_ambient_dim = 8) - sage: K_S = _rho(K, K.dual() ) - sage: K_S.lattice_dim() == K.dual().dim() + sage: K_P = _restrict_to_space(K, K.dual().span()) + sage: K_P.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_ambient_dim = 8) - sage: K.dim() == _rho(K).dim() + sage: K.dim() == _restrict_to_space(K,K.span()).dim() True Nor should it affect the lineality of a cone:: sage: set_random_seed() sage: K = random_cone(max_ambient_dim = 8) - sage: K.lineality() == _rho(K).lineality() + sage: K.lineality() == _restrict_to_space(K, K.span()).lineality() True No matter which space we restrict to, the lineality should not @@ -159,56 +165,50 @@ def _rho(K, K2=None): sage: set_random_seed() sage: K = random_cone(max_ambient_dim = 8) - sage: K.lineality() >= _rho(K).lineality() + sage: S = K.span(); P = K.dual().span() + sage: K.lineality() >= _restrict_to_space(K,S).lineality() True - sage: K.lineality() >= _rho(K, K.dual()).lineality() + sage: K.lineality() >= _restrict_to_space(K,P).lineality() True If we do this according to our paper, then the result is proper:: sage: set_random_seed() sage: K = random_cone(max_ambient_dim = 8) - sage: K_S = _rho(K) - sage: K_SP = _rho(K_S.dual()).dual() + sage: K_S = _restrict_to_space(K, K.span()) + sage: K_SP = _restrict_to_space(K_S.dual(), K_S.dual().span()).dual() sage: K_SP.is_proper() True - sage: K_SP = _rho(K_S, K_S.dual()) + sage: K_SP = _restrict_to_space(K_S, K_S.dual().span()) sage: K_SP.is_proper() True Test the proposition 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:: + it. The operation of dual-taking should then commute with + _restrict_to_space:: sage: set_random_seed() sage: J = random_cone(max_ambient_dim = 8) 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: K_W_star = _restrict_to_space(K, J.span()).dual() + sage: K_star_W = _restrict_to_space(K.dual(), J.span()) sage: _basically_the_same(K_W_star, K_star_W) True """ - if K2 is None: - K2 = K - - # First we project K onto the span of K2. This will explode if the - # rank of ``K2.lattice()`` doesn't match ours. - span_K2 = Cone(K2.rays() + (-K2).rays(), lattice=K.lattice()) - K = K.intersection(span_K2) - - # Cheat a little to get the subspace span(K2). The paper uses the - # rays of K2 as a basis, but everything is invariant under linear - # isomorphism (i.e. a change of basis), and this is a little - # faster. - W = span_K2.linear_subspace() + # First we want to intersect ``K`` with ``W``. The easiest way to + # do this is via cone intersection, so we turn the subspace ``W`` + # into a cone. + W_cone = Cone(W.basis() + [-b for b in W.basis()], lattice=K.lattice()) + K = K.intersection(W_cone) # We've already intersected K with the span of K2, so every # generator of K should belong to W now. - W_rays = [ W.coordinate_vector(r) for r in K.rays() ] + K_W_rays = [ W.coordinate_vector(r) for r in K.rays() ] - L = ToricLattice(K2.dim()) - return Cone(W_rays, lattice=L) + L = ToricLattice(W.dimension()) + return Cone(K_W_rays, lattice=L) @@ -442,7 +442,7 @@ def LL(K): def lyapunov_rank(K): r""" - Compute the Lyapunov (or bilinearity) rank of this cone. + Compute the Lyapunov rank (or bilinearity rank) of this cone. The Lyapunov rank of a cone can be thought of in (mainly) two ways: @@ -461,16 +461,7 @@ def lyapunov_rank(K): An integer representing the Lyapunov rank of the cone. If the dimension of the ambient vector space is `n`, then the Lyapunov rank will be between `1` and `n` inclusive; however a rank of `n-1` is - not possible (see the first reference). - - .. note:: - - In the references, the cones are always assumed to be proper. We - do not impose this restriction. - - .. seealso:: - - :meth:`is_proper` + not possible (see [Orlitzky/Gowda]_). ALGORITHM: @@ -644,8 +635,8 @@ def lyapunov_rank(K): sage: set_random_seed() 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() + sage: K_S = _restrict_to_space(K, K.span()) + sage: K_SP = _restrict_to_space(K_S.dual(), K_S.dual().span()).dual() sage: l = K.lineality() sage: c = K.codim() sage: expected = lyapunov_rank(K_SP) + K.dim()*(l + c) + c**2 @@ -659,7 +650,8 @@ def lyapunov_rank(K): sage: lyapunov_rank(K) == len(LL(K)) True - Test Theorem 3 in [Orlitzky/Gowda]_:: + We can make an imperfect cone perfect by adding a slack variable + (a Theorem in [Orlitzky/Gowda]_):: sage: set_random_seed() sage: K = random_cone(max_ambient_dim=8, @@ -679,19 +671,19 @@ def lyapunov_rank(K): if m < n: # K is not solid, restrict to its span. - K = _rho(K) + K = _restrict_to_space(K, K.span()) - # Lemma 2 - beta += m*(n - m) + (n - m)**2 + # Non-solid reduction lemma. + beta += (n - m)*n 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()) + K = _restrict_to_space(K, K.dual().span()) - # Lemma 3 - beta += m * l + # Non-pointed reduction lemma. + beta += l * m beta += len(LL(K)) return beta