This module was designed as a part of framework for toric varieties (variety, fano_variety). While the emphasis is on strictly convex cones, non-strictly convex cones are supported as well. Work with distinct lattices (in the sense of discrete subgroups spanning vector spaces) is supported. The default lattice is ToricLattice \(N\) of the appropriate dimension. The only case when you must specify lattice explicitly is creation of a 0-dimensional cone, where dimension of the ambient space cannot be guessed.
AUTHORS:
EXAMPLES:
Use Cone() to construct cones:
sage: octant = Cone([(1,0,0), (0,1,0), (0,0,1)])
sage: halfspace = Cone([(1,0,0), (0,1,0), (-1,-1,0), (0,0,1)])
sage: positive_xy = Cone([(1,0,0), (0,1,0)])
sage: four_rays = Cone([(1,1,1), (1,-1,1), (-1,-1,1), (-1,1,1)])
For all of the cones above we have provided primitive generating rays, but in fact this is not necessary - a cone can be constructed from any collection of rays (from the same space, of course). If there are non-primitive (or even non-integral) rays, they will be replaced with primitive ones. If there are extra rays, they will be discarded. Of course, this means that Cone() has to do some work before actually constructing the cone and sometimes it is not desirable, if you know for sure that your input is already “good”. In this case you can use options check=False to force Cone() to use exactly the directions that you have specified and normalize=False to force it to use exactly the rays that you have specified. However, it is better not to use these possibilities without necessity, since cones are assumed to be represented by a minimal set of primitive generating rays. See Cone() for further documentation on construction.
Once you have a cone, you can perform numerous operations on it. The most important ones are, probably, ray accessing methods:
sage: rays = halfspace.rays()
sage: rays
N( 0, 0, 1),
N( 0, 1, 0),
N( 0, -1, 0),
N( 1, 0, 0),
N(-1, 0, 0)
in 3-d lattice N
sage: rays.set()
frozenset([N(1, 0, 0), N(-1, 0, 0), N(0, 1, 0), N(0, 0, 1), N(0, -1, 0)])
sage: rays.matrix()
[ 0 0 1]
[ 0 1 0]
[ 0 -1 0]
[ 1 0 0]
[-1 0 0]
sage: rays.column_matrix()
[ 0 0 0 1 -1]
[ 0 1 -1 0 0]
[ 1 0 0 0 0]
sage: rays(3)
N(1, 0, 0)
in 3-d lattice N
sage: rays[3]
N(1, 0, 0)
sage: halfspace.ray(3)
N(1, 0, 0)
The method rays() returns a PointCollection with the \(i\)-th element being the primitive integral generator of the \(i\)-th ray. It is possible to convert this collection to a matrix with either rows or columns corresponding to these generators. You may also change the default output_format() of all point collections to be such a matrix.
If you want to do something with each ray of a cone, you can write
sage: for ray in positive_xy: print ray
N(1, 0, 0)
N(0, 1, 0)
There are two dimensions associated to each cone - the dimension of the subspace spanned by the cone and the dimension of the space where it lives:
sage: positive_xy.dim()
2
sage: positive_xy.lattice_dim()
3
You also may be interested in this dimension:
sage: dim(positive_xy.linear_subspace())
0
sage: dim(halfspace.linear_subspace())
2
Or, perhaps, all you care about is whether it is zero or not:
sage: positive_xy.is_strictly_convex()
True
sage: halfspace.is_strictly_convex()
False
You can also perform these checks:
sage: positive_xy.is_simplicial()
True
sage: four_rays.is_simplicial()
False
sage: positive_xy.is_smooth()
True
You can work with subcones that form faces of other cones:
sage: face = four_rays.faces(dim=2)[0]
sage: face
2-d face of 3-d cone in 3-d lattice N
sage: face.rays()
N(-1, -1, 1),
N(-1, 1, 1)
in 3-d lattice N
sage: face.ambient_ray_indices()
(2, 3)
sage: four_rays.rays(face.ambient_ray_indices())
N(-1, -1, 1),
N(-1, 1, 1)
in 3-d lattice N
If you need to know inclusion relations between faces, you can use
sage: L = four_rays.face_lattice()
sage: map(len, L.level_sets())
[1, 4, 4, 1]
sage: face = L.level_sets()[2][0]
sage: face.rays()
N(1, 1, 1),
N(1, -1, 1)
in 3-d lattice N
sage: L.hasse_diagram().neighbors_in(face)
[1-d face of 3-d cone in 3-d lattice N,
1-d face of 3-d cone in 3-d lattice N]
Warning
The order of faces in level sets of the face lattice may differ from the order of faces returned by faces(). While the first order is random, the latter one ensures that one-dimensional faces are listed in the same order as generating rays.
When all the functionality provided by cones is not enough, you may want to check if you can do necessary things using polyhedra corresponding to cones:
sage: four_rays.polyhedron()
A 3-dimensional polyhedron in ZZ^3 defined as
the convex hull of 1 vertex and 4 rays
And of course you are always welcome to suggest new features that should be added to cones!
REFERENCES:
[Fulton] | (1, 2, 3, 4, 5) Wiliam Fulton, “Introduction to Toric Varieties”, Princeton University Press |
Construct a (not necessarily strictly) convex rational polyhedral cone.
INPUT:
OUTPUT:
EXAMPLES:
Let’s define a cone corresponding to the first quadrant of the plane (note, you can even mix objects of different types to represent rays, as long as you let this function to perform all the checks and necessary conversions!):
sage: quadrant = Cone([(1,0), [0,1]])
sage: quadrant
2-d cone in 2-d lattice N
sage: quadrant.rays()
N(1, 0),
N(0, 1)
in 2-d lattice N
If you give more rays than necessary, the extra ones will be discarded:
sage: Cone([(1,0), (0,1), (1,1), (0,1)]).rays()
N(0, 1),
N(1, 0)
in 2-d lattice N
However, this work is not done with check=False option, so use it carefully!
sage: Cone([(1,0), (0,1), (1,1), (0,1)], check=False).rays()
N(1, 0),
N(0, 1),
N(1, 1),
N(0, 1)
in 2-d lattice N
Even worse things can happen with normalize=False option:
sage: Cone([(1,0), (0,1)], check=False, normalize=False)
Traceback (most recent call last):
...
AttributeError: 'tuple' object has no attribute 'parent'
You can construct different “not” cones: not full-dimensional, not strictly convex, not containing any rays:
sage: one_dimensional_cone = Cone([(1,0)])
sage: one_dimensional_cone.dim()
1
sage: half_plane = Cone([(1,0), (0,1), (-1,0)])
sage: half_plane.rays()
N( 0, 1),
N( 1, 0),
N(-1, 0)
in 2-d lattice N
sage: half_plane.is_strictly_convex()
False
sage: origin = Cone([(0,0)])
sage: origin.rays()
Empty collection
in 2-d lattice N
sage: origin.dim()
0
sage: origin.lattice_dim()
2
You may construct the cone above without giving any rays, but in this case you must provide lattice explicitly:
sage: origin = Cone([])
Traceback (most recent call last):
...
ValueError: lattice must be given explicitly if there are no rays!
sage: origin = Cone([], lattice=ToricLattice(2))
sage: origin.dim()
0
sage: origin.lattice_dim()
2
sage: origin.lattice()
2-d lattice N
Of course, you can also provide lattice in other cases:
sage: L = ToricLattice(3, "L")
sage: c1 = Cone([(1,0,0),(1,1,1)], lattice=L)
sage: c1.rays()
L(1, 0, 0),
L(1, 1, 1)
in 3-d lattice L
Or you can construct cones from rays of a particular lattice:
sage: ray1 = L(1,0,0)
sage: ray2 = L(1,1,1)
sage: c2 = Cone([ray1, ray2])
sage: c2.rays()
L(1, 0, 0),
L(1, 1, 1)
in 3-d lattice L
sage: c1 == c2
True
When the cone in question is not strictly convex, the standard form for the “generating rays” of the linear subspace is “basis vectors and their negatives”, as in the following example:
sage: plane = Cone([(1,0), (0,1), (-1,-1)])
sage: plane.rays()
N( 0, 1),
N( 0, -1),
N( 1, 0),
N(-1, 0)
in 2-d lattice N
The cone can also be specified by a Polyhedron_base:
sage: p = plane.polyhedron()
sage: Cone(p)
2-d cone in 2-d lattice N
sage: Cone(p) == plane
True
TESTS:
sage: N = ToricLattice(2)
sage: Nsub = N.span([ N(1,2) ])
sage: Cone(Nsub.basis())
1-d cone in Sublattice <N(1, 2)>
sage: Cone([N(0)])
0-d cone in 2-d lattice N
Bases: sage.geometry.cone.IntegralRayCollection, _abcoll.Container
Create a convex rational polyhedral cone.
Warning
This class does not perform any checks of correctness of input nor does it convert input into the standard representation. Use Cone() to construct cones.
Cones are immutable, but they cache most of the returned values.
INPUT:
The input can be either:
or (these parameters must be given as keywords):
In both cases, the following keyword parameter may be specified in addition:
OUTPUT:
Note
Every cone has its ambient structure. If it was not specified, it is this cone itself.
Return the Hilbert basis of the cone.
Given a strictly convex cone \(C\subset \RR^d\), the Hilbert basis of \(C\) is the set of all irreducible elements in the semigroup \(C\cap \ZZ^d\). It is the unique minimal generating set over \(\ZZ\) for the integral points \(C\cap \ZZ^d\).
If the cone \(C\) is not strictly convex, this method finds the (unique) minimial set of lattice points that need to be added to the defining rays of the cone to generate the whole semigroup \(C\cap \ZZ^d\). But because the rays of the cone are not unique nor necessarily minimal in this case, neither is the returned generating set (consisting of the rays plus additional generators).
See also semigroup_generators() if you are not interested in a minimal set of generators.
OUTPUT:
EXAMPLES:
The following command ensures that the output ordering in the examples below is independent of TOPCOM, you don’t have to use it:
sage: PointConfiguration.set_engine('internal')
We start with a simple case of a non-smooth 2-dimensional cone:
sage: Cone([ (1,0), (1,2) ]).Hilbert_basis()
N(1, 0),
N(1, 2),
N(1, 1)
in 2-d lattice N
Two more complicated example from GAP/toric:
sage: Cone([[1,0],[3,4]]).dual().Hilbert_basis()
M(0, 1),
M(4, -3),
M(3, -2),
M(2, -1),
M(1, 0)
in 2-d lattice M
sage: cone = Cone([[1,2,3,4],[0,1,0,7],[3,1,0,2],[0,0,1,0]]).dual()
sage: cone.Hilbert_basis() # long time
M(10, -7, 0, 1),
M(-5, 21, 0, -3),
M( 0, -2, 0, 1),
M(15, -63, 25, 9),
M( 2, -3, 0, 1),
M( 1, -4, 1, 1),
M(-1, 3, 0, 0),
M( 4, -4, 0, 1),
M( 1, -5, 2, 1),
M( 3, -5, 1, 1),
M( 6, -5, 0, 1),
M( 3, -13, 5, 2),
M( 2, -6, 2, 1),
M( 5, -6, 1, 1),
M( 0, 1, 0, 0),
M( 8, -6, 0, 1),
M(-2, 8, 0, -1),
M(10, -42, 17, 6),
M( 7, -28, 11, 4),
M( 5, -21, 9, 3),
M( 6, -21, 8, 3),
M( 5, -14, 5, 2),
M( 2, -7, 3, 1),
M( 4, -7, 2, 1),
M( 7, -7, 1, 1),
M( 0, 0, 1, 0),
M(-3, 14, 0, -2),
M(-1, 7, 0, -1),
M( 1, 0, 0, 0)
in 4-d lattice M
Not a strictly convex cone:
sage: wedge = Cone([ (1,0,0), (1,2,0), (0,0,1), (0,0,-1) ])
sage: wedge.semigroup_generators()
(N(1, 0, 0), N(1, 1, 0), N(1, 2, 0), N(0, 0, 1), N(0, 0, -1))
sage: wedge.Hilbert_basis()
N(1, 2, 0),
N(1, 0, 0),
N(0, 0, 1),
N(0, 0, -1),
N(1, 1, 0)
in 3-d lattice N
Not full-dimensional cones are ok, too (see http://trac.sagemath.org/sage_trac/ticket/11312):
sage: Cone([(1,1,0), (-1,1,0)]).Hilbert_basis()
N( 1, 1, 0),
N(-1, 1, 0),
N( 0, 1, 0)
in 3-d lattice N
ALGORITHM:
The primal Normaliz algorithm, see [Normaliz].
REFERENCES:
[Normaliz] | (1, 2) Winfried Bruns, Bogdan Ichim, and Christof Soeger: Normaliz. http://www.mathematik.uni-osnabrueck.de/normaliz/ |
Return the expansion coefficients of point with respect to Hilbert_basis().
INPUT:
OUTPUT:
A \(\ZZ\)-vector of length len(self.Hilbert_basis()) with nonnegative components.
Note
Since the Hilbert basis elements are not necessarily linearly independent, the expansion coefficients are not unique. However, this method will always return the same expansion coefficients when invoked with the same argument.
EXAMPLES:
sage: cone = Cone([(1,0),(0,1)])
sage: cone.rays()
N(1, 0),
N(0, 1)
in 2-d lattice N
sage: cone.Hilbert_coefficients([3,2])
(3, 2)
A more complicated example:
sage: N = ToricLattice(2)
sage: cone = Cone([N(1,0),N(1,2)])
sage: cone.Hilbert_basis()
N(1, 0),
N(1, 2),
N(1, 1)
in 2-d lattice N
sage: cone.Hilbert_coefficients( N(1,1) )
(0, 0, 1)
The cone need not be strictly convex:
sage: N = ToricLattice(3)
sage: cone = Cone([N(1,0,0),N(1,2,0),N(0,0,1),N(0,0,-1)])
sage: cone.Hilbert_basis()
N(1, 2, 0),
N(1, 0, 0),
N(0, 0, 1),
N(0, 0, -1),
N(1, 1, 0)
in 3-d lattice N
sage: cone.Hilbert_coefficients( N(1,1,3) )
(0, 0, 3, 0, 1)
Return faces adjacent to self in the ambient face lattice.
Two distinct faces \(F_1\) and \(F_2\) of the same face lattice are adjacent if all of the following conditions hold:
OUTPUT:
EXAMPLES:
sage: octant = Cone([(1,0,0), (0,1,0), (0,0,1)])
sage: octant.adjacent()
()
sage: one_face = octant.faces(1)[0]
sage: len(one_face.adjacent())
2
sage: one_face.adjacent()[1]
1-d face of 3-d cone in 3-d lattice N
Things are a little bit subtle with fans, as we illustrate below.
First, we create a fan from two cones in the plane:
sage: fan = Fan(cones=[(0,1), (1,2)],
... rays=[(1,0), (0,1), (-1,0)])
sage: cone = fan.generating_cone(0)
sage: len(cone.adjacent())
1
The second generating cone is adjacent to this one. Now we create the same fan, but embedded into the 3-dimensional space:
sage: fan = Fan(cones=[(0,1), (1,2)],
... rays=[(1,0,0), (0,1,0), (-1,0,0)])
sage: cone = fan.generating_cone(0)
sage: len(cone.adjacent())
1
The result is as before, since we still have:
sage: fan.dim()
2
Now we add another cone to make the fan 3-dimensional:
sage: fan = Fan(cones=[(0,1), (1,2), (3,)],
... rays=[(1,0,0), (0,1,0), (-1,0,0), (0,0,1)])
sage: cone = fan.generating_cone(0)
sage: len(cone.adjacent())
0
Since now cone has smaller dimension than fan, it and its adjacent cones must be facets of a bigger one, but since cone in this example is generating, it is not contained in any other.
Return the ambient structure of self.
OUTPUT:
EXAMPLES:
sage: cone = Cone([(1,2,3), (4,6,5), (9,8,7)])
sage: cone.ambient()
3-d cone in 3-d lattice N
sage: cone.ambient() is cone
True
sage: face = cone.faces(1)[0]
sage: face
1-d face of 3-d cone in 3-d lattice N
sage: face.ambient()
3-d cone in 3-d lattice N
sage: face.ambient() is cone
True
Return indices of rays of the ambient structure generating self.
OUTPUT:
EXAMPLES:
sage: quadrant = Cone([(1,0), (0,1)])
sage: quadrant.ambient_ray_indices()
(0, 1)
sage: quadrant.facets()[1].ambient_ray_indices()
(1,)
Return the Cartesian product of self with other.
INPUT:
OUTPUT:
EXAMPLES:
sage: c = Cone([(1,)])
sage: c.cartesian_product(c)
2-d cone in 2-d lattice N+N
sage: _.rays()
N+N(1, 0),
N+N(0, 1)
in 2-d lattice N+N
Check if a given point is contained in self.
INPUT:
OUTPUT:
EXAMPLES:
sage: c = Cone([(1,0), (0,1)])
sage: c.contains(c.lattice()(1,0))
True
sage: c.contains((1,0))
True
sage: c.contains((1,1))
True
sage: c.contains(1,1)
True
sage: c.contains((-1,0))
False
sage: c.contains(c.dual_lattice()(1,0)) #random output (warning)
False
sage: c.contains(c.dual_lattice()(1,0))
False
sage: c.contains(1)
False
sage: c.contains(1/2, sqrt(3))
True
sage: c.contains(-1/2, sqrt(3))
False
Return the dual cone of self.
OUTPUT:
EXAMPLES:
sage: cone = Cone([(1,0), (-1,3)])
sage: cone.dual().rays()
M(0, 1),
M(3, 1)
in 2-d lattice M
Now let’s look at a more complicated case:
sage: cone = Cone([(-2,-1,2), (4,1,0), (-4,-1,-5), (4,1,5)])
sage: cone.is_strictly_convex()
False
sage: cone.dim()
3
sage: cone.dual().rays()
M(7, -18, -2),
M(1, -4, 0)
in 3-d lattice M
sage: cone.dual().dual() is cone
True
We correctly handle the degenerate cases:
sage: N = ToricLattice(2)
sage: Cone([], lattice=N).dual().rays() # empty cone
M( 1, 0),
M(-1, 0),
M( 0, 1),
M( 0, -1)
in 2-d lattice M
sage: Cone([(1,0)], lattice=N).dual().rays() # ray in 2d
M(1, 0),
M(0, 1),
M(0, -1)
in 2-d lattice M
sage: Cone([(1,0),(-1,0)], lattice=N).dual().rays() # line in 2d
M(0, 1),
M(0, -1)
in 2-d lattice M
sage: Cone([(1,0),(0,1)], lattice=N).dual().rays() # strictly convex cone
M(0, 1),
M(1, 0)
in 2-d lattice M
sage: Cone([(1,0),(-1,0),(0,1)], lattice=N).dual().rays() # half space
M(0, 1)
in 2-d lattice M
sage: Cone([(1,0),(0,1),(-1,-1)], lattice=N).dual().rays() # whole space
Empty collection
in 2-d lattice M
Return the cone equivalent to the given one, but sitting in self as a face.
You may need to use this method before calling methods of cone that depend on the ambient structure, such as ambient_ray_indices() or facet_of(). The cone returned by this method will have self as ambient. If cone does not represent a valid cone of self, ValueError exception is raised.
Note
This method is very quick if self is already the ambient structure of cone, so you can use without extra checks and performance hit even if cone is likely to sit in self but in principle may not.
INPUT:
OUTPUT:
EXAMPLES:
Let’s take a 3-d cone on 4 rays:
sage: c = Cone([(1,0,1), (0,1,1), (-1,0,1), (0,-1,1)])
Then any ray generates a 1-d face of this cone, but if you construct such a face directly, it will not “sit” inside the cone:
sage: ray = Cone([(0,-1,1)])
sage: ray
1-d cone in 3-d lattice N
sage: ray.ambient_ray_indices()
(0,)
sage: ray.adjacent()
()
sage: ray.ambient()
1-d cone in 3-d lattice N
If we want to operate with this ray as a face of the cone, we need to embed it first:
sage: e_ray = c.embed(ray)
sage: e_ray
1-d face of 3-d cone in 3-d lattice N
sage: e_ray.rays()
N(0, -1, 1)
in 3-d lattice N
sage: e_ray is ray
False
sage: e_ray.is_equivalent(ray)
True
sage: e_ray.ambient_ray_indices()
(3,)
sage: e_ray.adjacent()
(1-d face of 3-d cone in 3-d lattice N,
1-d face of 3-d cone in 3-d lattice N)
sage: e_ray.ambient()
3-d cone in 3-d lattice N
Not every cone can be embedded into a fixed ambient cone:
sage: c.embed(Cone([(0,0,1)]))
Traceback (most recent call last):
...
ValueError: 1-d cone in 3-d lattice N is not a face
of 3-d cone in 3-d lattice N!
sage: c.embed(Cone([(1,0,1), (-1,0,1)]))
Traceback (most recent call last):
...
ValueError: 2-d cone in 3-d lattice N is not a face
of 3-d cone in 3-d lattice N!
Return the face lattice of self.
This lattice will have the origin as the bottom (we do not include the empty set as a face) and this cone itself as the top.
OUTPUT:
EXAMPLES:
Let’s take a look at the face lattice of the first quadrant:
sage: quadrant = Cone([(1,0), (0,1)])
sage: L = quadrant.face_lattice()
sage: L
Finite poset containing 4 elements
To see all faces arranged by dimension, you can do this:
sage: for level in L.level_sets(): print level
[0-d face of 2-d cone in 2-d lattice N]
[1-d face of 2-d cone in 2-d lattice N,
1-d face of 2-d cone in 2-d lattice N]
[2-d cone in 2-d lattice N]
For a particular face you can look at its actual rays...
sage: face = L.level_sets()[1][0]
sage: face.rays()
N(1, 0)
in 2-d lattice N
... or you can see the index of the ray of the original cone that corresponds to the above one:
sage: face.ambient_ray_indices()
(0,)
sage: quadrant.ray(0)
N(1, 0)
An alternative to extracting faces from the face lattice is to use faces() method:
sage: face is quadrant.faces(dim=1)[0]
True
The advantage of working with the face lattice directly is that you can (relatively easily) get faces that are related to the given one:
sage: face = L.level_sets()[1][0]
sage: D = L.hasse_diagram()
sage: D.neighbors(face)
[2-d cone in 2-d lattice N,
0-d face of 2-d cone in 2-d lattice N]
However, you can achieve some of this functionality using facets(), facet_of(), and adjacent() methods:
sage: face = quadrant.faces(1)[0]
sage: face
1-d face of 2-d cone in 2-d lattice N
sage: face.rays()
N(1, 0)
in 2-d lattice N
sage: face.facets()
(0-d face of 2-d cone in 2-d lattice N,)
sage: face.facet_of()
(2-d cone in 2-d lattice N,)
sage: face.adjacent()
(1-d face of 2-d cone in 2-d lattice N,)
sage: face.adjacent()[0].rays()
N(0, 1)
in 2-d lattice N
Note that if cone is a face of supercone, then the face lattice of cone consists of (appropriate) faces of supercone:
sage: supercone = Cone([(1,2,3,4), (5,6,7,8),
... (1,2,4,8), (1,3,9,7)])
sage: supercone.face_lattice()
Finite poset containing 16 elements
sage: supercone.face_lattice().top()
4-d cone in 4-d lattice N
sage: cone = supercone.facets()[0]
sage: cone
3-d face of 4-d cone in 4-d lattice N
sage: cone.face_lattice()
Finite poset containing 8 elements
sage: cone.face_lattice().bottom()
0-d face of 4-d cone in 4-d lattice N
sage: cone.face_lattice().top()
3-d face of 4-d cone in 4-d lattice N
sage: cone.face_lattice().top() == cone
True
TESTS:
sage: C1 = Cone([(0,1)])
sage: C2 = Cone([(0,1)])
sage: C1 == C2
True
sage: C1 is C2
False
C1 and C2 are equal, but not identical. We currently want them to have non identical face lattices, even if the faces themselves are equal (see #10998):
sage: C1.face_lattice() is C2.face_lattice()
False
sage: C1.facets()[0]
0-d face of 1-d cone in 2-d lattice N
sage: C2.facets()[0]
0-d face of 1-d cone in 2-d lattice N
sage: C1.facets()[0].ambient() is C1
True
sage: C2.facets()[0].ambient() is C1
False
sage: C2.facets()[0].ambient() is C2
True
Return faces of self of specified (co)dimension.
INPUT:
Note
You can specify at most one parameter. If you don’t give any, then all faces will be returned.
OUTPUT:
EXAMPLES:
Let’s take a look at the faces of the first quadrant:
sage: quadrant = Cone([(1,0), (0,1)])
sage: quadrant.faces()
((0-d face of 2-d cone in 2-d lattice N,),
(1-d face of 2-d cone in 2-d lattice N,
1-d face of 2-d cone in 2-d lattice N),
(2-d cone in 2-d lattice N,))
sage: quadrant.faces(dim=1)
(1-d face of 2-d cone in 2-d lattice N,
1-d face of 2-d cone in 2-d lattice N)
sage: face = quadrant.faces(dim=1)[0]
Now you can look at the actual rays of this face...
sage: face.rays()
N(1, 0)
in 2-d lattice N
... or you can see indices of the rays of the orginal cone that correspond to the above ray:
sage: face.ambient_ray_indices()
(0,)
sage: quadrant.ray(0)
N(1, 0)
Note that it is OK to ask for faces of too small or high dimension:
sage: quadrant.faces(-1)
()
sage: quadrant.faces(3)
()
In the case of non-strictly convex cones even faces of small non-negative dimension may be missing:
sage: halfplane = Cone([(1,0), (0,1), (-1,0)])
sage: halfplane.faces(0)
()
sage: halfplane.faces()
((1-d face of 2-d cone in 2-d lattice N,),
(2-d cone in 2-d lattice N,))
sage: plane = Cone([(1,0), (0,1), (-1,-1)])
sage: plane.faces(1)
()
sage: plane.faces()
((2-d cone in 2-d lattice N,),)
TESTS:
Now we check that “general” cones whose dimension is smaller than the dimension of the ambient space work as expected (see trac ticket #9188):
sage: c = Cone([(1,1,1,3),(1,-1,1,3),(-1,-1,1,3)])
sage: c.faces()
((0-d face of 3-d cone in 4-d lattice N,),
(1-d face of 3-d cone in 4-d lattice N,
1-d face of 3-d cone in 4-d lattice N,
1-d face of 3-d cone in 4-d lattice N),
(2-d face of 3-d cone in 4-d lattice N,
2-d face of 3-d cone in 4-d lattice N,
2-d face of 3-d cone in 4-d lattice N),
(3-d cone in 4-d lattice N,))
We also ensure that a call to this function does not break facets() method (see trac ticket #9780):
sage: cone = toric_varieties.dP8().fan().generating_cone(0)
sage: cone
2-d cone of Rational polyhedral fan in 2-d lattice N
sage: for f in cone.facets(): print f.rays()
N(1, 1)
in 2-d lattice N
N(0, 1)
in 2-d lattice N
sage: len(cone.faces())
3
sage: for f in cone.facets(): print f.rays()
N(1, 1)
in 2-d lattice N
N(0, 1)
in 2-d lattice N
Return inward normals to facets of self.
Note
OUTPUT:
If the ambient lattice() of self is a toric lattice, the facet nomals will be elements of the dual lattice. If it is a general lattice (like ZZ^n) that does not have a dual() method, the facet normals will be returned as integral vectors.
EXAMPLES:
sage: cone = Cone([(1,0), (-1,3)])
sage: cone.facet_normals()
M(0, 1),
M(3, 1)
in 2-d lattice M
Now let’s look at a more complicated case:
sage: cone = Cone([(-2,-1,2), (4,1,0), (-4,-1,-5), (4,1,5)])
sage: cone.is_strictly_convex()
False
sage: cone.dim()
3
sage: cone.linear_subspace().dimension()
1
sage: lsg = (QQ^3)(cone.linear_subspace().gen(0)); lsg
(1, 1/4, 5/4)
sage: cone.facet_normals()
M(7, -18, -2),
M(1, -4, 0)
in 3-d lattice M
sage: [lsg*normal for normal in cone.facet_normals()]
[0, 0]
A lattice that does not have a dual() method:
sage: Cone([(1,1),(0,1)], lattice=ZZ^2).facet_normals()
(-1, 1),
( 1, 0)
in Ambient free module of rank 2
over the principal ideal domain Integer Ring
We correctly handle the degenerate cases:
sage: N = ToricLattice(2)
sage: Cone([], lattice=N).facet_normals() # empty cone
Empty collection
in 2-d lattice M
sage: Cone([(1,0)], lattice=N).facet_normals() # ray in 2d
M(1, 0)
in 2-d lattice M
sage: Cone([(1,0),(-1,0)], lattice=N).facet_normals() # line in 2d
Empty collection
in 2-d lattice M
sage: Cone([(1,0),(0,1)], lattice=N).facet_normals() # strictly convex cone
M(0, 1),
M(1, 0)
in 2-d lattice M
sage: Cone([(1,0),(-1,0),(0,1)], lattice=N).facet_normals() # half space
M(0, 1)
in 2-d lattice M
sage: Cone([(1,0),(0,1),(-1,-1)], lattice=N).facet_normals() # whole space
Empty collection
in 2-d lattice M
Return cones of the ambient face lattice having self as a facet.
OUTPUT:
EXAMPLES:
sage: octant = Cone([(1,0,0), (0,1,0), (0,0,1)])
sage: octant.facet_of()
()
sage: one_face = octant.faces(1)[0]
sage: len(one_face.facet_of())
2
sage: one_face.facet_of()[1]
2-d face of 3-d cone in 3-d lattice N
While fan is the top element of its own cone lattice, which is a variant of a face lattice, we do not refer to cones as its facets:
sage: fan = Fan([octant])
sage: fan.generating_cone(0).facet_of()
()
Subcones of generating cones work as before:
sage: one_cone = fan(1)[0]
sage: len(one_cone.facet_of())
2
Return facets (faces of codimension 1) of self.
OUTPUT:
EXAMPLES:
sage: quadrant = Cone([(1,0), (0,1)])
sage: quadrant.facets()
(1-d face of 2-d cone in 2-d lattice N,
1-d face of 2-d cone in 2-d lattice N)
Check if a given point is contained in the interior of self.
For a cone of strictly lower-dimension than the ambient space, the interior is always empty. You probably want to use relative_interior_contains() in this case.
INPUT:
OUTPUT:
EXAMPLES:
sage: c = Cone([(1,0), (0,1)])
sage: c.contains((1,1))
True
sage: c.interior_contains((1,1))
True
sage: c.contains((1,0))
True
sage: c.interior_contains((1,0))
False
Compute the intersection of two cones.
INPUT:
OUTPUT:
Raises ValueError if the ambient space dimensions are not compatible.
EXAMPLES:
sage: cone1 = Cone([(1,0), (-1, 3)])
sage: cone2 = Cone([(-1,0), (2, 5)])
sage: cone1.intersection(cone2).rays()
N(-1, 3),
N( 2, 5)
in 2-d lattice N
It is OK to intersect cones living in sublattices of the same ambient lattice:
sage: N = cone1.lattice()
sage: Ns = N.submodule([(1,1)])
sage: cone3 = Cone([(1,1)], lattice=Ns)
sage: I = cone1.intersection(cone3)
sage: I.rays()
N(1, 1)
in Sublattice <N(1, 1)>
sage: I.lattice()
Sublattice <N(1, 1)>
But you cannot intersect cones from incompatible lattices without explicit conversion:
sage: cone1.intersection(cone1.dual())
Traceback (most recent call last):
...
ValueError: 2-d lattice N and 2-d lattice M
have different ambient lattices!
sage: cone1.intersection(Cone(cone1.dual().rays(), N)).rays()
N(3, 1),
N(0, 1)
in 2-d lattice N
Check if self is “mathematically” the same as other.
INPUT:
OUTPUT:
There are three different equivalences between cones \(C_1\) and \(C_2\) in the same lattice:
EXAMPLES:
sage: cone1 = Cone([(1,0), (-1, 3)])
sage: cone2 = Cone([(-1,3), (1, 0)])
sage: cone1.rays()
N( 1, 0),
N(-1, 3)
in 2-d lattice N
sage: cone2.rays()
N(-1, 3),
N( 1, 0)
in 2-d lattice N
sage: cone1 == cone2
False
sage: cone1.is_equivalent(cone2)
True
Check if self forms a face of another cone.
INPUT:
OUTPUT:
EXAMPLES:
sage: quadrant = Cone([(1,0), (0,1)])
sage: cone1 = Cone([(1,0)])
sage: cone2 = Cone([(1,2)])
sage: quadrant.is_face_of(quadrant)
True
sage: cone1.is_face_of(quadrant)
True
sage: cone2.is_face_of(quadrant)
False
Being a face means more than just saturating a facet inequality:
sage: octant = Cone([(1,0,0), (0,1,0), (0,0,1)])
sage: cone = Cone([(2,1,0),(1,2,0)])
sage: cone.is_face_of(octant)
False
Check if self is in the same \(GL(n, \ZZ)\)-orbit as other.
INPUT:
OUTPUT:
There are three different equivalences between cones \(C_1\) and \(C_2\) in the same lattice:
EXAMPLES:
sage: cone1 = Cone([(1,0), (0, 3)])
sage: m = matrix(ZZ, [(1, -5), (-1, 4)]) # a GL(2,ZZ)-matrix
sage: cone2 = Cone([m*r for r in cone1.rays()])
sage: cone1.is_isomorphic(cone2)
True
sage: cone1 = Cone([(1,0), (0, 3)])
sage: cone2 = Cone([(-1,3), (1, 0)])
sage: cone1.is_isomorphic(cone2)
False
TESTS:
sage: from sage.geometry.cone import classify_cone_2d
sage: classify_cone_2d(*cone1.rays())
(1, 0)
sage: classify_cone_2d(*cone2.rays())
(3, 2)
Check if self is simplicial.
A cone is called simplicial if primitive vectors along its generating rays form a part of a rational basis of the ambient space.
OUTPUT:
EXAMPLES:
sage: cone1 = Cone([(1,0), (0, 3)])
sage: cone2 = Cone([(1,0), (0, 3), (-1,-1)])
sage: cone1.is_simplicial()
True
sage: cone2.is_simplicial()
False
Check if self is smooth.
A cone is called smooth if primitive vectors along its generating rays form a part of an integral basis of the ambient space. Equivalently, they generate the whole lattice on the linear subspace spanned by the rays.
OUTPUT:
EXAMPLES:
sage: cone1 = Cone([(1,0), (0, 1)])
sage: cone2 = Cone([(1,0), (-1, 3)])
sage: cone1.is_smooth()
True
sage: cone2.is_smooth()
False
The following cones are the same up to a \(SL(2,\ZZ)\) coordinate transformation:
sage: Cone([(1,0,0), (2,1,-1)]).is_smooth()
True
sage: Cone([(1,0,0), (2,1,1)]).is_smooth()
True
sage: Cone([(1,0,0), (2,1,2)]).is_smooth()
True
Check if self is strictly convex.
A cone is called strictly convex if it does not contain any lines.
OUTPUT:
EXAMPLES:
sage: cone1 = Cone([(1,0), (0, 1)])
sage: cone2 = Cone([(1,0), (-1, 0)])
sage: cone1.is_strictly_convex()
True
sage: cone2.is_strictly_convex()
False
Checks if the cone has no rays.
OUTPUT:
EXAMPLES:
sage: c0 = Cone([], lattice=ToricLattice(3))
sage: c0.is_trivial()
True
sage: c0.nrays()
0
Return the lattice polytope associated to self.
The vertices of this polytope are primitive vectors along the generating rays of self and the origin, if self is strictly convex. In this case the origin is the last vertex, so the \(i\)-th ray of the cone always corresponds to the \(i\)-th vertex of the polytope.
See also polyhedron().
OUTPUT:
EXAMPLES:
sage: quadrant = Cone([(1,0), (0,1)])
sage: lp = quadrant.lattice_polytope()
doctest:...: DeprecationWarning: lattice_polytope(...) is deprecated!
See http://trac.sagemath.org/16180 for details.
sage: lp
2-d lattice polytope in 2-d lattice N
sage: lp.vertices_pc()
N(1, 0),
N(0, 1),
N(0, 0)
in 2-d lattice N
sage: line = Cone([(1,0), (-1,0)])
sage: lp = line.lattice_polytope()
sage: lp
1-d lattice polytope in 2-d lattice N
sage: lp.vertices_pc()
N( 1, 0),
N(-1, 0)
in 2-d lattice N
Return a set of lines generating the linear subspace of self.
OUTPUT:
EXAMPLES:
sage: halfplane = Cone([(1,0), (0,1), (-1,0)])
sage: halfplane.line_set()
doctest:...: DeprecationWarning:
line_set(...) is deprecated, please use lines().set() instead!
See http://trac.sagemath.org/12544 for details.
frozenset([N(1, 0)])
sage: fullplane = Cone([(1,0), (0,1), (-1,-1)])
sage: fullplane.line_set()
frozenset([N(0, 1), N(1, 0)])
Return the largest linear subspace contained inside of self.
OUTPUT:
EXAMPLES:
sage: halfplane = Cone([(1,0), (0,1), (-1,0)])
sage: halfplane.linear_subspace()
Vector space of degree 2 and dimension 1 over Rational Field
Basis matrix:
[1 0]
Return lines generating the linear subspace of self.
OUTPUT:
EXAMPLES:
sage: halfplane = Cone([(1,0), (0,1), (-1,0)])
sage: halfplane.lines()
N(1, 0)
in 2-d lattice N
sage: fullplane = Cone([(1,0), (0,1), (-1,-1)])
sage: fullplane.lines()
N(0, 1),
N(1, 0)
in 2-d lattice N
The sublattice (in the dual lattice) orthogonal to the sublattice spanned by the cone.
Let \(M=\) self.dual_lattice() be the lattice dual to the ambient lattice of the given cone \(\sigma\). Then, in the notation of [Fulton], this method returns the sublattice
INPUT:
OUTPUT:
EXAMPLES:
sage: c = Cone([(1,1,1), (1,-1,1), (-1,-1,1), (-1,1,1)])
sage: c.orthogonal_sublattice()
Sublattice <>
sage: c12 = Cone([(1,1,1), (1,-1,1)])
sage: c12.sublattice()
Sublattice <N(1, -1, 1), N(0, 1, 0)>
sage: c12.orthogonal_sublattice()
Sublattice <M(1, 0, -1)>
Plot self.
INPUT:
OUTPUT:
EXAMPLES:
sage: quadrant = Cone([(1,0), (0,1)])
sage: quadrant.plot()
Return the polyhedron associated to self.
Mathematically this polyhedron is the same as self.
OUTPUT:
EXAMPLES:
sage: quadrant = Cone([(1,0), (0,1)])
sage: quadrant.polyhedron()
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull
of 1 vertex and 2 rays
sage: line = Cone([(1,0), (-1,0)])
sage: line.polyhedron()
A 1-dimensional polyhedron in ZZ^2 defined as the convex hull
of 1 vertex and 1 line
Here is an example of a trivial cone (see trac ticket #10237):
sage: origin = Cone([], lattice=ZZ^2)
sage: origin.polyhedron()
A 0-dimensional polyhedron in ZZ^2 defined as the convex hull
of 1 vertex
Check if a given point is contained in the relative interior of self.
For a full-dimensional cone the relative interior is simply the interior, so this method will do the same check as interior_contains(). For a strictly lower-dimensional cone, the relative interior is the cone without its facets.
INPUT:
OUTPUT:
EXAMPLES:
sage: c = Cone([(1,0,0), (0,1,0)])
sage: c.contains((1,1,0))
True
sage: c.relative_interior_contains((1,1,0))
True
sage: c.interior_contains((1,1,0))
False
sage: c.contains((1,0,0))
True
sage: c.relative_interior_contains((1,0,0))
False
sage: c.interior_contains((1,0,0))
False
The quotient of the dual spanned lattice by the dual of the supercone’s spanned lattice.
In the notation of [Fulton], if supercone = \(\rho > \sigma\) = self is a cone that contains \(\sigma\) as a face, then \(M(\rho)\) = supercone.orthogonal_sublattice() is a saturated sublattice of \(M(\sigma)\) = self.orthogonal_sublattice(). This method returns the quotient lattice. The lifts of the quotient generators are \(\dim(\rho)-\dim(\sigma)\) linearly independent M-lattice lattice points that, together with \(M(\rho)\), generate \(M(\sigma)\).
OUTPUT:
If we call the output Mrho, then
- Mrho.cover() == self.orthogonal_sublattice(), and
- Mrho.relations() == supercone.orthogonal_sublattice().
Note
EXAMPLES:
sage: rho = Cone([(1,1,1,3),(1,-1,1,3),(-1,-1,1,3),(-1,1,1,3)])
sage: rho.orthogonal_sublattice()
Sublattice <M(0, 0, 3, -1)>
sage: sigma = rho.facets()[1]
sage: sigma.orthogonal_sublattice()
Sublattice <M(0, 1, 1, 0), M(0, 0, 3, -1)>
sage: sigma.is_face_of(rho)
True
sage: Q = sigma.relative_orthogonal_quotient(rho); Q
1-d lattice, quotient
of Sublattice <M(0, 1, 1, 0), M(0, 0, 3, -1)>
by Sublattice <M(0, 0, 3, -1)>
sage: Q.gens()
(M[0, 1, 1, 0],)
Different codimension:
sage: rho = Cone([[1,-1,1,3],[-1,-1,1,3]])
sage: sigma = rho.facets()[0]
sage: sigma.orthogonal_sublattice()
Sublattice <M(1, 0, 2, -1), M(0, 1, 1, 0), M(0, 0, 3, -1)>
sage: rho.orthogonal_sublattice()
Sublattice <M(0, 1, 1, 0), M(0, 0, 3, -1)>
sage: sigma.relative_orthogonal_quotient(rho).gens()
(M[-1, 0, -2, 1],)
Sign choice in the codimension one case:
sage: sigma1 = Cone([(1, 2, 3), (1, -1, 1), (-1, 1, 1), (-1, -1, 1)]) # 3d
sage: sigma2 = Cone([(1, 1, -1), (1, 2, 3), (1, -1, 1), (1, -1, -1)]) # 3d
sage: rho = sigma1.intersection(sigma2)
sage: rho.relative_orthogonal_quotient(sigma1).gens()
(M[-5, -2, 3],)
sage: rho.relative_orthogonal_quotient(sigma2).gens()
(M[5, 2, -3],)
The quotient of the spanned lattice by the lattice spanned by a subcone.
In the notation of [Fulton], let \(N\) be the ambient lattice and \(N_\sigma\) the sublattice spanned by the given cone \(\sigma\). If \(\rho < \sigma\) is a subcone, then \(N_\rho\) = rho.sublattice() is a saturated sublattice of \(N_\sigma\) = self.sublattice(). This method returns the quotient lattice. The lifts of the quotient generators are \(\dim(\sigma)-\dim(\rho)\) linearly independent primitive lattice lattice points that, together with \(N_\rho\), generate \(N_\sigma\).
OUTPUT:
Note
EXAMPLES:
sage: sigma = Cone([(1,1,1,3),(1,-1,1,3),(-1,-1,1,3),(-1,1,1,3)])
sage: rho = Cone([(-1, -1, 1, 3), (-1, 1, 1, 3)])
sage: sigma.sublattice()
Sublattice <N(-1, -1, 1, 3), N(1, 0, 0, 0), N(1, 1, 0, 0)>
sage: rho.sublattice()
Sublattice <N(-1, 1, 1, 3), N(0, -1, 0, 0)>
sage: sigma.relative_quotient(rho)
1-d lattice, quotient
of Sublattice <N(-1, -1, 1, 3), N(1, 0, 0, 0), N(1, 1, 0, 0)>
by Sublattice <N(1, 0, -1, -3), N(0, 1, 0, 0)>
sage: sigma.relative_quotient(rho).gens()
(N[1, 1, 0, 0],)
More complicated example:
sage: rho = Cone([(1, 2, 3), (1, -1, 1)])
sage: sigma = Cone([(1, 2, 3), (1, -1, 1), (-1, 1, 1), (-1, -1, 1)])
sage: N_sigma = sigma.sublattice()
sage: N_sigma
Sublattice <N(-1, 1, 1), N(1, 2, 3), N(0, 1, 1)>
sage: N_rho = rho.sublattice()
sage: N_rho
Sublattice <N(1, -1, 1), N(1, 2, 3)>
sage: sigma.relative_quotient(rho).gens()
(N[0, 1, 1],)
sage: N = rho.lattice()
sage: N_sigma == N.span(N_rho.gens() + tuple(q.lift()
... for q in sigma.relative_quotient(rho).gens()))
True
Sign choice in the codimension one case:
sage: sigma1 = Cone([(1, 2, 3), (1, -1, 1), (-1, 1, 1), (-1, -1, 1)]) # 3d
sage: sigma2 = Cone([(1, 1, -1), (1, 2, 3), (1, -1, 1), (1, -1, -1)]) # 3d
sage: rho = sigma1.intersection(sigma2)
sage: rho.sublattice()
Sublattice <N(1, -1, 1), N(1, 2, 3)>
sage: sigma1.relative_quotient(rho)
1-d lattice, quotient
of Sublattice <N(-1, 1, 1), N(1, 2, 3), N(0, 1, 1)>
by Sublattice <N(1, 2, 3), N(0, 3, 2)>
sage: sigma1.relative_quotient(rho).gens()
(N[0, 1, 1],)
sage: sigma2.relative_quotient(rho).gens()
(N[-1, 0, -2],)
Return generators for the semigroup of lattice points of self.
OUTPUT:
Note
No attempt is made to return a minimal set of generators, see Hilbert_basis() for that.
EXAMPLES:
The following command ensures that the output ordering in the examples below is independent of TOPCOM, you don’t have to use it:
sage: PointConfiguration.set_engine('internal')
We start with a simple case of a non-smooth 2-dimensional cone:
sage: Cone([ (1,0), (1,2) ]).semigroup_generators()
N(1, 1),
N(1, 0),
N(1, 2)
in 2-d lattice N
A non-simplicial cone works, too:
sage: cone = Cone([(3,0,-1), (1,-1,0), (0,1,0), (0,0,1)])
sage: cone.semigroup_generators()
(N(1, 0, 0), N(0, 0, 1), N(0, 1, 0), N(3, 0, -1), N(1, -1, 0))
GAP’s toric package thinks this is challenging:
sage: cone = Cone([[1,2,3,4],[0,1,0,7],[3,1,0,2],[0,0,1,0]]).dual()
sage: len( cone.semigroup_generators() )
2806
The cone need not be strictly convex:
sage: halfplane = Cone([(1,0),(2,1),(-1,0)])
sage: halfplane.semigroup_generators()
(N(0, 1), N(1, 0), N(-1, 0))
sage: line = Cone([(1,1,1),(-1,-1,-1)])
sage: line.semigroup_generators()
(N(1, 1, 1), N(-1, -1, -1))
sage: wedge = Cone([ (1,0,0), (1,2,0), (0,0,1), (0,0,-1) ])
sage: wedge.semigroup_generators()
(N(1, 0, 0), N(1, 1, 0), N(1, 2, 0), N(0, 0, 1), N(0, 0, -1))
Nor does it have to be full-dimensional (see http://trac.sagemath.org/sage_trac/ticket/11312):
sage: Cone([(1,1,0), (-1,1,0)]).semigroup_generators()
N( 0, 1, 0),
N( 1, 1, 0),
N(-1, 1, 0)
in 3-d lattice N
Neither full-dimensional nor simplicial:
sage: A = matrix([(1, 3, 0), (-1, 0, 1), (1, 1, -2), (15, -2, 0)])
sage: A.elementary_divisors()
[1, 1, 1, 0]
sage: cone3d = Cone([(3,0,-1), (1,-1,0), (0,1,0), (0,0,1)])
sage: rays = [ A*vector(v) for v in cone3d.rays() ]
sage: gens = Cone(rays).semigroup_generators(); gens
(N(1, -1, 1, 15), N(0, 1, -2, 0), N(-2, -1, 0, 17), N(3, -4, 5, 45), N(3, 0, 1, -2))
sage: set(map(tuple,gens)) == set([ tuple(A*r) for r in cone3d.semigroup_generators() ])
True
TESTS:
sage: len(Cone(identity_matrix(10).rows()).semigroup_generators())
10
sage: trivial_cone = Cone([], lattice=ToricLattice(3))
sage: trivial_cone.semigroup_generators()
Empty collection
in 3-d lattice N
ALGORITHM:
If the cone is not simplicial, it is first triangulated. Each simplicial subcone has the integral points of the spaned parallelotope as generators. This is the first step of the primal Normaliz algorithm, see [Normaliz]. For each simplicial cone (of dimension \(d\)), the integral points of the open parallelotope
are then computed [BrunsKoch].
Finally, the the union of the generators of all simplicial subcones is returned.
REFERENCES:
[BrunsKoch] | W. Bruns and R. Koch, Computing the integral closure of an affine semigroup. Uni. Iaggelonicae Acta Math. 39, (2001), 59-70 |
Return the quotient of self by the linear subspace.
We define the strict quotient of a cone to be the image of this cone in the quotient of the ambient space by the linear subspace of the cone, i.e. it is the “complementary part” to the linear subspace.
OUTPUT:
EXAMPLES:
sage: halfplane = Cone([(1,0), (0,1), (-1,0)])
sage: ssc = halfplane.strict_quotient()
sage: ssc
1-d cone in 1-d lattice N
sage: ssc.rays()
N(1)
in 1-d lattice N
sage: line = Cone([(1,0), (-1,0)])
sage: ssc = line.strict_quotient()
sage: ssc
0-d cone in 1-d lattice N
sage: ssc.rays()
Empty collection
in 1-d lattice N
The sublattice spanned by the cone.
Let \(\sigma\) be the given cone and \(N=\) self.lattice() the ambient lattice. Then, in the notation of [Fulton], this method returns the sublattice
INPUT:
OUTPUT:
Note
EXAMPLES:
sage: cone = Cone([(1, 1, 1), (1, -1, 1), (-1, -1, 1), (-1, 1, 1)])
sage: cone.rays().basis()
N( 1, 1, 1),
N( 1, -1, 1),
N(-1, -1, 1)
in 3-d lattice N
sage: cone.rays().basis().matrix().det()
-4
sage: cone.sublattice()
Sublattice <N(-1, -1, 1), N(1, 0, 0), N(1, 1, 0)>
sage: matrix( cone.sublattice().gens() ).det()
1
Another example:
sage: c = Cone([(1,2,3), (4,-5,1)])
sage: c
2-d cone in 3-d lattice N
sage: c.rays()
N(1, 2, 3),
N(4, -5, 1)
in 3-d lattice N
sage: c.sublattice()
Sublattice <N(1, 2, 3), N(4, -5, 1)>
sage: c.sublattice(5, -3, 4)
N(5, -3, 4)
sage: c.sublattice(1, 0, 0)
Traceback (most recent call last):
...
TypeError: element (= [1, 0, 0]) is not in free module
A complement of the sublattice spanned by the cone.
In other words, sublattice() and sublattice_complement() together form a \(\ZZ\)-basis for the ambient lattice().
In the notation of [Fulton], let \(\sigma\) be the given cone and \(N=\) self.lattice() the ambient lattice. Then this method returns
lifted (non-canonically) to a sublattice of \(N\).
INPUT:
OUTPUT:
EXAMPLES:
sage: C2_Z2 = Cone([(1,0),(1,2)]) # C^2/Z_2
sage: c1, c2 = C2_Z2.facets()
sage: c2.sublattice()
Sublattice <N(1, 2)>
sage: c2.sublattice_complement()
Sublattice <N(0, 1)>
A more complicated example:
sage: c = Cone([(1,2,3), (4,-5,1)])
sage: c.sublattice()
Sublattice <N(1, 2, 3), N(4, -5, 1)>
sage: c.sublattice_complement()
Sublattice <N(0, -6, -5)>
sage: m = matrix( c.sublattice().gens() + c.sublattice_complement().gens() )
sage: m
[ 1 2 3]
[ 4 -5 1]
[ 0 -6 -5]
sage: m.det()
-1
The quotient of the ambient lattice by the sublattice spanned by the cone.
INPUT:
OUTPUT:
EXAMPLES:
sage: C2_Z2 = Cone([(1,0),(1,2)]) # C^2/Z_2
sage: c1, c2 = C2_Z2.facets()
sage: c2.sublattice_quotient()
1-d lattice, quotient of 2-d lattice N by Sublattice <N(1, 2)>
sage: N = C2_Z2.lattice()
sage: n = N(1,1)
sage: n_bar = c2.sublattice_quotient(n); n_bar
N[1, 1]
sage: n_bar.lift()
N(1, 1)
sage: vector(n_bar)
(-1)
Bases: sage.structure.sage_object.SageObject, _abcoll.Hashable, _abcoll.Iterable
Create a collection of integral rays.
Warning
No correctness check or normalization is performed on the input data. This class is designed for internal operations and you probably should not use it directly.
This is a base class for convex rational polyhedral cones and fans.
Ray collections are immutable, but they cache most of the returned values.
INPUT:
OUTPUT:
Return the Cartesian product of self with other.
INPUT:
OUTPUT:
By the Cartesian product of ray collections \((r_0, \dots, r_{n-1})\) and \((s_0, \dots, s_{m-1})\) we understand the ray collection of the form \(((r_0, 0), \dots, (r_{n-1}, 0), (0, s_0), \dots, (0, s_{m-1}))\), which is suitable for Cartesian products of cones and fans. The ray order is guaranteed to be as described.
EXAMPLES:
sage: c = Cone([(1,)])
sage: c.cartesian_product(c) # indirect doctest
2-d cone in 2-d lattice N+N
sage: _.rays()
N+N(1, 0),
N+N(0, 1)
in 2-d lattice N+N
Return the dimension of the subspace spanned by rays of self.
OUTPUT:
EXAMPLES:
sage: c = Cone([(1,0)])
sage: c.lattice_dim()
2
sage: c.dim()
1
Return the dual of the ambient lattice of self.
OUTPUT:
EXAMPLES:
sage: c = Cone([(1,0)])
sage: c.dual_lattice()
2-d lattice M
sage: Cone([], ZZ^3).dual_lattice()
Ambient free module of rank 3
over the principal ideal domain Integer Ring
Return the ambient lattice of self.
OUTPUT:
EXAMPLES:
sage: c = Cone([(1,0)])
sage: c.lattice()
2-d lattice N
sage: Cone([], ZZ^3).lattice()
Ambient free module of rank 3
over the principal ideal domain Integer Ring
Return the dimension of the ambient lattice of self.
OUTPUT:
EXAMPLES:
sage: c = Cone([(1,0)])
sage: c.lattice_dim()
2
sage: c.dim()
1
Return the number of rays of self.
OUTPUT:
EXAMPLES:
sage: c = Cone([(1,0), (0,1)])
sage: c.nrays()
2
Plot self.
INPUT:
OUTPUT:
EXAMPLES:
sage: quadrant = Cone([(1,0), (0,1)])
sage: quadrant.plot()
Return the n-th ray of self.
INPUT:
OUTPUT:
EXAMPLES:
sage: c = Cone([(1,0), (0,1)])
sage: c.ray(0)
N(1, 0)
Return (some of the) rays of self.
INPUT:
OUTPUT:
EXAMPLES:
sage: c = Cone([(1,0), (0,1), (-1, 0)])
sage: c.rays()
N( 0, 1),
N( 1, 0),
N(-1, 0)
in 2-d lattice N
sage: c.rays([0, 2])
N( 0, 1),
N(-1, 0)
in 2-d lattice N
You can also give ray indices directly, without packing them into a list:
sage: c.rays(0, 2)
N( 0, 1),
N(-1, 0)
in 2-d lattice N
Return \((d,k)\) classifying the lattice cone spanned by the two rays.
INPUT:
OUTPUT:
A pair \((d,k)\) of integers classifying the cone up to \(GL(2, \ZZ)\) equivalence. See Proposition 10.1.1 of [CLS] for the definition. We return the unique \((d,k)\) with minmial \(k\), see Proposition 10.1.3 of [CLS].
EXAMPLES:
sage: ray0 = vector([1,0])
sage: ray1 = vector([2,3])
sage: from sage.geometry.cone import classify_cone_2d
sage: classify_cone_2d(ray0, ray1)
(3, 2)
sage: ray0 = vector([2,4,5])
sage: ray1 = vector([5,19,11])
sage: classify_cone_2d(ray0, ray1)
(3, 1)
sage: m = matrix(ZZ, [(19, -14, -115), (-2, 5, 25), (43, -42, -298)])
sage: m.det() # check that it is in GL(3,ZZ)
-1
sage: classify_cone_2d(m*ray0, m*ray1)
(3, 1)
TESTS:
Check using the connection between the Hilbert basis of the cone spanned by the two rays (in arbitrary dimension) and the Hirzebruch-Jung continued fraction expansion, see Chapter 10 of [CLS]
sage: from sage.geometry.cone import normalize_rays
sage: for i in range(10):
... ray0 = random_vector(ZZ, 3)
... ray1 = random_vector(ZZ, 3)
... if ray0.is_zero() or ray1.is_zero(): continue
... ray0, ray1 = normalize_rays([ray0, ray1], ZZ^3)
... d, k = classify_cone_2d(ray0, ray1, check=True)
... assert (d,k) == classify_cone_2d(ray1, ray0)
... if d == 0: continue
... frac = Hirzebruch_Jung_continued_fraction_list(k/d)
... if len(frac)>100: continue # avoid expensive computation
... hilb = Cone([ray0, ray1]).Hilbert_basis()
... assert len(hilb) == len(frac) + 1
Check if x is a cone.
INPUT:
OUTPUT:
EXAMPLES:
sage: from sage.geometry.cone import is_Cone
sage: is_Cone(1)
False
sage: quadrant = Cone([(1,0), (0,1)])
sage: quadrant
2-d cone in 2-d lattice N
sage: is_Cone(quadrant)
True
Normalize a list of rational rays: make them primitive and immutable.
INPUT:
OUTPUT:
EXAMPLES:
sage: from sage.geometry.cone import normalize_rays
sage: normalize_rays([(0, 1), (0, 2), (3, 2), (5/7, 10/3)], None)
[N(0, 1), N(0, 1), N(3, 2), N(3, 14)]
sage: L = ToricLattice(2, "L")
sage: normalize_rays([(0, 1), (0, 2), (3, 2), (5/7, 10/3)], L.dual())
[L*(0, 1), L*(0, 1), L*(3, 2), L*(3, 14)]
sage: ray_in_L = L(0,1)
sage: normalize_rays([ray_in_L, (0, 2), (3, 2), (5/7, 10/3)], None)
[L(0, 1), L(0, 1), L(3, 2), L(3, 14)]
sage: normalize_rays([(0, 1), (0, 2), (3, 2), (5/7, 10/3)], ZZ^2)
[(0, 1), (0, 1), (3, 2), (3, 14)]
sage: normalize_rays([(0, 1), (0, 2), (3, 2), (5/7, 10/3)], ZZ^3)
Traceback (most recent call last):
...
TypeError: cannot convert (0, 1) to
Vector space of dimension 3 over Rational Field!
sage: normalize_rays([], ZZ^3)
[]