Convex rational polyhedral cones

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:

  • Andrey Novoseltsev (2010-05-13): initial version.
  • Andrey Novoseltsev (2010-06-17): substantial improvement during review by Volker Braun.
  • Volker Braun (2010-06-21): various spanned/quotient/dual lattice computations added.
  • Volker Braun (2010-12-28): Hilbert basis for cones.
  • Andrey Novoseltsev (2012-02-23): switch to PointCollection container.

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(0, -1, 0), N(0, 0, 1), N(0, 1, 0), N(1, 0, 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
sage.geometry.cone.Cone(rays, lattice=None, check=True, normalize=True)

Construct a (not necessarily strictly) convex rational polyhedral cone.

INPUT:

  • rays – a list of rays. Each ray should be given as a list or a vector convertible to the rational extension of the given lattice. May also be specified by a Polyhedron_base object;
  • latticeToricLattice, \(\ZZ^n\), or any other object that behaves like these. If not specified, an attempt will be made to determine an appropriate toric lattice automatically;
  • check – by default the input data will be checked for correctness (e.g. that all rays have the same number of components) and generating rays will be constructed from rays. If you know that the input is a minimal set of generators of a valid cone, you may significantly decrease construction time using check=False option;
  • normalize – you can further speed up construction using normalize=False option. In this case rays must be a list of immutable primitive rays in lattice. In general, you should not use this option, it is designed for code optimization and does not give as drastic improvement in speed as the previous one.

OUTPUT:

  • convex rational polyhedral cone determined by rays.

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
class sage.geometry.cone.ConvexRationalPolyhedralCone(rays=None, lattice=None, ambient=None, ambient_ray_indices=None, PPL=None)

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:

  • rays – list of immutable primitive vectors in lattice;
  • latticeToricLattice, \(\ZZ^n\), or any other object that behaves like these. If None, it will be determined as parent() of the first ray. Of course, this cannot be done if there are no rays, so in this case you must give an appropriate lattice directly.

or (these parameters must be given as keywords):

  • ambient – ambient structure of this cone, a bigger cone or a fan, this cone must be a face of ambient;
  • ambient_ray_indices – increasing list or tuple of integers, indices of rays of ambient generating this cone.

In both cases, the following keyword parameter may be specified in addition:

  • PPL – either None (default) or a C_Polyhedron representing the cone. This serves only to cache the polyhedral data if you know it already. The polyhedron will be set immutable.

OUTPUT:

  • convex rational polyhedral cone.

Note

Every cone has its ambient structure. If it was not specified, it is this cone itself.

Hilbert_basis()

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/
Hilbert_coefficients(point)

Return the expansion coefficients of point with respect to Hilbert_basis().

INPUT:

  • point – a lattice() point in the cone, or something that can be converted to a point. For example, a list or tuple of integers.

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)
adjacent()

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:

  • \(F_1\) and \(F_2\) have the same dimension \(d\);
  • \(F_1\) and \(F_2\) share a facet of dimension \(d-1\);
  • \(F_1\) and \(F_2\) are facets of some face of dimension \(d+1\), unless \(d\) is the dimension of the ambient structure.

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.

ambient()

Return the ambient structure of self.

OUTPUT:

  • cone or fan containing self as a face.

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
ambient_ray_indices()

Return indices of rays of the ambient structure generating self.

OUTPUT:

  • increasing tuple of integers.

EXAMPLES:

sage: quadrant = Cone([(1,0), (0,1)])
sage: quadrant.ambient_ray_indices()
(0, 1)
sage: quadrant.facets()[1].ambient_ray_indices()
(1,)
cartesian_product(other, lattice=None)

Return the Cartesian product of self with other.

INPUT:

  • other – a cone;
  • lattice – (optional) the ambient lattice for the Cartesian product cone. By default, the direct sum of the ambient lattices of self and other is constructed.

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
contains(*args)

Check if a given point is contained in self.

INPUT:

  • anything. An attempt will be made to convert all arguments into a single element of the ambient space of self. If it fails, False will be returned.

OUTPUT:

  • True if the given point is contained in self, False otherwise.

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
dual()

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
embed(cone)

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:

  • a cone, equivalent to cone but sitting inside self.

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!
face_lattice()

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 with distinguished linear extension

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 with distinguished linear extension
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 with distinguished linear extension
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
faces(dim=None, codim=None)

Return faces of self of specified (co)dimension.

INPUT:

  • dim – integer, dimension of the requested faces;
  • codim – integer, codimension of the requested faces.

Note

You can specify at most one parameter. If you don’t give any, then all faces will be returned.

OUTPUT:

  • if either dim or codim is given, the output will be a tuple of cones;
  • if neither dim nor codim is given, the output will be the tuple of tuples as above, giving faces of all existing dimensions. If you care about inclusion relations between faces, consider using face_lattice() or adjacent(), facet_of(), and facets().

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
facet_normals()

Return inward normals to facets of self.

Note

  1. For a not full-dimensional cone facet normals will specify hyperplanes whose intersections with the space spanned by self give facets of self.
  2. For a not strictly convex cone facet normals will be orthogonal to the linear subspace of self, i.e. they always will be elements of the dual cone of self.
  3. The order of normals is random, but consistent with facets().

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
facet_of()

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
facets()

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)
interior_contains(*args)

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:

  • anything. An attempt will be made to convert all arguments into a single element of the ambient space of self. If it fails, False will be returned.

OUTPUT:

  • True if the given point is contained in the interior of self, False otherwise.

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
intersection(other)

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
is_equivalent(other)

Check if self is “mathematically” the same as other.

INPUT:

  • other - cone.

OUTPUT:

  • True if self and other define the same cones as sets of points in the same lattice, False otherwise.

There are three different equivalences between cones \(C_1\) and \(C_2\) in the same lattice:

  1. They have the same generating rays in the same order. This is tested by C1 == C2.
  2. They describe the same sets of points. This is tested by C1.is_equivalent(C2).
  3. They are in the same orbit of \(GL(n,\ZZ)\) (and, therefore, correspond to isomorphic affine toric varieties). This is tested by C1.is_isomorphic(C2).

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
is_face_of(cone)

Check if self forms a face of another cone.

INPUT:

  • cone – cone.

OUTPUT:

  • True if self is a face of cone, False otherwise.

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
is_isomorphic(other)

Check if self is in the same \(GL(n, \ZZ)\)-orbit as other.

INPUT:

  • other - cone.

OUTPUT:

  • True if self and other are in the same \(GL(n, \ZZ)\)-orbit, False otherwise.

There are three different equivalences between cones \(C_1\) and \(C_2\) in the same lattice:

  1. They have the same generating rays in the same order. This is tested by C1 == C2.
  2. They describe the same sets of points. This is tested by C1.is_equivalent(C2).
  3. They are in the same orbit of \(GL(n,\ZZ)\) (and, therefore, correspond to isomorphic affine toric varieties). This is tested by C1.is_isomorphic(C2).

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)
is_simplicial()

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:

  • True if self is simplicial, False otherwise.

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
is_smooth()

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:

  • True if self is smooth, False otherwise.

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
is_strictly_convex()

Check if self is strictly convex.

A cone is called strictly convex if it does not contain any lines.

OUTPUT:

  • True if self is strictly convex, False otherwise.

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
is_trivial()

Checks if the cone has no rays.

OUTPUT:

  • True if the cone has no rays, False otherwise.

EXAMPLES:

sage: c0 = Cone([], lattice=ToricLattice(3))
sage: c0.is_trivial()
True
sage: c0.nrays()
0
lattice_polytope()

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
line_set()

Return a set of lines generating the linear subspace of self.

OUTPUT:

  • frozenset of primitive vectors in the lattice of self giving directions of lines that span the linear subspace of self. These lines are arbitrary, but fixed. See also lines().

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)})
linear_subspace()

Return the largest linear subspace contained inside of self.

OUTPUT:

  • subspace of the ambient space of self.

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]
lines()

Return lines generating the linear subspace of self.

OUTPUT:

  • tuple of primitive vectors in the lattice of self giving directions of lines that span the linear subspace of self. These lines are arbitrary, but fixed. If you do not care about the order, see also line_set().

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
orthogonal_sublattice(*args, **kwds)

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

\[M(\sigma) \stackrel{\text{def}}{=} \sigma^\perp \cap M \subset M\]

INPUT:

  • either nothing or something that can be turned into an element of this lattice.

OUTPUT:

  • if no arguments were given, a toric sublattice, otherwise the corresponding element of it.

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(**options)

Plot self.

INPUT:

OUTPUT:

  • a plot.

EXAMPLES:

sage: quadrant = Cone([(1,0), (0,1)])
sage: quadrant.plot()
Graphics object consisting of 9 graphics primitives
polyhedron()

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
relative_interior_contains(*args)

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:

  • anything. An attempt will be made to convert all arguments into a single element of the ambient space of self. If it fails, False will be returned.

OUTPUT:

  • True if the given point is contained in the relative interior of self, False otherwise.

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
relative_orthogonal_quotient(supercone)

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

  • \(M(\sigma) / M(\rho)\) has no torsion since the sublattice \(M(\rho)\) is saturated.
  • In the codimension one case, (a lift of) the generator of \(M(\sigma) / M(\rho)\) is chosen to be positive on \(\sigma\).

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],)
relative_quotient(subcone)

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

  • The quotient \(N_\sigma / N_\rho\) of spanned sublattices has no torsion since the sublattice \(N_\rho\) is saturated.
  • In the codimension one case, the generator of \(N_\sigma / N_\rho\) is chosen to be in the same direction as the image \(\sigma / N_\rho\)

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],)
semigroup_generators()

Return generators for the semigroup of lattice points of self.

OUTPUT:

  • a PointCollection of lattice points generating the semigroup of lattice points contained in self.

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

\[\begin{split}par \langle x_1, \dots, x_d \rangle = \ZZ^n \cap \left\{ q_1 x_1 + \cdots +q_d x_d :~ 0 \leq q_i < 1 \right\}\end{split}\]

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
strict_quotient()

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:

  • cone.

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
sublattice(*args, **kwds)

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

\[N_\sigma \stackrel{\text{def}}{=} \mathop{span}( N\cap \sigma )\]

INPUT:

  • either nothing or something that can be turned into an element of this lattice.

OUTPUT:

  • if no arguments were given, a toric sublattice, otherwise the corresponding element of it.

Note

  • The sublattice spanned by the cone is the saturation of the sublattice generated by the rays of the cone.
  • See sage.geometry.cone.IntegralRayCollection.ray_basis() if you only need a \(\QQ\)-basis.
  • The returned lattice points are usually not rays of the cone. In fact, for a non-smooth cone the rays do not generate the sublattice \(N_\sigma\), but only a finite index sublattice.

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
sublattice_complement(*args, **kwds)

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

\[N(\sigma) \stackrel{\text{def}}{=} N / N_\sigma\]

lifted (non-canonically) to a sublattice of \(N\).

INPUT:

  • either nothing or something that can be turned into an element of this lattice.

OUTPUT:

  • if no arguments were given, a toric sublattice, otherwise the corresponding element of it.

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
sublattice_quotient(*args, **kwds)

The quotient of the ambient lattice by the sublattice spanned by the cone.

INPUT:

  • either nothing or something that can be turned into an element of this lattice.

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)
class sage.geometry.cone.IntegralRayCollection(rays, lattice)

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:

  • rays – list of immutable vectors in lattice;
  • latticeToricLattice, \(\ZZ^n\), or any other object that behaves like these. If None, it will be determined as parent() of the first ray. Of course, this cannot be done if there are no rays, so in this case you must give an appropriate lattice directly. Note that None is not the default value - you always must give this argument explicitly, even if it is None.

OUTPUT:

  • collection of given integral rays.
cartesian_product(other, lattice=None)

Return the Cartesian product of self with other.

INPUT:

  • other – an IntegralRayCollection;
  • lattice – (optional) the ambient lattice for the result. By default, the direct sum of the ambient lattices of self and other is constructed.

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
dim()

Return the dimension of the subspace spanned by rays of self.

OUTPUT:

  • integer.

EXAMPLES:

sage: c = Cone([(1,0)])
sage: c.lattice_dim()
2
sage: c.dim()
1
dual_lattice()

Return the dual of the ambient lattice of self.

OUTPUT:

  • lattice. If possible (that is, if lattice() has a dual() method), the dual lattice is returned. Otherwise, \(\ZZ^n\) is returned, where \(n\) is the dimension of lattice().

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
lattice()

Return the ambient lattice of self.

OUTPUT:

  • lattice.

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
lattice_dim()

Return the dimension of the ambient lattice of self.

OUTPUT:

  • integer.

EXAMPLES:

sage: c = Cone([(1,0)])
sage: c.lattice_dim()
2
sage: c.dim()
1
nrays()

Return the number of rays of self.

OUTPUT:

  • integer.

EXAMPLES:

sage: c = Cone([(1,0), (0,1)])
sage: c.nrays()
2
plot(**options)

Plot self.

INPUT:

OUTPUT:

  • a plot.

EXAMPLES:

sage: quadrant = Cone([(1,0), (0,1)])
sage: quadrant.plot()
Graphics object consisting of 9 graphics primitives
ray(n)

Return the n-th ray of self.

INPUT:

  • n – integer, an index of a ray of self. Enumeration of rays starts with zero.

OUTPUT:

  • ray, an element of the lattice of self.

EXAMPLES:

sage: c = Cone([(1,0), (0,1)])
sage: c.ray(0)
N(1, 0)
rays(*args)

Return (some of the) rays of self.

INPUT:

  • ray_list – a list of integers, the indices of the requested rays. If not specified, all rays of self will be returned.

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
sage.geometry.cone.classify_cone_2d(ray0, ray1, check=True)

Return \((d,k)\) classifying the lattice cone spanned by the two rays.

INPUT:

  • ray0, ray1 – two primitive integer vectors. The generators of the two rays generating the two-dimensional cone.
  • check – boolean (default: True). Whether to check the input rays for consistency.

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
sage.geometry.cone.is_Cone(x)

Check if x is a cone.

INPUT:

  • x – anything.

OUTPUT:

  • True if x is a cone and False otherwise.

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
sage.geometry.cone.normalize_rays(rays, lattice)

Normalize a list of rational rays: make them primitive and immutable.

INPUT:

  • rays – list of rays which can be converted to the rational extension of lattice;
  • latticeToricLattice, \(\ZZ^n\), or any other object that behaves like these. If None, an attempt will be made to determine an appropriate toric lattice automatically.

OUTPUT:

  • list of immutable primitive vectors of the lattice in the same directions as original rays.

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)
[]

Previous topic

Toric lattices

Next topic

Rational polyhedral fans

This Page