+ Construct the space `W \times W^{\perp}` isomorphic to the ambient space
+ of ``K`` where `W` is equal to the span of ``K``.
+ """
+ V = K.lattice().vector_space()
+
+ # Create the space W \times W^{\perp} isomorphic to V.
+ # First we get an orthogonal (but not normal) basis...
+ M = matrix(V.base_field(), K.rays())
+ W_basis,_ = M.gram_schmidt()
+
+ W = V.subspace_with_basis(W_basis)
+ W_perp = W.complement()
+
+ return W.cartesian_product(W_perp)
+
+
+def ips_iso(K):
+ r"""
+ Construct the IPS isomorphism and its inverse from our paper.
+
+ Given a cone ``K``, the returned isomorphism will split its ambient
+ vector space `V` into a cartesian product `W \times W^{\perp}` where
+ `W` equals the span of ``K``.
+ """
+ V = K.lattice().vector_space()
+ V_iso = iso_space(K)
+ (W, W_perp) = V_iso.cartesian_factors()
+
+ # A space equivalent to V, but using our basis.
+ V_user = V.subspace_with_basis( W.basis() + W_perp.basis() )
+
+ def phi(v):
+ # Write v in terms of our custom basis, where the first dim(W)
+ # coordinates are for the W-part of the basis.
+ cs = V_user.coordinates(v)
+
+ w1 = sum([ V_user.basis()[idx]*cs[idx]
+ for idx in range(0, W.dimension()) ])
+ w2 = sum([ V_user.basis()[idx]*cs[idx]
+ for idx in range(W.dimension(), V.dimension()) ])
+
+ return V_iso( (w1, w2) )
+
+
+ def phi_inv( pair ):
+ # Crash if the arguments are in the wrong spaces.
+ V_iso(pair)
+
+ #w = sum([ sub_w[idx]*W.basis()[idx] for idx in range(0,m) ])
+ #w_prime = sum([ sub_w_prime[idx]*W_perp.basis()[idx]
+ # for idx in range(0,n-m) ])
+
+ return sum( pair.cartesian_factors() )
+
+
+ return (phi,phi_inv)
+
+
+
+def unrestrict_span(K, K2=None):
+ if K2 is None:
+ K2 = K
+
+ _,phi_inv = ips_iso(K2)
+ V_iso = iso_space(K2)
+ (W, W_perp) = V_iso.cartesian_factors()
+
+ rays = []
+ for r in K.rays():
+ w = sum([ r[idx]*W.basis()[idx] for idx in range(0,len(r)) ])
+ pair = V_iso( (w, W_perp.zero()) )
+ rays.append( phi_inv(pair) )
+
+ L = ToricLattice(W.dimension() + W_perp.dimension())
+
+ return Cone(rays, lattice=L)
+
+
+
+def intersect_span(K1, K2):
+ r"""
+ Return a new cone obtained by intersecting ``K1`` with the span of ``K2``.
+ """
+ L = K1.lattice()
+
+ if L.rank() != K2.lattice().rank():
+ raise ValueError('K1 and K2 must belong to lattices of the same rank.')
+
+ SL_gens = list(K2.rays())
+ span_K2_gens = SL_gens + [ -g for g in SL_gens ]
+
+ # The lattices have the same rank (see above) so this should work.
+ span_K2 = Cone(span_K2_gens, L)
+ return K1.intersection(span_K2)
+
+
+
+def restrict_span(K, K2=None):
+ r"""
+ Restrict ``K`` into its own span, or the span of another cone.
+
+ INPUT:
+
+ - ``K2`` -- another cone whose lattice has the same rank as this cone.
+
+ OUTPUT:
+
+ A new cone in a sublattice.