Lattice and reflexive polytopes#

This module provides tools for work with lattice and reflexive polytopes. A convex polytope is the convex hull of finitely many points in \(\RR^n\). The dimension \(n\) of a polytope is the smallest \(n\) such that the polytope can be embedded in \(\RR^n\).

A lattice polytope is a polytope whose vertices all have integer coordinates.

If \(L\) is a lattice polytope, the dual polytope of \(L\) is

\[\{y \in \ZZ^n : x\cdot y \geq -1 \text{ all } x \in L\}\]

A reflexive polytope is a lattice polytope, such that its polar is also a lattice polytope, i.e. it is bounded and has vertices with integer coordinates.

This Sage module uses Package for Analyzing Lattice Polytopes (PALP), which is a program written in C by Maximilian Kreuzer and Harald Skarke, which is freely available under the GNU license terms at http://hep.itp.tuwien.ac.at/~kreuzer/CY/. Moreover, PALP is included standard with Sage.

PALP is described in the paper arXiv math.SC/0204356. Its distribution also contains the application nef.x, which was created by Erwin Riegler and computes nef-partitions and Hodge data for toric complete intersections.

ACKNOWLEDGMENT: polytope.py module written by William Stein was used as an example of organizing an interface between an external program and Sage. William Stein also helped Andrey Novoseltsev with debugging and tuning of this module.

Robert Bradshaw helped Andrey Novoseltsev to realize plot3d function.

Note

IMPORTANT: PALP requires some parameters to be determined during compilation time, i.e., the maximum dimension of polytopes, the maximum number of points, etc. These limitations may lead to errors during calls to different functions of these module. Currently, a ValueError exception will be raised if the output of poly.x or nef.x is empty or contains the exclamation mark. The error message will contain the exact command that caused an error, the description and vertices of the polytope, and the obtained output.

Data obtained from PALP and some other data is cached and most returned values are immutable. In particular, you cannot change the vertices of the polytope or their order after creation of the polytope.

If you are going to work with large sets of data, take a look at all_* functions in this module. They precompute different data for sequences of polynomials with a few runs of external programs. This can significantly affect the time of future computations. You can also use dump/load, but not all data will be stored (currently only faces and the number of their internal and boundary points are stored, in addition to polytope vertices and its polar).

AUTHORS:

  • Andrey Novoseltsev (2007-01-11): initial version

  • Andrey Novoseltsev (2007-01-15): all_* functions

  • Andrey Novoseltsev (2008-04-01): second version, including:

    • dual nef-partitions and necessary convex_hull and minkowski_sum

    • built-in sequences of 2- and 3-dimensional reflexive polytopes

    • plot3d, skeleton_show

  • Andrey Novoseltsev (2009-08-26): dropped maximal dimension requirement

  • Andrey Novoseltsev (2010-12-15): new version of nef-partitions

  • Andrey Novoseltsev (2013-09-30): switch to PointCollection.

  • Maximilian Kreuzer and Harald Skarke: authors of PALP (which was also used to obtain the list of 3-dimensional reflexive polytopes)

  • Erwin Riegler: the author of nef.x

sage.geometry.lattice_polytope.LatticePolytope(data, compute_vertices=True, n=0, lattice=None)[source]#

Construct a lattice polytope.

INPUT:

  • data – points spanning the lattice polytope, specified as one of:

    • a point collection (this is the preferred input and it is the quickest and the most memory efficient one);

    • an iterable of iterables (for example, a list of vectors) defining the point coordinates;

    • a file with matrix data, opened for reading, or

    • a filename of such a file, see read_palp_point_collection() for the file format;

  • compute_vertices – boolean (default: True). If True, the

    convex hull of the given points will be computed for determining vertices. Otherwise, the given points must be vertices;

  • n – an integer (default: 0) if data is a name of a file,

    that contains data blocks for several polytopes, the n-th block will be used;

  • lattice – the ambient lattice of the polytope. If not given, a suitable lattice will be determined automatically, most likely the toric lattice \(M\) of the appropriate dimension.

OUTPUT:

EXAMPLES:

sage: points = [(1,0,0), (0,1,0), (0,0,1), (-1,0,0), (0,-1,0), (0,0,-1)]
sage: p = LatticePolytope(points)
sage: p
3-d reflexive polytope in 3-d lattice M
sage: p.vertices()
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1)
in 3-d lattice M
>>> from sage.all import *
>>> points = [(Integer(1),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0)), (Integer(0),Integer(0),Integer(1)), (-Integer(1),Integer(0),Integer(0)), (Integer(0),-Integer(1),Integer(0)), (Integer(0),Integer(0),-Integer(1))]
>>> p = LatticePolytope(points)
>>> p
3-d reflexive polytope in 3-d lattice M
>>> p.vertices()
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1)
in 3-d lattice M

We draw a pretty picture of the polytope in 3-dimensional space:

sage: p.plot3d().show()                                                         # needs palp sage.plot
>>> from sage.all import *
>>> p.plot3d().show()                                                         # needs palp sage.plot

Now we add an extra point, which is in the interior of the polytope…

sage: points.append((0,0,0))
sage: p = LatticePolytope(points)
sage: p.nvertices()
6
>>> from sage.all import *
>>> points.append((Integer(0),Integer(0),Integer(0)))
>>> p = LatticePolytope(points)
>>> p.nvertices()
6

You can suppress vertex computation for speed but this can lead to mistakes:

sage: p = LatticePolytope(points, compute_vertices=False)
...
sage: p.nvertices()
7
>>> from sage.all import *
>>> p = LatticePolytope(points, compute_vertices=False)
...
>>> p.nvertices()
7

Given points must be in the lattice:

sage: LatticePolytope([[1/2], [3/2]])
Traceback (most recent call last):
...
ValueError: points
[[1/2], [3/2]]
are not in 1-d lattice M!
>>> from sage.all import *
>>> LatticePolytope([[Integer(1)/Integer(2)], [Integer(3)/Integer(2)]])
Traceback (most recent call last):
...
ValueError: points
[[1/2], [3/2]]
are not in 1-d lattice M!

But it is OK to create polytopes of non-maximal dimension:

sage: p = LatticePolytope([(1,0,0), (0,1,0), (0,0,0),
....:       (-1,0,0), (0,-1,0), (0,0,0), (0,0,0)])
sage: p
2-d lattice polytope in 3-d lattice M
sage: p.vertices()
M(-1,  0, 0),
M( 0, -1, 0),
M( 1,  0, 0),
M( 0,  1, 0)
in 3-d lattice M
>>> from sage.all import *
>>> p = LatticePolytope([(Integer(1),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0)), (Integer(0),Integer(0),Integer(0)),
...       (-Integer(1),Integer(0),Integer(0)), (Integer(0),-Integer(1),Integer(0)), (Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(0))])
>>> p
2-d lattice polytope in 3-d lattice M
>>> p.vertices()
M(-1,  0, 0),
M( 0, -1, 0),
M( 1,  0, 0),
M( 0,  1, 0)
in 3-d lattice M

An empty lattice polytope can be considered as well:

sage: p = LatticePolytope([], lattice=ToricLattice(3).dual()); p
-1-d lattice polytope in 3-d lattice M
sage: p.lattice_dim()
3
sage: p.npoints()
0
sage: p.nfacets()
0
sage: p.points()
Empty collection
in 3-d lattice M
sage: p.faces()                                                                 # needs sage.graphs
((-1-d lattice polytope in 3-d lattice M,),)
>>> from sage.all import *
>>> p = LatticePolytope([], lattice=ToricLattice(Integer(3)).dual()); p
-1-d lattice polytope in 3-d lattice M
>>> p.lattice_dim()
3
>>> p.npoints()
0
>>> p.nfacets()
0
>>> p.points()
Empty collection
in 3-d lattice M
>>> p.faces()                                                                 # needs sage.graphs
((-1-d lattice polytope in 3-d lattice M,),)
class sage.geometry.lattice_polytope.LatticePolytopeClass(points=None, compute_vertices=None, ambient=None, ambient_vertex_indices=None, ambient_facet_indices=None)[source]#

Bases: ConvexSet_compact, Hashable, LatticePolytope

Create a lattice polytope.

Warning

This class does not perform any checks of correctness of input nor does it convert input into the standard representation. Use LatticePolytope() to construct lattice polytopes.

Lattice polytopes are immutable, but they cache most of the returned values.

INPUT:

The input can be either:

or (these parameters must be given as keywords):

  • ambient – ambient structure, this polytope must be a face of ambient;

  • ambient_vertex_indices – increasing list or tuple of integers, indices of vertices of ambient generating this polytope;

  • ambient_facet_indices – increasing list or tuple of integers, indices of facets of ambient generating this polytope.

OUTPUT:

  • lattice polytope.

Note

Every polytope has an ambient structure. If it was not specified, it is this polytope itself.

adjacent()[source]#

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: o = lattice_polytope.cross_polytope(3)
sage: o.adjacent()                                                          # needs sage.graphs
()
sage: face = o.faces(1)[0]                                                  # needs sage.graphs
sage: face.adjacent()                                                       # needs sage.graphs
(1-d face of 3-d reflexive polytope in 3-d lattice M,
 1-d face of 3-d reflexive polytope in 3-d lattice M,
 1-d face of 3-d reflexive polytope in 3-d lattice M,
 1-d face of 3-d reflexive polytope in 3-d lattice M)
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.adjacent()                                                          # needs sage.graphs
()
>>> face = o.faces(Integer(1))[Integer(0)]                                                  # needs sage.graphs
>>> face.adjacent()                                                       # needs sage.graphs
(1-d face of 3-d reflexive polytope in 3-d lattice M,
 1-d face of 3-d reflexive polytope in 3-d lattice M,
 1-d face of 3-d reflexive polytope in 3-d lattice M,
 1-d face of 3-d reflexive polytope in 3-d lattice M)
affine_transform(a=1, b=0)[source]#

Return a*P+b, where P is this lattice polytope.

Note

  1. While a and b may be rational, the final result must be a lattice polytope, i.e. all vertices must be integral.

  2. If the transform (restricted to this polytope) is bijective, facial structure will be preserved, e.g. the first facet of the image will be spanned by the images of vertices which span the first facet of the original polytope.

INPUT:

  • a – (default: 1) rational scalar or matrix

  • b – (default: 0) rational scalar or vector, scalars are interpreted as vectors with the same components

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(2)
sage: o.vertices()
M( 1,  0),
M( 0,  1),
M(-1,  0),
M( 0, -1)
in 2-d lattice M
sage: o.affine_transform(2).vertices()
M( 2,  0),
M( 0,  2),
M(-2,  0),
M( 0, -2)
in 2-d lattice M
sage: o.affine_transform(1,1).vertices()
M(2, 1),
M(1, 2),
M(0, 1),
M(1, 0)
in 2-d lattice M
sage: o.affine_transform(b=1).vertices()
M(2, 1),
M(1, 2),
M(0, 1),
M(1, 0)
in 2-d lattice M
sage: o.affine_transform(b=(1, 0)).vertices()
M(2,  0),
M(1,  1),
M(0,  0),
M(1, -1)
in 2-d lattice M
sage: a = matrix(QQ, 2, [1/2, 0, 0, 3/2])
sage: o.polar().vertices()
N( 1,  1),
N( 1, -1),
N(-1, -1),
N(-1,  1)
in 2-d lattice N
sage: o.polar().affine_transform(a, (1/2, -1/2)).vertices()
M(1,  1),
M(1, -2),
M(0, -2),
M(0,  1)
in 2-d lattice M
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(2))
>>> o.vertices()
M( 1,  0),
M( 0,  1),
M(-1,  0),
M( 0, -1)
in 2-d lattice M
>>> o.affine_transform(Integer(2)).vertices()
M( 2,  0),
M( 0,  2),
M(-2,  0),
M( 0, -2)
in 2-d lattice M
>>> o.affine_transform(Integer(1),Integer(1)).vertices()
M(2, 1),
M(1, 2),
M(0, 1),
M(1, 0)
in 2-d lattice M
>>> o.affine_transform(b=Integer(1)).vertices()
M(2, 1),
M(1, 2),
M(0, 1),
M(1, 0)
in 2-d lattice M
>>> o.affine_transform(b=(Integer(1), Integer(0))).vertices()
M(2,  0),
M(1,  1),
M(0,  0),
M(1, -1)
in 2-d lattice M
>>> a = matrix(QQ, Integer(2), [Integer(1)/Integer(2), Integer(0), Integer(0), Integer(3)/Integer(2)])
>>> o.polar().vertices()
N( 1,  1),
N( 1, -1),
N(-1, -1),
N(-1,  1)
in 2-d lattice N
>>> o.polar().affine_transform(a, (Integer(1)/Integer(2), -Integer(1)/Integer(2))).vertices()
M(1,  1),
M(1, -2),
M(0, -2),
M(0,  1)
in 2-d lattice M

While you can use rational transformation, the result must be integer:

sage: o.affine_transform(a)
Traceback (most recent call last):
...
ValueError: points
[(1/2, 0), (0, 3/2), (-1/2, 0), (0, -3/2)]
are not in 2-d lattice M!
>>> from sage.all import *
>>> o.affine_transform(a)
Traceback (most recent call last):
...
ValueError: points
[(1/2, 0), (0, 3/2), (-1/2, 0), (0, -3/2)]
are not in 2-d lattice M!
ambient()[source]#

Return the ambient structure of self.

OUTPUT:

  • lattice polytope containing self as a face.

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(3)
sage: o.ambient()
3-d reflexive polytope in 3-d lattice M
sage: o.ambient() is o
True

sage: # needs sage.graphs
sage: face = o.faces(1)[0]
sage: face
1-d face of 3-d reflexive polytope in 3-d lattice M
sage: face.ambient()
3-d reflexive polytope in 3-d lattice M
sage: face.ambient() is o
True
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.ambient()
3-d reflexive polytope in 3-d lattice M
>>> o.ambient() is o
True

>>> # needs sage.graphs
>>> face = o.faces(Integer(1))[Integer(0)]
>>> face
1-d face of 3-d reflexive polytope in 3-d lattice M
>>> face.ambient()
3-d reflexive polytope in 3-d lattice M
>>> face.ambient() is o
True
ambient_dim()[source]#

Return the dimension of the ambient lattice of self.

An alias is ambient_dim().

OUTPUT:

  • integer.

EXAMPLES:

sage: p = LatticePolytope([(1,0)])
sage: p.lattice_dim()
2
sage: p.dim()
0
>>> from sage.all import *
>>> p = LatticePolytope([(Integer(1),Integer(0))])
>>> p.lattice_dim()
2
>>> p.dim()
0
ambient_facet_indices()[source]#

Return indices of facets of the ambient polytope containing self.

OUTPUT:

  • increasing tuple of integers.

EXAMPLES:

The polytope itself is not contained in any of its facets:

sage: o = lattice_polytope.cross_polytope(3)
sage: o.ambient_facet_indices()
()
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.ambient_facet_indices()
()

But each of its other faces is contained in one or more facets:

sage: # needs sage.graphs
sage: face = o.faces(1)[0]
sage: face.ambient_facet_indices()
(4, 5)
sage: face.vertices()
M(1, 0, 0),
M(0, 1, 0)
in 3-d lattice M
sage: o.facets()[face.ambient_facet_indices()[0]].vertices()
M(1, 0,  0),
M(0, 1,  0),
M(0, 0, -1)
in 3-d lattice M
>>> from sage.all import *
>>> # needs sage.graphs
>>> face = o.faces(Integer(1))[Integer(0)]
>>> face.ambient_facet_indices()
(4, 5)
>>> face.vertices()
M(1, 0, 0),
M(0, 1, 0)
in 3-d lattice M
>>> o.facets()[face.ambient_facet_indices()[Integer(0)]].vertices()
M(1, 0,  0),
M(0, 1,  0),
M(0, 0, -1)
in 3-d lattice M
ambient_ordered_point_indices()[source]#

Return indices of points of the ambient polytope contained in this one.

OUTPUT:

  • tuple of integers such that ambient points in this order are geometrically ordered, e.g. for an edge points will appear from one end point to the other.

EXAMPLES:

sage: cube = lattice_polytope.cross_polytope(3).polar()
sage: face = cube.facets()[0]                                               # needs sage.graphs
sage: face.ambient_ordered_point_indices()                                  # needs palp sage.graphs
(5, 8, 4, 9, 10, 11, 6, 12, 7)
sage: cube.points(face.ambient_ordered_point_indices())                     # needs palp sage.graphs
N(-1, -1, -1),
N(-1, -1,  0),
N(-1, -1,  1),
N(-1,  0, -1),
N(-1,  0,  0),
N(-1,  0,  1),
N(-1,  1, -1),
N(-1,  1,  0),
N(-1,  1,  1)
in 3-d lattice N
>>> from sage.all import *
>>> cube = lattice_polytope.cross_polytope(Integer(3)).polar()
>>> face = cube.facets()[Integer(0)]                                               # needs sage.graphs
>>> face.ambient_ordered_point_indices()                                  # needs palp sage.graphs
(5, 8, 4, 9, 10, 11, 6, 12, 7)
>>> cube.points(face.ambient_ordered_point_indices())                     # needs palp sage.graphs
N(-1, -1, -1),
N(-1, -1,  0),
N(-1, -1,  1),
N(-1,  0, -1),
N(-1,  0,  0),
N(-1,  0,  1),
N(-1,  1, -1),
N(-1,  1,  0),
N(-1,  1,  1)
in 3-d lattice N
ambient_point_indices()[source]#

Return indices of points of the ambient polytope contained in this one.

OUTPUT:

  • tuple of integers, the order corresponds to the order of points of this polytope.

EXAMPLES:

sage: cube = lattice_polytope.cross_polytope(3).polar()
sage: face = cube.facets()[0]                                               # needs sage.graphs
sage: face.ambient_point_indices()                                          # needs palp sage.graphs
(4, 5, 6, 7, 8, 9, 10, 11, 12)
sage: cube.points(face.ambient_point_indices()) == face.points()            # needs palp sage.graphs
True
>>> from sage.all import *
>>> cube = lattice_polytope.cross_polytope(Integer(3)).polar()
>>> face = cube.facets()[Integer(0)]                                               # needs sage.graphs
>>> face.ambient_point_indices()                                          # needs palp sage.graphs
(4, 5, 6, 7, 8, 9, 10, 11, 12)
>>> cube.points(face.ambient_point_indices()) == face.points()            # needs palp sage.graphs
True
ambient_vector_space(base_field=None)[source]#

Return the ambient vector space.

It is the ambient lattice (lattice()) tensored with a field.

INPUT:

  • base_field – (default: the rationals) a field.

EXAMPLES:

sage: p = LatticePolytope([(1,0)])
sage: p.ambient_vector_space()
Vector space of dimension 2 over Rational Field
sage: p.ambient_vector_space(AA)                                            # needs sage.rings.number_field
Vector space of dimension 2 over Algebraic Real Field
>>> from sage.all import *
>>> p = LatticePolytope([(Integer(1),Integer(0))])
>>> p.ambient_vector_space()
Vector space of dimension 2 over Rational Field
>>> p.ambient_vector_space(AA)                                            # needs sage.rings.number_field
Vector space of dimension 2 over Algebraic Real Field
ambient_vertex_indices()[source]#

Return indices of vertices of the ambient structure generating self.

OUTPUT:

  • increasing tuple of integers.

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(3)
sage: o.ambient_vertex_indices()
(0, 1, 2, 3, 4, 5)
sage: face = o.faces(1)[0]                                                  # needs sage.graphs
sage: face.ambient_vertex_indices()                                         # needs sage.graphs
(0, 1)
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.ambient_vertex_indices()
(0, 1, 2, 3, 4, 5)
>>> face = o.faces(Integer(1))[Integer(0)]                                                  # needs sage.graphs
>>> face.ambient_vertex_indices()                                         # needs sage.graphs
(0, 1)
boundary_point_indices()[source]#

Return indices of (relative) boundary lattice points of this polytope.

OUTPUT:

  • increasing tuple of integers.

EXAMPLES:

All points but the origin are on the boundary of this square:

sage: square = lattice_polytope.cross_polytope(2).polar()
sage: square.points()                                                       # needs palp
N( 1,  1),
N( 1, -1),
N(-1, -1),
N(-1,  1),
N(-1,  0),
N( 0, -1),
N( 0,  0),
N( 0,  1),
N( 1,  0)
in 2-d lattice N
sage: square.boundary_point_indices()                                       # needs palp
(0, 1, 2, 3, 4, 5, 7, 8)
>>> from sage.all import *
>>> square = lattice_polytope.cross_polytope(Integer(2)).polar()
>>> square.points()                                                       # needs palp
N( 1,  1),
N( 1, -1),
N(-1, -1),
N(-1,  1),
N(-1,  0),
N( 0, -1),
N( 0,  0),
N( 0,  1),
N( 1,  0)
in 2-d lattice N
>>> square.boundary_point_indices()                                       # needs palp
(0, 1, 2, 3, 4, 5, 7, 8)

For an edge the boundary is formed by the end points:

sage: face = square.edges()[0]                                              # needs sage.graphs
sage: face.points()                                                         # needs sage.graphs
N(-1, -1),
N(-1,  1),
N(-1,  0)
in 2-d lattice N
sage: face.boundary_point_indices()                                         # needs sage.graphs
(0, 1)
>>> from sage.all import *
>>> face = square.edges()[Integer(0)]                                              # needs sage.graphs
>>> face.points()                                                         # needs sage.graphs
N(-1, -1),
N(-1,  1),
N(-1,  0)
in 2-d lattice N
>>> face.boundary_point_indices()                                         # needs sage.graphs
(0, 1)
boundary_points()[source]#

Return (relative) boundary lattice points of this polytope.

OUTPUT:

EXAMPLES:

All points but the origin are on the boundary of this square:

sage: square = lattice_polytope.cross_polytope(2).polar()
sage: square.boundary_points()                                              # needs palp
N( 1,  1),
N( 1, -1),
N(-1, -1),
N(-1,  1),
N(-1,  0),
N( 0, -1),
N( 0,  1),
N( 1,  0)
in 2-d lattice N
>>> from sage.all import *
>>> square = lattice_polytope.cross_polytope(Integer(2)).polar()
>>> square.boundary_points()                                              # needs palp
N( 1,  1),
N( 1, -1),
N(-1, -1),
N(-1,  1),
N(-1,  0),
N( 0, -1),
N( 0,  1),
N( 1,  0)
in 2-d lattice N

For an edge the boundary is formed by the end points:

sage: face = square.edges()[0]                                              # needs sage.graphs
sage: face.boundary_points()                                                # needs sage.graphs
N(-1, -1),
N(-1,  1)
in 2-d lattice N
>>> from sage.all import *
>>> face = square.edges()[Integer(0)]                                              # needs sage.graphs
>>> face.boundary_points()                                                # needs sage.graphs
N(-1, -1),
N(-1,  1)
in 2-d lattice N
contains(*args)[source]#

Check if a given point is contained in self.

INPUT:

  • 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: p = lattice_polytope.cross_polytope(2)
sage: p.contains(p.lattice()(1,0))
True
sage: p.contains((1,0))
True
sage: p.contains(1,0)
True
sage: p.contains((2,0))
False
>>> from sage.all import *
>>> p = lattice_polytope.cross_polytope(Integer(2))
>>> p.contains(p.lattice()(Integer(1),Integer(0)))
True
>>> p.contains((Integer(1),Integer(0)))
True
>>> p.contains(Integer(1),Integer(0))
True
>>> p.contains((Integer(2),Integer(0)))
False
dim()[source]#

Return the dimension of this polytope.

EXAMPLES:

We create a 3-dimensional octahedron and check its dimension:

sage: o = lattice_polytope.cross_polytope(3)
sage: o.dim()
3
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.dim()
3

Now we create a 2-dimensional diamond in a 3-dimensional space:

sage: p = LatticePolytope([(1,0,0), (0,1,0), (-1,0,0), (0,-1,0)])
sage: p.dim()
2
sage: p.lattice_dim()
3
>>> from sage.all import *
>>> p = LatticePolytope([(Integer(1),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0)), (-Integer(1),Integer(0),Integer(0)), (Integer(0),-Integer(1),Integer(0))])
>>> p.dim()
2
>>> p.lattice_dim()
3
distances(point=None)[source]#

Return the matrix of distances for this polytope or distances for the given point.

The matrix of distances m gives distances m[i,j] between the i-th facet (which is also the i-th vertex of the polar polytope in the reflexive case) and j-th point of this polytope.

If point is specified, integral distances from the point to all facets of this polytope will be computed.

EXAMPLES: The matrix of distances for a 3-dimensional octahedron:

sage: o = lattice_polytope.cross_polytope(3)
sage: o.distances()                                                         # needs palp
[2 0 0 0 2 2 1]
[2 2 0 0 0 2 1]
[2 2 2 0 0 0 1]
[2 0 2 0 2 0 1]
[0 0 2 2 2 0 1]
[0 0 0 2 2 2 1]
[0 2 0 2 0 2 1]
[0 2 2 2 0 0 1]
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.distances()                                                         # needs palp
[2 0 0 0 2 2 1]
[2 2 0 0 0 2 1]
[2 2 2 0 0 0 1]
[2 0 2 0 2 0 1]
[0 0 2 2 2 0 1]
[0 0 0 2 2 2 1]
[0 2 0 2 0 2 1]
[0 2 2 2 0 0 1]

Distances from facets to the point (1,2,3):

sage: o.distances([1,2,3])
(-3, 1, 7, 3, 1, -5, -1, 5)
>>> from sage.all import *
>>> o.distances([Integer(1),Integer(2),Integer(3)])
(-3, 1, 7, 3, 1, -5, -1, 5)

It is OK to use RATIONAL coordinates:

sage: o.distances([1,2,3/2])
(-3/2, 5/2, 11/2, 3/2, -1/2, -7/2, 1/2, 7/2)
sage: o.distances([1,2,sqrt(2)])                                            # needs sage.symbolic
Traceback (most recent call last):
...
TypeError: unable to convert sqrt(2) to an element of Rational Field
>>> from sage.all import *
>>> o.distances([Integer(1),Integer(2),Integer(3)/Integer(2)])
(-3/2, 5/2, 11/2, 3/2, -1/2, -7/2, 1/2, 7/2)
>>> o.distances([Integer(1),Integer(2),sqrt(Integer(2))])                                            # needs sage.symbolic
Traceback (most recent call last):
...
TypeError: unable to convert sqrt(2) to an element of Rational Field

Now we create a non-spanning polytope:

sage: p = LatticePolytope([(1,0,0), (0,1,0), (-1,0,0), (0,-1,0)])
sage: p.distances()                                                         # needs palp
[2 2 0 0 1]
[2 0 0 2 1]
[0 0 2 2 1]
[0 2 2 0 1]
sage: p.distances((1/2, 3, 0))                                              # needs palp
(9/2, -3/2, -5/2, 7/2)
>>> from sage.all import *
>>> p = LatticePolytope([(Integer(1),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0)), (-Integer(1),Integer(0),Integer(0)), (Integer(0),-Integer(1),Integer(0))])
>>> p.distances()                                                         # needs palp
[2 2 0 0 1]
[2 0 0 2 1]
[0 0 2 2 1]
[0 2 2 0 1]
>>> p.distances((Integer(1)/Integer(2), Integer(3), Integer(0)))                                              # needs palp
(9/2, -3/2, -5/2, 7/2)

This point is not even in the affine subspace of the polytope:

sage: p.distances((1, 1, 1))                                                # needs palp
(3, 1, -1, 1)
>>> from sage.all import *
>>> p.distances((Integer(1), Integer(1), Integer(1)))                                                # needs palp
(3, 1, -1, 1)
dual()[source]#

Return the dual face under face duality of polar reflexive polytopes.

This duality extends the correspondence between vertices and facets.

OUTPUT:

EXAMPLES:

sage: # needs sage.graphs
sage: o = lattice_polytope.cross_polytope(4)
sage: e = o.edges()[0]; e
1-d face of 4-d reflexive polytope in 4-d lattice M
sage: ed = e.dual(); ed
2-d face of 4-d reflexive polytope in 4-d lattice N
sage: ed.ambient() is e.ambient().polar()
True
sage: e.ambient_vertex_indices() == ed.ambient_facet_indices()
True
sage: e.ambient_facet_indices() == ed.ambient_vertex_indices()
True
>>> from sage.all import *
>>> # needs sage.graphs
>>> o = lattice_polytope.cross_polytope(Integer(4))
>>> e = o.edges()[Integer(0)]; e
1-d face of 4-d reflexive polytope in 4-d lattice M
>>> ed = e.dual(); ed
2-d face of 4-d reflexive polytope in 4-d lattice N
>>> ed.ambient() is e.ambient().polar()
True
>>> e.ambient_vertex_indices() == ed.ambient_facet_indices()
True
>>> e.ambient_facet_indices() == ed.ambient_vertex_indices()
True
dual_lattice()[source]#

Return the dual of the ambient lattice of self.

OUTPUT:

  • a 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 self.

EXAMPLES:

sage: LatticePolytope([(1,0)]).dual_lattice()
2-d lattice N
sage: LatticePolytope([], lattice=ZZ^3).dual_lattice()
Ambient free module of rank 3
over the principal ideal domain Integer Ring
>>> from sage.all import *
>>> LatticePolytope([(Integer(1),Integer(0))]).dual_lattice()
2-d lattice N
>>> LatticePolytope([], lattice=ZZ**Integer(3)).dual_lattice()
Ambient free module of rank 3
over the principal ideal domain Integer Ring
edges()[source]#

Return edges (faces of dimension 1) of self.

OUTPUT:

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(3)
sage: o.edges()                                                             # needs sage.graphs
(1-d face of 3-d reflexive polytope in 3-d lattice M,
...
 1-d face of 3-d reflexive polytope in 3-d lattice M)
sage: len(o.edges())                                                        # needs sage.graphs
12
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.edges()                                                             # needs sage.graphs
(1-d face of 3-d reflexive polytope in 3-d lattice M,
...
 1-d face of 3-d reflexive polytope in 3-d lattice M)
>>> len(o.edges())                                                        # needs sage.graphs
12
face_lattice()[source]#

Return the face lattice of self.

This lattice will have the empty polytope as the bottom and this polytope itself as the top.

OUTPUT:

EXAMPLES:

Let’s take a look at the face lattice of a square:

sage: square = LatticePolytope([(0,0), (1,0), (1,1), (0,1)])
sage: L = square.face_lattice(); L                                          # needs sage.graphs
Finite lattice containing 10 elements with distinguished linear extension
>>> from sage.all import *
>>> square = LatticePolytope([(Integer(0),Integer(0)), (Integer(1),Integer(0)), (Integer(1),Integer(1)), (Integer(0),Integer(1))])
>>> L = square.face_lattice(); L                                          # needs sage.graphs
Finite lattice containing 10 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)                             # needs sage.graphs
[-1-d face of 2-d lattice polytope in 2-d lattice M]
[0-d face of 2-d lattice polytope in 2-d lattice M,
 0-d face of 2-d lattice polytope in 2-d lattice M,
 0-d face of 2-d lattice polytope in 2-d lattice M,
 0-d face of 2-d lattice polytope in 2-d lattice M]
[1-d face of 2-d lattice polytope in 2-d lattice M,
 1-d face of 2-d lattice polytope in 2-d lattice M,
 1-d face of 2-d lattice polytope in 2-d lattice M,
 1-d face of 2-d lattice polytope in 2-d lattice M]
[2-d lattice polytope in 2-d lattice M]
>>> from sage.all import *
>>> for level in L.level_sets(): print(level)                             # needs sage.graphs
[-1-d face of 2-d lattice polytope in 2-d lattice M]
[0-d face of 2-d lattice polytope in 2-d lattice M,
 0-d face of 2-d lattice polytope in 2-d lattice M,
 0-d face of 2-d lattice polytope in 2-d lattice M,
 0-d face of 2-d lattice polytope in 2-d lattice M]
[1-d face of 2-d lattice polytope in 2-d lattice M,
 1-d face of 2-d lattice polytope in 2-d lattice M,
 1-d face of 2-d lattice polytope in 2-d lattice M,
 1-d face of 2-d lattice polytope in 2-d lattice M]
[2-d lattice polytope in 2-d lattice M]

For a particular face you can look at its actual vertices…

sage: face = L.level_sets()[1][0]                                           # needs sage.graphs
sage: face.vertices()                                                       # needs sage.graphs
M(0, 0)
in 2-d lattice M
>>> from sage.all import *
>>> face = L.level_sets()[Integer(1)][Integer(0)]                                           # needs sage.graphs
>>> face.vertices()                                                       # needs sage.graphs
M(0, 0)
in 2-d lattice M

… or you can see the index of the vertex of the original polytope that corresponds to the above one:

sage: face.ambient_vertex_indices()                                         # needs sage.graphs
(0,)
sage: square.vertex(0)
M(0, 0)
>>> from sage.all import *
>>> face.ambient_vertex_indices()                                         # needs sage.graphs
(0,)
>>> square.vertex(Integer(0))
M(0, 0)

An alternative to extracting faces from the face lattice is to use faces() method:

sage: face is square.faces(dim=0)[0]                                        # needs sage.graphs
True
>>> from sage.all import *
>>> face is square.faces(dim=Integer(0))[Integer(0)]                                        # needs sage.graphs
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]                                           # needs sage.graphs
sage: D = L.hasse_diagram()                                                 # needs sage.graphs
sage: sorted(D.neighbors(face))                                             # needs sage.graphs
[-1-d face of 2-d lattice polytope in 2-d lattice M,
 1-d face of 2-d lattice polytope in 2-d lattice M,
 1-d face of 2-d lattice polytope in 2-d lattice M]
>>> from sage.all import *
>>> face = L.level_sets()[Integer(1)][Integer(0)]                                           # needs sage.graphs
>>> D = L.hasse_diagram()                                                 # needs sage.graphs
>>> sorted(D.neighbors(face))                                             # needs sage.graphs
[-1-d face of 2-d lattice polytope in 2-d lattice M,
 1-d face of 2-d lattice polytope in 2-d lattice M,
 1-d face of 2-d lattice polytope in 2-d lattice M]

However, you can achieve some of this functionality using facets(), facet_of(), and adjacent() methods:

sage: # needs sage.graphs
sage: face = square.faces(0)[0]
sage: face
0-d face of 2-d lattice polytope in 2-d lattice M
sage: face.vertices()
M(0, 0)
in 2-d lattice M
sage: face.facets()
(-1-d face of 2-d lattice polytope in 2-d lattice M,)
sage: face.facet_of()
(1-d face of 2-d lattice polytope in 2-d lattice M,
 1-d face of 2-d lattice polytope in 2-d lattice M)
sage: face.adjacent()
(0-d face of 2-d lattice polytope in 2-d lattice M,
 0-d face of 2-d lattice polytope in 2-d lattice M)
sage: face.adjacent()[0].vertices()
M(1, 0)
in 2-d lattice M
>>> from sage.all import *
>>> # needs sage.graphs
>>> face = square.faces(Integer(0))[Integer(0)]
>>> face
0-d face of 2-d lattice polytope in 2-d lattice M
>>> face.vertices()
M(0, 0)
in 2-d lattice M
>>> face.facets()
(-1-d face of 2-d lattice polytope in 2-d lattice M,)
>>> face.facet_of()
(1-d face of 2-d lattice polytope in 2-d lattice M,
 1-d face of 2-d lattice polytope in 2-d lattice M)
>>> face.adjacent()
(0-d face of 2-d lattice polytope in 2-d lattice M,
 0-d face of 2-d lattice polytope in 2-d lattice M)
>>> face.adjacent()[Integer(0)].vertices()
M(1, 0)
in 2-d lattice M

Note that if p is a face of superp, then the face lattice of p consists of (appropriate) faces of superp:

sage: # needs sage.graphs
sage: superp = LatticePolytope([(1,2,3,4), (5,6,7,8),
....:                           (1,2,4,8), (1,3,9,7)])
sage: superp.face_lattice()
Finite lattice containing 16 elements with distinguished linear extension
sage: superp.face_lattice().top()
3-d lattice polytope in 4-d lattice M
sage: p = superp.facets()[0]
sage: p
2-d face of 3-d lattice polytope in 4-d lattice M
sage: p.face_lattice()
Finite poset containing 8 elements with distinguished linear extension
sage: p.face_lattice().bottom()
-1-d face of 3-d lattice polytope in 4-d lattice M
sage: p.face_lattice().top()
2-d face of 3-d lattice polytope in 4-d lattice M
sage: p.face_lattice().top() is p
True
>>> from sage.all import *
>>> # needs sage.graphs
>>> superp = LatticePolytope([(Integer(1),Integer(2),Integer(3),Integer(4)), (Integer(5),Integer(6),Integer(7),Integer(8)),
...                           (Integer(1),Integer(2),Integer(4),Integer(8)), (Integer(1),Integer(3),Integer(9),Integer(7))])
>>> superp.face_lattice()
Finite lattice containing 16 elements with distinguished linear extension
>>> superp.face_lattice().top()
3-d lattice polytope in 4-d lattice M
>>> p = superp.facets()[Integer(0)]
>>> p
2-d face of 3-d lattice polytope in 4-d lattice M
>>> p.face_lattice()
Finite poset containing 8 elements with distinguished linear extension
>>> p.face_lattice().bottom()
-1-d face of 3-d lattice polytope in 4-d lattice M
>>> p.face_lattice().top()
2-d face of 3-d lattice polytope in 4-d lattice M
>>> p.face_lattice().top() is p
True
faces(dim=None, codim=None)[source]#

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 lattice polytopes;

  • 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 a square:

sage: square = LatticePolytope([(0,0), (1,0), (1,1), (0,1)])
sage: square.faces()                                                        # needs sage.graphs
((-1-d face of 2-d lattice polytope in 2-d lattice M,),
 (0-d face of 2-d lattice polytope in 2-d lattice M,
  0-d face of 2-d lattice polytope in 2-d lattice M,
  0-d face of 2-d lattice polytope in 2-d lattice M,
  0-d face of 2-d lattice polytope in 2-d lattice M),
 (1-d face of 2-d lattice polytope in 2-d lattice M,
  1-d face of 2-d lattice polytope in 2-d lattice M,
  1-d face of 2-d lattice polytope in 2-d lattice M,
  1-d face of 2-d lattice polytope in 2-d lattice M),
 (2-d lattice polytope in 2-d lattice M,))
>>> from sage.all import *
>>> square = LatticePolytope([(Integer(0),Integer(0)), (Integer(1),Integer(0)), (Integer(1),Integer(1)), (Integer(0),Integer(1))])
>>> square.faces()                                                        # needs sage.graphs
((-1-d face of 2-d lattice polytope in 2-d lattice M,),
 (0-d face of 2-d lattice polytope in 2-d lattice M,
  0-d face of 2-d lattice polytope in 2-d lattice M,
  0-d face of 2-d lattice polytope in 2-d lattice M,
  0-d face of 2-d lattice polytope in 2-d lattice M),
 (1-d face of 2-d lattice polytope in 2-d lattice M,
  1-d face of 2-d lattice polytope in 2-d lattice M,
  1-d face of 2-d lattice polytope in 2-d lattice M,
  1-d face of 2-d lattice polytope in 2-d lattice M),
 (2-d lattice polytope in 2-d lattice M,))

Its faces of dimension one (i.e., edges):

sage: square.faces(dim=1)                                                   # needs sage.graphs
(1-d face of 2-d lattice polytope in 2-d lattice M,
 1-d face of 2-d lattice polytope in 2-d lattice M,
 1-d face of 2-d lattice polytope in 2-d lattice M,
 1-d face of 2-d lattice polytope in 2-d lattice M)
>>> from sage.all import *
>>> square.faces(dim=Integer(1))                                                   # needs sage.graphs
(1-d face of 2-d lattice polytope in 2-d lattice M,
 1-d face of 2-d lattice polytope in 2-d lattice M,
 1-d face of 2-d lattice polytope in 2-d lattice M,
 1-d face of 2-d lattice polytope in 2-d lattice M)

Its faces of codimension one are the same (also edges):

sage: square.faces(codim=1) is square.faces(dim=1)                          # needs sage.graphs
True
>>> from sage.all import *
>>> square.faces(codim=Integer(1)) is square.faces(dim=Integer(1))                          # needs sage.graphs
True

Let’s pick a particular face:

sage: face = square.faces(dim=1)[0]                                         # needs sage.graphs
>>> from sage.all import *
>>> face = square.faces(dim=Integer(1))[Integer(0)]                                         # needs sage.graphs

Now you can look at the actual vertices of this face…

sage: face.vertices()                                                       # needs sage.graphs
M(0, 0),
M(0, 1)
in 2-d lattice M
>>> from sage.all import *
>>> face.vertices()                                                       # needs sage.graphs
M(0, 0),
M(0, 1)
in 2-d lattice M

… or you can see indices of the vertices of the original polytope that correspond to the above ones:

sage: face.ambient_vertex_indices()                                         # needs sage.graphs
(0, 3)
sage: square.vertices(face.ambient_vertex_indices())                        # needs sage.graphs
M(0, 0),
M(0, 1)
in 2-d lattice M
>>> from sage.all import *
>>> face.ambient_vertex_indices()                                         # needs sage.graphs
(0, 3)
>>> square.vertices(face.ambient_vertex_indices())                        # needs sage.graphs
M(0, 0),
M(0, 1)
in 2-d lattice M
facet_constant(i)[source]#

Return the constant in the i-th facet inequality of this polytope.

This is equivalent to facet_constants()[i].

INPUT:

  • i – integer; the index of the facet

OUTPUT:

  • integer – the constant in the i-th facet inequality.

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(3)
sage: o.facet_constant(0)
1
sage: o.facet_constant(0) == o.facet_constants()[0]
True
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.facet_constant(Integer(0))
1
>>> o.facet_constant(Integer(0)) == o.facet_constants()[Integer(0)]
True
facet_constants()[source]#

Return facet constants of self.

Facet inequalities have form \(n \cdot x + c \geq 0\) where \(n\) is the inner normal and \(c\) is a constant.

OUTPUT:

  • an integer vector

EXAMPLES:

For reflexive polytopes all constants are 1:

sage: o = lattice_polytope.cross_polytope(3)
sage: o.vertices()
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1)
in 3-d lattice M
sage: o.facet_constants()
(1, 1, 1, 1, 1, 1, 1, 1)
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.vertices()
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1)
in 3-d lattice M
>>> o.facet_constants()
(1, 1, 1, 1, 1, 1, 1, 1)

Here is an example of a 3-dimensional polytope in a 4-dimensional space with 3 facets containing the origin:

sage: p = LatticePolytope([(0,0,0,0), (1,1,1,3),
....:                      (1,-1,1,3), (-1,-1,1,3)])
sage: p.vertices()
M( 0,  0, 0, 0),
M( 1,  1, 1, 3),
M( 1, -1, 1, 3),
M(-1, -1, 1, 3)
in 4-d lattice M
sage: p.facet_constants()
(0, 0, 3, 0)
>>> from sage.all import *
>>> p = LatticePolytope([(Integer(0),Integer(0),Integer(0),Integer(0)), (Integer(1),Integer(1),Integer(1),Integer(3)),
...                      (Integer(1),-Integer(1),Integer(1),Integer(3)), (-Integer(1),-Integer(1),Integer(1),Integer(3))])
>>> p.vertices()
M( 0,  0, 0, 0),
M( 1,  1, 1, 3),
M( 1, -1, 1, 3),
M(-1, -1, 1, 3)
in 4-d lattice M
>>> p.facet_constants()
(0, 0, 3, 0)
facet_normal(i)[source]#

Return the inner normal to the i-th facet of this polytope.

This is equivalent to facet_normals()[i].

INPUT:

  • i – integer; the index of the facet

OUTPUT:

  • a vector

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(3)
sage: o.facet_normal(0)
N(1, -1, -1)
sage: o.facet_normal(0) is o.facet_normals()[0]
True
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.facet_normal(Integer(0))
N(1, -1, -1)
>>> o.facet_normal(Integer(0)) is o.facet_normals()[Integer(0)]
True
facet_normals()[source]#

Return inner normals to the facets of self.

If this polytope is not full-dimensional, facet normals will define this polytope in the affine subspace spanned by it.

OUTPUT:

EXAMPLES:

Normals to facets of an octahedron are vertices of a cube:

sage: o = lattice_polytope.cross_polytope(3)
sage: o.vertices()
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1)
in 3-d lattice M
sage: o.facet_normals()
N( 1, -1, -1),
N( 1,  1, -1),
N( 1,  1,  1),
N( 1, -1,  1),
N(-1, -1,  1),
N(-1, -1, -1),
N(-1,  1, -1),
N(-1,  1,  1)
in 3-d lattice N
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.vertices()
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1)
in 3-d lattice M
>>> o.facet_normals()
N( 1, -1, -1),
N( 1,  1, -1),
N( 1,  1,  1),
N( 1, -1,  1),
N(-1, -1,  1),
N(-1, -1, -1),
N(-1,  1, -1),
N(-1,  1,  1)
in 3-d lattice N

Here is an example of a 3-dimensional polytope in a 4-dimensional space:

sage: p = LatticePolytope([(0,0,0,0), (1,1,1,3),
....:                      (1,-1,1,3), (-1,-1,1,3)])
sage: p.vertices()
M( 0,  0, 0, 0),
M( 1,  1, 1, 3),
M( 1, -1, 1, 3),
M(-1, -1, 1, 3)
in 4-d lattice M
sage: p.facet_normals()
N( 0,  3, 0,  1),
N( 1, -1, 0,  0),
N( 0,  0, 0, -1),
N(-3,  0, 0,  1)
in 4-d lattice N
sage: p.facet_constants()
(0, 0, 3, 0)
>>> from sage.all import *
>>> p = LatticePolytope([(Integer(0),Integer(0),Integer(0),Integer(0)), (Integer(1),Integer(1),Integer(1),Integer(3)),
...                      (Integer(1),-Integer(1),Integer(1),Integer(3)), (-Integer(1),-Integer(1),Integer(1),Integer(3))])
>>> p.vertices()
M( 0,  0, 0, 0),
M( 1,  1, 1, 3),
M( 1, -1, 1, 3),
M(-1, -1, 1, 3)
in 4-d lattice M
>>> p.facet_normals()
N( 0,  3, 0,  1),
N( 1, -1, 0,  0),
N( 0,  0, 0, -1),
N(-3,  0, 0,  1)
in 4-d lattice N
>>> p.facet_constants()
(0, 0, 3, 0)

Now we manually compute the distance matrix of this polytope. Since it is a simplex, each line (corresponding to a facet) should consist of zeros (indicating generating vertices of the corresponding facet) and a single positive number (since our normals are inner):

sage: matrix([[n * v + c for v in p.vertices()]
....:     for n, c in zip(p.facet_normals(), p.facet_constants())])
[0 6 0 0]
[0 0 2 0]
[3 0 0 0]
[0 0 0 6]
>>> from sage.all import *
>>> matrix([[n * v + c for v in p.vertices()]
...     for n, c in zip(p.facet_normals(), p.facet_constants())])
[0 6 0 0]
[0 0 2 0]
[3 0 0 0]
[0 0 0 6]
facet_of()[source]#

Return elements of the ambient face lattice having self as a facet.

OUTPUT:

EXAMPLES:

sage: # needs sage.graphs
sage: square = LatticePolytope([(0,0), (1,0), (1,1), (0,1)])
sage: square.facet_of()
()
sage: face = square.faces(0)[0]
sage: len(face.facet_of())
2
sage: face.facet_of()[1]
1-d face of 2-d lattice polytope in 2-d lattice M
>>> from sage.all import *
>>> # needs sage.graphs
>>> square = LatticePolytope([(Integer(0),Integer(0)), (Integer(1),Integer(0)), (Integer(1),Integer(1)), (Integer(0),Integer(1))])
>>> square.facet_of()
()
>>> face = square.faces(Integer(0))[Integer(0)]
>>> len(face.facet_of())
2
>>> face.facet_of()[Integer(1)]
1-d face of 2-d lattice polytope in 2-d lattice M
facets()[source]#

Return facets (faces of codimension 1) of self.

OUTPUT:

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(3)
sage: o.facets()                                                            # needs sage.graphs
(2-d face of 3-d reflexive polytope in 3-d lattice M,
...
 2-d face of 3-d reflexive polytope in 3-d lattice M)
sage: len(o.facets())                                                       # needs sage.graphs
8
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.facets()                                                            # needs sage.graphs
(2-d face of 3-d reflexive polytope in 3-d lattice M,
...
 2-d face of 3-d reflexive polytope in 3-d lattice M)
>>> len(o.facets())                                                       # needs sage.graphs
8
incidence_matrix()[source]#

Return the incidence matrix.

Note

The columns correspond to facets/facet normals in the order of facet_normals(), the rows correspond to the vertices in the order of vertices().

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(2)
sage: o.incidence_matrix()
[0 0 1 1]
[0 1 1 0]
[1 1 0 0]
[1 0 0 1]
sage: o.faces(1)[0].incidence_matrix()                                      # needs sage.graphs
[1 0]
[0 1]

sage: o = lattice_polytope.cross_polytope(4)
sage: o.incidence_matrix().column(3).nonzero_positions()
[3, 4, 5, 6]
sage: o.facets()[3].ambient_vertex_indices()                                # needs sage.graphs
(3, 4, 5, 6)
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(2))
>>> o.incidence_matrix()
[0 0 1 1]
[0 1 1 0]
[1 1 0 0]
[1 0 0 1]
>>> o.faces(Integer(1))[Integer(0)].incidence_matrix()                                      # needs sage.graphs
[1 0]
[0 1]

>>> o = lattice_polytope.cross_polytope(Integer(4))
>>> o.incidence_matrix().column(Integer(3)).nonzero_positions()
[3, 4, 5, 6]
>>> o.facets()[Integer(3)].ambient_vertex_indices()                                # needs sage.graphs
(3, 4, 5, 6)
index()[source]#

Return the index of this polytope in the internal database of 2- or 3-dimensional reflexive polytopes. Databases are stored in the directory of the package.

Note

The first call to this function for each dimension can take a few seconds while the dictionary of all polytopes is constructed, but after that it is cached and fast.

Return type:

integer

EXAMPLES: We check what is the index of the “diamond” in the database:

sage: d = lattice_polytope.cross_polytope(2)
sage: d.index()                                                             # needs palp
3
>>> from sage.all import *
>>> d = lattice_polytope.cross_polytope(Integer(2))
>>> d.index()                                                             # needs palp
3

Note that polytopes with the same index are not necessarily the same:

sage: d.vertices()
M( 1,  0),
M( 0,  1),
M(-1,  0),
M( 0, -1)
in 2-d lattice M
sage: lattice_polytope.ReflexivePolytope(2,3).vertices()
M( 1,  0),
M( 0,  1),
M( 0, -1),
M(-1,  0)
in 2-d lattice M
>>> from sage.all import *
>>> d.vertices()
M( 1,  0),
M( 0,  1),
M(-1,  0),
M( 0, -1)
in 2-d lattice M
>>> lattice_polytope.ReflexivePolytope(Integer(2),Integer(3)).vertices()
M( 1,  0),
M( 0,  1),
M( 0, -1),
M(-1,  0)
in 2-d lattice M

But they are in the same \(GL(\ZZ^n)\) orbit and have the same normal form:

sage: d.normal_form()                                                       # needs sage.groups
M( 1,  0),
M( 0,  1),
M( 0, -1),
M(-1,  0)
in 2-d lattice M
sage: lattice_polytope.ReflexivePolytope(2,3).normal_form()                 # needs sage.groups
M( 1,  0),
M( 0,  1),
M( 0, -1),
M(-1,  0)
in 2-d lattice M
>>> from sage.all import *
>>> d.normal_form()                                                       # needs sage.groups
M( 1,  0),
M( 0,  1),
M( 0, -1),
M(-1,  0)
in 2-d lattice M
>>> lattice_polytope.ReflexivePolytope(Integer(2),Integer(3)).normal_form()                 # needs sage.groups
M( 1,  0),
M( 0,  1),
M( 0, -1),
M(-1,  0)
in 2-d lattice M
interior_point_indices()[source]#

Return indices of (relative) interior lattice points of this polytope.

OUTPUT:

  • increasing tuple of integers.

EXAMPLES:

The origin is the only interior point of this square:

sage: square = lattice_polytope.cross_polytope(2).polar()
sage: square.points()                                                       # needs palp
N( 1,  1),
N( 1, -1),
N(-1, -1),
N(-1,  1),
N(-1,  0),
N( 0, -1),
N( 0,  0),
N( 0,  1),
N( 1,  0)
in 2-d lattice N
sage: square.interior_point_indices()                                       # needs palp
(6,)
>>> from sage.all import *
>>> square = lattice_polytope.cross_polytope(Integer(2)).polar()
>>> square.points()                                                       # needs palp
N( 1,  1),
N( 1, -1),
N(-1, -1),
N(-1,  1),
N(-1,  0),
N( 0, -1),
N( 0,  0),
N( 0,  1),
N( 1,  0)
in 2-d lattice N
>>> square.interior_point_indices()                                       # needs palp
(6,)

Its edges also have a single interior point each:

sage: face = square.edges()[0]                                              # needs sage.graphs
sage: face.points()                                                         # needs sage.graphs
N(-1, -1),
N(-1,  1),
N(-1,  0)
in 2-d lattice N
sage: face.interior_point_indices()                                         # needs sage.graphs
(2,)
>>> from sage.all import *
>>> face = square.edges()[Integer(0)]                                              # needs sage.graphs
>>> face.points()                                                         # needs sage.graphs
N(-1, -1),
N(-1,  1),
N(-1,  0)
in 2-d lattice N
>>> face.interior_point_indices()                                         # needs sage.graphs
(2,)
interior_points()[source]#

Return (relative) boundary lattice points of this polytope.

OUTPUT:

EXAMPLES:

The origin is the only interior point of this square:

sage: square = lattice_polytope.cross_polytope(2).polar()
sage: square.interior_points()                                              # needs palp
N(0, 0)
in 2-d lattice N
>>> from sage.all import *
>>> square = lattice_polytope.cross_polytope(Integer(2)).polar()
>>> square.interior_points()                                              # needs palp
N(0, 0)
in 2-d lattice N

Its edges also have a single interior point each:

sage: face = square.edges()[0]                                              # needs sage.graphs
sage: face.interior_points()                                                # needs sage.graphs
N(-1, 0)
in 2-d lattice N
>>> from sage.all import *
>>> face = square.edges()[Integer(0)]                                              # needs sage.graphs
>>> face.interior_points()                                                # needs sage.graphs
N(-1, 0)
in 2-d lattice N
is_reflexive()[source]#

Return True if this polytope is reflexive.

EXAMPLES: The 3-dimensional octahedron is reflexive (and 4319 other 3-polytopes):

sage: o = lattice_polytope.cross_polytope(3)
sage: o.is_reflexive()
True
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.is_reflexive()
True

But not all polytopes are reflexive:

sage: p = LatticePolytope([(1,0,0), (0,1,17), (-1,0,0), (0,-1,0)])
sage: p.is_reflexive()
False
>>> from sage.all import *
>>> p = LatticePolytope([(Integer(1),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(17)), (-Integer(1),Integer(0),Integer(0)), (Integer(0),-Integer(1),Integer(0))])
>>> p.is_reflexive()
False

Only full-dimensional polytopes can be reflexive (otherwise the polar set is not a polytope at all, since it is unbounded):

sage: p = LatticePolytope([(1,0,0), (0,1,0), (-1,0,0), (0,-1,0)])
sage: p.is_reflexive()
False
>>> from sage.all import *
>>> p = LatticePolytope([(Integer(1),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0)), (-Integer(1),Integer(0),Integer(0)), (Integer(0),-Integer(1),Integer(0))])
>>> p.is_reflexive()
False
lattice()[source]#

Return the ambient lattice of self.

OUTPUT:

  • a lattice.

EXAMPLES:

sage: lattice_polytope.cross_polytope(3).lattice()
3-d lattice M
>>> from sage.all import *
>>> lattice_polytope.cross_polytope(Integer(3)).lattice()
3-d lattice M
lattice_dim()[source]#

Return the dimension of the ambient lattice of self.

An alias is ambient_dim().

OUTPUT:

  • integer.

EXAMPLES:

sage: p = LatticePolytope([(1,0)])
sage: p.lattice_dim()
2
sage: p.dim()
0
>>> from sage.all import *
>>> p = LatticePolytope([(Integer(1),Integer(0))])
>>> p.lattice_dim()
2
>>> p.dim()
0
linearly_independent_vertices()[source]#

Return a maximal set of linearly independent vertices.

OUTPUT:

A tuple of vertex indices.

EXAMPLES:

sage: L = LatticePolytope([[0, 0], [-1, 1], [-1, -1]])
sage: L.linearly_independent_vertices()
(1, 2)
sage: L = LatticePolytope([[0, 0, 0]])
sage: L.linearly_independent_vertices()
()
sage: L = LatticePolytope([[0, 1, 0]])
sage: L.linearly_independent_vertices()
(0,)
>>> from sage.all import *
>>> L = LatticePolytope([[Integer(0), Integer(0)], [-Integer(1), Integer(1)], [-Integer(1), -Integer(1)]])
>>> L.linearly_independent_vertices()
(1, 2)
>>> L = LatticePolytope([[Integer(0), Integer(0), Integer(0)]])
>>> L.linearly_independent_vertices()
()
>>> L = LatticePolytope([[Integer(0), Integer(1), Integer(0)]])
>>> L.linearly_independent_vertices()
(0,)
nef_partitions(keep_symmetric=False, keep_products=True, keep_projections=True, hodge_numbers=False)[source]#

Return 2-part nef-partitions of self.

INPUT:

  • keep_symmetric – (default: False) if True, “-s” option will be passed to nef.x in order to keep symmetric partitions, i.e. partitions related by lattice automorphisms preserving self;

  • keep_products – (default: True) if True, “-D” option will be passed to nef.x in order to keep product partitions, with corresponding complete intersections being direct products;

  • keep_projections – (default: True) if True, “-P” option will be passed to nef.x in order to keep projection partitions, i.e. partitions with one of the parts consisting of a single vertex;

  • hodge_numbers – (default: False) if False, “-p” option will be passed to nef.x in order to skip Hodge numbers computation, which takes a lot of time.

OUTPUT:

Type NefPartition? for definitions and notation.

EXAMPLES:

Nef-partitions of the 4-dimensional cross-polytope:

sage: p = lattice_polytope.cross_polytope(4)
sage: p.nef_partitions()                                                    # needs palp
[
Nef-partition {0, 1, 4, 5} ⊔ {2, 3, 6, 7} (direct product),
Nef-partition {0, 1, 2, 4} ⊔ {3, 5, 6, 7},
Nef-partition {0, 1, 2, 4, 5} ⊔ {3, 6, 7},
Nef-partition {0, 1, 2, 4, 5, 6} ⊔ {3, 7} (direct product),
Nef-partition {0, 1, 2, 3} ⊔ {4, 5, 6, 7},
Nef-partition {0, 1, 2, 3, 4} ⊔ {5, 6, 7},
Nef-partition {0, 1, 2, 3, 4, 5} ⊔ {6, 7},
Nef-partition {0, 1, 2, 3, 4, 5, 6} ⊔ {7} (projection)
]
>>> from sage.all import *
>>> p = lattice_polytope.cross_polytope(Integer(4))
>>> p.nef_partitions()                                                    # needs palp
[
Nef-partition {0, 1, 4, 5} ⊔ {2, 3, 6, 7} (direct product),
Nef-partition {0, 1, 2, 4} ⊔ {3, 5, 6, 7},
Nef-partition {0, 1, 2, 4, 5} ⊔ {3, 6, 7},
Nef-partition {0, 1, 2, 4, 5, 6} ⊔ {3, 7} (direct product),
Nef-partition {0, 1, 2, 3} ⊔ {4, 5, 6, 7},
Nef-partition {0, 1, 2, 3, 4} ⊔ {5, 6, 7},
Nef-partition {0, 1, 2, 3, 4, 5} ⊔ {6, 7},
Nef-partition {0, 1, 2, 3, 4, 5, 6} ⊔ {7} (projection)
]

Now we omit projections:

sage: p.nef_partitions(keep_projections=False)                              # needs palp
[
Nef-partition {0, 1, 4, 5} ⊔ {2, 3, 6, 7} (direct product),
Nef-partition {0, 1, 2, 4} ⊔ {3, 5, 6, 7},
Nef-partition {0, 1, 2, 4, 5} ⊔ {3, 6, 7},
Nef-partition {0, 1, 2, 4, 5, 6} ⊔ {3, 7} (direct product),
Nef-partition {0, 1, 2, 3} ⊔ {4, 5, 6, 7},
Nef-partition {0, 1, 2, 3, 4} ⊔ {5, 6, 7},
Nef-partition {0, 1, 2, 3, 4, 5} ⊔ {6, 7}
]
>>> from sage.all import *
>>> p.nef_partitions(keep_projections=False)                              # needs palp
[
Nef-partition {0, 1, 4, 5} ⊔ {2, 3, 6, 7} (direct product),
Nef-partition {0, 1, 2, 4} ⊔ {3, 5, 6, 7},
Nef-partition {0, 1, 2, 4, 5} ⊔ {3, 6, 7},
Nef-partition {0, 1, 2, 4, 5, 6} ⊔ {3, 7} (direct product),
Nef-partition {0, 1, 2, 3} ⊔ {4, 5, 6, 7},
Nef-partition {0, 1, 2, 3, 4} ⊔ {5, 6, 7},
Nef-partition {0, 1, 2, 3, 4, 5} ⊔ {6, 7}
]

Currently Hodge numbers cannot be computed for a given nef-partition:

sage: p.nef_partitions()[1].hodge_numbers()                                 # needs palp
Traceback (most recent call last):
...
NotImplementedError: use nef_partitions(hodge_numbers=True)!
>>> from sage.all import *
>>> p.nef_partitions()[Integer(1)].hodge_numbers()                                 # needs palp
Traceback (most recent call last):
...
NotImplementedError: use nef_partitions(hodge_numbers=True)!

But they can be obtained from nef.x for all nef-partitions at once. Partitions will be exactly the same:

sage: p.nef_partitions(hodge_numbers=True)  # long time (2s on sage.math, 2011), needs palp
[
Nef-partition {0, 1, 4, 5} ⊔ {2, 3, 6, 7} (direct product),
Nef-partition {0, 1, 2, 4} ⊔ {3, 5, 6, 7},
Nef-partition {0, 1, 2, 4, 5} ⊔ {3, 6, 7},
Nef-partition {0, 1, 2, 4, 5, 6} ⊔ {3, 7} (direct product),
Nef-partition {0, 1, 2, 3} ⊔ {4, 5, 6, 7},
Nef-partition {0, 1, 2, 3, 4} ⊔ {5, 6, 7},
Nef-partition {0, 1, 2, 3, 4, 5} ⊔ {6, 7},
Nef-partition {0, 1, 2, 3, 4, 5, 6} ⊔ {7} (projection)
]
>>> from sage.all import *
>>> p.nef_partitions(hodge_numbers=True)  # long time (2s on sage.math, 2011), needs palp
[
Nef-partition {0, 1, 4, 5} ⊔ {2, 3, 6, 7} (direct product),
Nef-partition {0, 1, 2, 4} ⊔ {3, 5, 6, 7},
Nef-partition {0, 1, 2, 4, 5} ⊔ {3, 6, 7},
Nef-partition {0, 1, 2, 4, 5, 6} ⊔ {3, 7} (direct product),
Nef-partition {0, 1, 2, 3} ⊔ {4, 5, 6, 7},
Nef-partition {0, 1, 2, 3, 4} ⊔ {5, 6, 7},
Nef-partition {0, 1, 2, 3, 4, 5} ⊔ {6, 7},
Nef-partition {0, 1, 2, 3, 4, 5, 6} ⊔ {7} (projection)
]

Now it is possible to get Hodge numbers:

sage: p.nef_partitions(hodge_numbers=True)[1].hodge_numbers()               # needs palp
(20,)
>>> from sage.all import *
>>> p.nef_partitions(hodge_numbers=True)[Integer(1)].hodge_numbers()               # needs palp
(20,)

Since nef-partitions are cached, their Hodge numbers are accessible after the first request, even if you do not specify hodge_numbers=True anymore:

sage: p.nef_partitions()[1].hodge_numbers()                                 # needs palp
(20,)
>>> from sage.all import *
>>> p.nef_partitions()[Integer(1)].hodge_numbers()                                 # needs palp
(20,)

We illustrate removal of symmetric partitions on a diamond:

sage: p = lattice_polytope.cross_polytope(2)
sage: p.nef_partitions()                                                    # needs palp
[
Nef-partition {0, 2} ⊔ {1, 3} (direct product),
Nef-partition {0, 1} ⊔ {2, 3},
Nef-partition {0, 1, 2} ⊔ {3} (projection)
]
sage: p.nef_partitions(keep_symmetric=True)                                 # needs palp
[
Nef-partition {0, 1, 3} ⊔ {2} (projection),
Nef-partition {0, 2, 3} ⊔ {1} (projection),
Nef-partition {0, 3} ⊔ {1, 2},
Nef-partition {1, 2, 3} ⊔ {0} (projection),
Nef-partition {1, 3} ⊔ {0, 2} (direct product),
Nef-partition {2, 3} ⊔ {0, 1},
Nef-partition {0, 1, 2} ⊔ {3} (projection)
]
>>> from sage.all import *
>>> p = lattice_polytope.cross_polytope(Integer(2))
>>> p.nef_partitions()                                                    # needs palp
[
Nef-partition {0, 2} ⊔ {1, 3} (direct product),
Nef-partition {0, 1} ⊔ {2, 3},
Nef-partition {0, 1, 2} ⊔ {3} (projection)
]
>>> p.nef_partitions(keep_symmetric=True)                                 # needs palp
[
Nef-partition {0, 1, 3} ⊔ {2} (projection),
Nef-partition {0, 2, 3} ⊔ {1} (projection),
Nef-partition {0, 3} ⊔ {1, 2},
Nef-partition {1, 2, 3} ⊔ {0} (projection),
Nef-partition {1, 3} ⊔ {0, 2} (direct product),
Nef-partition {2, 3} ⊔ {0, 1},
Nef-partition {0, 1, 2} ⊔ {3} (projection)
]

Nef-partitions can be computed only for reflexive polytopes:

sage: p = LatticePolytope([(1,0,0), (0,1,0), (0,0,2),
....:                      (-1,0,0), (0,-1,0), (0,0,-1)])
sage: p.nef_partitions()                                                    # needs palp
Traceback (most recent call last):
...
ValueError: The given polytope is not reflexive!
Polytope: 3-d lattice polytope in 3-d lattice M
>>> from sage.all import *
>>> p = LatticePolytope([(Integer(1),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0)), (Integer(0),Integer(0),Integer(2)),
...                      (-Integer(1),Integer(0),Integer(0)), (Integer(0),-Integer(1),Integer(0)), (Integer(0),Integer(0),-Integer(1))])
>>> p.nef_partitions()                                                    # needs palp
Traceback (most recent call last):
...
ValueError: The given polytope is not reflexive!
Polytope: 3-d lattice polytope in 3-d lattice M
nef_x(keys)[source]#

Run nef.x with given keys on vertices of this polytope.

INPUT:

  • keys – a string of options passed to nef.x. The key “-f” is added automatically.

OUTPUT: the output of nef.x as a string.

EXAMPLES: This call is used internally for computing nef-partitions:

sage: o = lattice_polytope.cross_polytope(3)
sage: s = o.nef_x("-N -V -p")                                               # needs palp
sage: s                      # output contains random time                  # needs palp
M:27 8 N:7 6  codim=2 #part=5
3 6  Vertices of P:
    1    0    0   -1    0    0
    0    1    0    0   -1    0
    0    0    1    0    0   -1
 P:0 V:2 4 5       0sec  0cpu
 P:2 V:3 4 5       0sec  0cpu
 P:3 V:4 5       0sec  0cpu
np=3 d:1 p:1    0sec     0cpu
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> s = o.nef_x("-N -V -p")                                               # needs palp
>>> s                      # output contains random time                  # needs palp
M:27 8 N:7 6  codim=2 #part=5
3 6  Vertices of P:
    1    0    0   -1    0    0
    0    1    0    0   -1    0
    0    0    1    0    0   -1
 P:0 V:2 4 5       0sec  0cpu
 P:2 V:3 4 5       0sec  0cpu
 P:3 V:4 5       0sec  0cpu
np=3 d:1 p:1    0sec     0cpu
nfacets()[source]#

Return the number of facets of this polytope.

EXAMPLES: The number of facets of the 3-dimensional octahedron:

sage: o = lattice_polytope.cross_polytope(3)
sage: o.nfacets()
8
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.nfacets()
8

The number of facets of an interval is 2:

sage: LatticePolytope(([1],[2])).nfacets()
2
>>> from sage.all import *
>>> LatticePolytope(([Integer(1)],[Integer(2)])).nfacets()
2

Now consider a 2-dimensional diamond in a 3-dimensional space:

sage: p = LatticePolytope([(1,0,0), (0,1,0), (-1,0,0), (0,-1,0)])
sage: p.nfacets()
4
>>> from sage.all import *
>>> p = LatticePolytope([(Integer(1),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0)), (-Integer(1),Integer(0),Integer(0)), (Integer(0),-Integer(1),Integer(0))])
>>> p.nfacets()
4
normal_form(algorithm='palp_native', permutation=False)[source]#

Return the normal form of vertices of self.

Two full-dimensional lattice polytopes are in the same \(GL(\ZZ^n)\)-orbit if and only if their normal forms are the same. Normal form is not defined and thus cannot be used for polytopes whose dimension is smaller than the dimension of the ambient space.

The original algorithm was presented in [KS1998] and implemented in PALP. A modified version of the PALP algorithm is discussed in [GK2013] and available here as "palp_modified".

INPUT:

  • algorithm – (default: "palp_native") The algorithm which is used to compute the normal form. Options are:

    • "palp" – Run external PALP code, usually the fastest option when it works; but reproducible crashes have been observed in dimension 5 and higher.

    • "palp_native" – The original PALP algorithm implemented in sage. Currently competitive with PALP in many cases.

    • "palp_modified" – A modified version of the PALP algorithm which determines the maximal vertex-facet pairing matrix first and then computes its automorphisms, while the PALP algorithm does both things concurrently.

  • permutation – boolean (default: False); if True, the permutation applied to vertices to obtain the normal form is returned as well. Note that the different algorithms may return different results that nevertheless lead to the same normal form.

OUTPUT:

EXAMPLES:

We compute the normal form of the “diamond”:

sage: d = LatticePolytope([(1,0), (0,1), (-1,0), (0,-1)])
sage: d.vertices()
M( 1,  0),
M( 0,  1),
M(-1,  0),
M( 0, -1)
in 2-d lattice M
sage: d.normal_form()                                                       # needs sage.groups
M( 1,  0),
M( 0,  1),
M( 0, -1),
M(-1,  0)
in 2-d lattice M
>>> from sage.all import *
>>> d = LatticePolytope([(Integer(1),Integer(0)), (Integer(0),Integer(1)), (-Integer(1),Integer(0)), (Integer(0),-Integer(1))])
>>> d.vertices()
M( 1,  0),
M( 0,  1),
M(-1,  0),
M( 0, -1)
in 2-d lattice M
>>> d.normal_form()                                                       # needs sage.groups
M( 1,  0),
M( 0,  1),
M( 0, -1),
M(-1,  0)
in 2-d lattice M

The diamond is the 3rd polytope in the internal database:

sage: d.index()                                                             # needs palp
3
sage: d                                                                     # needs palp
2-d reflexive polytope #3 in 2-d lattice M
>>> from sage.all import *
>>> d.index()                                                             # needs palp
3
>>> d                                                                     # needs palp
2-d reflexive polytope #3 in 2-d lattice M

You can get it in its normal form (in the default lattice) as

sage: lattice_polytope.ReflexivePolytope(2, 3).vertices()
M( 1,  0),
M( 0,  1),
M( 0, -1),
M(-1,  0)
in 2-d lattice M
>>> from sage.all import *
>>> lattice_polytope.ReflexivePolytope(Integer(2), Integer(3)).vertices()
M( 1,  0),
M( 0,  1),
M( 0, -1),
M(-1,  0)
in 2-d lattice M

It is not possible to compute normal forms for polytopes which do not span the space:

sage: p = LatticePolytope([(1,0,0), (0,1,0), (-1,0,0), (0,-1,0)])
sage: p.normal_form()
Traceback (most recent call last):
...
ValueError: normal form is not defined for
2-d lattice polytope in 3-d lattice M
>>> from sage.all import *
>>> p = LatticePolytope([(Integer(1),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0)), (-Integer(1),Integer(0),Integer(0)), (Integer(0),-Integer(1),Integer(0))])
>>> p.normal_form()
Traceback (most recent call last):
...
ValueError: normal form is not defined for
2-d lattice polytope in 3-d lattice M

We can perform the same examples using other algorithms:

sage: o = lattice_polytope.cross_polytope(2)
sage: o.normal_form(algorithm="palp_native")                                # needs sage.groups
M( 1,  0),
M( 0,  1),
M( 0, -1),
M(-1,  0)
in 2-d lattice M

sage: o = lattice_polytope.cross_polytope(2)
sage: o.normal_form(algorithm="palp_modified")                              # needs sage.groups
M( 1,  0),
M( 0,  1),
M( 0, -1),
M(-1,  0)
in 2-d lattice M
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(2))
>>> o.normal_form(algorithm="palp_native")                                # needs sage.groups
M( 1,  0),
M( 0,  1),
M( 0, -1),
M(-1,  0)
in 2-d lattice M

>>> o = lattice_polytope.cross_polytope(Integer(2))
>>> o.normal_form(algorithm="palp_modified")                              # needs sage.groups
M( 1,  0),
M( 0,  1),
M( 0, -1),
M(-1,  0)
in 2-d lattice M

The following examples demonstrate the speed of the available algorithms. In low dimensions, the default algorithm, "palp_native", is the fastest. As the dimension increases, "palp" is relatively faster than "palp_native". "palp_native" is usually much faster than "palp_modified". In some cases when the polytope has high symmetry, however, "palp_native" is slower:

sage: # not tested
sage: o = lattice_polytope.cross_polytope(2)
sage: %timeit o.normal_form.clear_cache(); o.normal_form("palp")
625 loops, best of 3: 3.07 ms per loop
sage: %timeit o.normal_form.clear_cache(); o.normal_form("palp_native")
625 loops, best of 3: 0.445 ms per loop
sage: %timeit o.normal_form.clear_cache(); o.normal_form("palp_modified")
625 loops, best of 3: 5.01 ms per loop
sage: o = lattice_polytope.cross_polytope(3)
sage: %timeit o.normal_form.clear_cache(); o.normal_form("palp")
625 loops, best of 3: 3.22 ms per loop
sage: %timeit o.normal_form.clear_cache(); o.normal_form("palp_native")
625 loops, best of 3: 2.73 ms per loop
sage: %timeit o.normal_form.clear_cache(); o.normal_form("palp_modified")
625 loops, best of 3: 20.7 ms per loop
sage: o = lattice_polytope.cross_polytope(4)
sage: %timeit o.normal_form.clear_cache(); o.normal_form("palp")
625 loops, best of 3: 4.84 ms per loop
sage: %timeit o.normal_form.clear_cache(); o.normal_form("palp_native")
625 loops, best of 3: 55.6 ms per loop
sage: %timeit o.normal_form.clear_cache(); o.normal_form("palp_modified")
625 loops, best of 3: 129 ms per loop
sage: o = lattice_polytope.cross_polytope(5)
sage: %timeit o.normal_form.clear_cache(); o.normal_form("palp")
10 loops, best of 3: 0.0364 s per loop
sage: %timeit o.normal_form.clear_cache(); o.normal_form("palp_native")
10 loops, best of 3: 1.68 s per loop
sage: %timeit o.normal_form.clear_cache(); o.normal_form("palp_modified")
10 loops, best of 3: 0.858 s per loop
>>> from sage.all import *
>>> # not tested
>>> o = lattice_polytope.cross_polytope(Integer(2))
>>> %timeit o.normal_form.clear_cache(); o.normal_form("palp")
625 loops, best of 3: 3.07 ms per loop
>>> %timeit o.normal_form.clear_cache(); o.normal_form("palp_native")
625 loops, best of 3: 0.445 ms per loop
>>> %timeit o.normal_form.clear_cache(); o.normal_form("palp_modified")
625 loops, best of 3: 5.01 ms per loop
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> %timeit o.normal_form.clear_cache(); o.normal_form("palp")
625 loops, best of 3: 3.22 ms per loop
>>> %timeit o.normal_form.clear_cache(); o.normal_form("palp_native")
625 loops, best of 3: 2.73 ms per loop
>>> %timeit o.normal_form.clear_cache(); o.normal_form("palp_modified")
625 loops, best of 3: 20.7 ms per loop
>>> o = lattice_polytope.cross_polytope(Integer(4))
>>> %timeit o.normal_form.clear_cache(); o.normal_form("palp")
625 loops, best of 3: 4.84 ms per loop
>>> %timeit o.normal_form.clear_cache(); o.normal_form("palp_native")
625 loops, best of 3: 55.6 ms per loop
>>> %timeit o.normal_form.clear_cache(); o.normal_form("palp_modified")
625 loops, best of 3: 129 ms per loop
>>> o = lattice_polytope.cross_polytope(Integer(5))
>>> %timeit o.normal_form.clear_cache(); o.normal_form("palp")
10 loops, best of 3: 0.0364 s per loop
>>> %timeit o.normal_form.clear_cache(); o.normal_form("palp_native")
10 loops, best of 3: 1.68 s per loop
>>> %timeit o.normal_form.clear_cache(); o.normal_form("palp_modified")
10 loops, best of 3: 0.858 s per loop

Note that the algorithm "palp" may crash for higher dimensions because of the overflow errors as mentioned in Issue #13525#comment:9. Then use "palp_native" instead, which is usually faster than "palp_modified". Below is an example where "palp" fails and "palp_native" is much faster than "palp_modified":

sage: P = LatticePolytope([[-3, -3, -6, -6, -1], [3, 3, 6, 6, 1], [-3, -3, -6, -6, 1],
....:                      [-3, -3, -3, -6, 0], [-3, -3, -3, 0, 0], [-3, -3, 0, 0, 0],
....:                      [-3, 0, -6, -6, 0], [-3, 0, -3, -6, 0], [-3, 0, -3, 0, 0],
....:                      [-3, 0, 0, 0, -1], [3, 3, 6, 6, -1], [-3, 0, 0, 0, 1],
....:                      [0, -3, -6, -6, 0], [0, -3, -3, -6, 0], [0, -3, -3, 0, 0],
....:                      [0, -3, 0, 0, -1], [3, 3, 3, 6, 0], [0, -3, 0, 0, 1],
....:                      [0, 0, -6, -6, 0], [0, 0, -3, -6, -1], [3, 3, 3, 0, 0],
....:                      [0, 0, -3, -6, 1], [0, 0, -3, 0, -1], [3, 3, 0, 0, 0],
....:                      [0, 0, -3, 0, 1], [0, 0, 3, 0, -1], [3, 0, 6, 6, 0],
....:                      [0, 0, 3, 0, 1], [0, 0, 3, 6, -1], [3, 0, 3, 6, 0],
....:                      [0, 0, 3, 6, 1], [0, 0, 6, 6, 0], [0, 3, 0, 0, -1],
....:                      [3, 0, 3, 0, 0], [0, 3, 0, 0, 1], [0, 3, 3, 0, 0],
....:                      [0, 3, 3, 6, 0], [0, 3, 6, 6, 0], [3, 0,0, 0, -1], [3, 0, 0, 0, 1]])
sage: P.normal_form(algorithm="palp")  # not tested
Traceback (most recent call last):
...
RuntimeError: Error executing ... for a polytope sequence!
Output:
b'*** stack smashing detected ***: terminated\nAborted\n'
sage: P.normal_form(algorithm="palp_native")                                # needs sage.groups
M(  6,  0,  0,  0,  0),
M( -6,  0,  0,  0,  0),
M(  0,  1,  0,  0,  0),
M(  0,  0,  3,  0,  0),
M(  0,  1,  0,  3,  0),
M(  0,  0,  0,  0,  3),
M( -6,  1,  6,  3, -6),
M( -6,  0,  6,  0, -3),
M(-12,  1,  6,  3, -3),
M( -6,  1,  0,  3,  0),
M( -6,  0,  3,  3,  0),
M(  6,  0, -6, -3,  6),
M(-12,  1,  6,  3, -6),
M(-12,  0,  9,  3, -6),
M(  0,  0,  0, -3,  0),
M(-12,  1,  6,  6, -6),
M(-12,  0,  6,  3, -3),
M(  0,  1, -3,  0,  0),
M(  0,  0, -3, -3,  3),
M(  0,  1,  0,  3, -3),
M(  0, -1,  0, -3,  3),
M(  0,  0,  3,  3, -3),
M(  0, -1,  3,  0,  0),
M( 12,  0, -6, -3,  3),
M( 12, -1, -6, -6,  6),
M(  0,  0,  0,  3,  0),
M( 12,  0, -9, -3,  6),
M( 12, -1, -6, -3,  6),
M( -6,  0,  6,  3, -6),
M(  6,  0, -3, -3,  0),
M(  6, -1,  0, -3,  0),
M(-12,  1,  9,  6, -6),
M(  6,  0, -6,  0,  3),
M(  6, -1, -6, -3,  6),
M(  0,  0,  0,  0, -3),
M(  0, -1,  0, -3,  0),
M(  0,  0, -3,  0,  0),
M(  0, -1,  0,  0,  0),
M( 12, -1, -9, -6,  6),
M( 12, -1, -6, -3,  3)
in 5-d lattice M
sage: P.normal_form(algorithm="palp_modified")      # not tested (22s; MemoryError on 32 bit), needs sage.groups
M(  6,  0,  0,  0,  0),
M( -6,  0,  0,  0,  0),
M(  0,  1,  0,  0,  0),
M(  0,  0,  3,  0,  0),
M(  0,  1,  0,  3,  0),
M(  0,  0,  0,  0,  3),
M( -6,  1,  6,  3, -6),
M( -6,  0,  6,  0, -3),
M(-12,  1,  6,  3, -3),
M( -6,  1,  0,  3,  0),
M( -6,  0,  3,  3,  0),
M(  6,  0, -6, -3,  6),
M(-12,  1,  6,  3, -6),
M(-12,  0,  9,  3, -6),
M(  0,  0,  0, -3,  0),
M(-12,  1,  6,  6, -6),
M(-12,  0,  6,  3, -3),
M(  0,  1, -3,  0,  0),
M(  0,  0, -3, -3,  3),
M(  0,  1,  0,  3, -3),
M(  0, -1,  0, -3,  3),
M(  0,  0,  3,  3, -3),
M(  0, -1,  3,  0,  0),
M( 12,  0, -6, -3,  3),
M( 12, -1, -6, -6,  6),
M(  0,  0,  0,  3,  0),
M( 12,  0, -9, -3,  6),
M( 12, -1, -6, -3,  6),
M( -6,  0,  6,  3, -6),
M(  6,  0, -3, -3,  0),
M(  6, -1,  0, -3,  0),
M(-12,  1,  9,  6, -6),
M(  6,  0, -6,  0,  3),
M(  6, -1, -6, -3,  6),
M(  0,  0,  0,  0, -3),
M(  0, -1,  0, -3,  0),
M(  0,  0, -3,  0,  0),
M(  0, -1,  0,  0,  0),
M( 12, -1, -9, -6,  6),
M( 12, -1, -6, -3,  3)
in 5-d lattice M
sage: %timeit P.normal_form.clear_cache(); P.normal_form("palp_native")    # not tested
10 loops, best of 3: 0.137 s per loop
sage: %timeit P.normal_form.clear_cache(); P.normal_form("palp_modified")  # not tested
10 loops, best of 3:  22.2 s per loop
>>> from sage.all import *
>>> P = LatticePolytope([[-Integer(3), -Integer(3), -Integer(6), -Integer(6), -Integer(1)], [Integer(3), Integer(3), Integer(6), Integer(6), Integer(1)], [-Integer(3), -Integer(3), -Integer(6), -Integer(6), Integer(1)],
...                      [-Integer(3), -Integer(3), -Integer(3), -Integer(6), Integer(0)], [-Integer(3), -Integer(3), -Integer(3), Integer(0), Integer(0)], [-Integer(3), -Integer(3), Integer(0), Integer(0), Integer(0)],
...                      [-Integer(3), Integer(0), -Integer(6), -Integer(6), Integer(0)], [-Integer(3), Integer(0), -Integer(3), -Integer(6), Integer(0)], [-Integer(3), Integer(0), -Integer(3), Integer(0), Integer(0)],
...                      [-Integer(3), Integer(0), Integer(0), Integer(0), -Integer(1)], [Integer(3), Integer(3), Integer(6), Integer(6), -Integer(1)], [-Integer(3), Integer(0), Integer(0), Integer(0), Integer(1)],
...                      [Integer(0), -Integer(3), -Integer(6), -Integer(6), Integer(0)], [Integer(0), -Integer(3), -Integer(3), -Integer(6), Integer(0)], [Integer(0), -Integer(3), -Integer(3), Integer(0), Integer(0)],
...                      [Integer(0), -Integer(3), Integer(0), Integer(0), -Integer(1)], [Integer(3), Integer(3), Integer(3), Integer(6), Integer(0)], [Integer(0), -Integer(3), Integer(0), Integer(0), Integer(1)],
...                      [Integer(0), Integer(0), -Integer(6), -Integer(6), Integer(0)], [Integer(0), Integer(0), -Integer(3), -Integer(6), -Integer(1)], [Integer(3), Integer(3), Integer(3), Integer(0), Integer(0)],
...                      [Integer(0), Integer(0), -Integer(3), -Integer(6), Integer(1)], [Integer(0), Integer(0), -Integer(3), Integer(0), -Integer(1)], [Integer(3), Integer(3), Integer(0), Integer(0), Integer(0)],
...                      [Integer(0), Integer(0), -Integer(3), Integer(0), Integer(1)], [Integer(0), Integer(0), Integer(3), Integer(0), -Integer(1)], [Integer(3), Integer(0), Integer(6), Integer(6), Integer(0)],
...                      [Integer(0), Integer(0), Integer(3), Integer(0), Integer(1)], [Integer(0), Integer(0), Integer(3), Integer(6), -Integer(1)], [Integer(3), Integer(0), Integer(3), Integer(6), Integer(0)],
...                      [Integer(0), Integer(0), Integer(3), Integer(6), Integer(1)], [Integer(0), Integer(0), Integer(6), Integer(6), Integer(0)], [Integer(0), Integer(3), Integer(0), Integer(0), -Integer(1)],
...                      [Integer(3), Integer(0), Integer(3), Integer(0), Integer(0)], [Integer(0), Integer(3), Integer(0), Integer(0), Integer(1)], [Integer(0), Integer(3), Integer(3), Integer(0), Integer(0)],
...                      [Integer(0), Integer(3), Integer(3), Integer(6), Integer(0)], [Integer(0), Integer(3), Integer(6), Integer(6), Integer(0)], [Integer(3), Integer(0),Integer(0), Integer(0), -Integer(1)], [Integer(3), Integer(0), Integer(0), Integer(0), Integer(1)]])
>>> P.normal_form(algorithm="palp")  # not tested
Traceback (most recent call last):
...
RuntimeError: Error executing ... for a polytope sequence!
Output:
b'*** stack smashing detected ***: terminated\nAborted\n'
>>> P.normal_form(algorithm="palp_native")                                # needs sage.groups
M(  6,  0,  0,  0,  0),
M( -6,  0,  0,  0,  0),
M(  0,  1,  0,  0,  0),
M(  0,  0,  3,  0,  0),
M(  0,  1,  0,  3,  0),
M(  0,  0,  0,  0,  3),
M( -6,  1,  6,  3, -6),
M( -6,  0,  6,  0, -3),
M(-12,  1,  6,  3, -3),
M( -6,  1,  0,  3,  0),
M( -6,  0,  3,  3,  0),
M(  6,  0, -6, -3,  6),
M(-12,  1,  6,  3, -6),
M(-12,  0,  9,  3, -6),
M(  0,  0,  0, -3,  0),
M(-12,  1,  6,  6, -6),
M(-12,  0,  6,  3, -3),
M(  0,  1, -3,  0,  0),
M(  0,  0, -3, -3,  3),
M(  0,  1,  0,  3, -3),
M(  0, -1,  0, -3,  3),
M(  0,  0,  3,  3, -3),
M(  0, -1,  3,  0,  0),
M( 12,  0, -6, -3,  3),
M( 12, -1, -6, -6,  6),
M(  0,  0,  0,  3,  0),
M( 12,  0, -9, -3,  6),
M( 12, -1, -6, -3,  6),
M( -6,  0,  6,  3, -6),
M(  6,  0, -3, -3,  0),
M(  6, -1,  0, -3,  0),
M(-12,  1,  9,  6, -6),
M(  6,  0, -6,  0,  3),
M(  6, -1, -6, -3,  6),
M(  0,  0,  0,  0, -3),
M(  0, -1,  0, -3,  0),
M(  0,  0, -3,  0,  0),
M(  0, -1,  0,  0,  0),
M( 12, -1, -9, -6,  6),
M( 12, -1, -6, -3,  3)
in 5-d lattice M
>>> P.normal_form(algorithm="palp_modified")      # not tested (22s; MemoryError on 32 bit), needs sage.groups
M(  6,  0,  0,  0,  0),
M( -6,  0,  0,  0,  0),
M(  0,  1,  0,  0,  0),
M(  0,  0,  3,  0,  0),
M(  0,  1,  0,  3,  0),
M(  0,  0,  0,  0,  3),
M( -6,  1,  6,  3, -6),
M( -6,  0,  6,  0, -3),
M(-12,  1,  6,  3, -3),
M( -6,  1,  0,  3,  0),
M( -6,  0,  3,  3,  0),
M(  6,  0, -6, -3,  6),
M(-12,  1,  6,  3, -6),
M(-12,  0,  9,  3, -6),
M(  0,  0,  0, -3,  0),
M(-12,  1,  6,  6, -6),
M(-12,  0,  6,  3, -3),
M(  0,  1, -3,  0,  0),
M(  0,  0, -3, -3,  3),
M(  0,  1,  0,  3, -3),
M(  0, -1,  0, -3,  3),
M(  0,  0,  3,  3, -3),
M(  0, -1,  3,  0,  0),
M( 12,  0, -6, -3,  3),
M( 12, -1, -6, -6,  6),
M(  0,  0,  0,  3,  0),
M( 12,  0, -9, -3,  6),
M( 12, -1, -6, -3,  6),
M( -6,  0,  6,  3, -6),
M(  6,  0, -3, -3,  0),
M(  6, -1,  0, -3,  0),
M(-12,  1,  9,  6, -6),
M(  6,  0, -6,  0,  3),
M(  6, -1, -6, -3,  6),
M(  0,  0,  0,  0, -3),
M(  0, -1,  0, -3,  0),
M(  0,  0, -3,  0,  0),
M(  0, -1,  0,  0,  0),
M( 12, -1, -9, -6,  6),
M( 12, -1, -6, -3,  3)
in 5-d lattice M
>>> %timeit P.normal_form.clear_cache(); P.normal_form("palp_native")    # not tested
10 loops, best of 3: 0.137 s per loop
>>> %timeit P.normal_form.clear_cache(); P.normal_form("palp_modified")  # not tested
10 loops, best of 3:  22.2 s per loop
npoints()[source]#

Return the number of lattice points of this polytope.

EXAMPLES: The number of lattice points of the 3-dimensional octahedron and its polar cube:

sage: o = lattice_polytope.cross_polytope(3)
sage: o.npoints()                                                           # needs palp
7
sage: cube = o.polar()
sage: cube.npoints()                                                        # needs palp
27
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.npoints()                                                           # needs palp
7
>>> cube = o.polar()
>>> cube.npoints()                                                        # needs palp
27
nvertices()[source]#

Return the number of vertices of this polytope.

EXAMPLES: The number of vertices of the 3-dimensional octahedron and its polar cube:

sage: o = lattice_polytope.cross_polytope(3)
sage: o.nvertices()
6
sage: cube = o.polar()
sage: cube.nvertices()
8
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.nvertices()
6
>>> cube = o.polar()
>>> cube.nvertices()
8
origin()[source]#

Return the index of the origin in the list of points of self.

OUTPUT:

  • integer if the origin belongs to this polytope, None otherwise.

EXAMPLES:

sage: p = lattice_polytope.cross_polytope(2)
sage: p.origin()                                                            # needs palp
4
sage: p.point(p.origin())                                                   # needs palp
M(0, 0)

sage: p = LatticePolytope(([1],[2]))
sage: p.points()
M(1),
M(2)
in 1-d lattice M
sage: print(p.origin())
None
>>> from sage.all import *
>>> p = lattice_polytope.cross_polytope(Integer(2))
>>> p.origin()                                                            # needs palp
4
>>> p.point(p.origin())                                                   # needs palp
M(0, 0)

>>> p = LatticePolytope(([Integer(1)],[Integer(2)]))
>>> p.points()
M(1),
M(2)
in 1-d lattice M
>>> print(p.origin())
None

Now we make sure that the origin of non-full-dimensional polytopes can be identified correctly (Issue #10661):

sage: LatticePolytope([(1,0,0), (-1,0,0)]).origin()
2
>>> from sage.all import *
>>> LatticePolytope([(Integer(1),Integer(0),Integer(0)), (-Integer(1),Integer(0),Integer(0))]).origin()
2
parent()[source]#

Return the set of all lattice polytopes.

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(3)
sage: o.parent()
Set of all Lattice Polytopes
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.parent()
Set of all Lattice Polytopes
plot3d(show_facets=True, facet_opacity=0.5, facet_color=(0, 1, 0), facet_colors=None, show_edges=True, edge_thickness=3, edge_color=(0.5, 0.5, 0.5), show_vertices=True, vertex_size=10, vertex_color=(1, 0, 0), show_points=True, point_size=10, point_color=(0, 0, 1), show_vindices=None, vindex_color=(0, 0, 0), vlabels=None, show_pindices=None, pindex_color=(0, 0, 0), index_shift=1.1)[source]#

Return a 3d-plot of this polytope.

Polytopes with ambient dimension 1 and 2 will be plotted along x-axis or in xy-plane respectively. Polytopes of dimension 3 and less with ambient dimension 4 and greater will be plotted in some basis of the spanned space.

By default, everything is shown with more or less pretty combination of size and color parameters.

INPUT:

Most of the parameters are self-explanatory:

  • show_facets – (default: True)

  • facet_opacity – (default:0.5)

  • facet_color – (default:(0,1,0))

  • facet_colors – (default:None) if specified, must be a list of colors for each facet separately, used instead of facet_color

  • show_edges – (default: True) whether to draw edges as lines

  • edge_thickness – (default:3)

  • edge_color – (default:(0.5,0.5,0.5))

  • show_vertices – (default: True) whether to draw vertices as balls

  • vertex_size – (default:10)

  • vertex_color – (default:(1,0,0))

  • show_points – (default: True) whether to draw other points as balls

  • point_size – (default:10)

  • point_color – (default:(0,0,1))

  • show_vindices – (default: same as show_vertices) whether to show indices of vertices

  • vindex_color – (default:(0,0,0)) color for vertex labels

  • vlabels – (default:None) if specified, must be a list of labels for each vertex, default labels are vertex indices

  • show_pindices – (default: same as show_points) whether to show indices of other points

  • pindex_color – (default:(0,0,0)) color for point labels

  • index_shift – (default:1.1)) if 1, labels are placed exactly at the corresponding points. Otherwise the label position is computed as a multiple of the point position vector.

EXAMPLES: The default plot of a cube:

sage: c = lattice_polytope.cross_polytope(3).polar()
sage: c.plot3d()                                                            # needs palp sage.plot
Graphics3d Object
>>> from sage.all import *
>>> c = lattice_polytope.cross_polytope(Integer(3)).polar()
>>> c.plot3d()                                                            # needs palp sage.plot
Graphics3d Object

Plot without facets and points, shown without the frame:

sage: c.plot3d(show_facets=false,                                           # needs palp sage.plot
....:          show_points=false).show(frame=False)
>>> from sage.all import *
>>> c.plot3d(show_facets=false,                                           # needs palp sage.plot
...          show_points=false).show(frame=False)

Plot with facets of different colors:

sage: c.plot3d(facet_colors=rainbow(c.nfacets(), 'rgbtuple'))               # needs palp sage.plot
Graphics3d Object
>>> from sage.all import *
>>> c.plot3d(facet_colors=rainbow(c.nfacets(), 'rgbtuple'))               # needs palp sage.plot
Graphics3d Object

It is also possible to plot lower dimensional polytops in 3D (let’s also change labels of vertices):

sage: c2 = lattice_polytope.cross_polytope(2)
sage: c2.plot3d(vlabels=["A", "B", "C", "D"])                               # needs palp sage.plot
Graphics3d Object
>>> from sage.all import *
>>> c2 = lattice_polytope.cross_polytope(Integer(2))
>>> c2.plot3d(vlabels=["A", "B", "C", "D"])                               # needs palp sage.plot
Graphics3d Object
point(i)[source]#

Return the i-th point of this polytope, i.e. the i-th column of the matrix returned by points().

EXAMPLES: First few points are actually vertices:

sage: o = lattice_polytope.cross_polytope(3)
sage: o.vertices()
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1)
in 3-d lattice M
sage: o.point(1)                                                            # needs palp
M(0, 1, 0)
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.vertices()
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1)
in 3-d lattice M
>>> o.point(Integer(1))                                                            # needs palp
M(0, 1, 0)

The only other point in the octahedron is the origin:

sage: o.point(6)                                                            # needs palp
M(0, 0, 0)
sage: o.points()                                                            # needs palp
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1),
M( 0,  0,  0)
in 3-d lattice M
>>> from sage.all import *
>>> o.point(Integer(6))                                                            # needs palp
M(0, 0, 0)
>>> o.points()                                                            # needs palp
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1),
M( 0,  0,  0)
in 3-d lattice M
points(*args, **kwds)[source]#

Return all lattice points of self.

INPUT:

  • any arguments given will be passed on to the returned object.

OUTPUT:

EXAMPLES:

Lattice points of the octahedron and its polar cube:

sage: o = lattice_polytope.cross_polytope(3)
sage: o.points()                                                            # needs palp
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1),
M( 0,  0,  0)
in 3-d lattice M
sage: cube = o.polar()
sage: cube.points()                                                         # needs palp
N( 1, -1, -1),
N( 1,  1, -1),
N( 1,  1,  1),
N( 1, -1,  1),
N(-1, -1,  1),
N(-1, -1, -1),
N(-1,  1, -1),
N(-1,  1,  1),
N(-1, -1,  0),
N(-1,  0, -1),
N(-1,  0,  0),
N(-1,  0,  1),
N(-1,  1,  0),
N( 0, -1, -1),
N( 0, -1,  0),
N( 0, -1,  1),
N( 0,  0, -1),
N( 0,  0,  0),
N( 0,  0,  1),
N( 0,  1, -1),
N( 0,  1,  0),
N( 0,  1,  1),
N( 1, -1,  0),
N( 1,  0, -1),
N( 1,  0,  0),
N( 1,  0,  1),
N( 1,  1,  0)
in 3-d lattice N
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.points()                                                            # needs palp
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1),
M( 0,  0,  0)
in 3-d lattice M
>>> cube = o.polar()
>>> cube.points()                                                         # needs palp
N( 1, -1, -1),
N( 1,  1, -1),
N( 1,  1,  1),
N( 1, -1,  1),
N(-1, -1,  1),
N(-1, -1, -1),
N(-1,  1, -1),
N(-1,  1,  1),
N(-1, -1,  0),
N(-1,  0, -1),
N(-1,  0,  0),
N(-1,  0,  1),
N(-1,  1,  0),
N( 0, -1, -1),
N( 0, -1,  0),
N( 0, -1,  1),
N( 0,  0, -1),
N( 0,  0,  0),
N( 0,  0,  1),
N( 0,  1, -1),
N( 0,  1,  0),
N( 0,  1,  1),
N( 1, -1,  0),
N( 1,  0, -1),
N( 1,  0,  0),
N( 1,  0,  1),
N( 1,  1,  0)
in 3-d lattice N

Lattice points of a 2-dimensional diamond in a 3-dimensional space:

sage: p = LatticePolytope([(1,0,0), (0,1,0), (-1,0,0), (0,-1,0)])
sage: p.points()                                                            # needs palp
M( 1,  0, 0),
M( 0,  1, 0),
M(-1,  0, 0),
M( 0, -1, 0),
M( 0,  0, 0)
in 3-d lattice M
>>> from sage.all import *
>>> p = LatticePolytope([(Integer(1),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0)), (-Integer(1),Integer(0),Integer(0)), (Integer(0),-Integer(1),Integer(0))])
>>> p.points()                                                            # needs palp
M( 1,  0, 0),
M( 0,  1, 0),
M(-1,  0, 0),
M( 0, -1, 0),
M( 0,  0, 0)
in 3-d lattice M

Only two of the above points:

sage: p.points(1, 3)                                                        # needs palp
M(0,  1, 0),
M(0, -1, 0)
in 3-d lattice M
>>> from sage.all import *
>>> p.points(Integer(1), Integer(3))                                                        # needs palp
M(0,  1, 0),
M(0, -1, 0)
in 3-d lattice M

We check that points of a zero-dimensional polytope can be computed:

sage: p = LatticePolytope([[1]])
sage: p.points()
M(1)
in 1-d lattice M
>>> from sage.all import *
>>> p = LatticePolytope([[Integer(1)]])
>>> p.points()
M(1)
in 1-d lattice M
polar()[source]#

Return the polar polytope, if this polytope is reflexive.

EXAMPLES: The polar polytope to the 3-dimensional octahedron:

sage: o = lattice_polytope.cross_polytope(3)
sage: cube = o.polar()
sage: cube
3-d reflexive polytope in 3-d lattice N
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> cube = o.polar()
>>> cube
3-d reflexive polytope in 3-d lattice N

The polar polytope “remembers” the original one:

sage: cube.polar()
3-d reflexive polytope in 3-d lattice M
sage: cube.polar().polar() is cube
True
>>> from sage.all import *
>>> cube.polar()
3-d reflexive polytope in 3-d lattice M
>>> cube.polar().polar() is cube
True

Only reflexive polytopes have polars:

sage: p = LatticePolytope([(1,0,0), (0,1,0), (0,0,2),
....:                      (-1,0,0), (0,-1,0), (0,0,-1)])
sage: p.polar()
Traceback (most recent call last):
...
ValueError: The given polytope is not reflexive!
Polytope: 3-d lattice polytope in 3-d lattice M
>>> from sage.all import *
>>> p = LatticePolytope([(Integer(1),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0)), (Integer(0),Integer(0),Integer(2)),
...                      (-Integer(1),Integer(0),Integer(0)), (Integer(0),-Integer(1),Integer(0)), (Integer(0),Integer(0),-Integer(1))])
>>> p.polar()
Traceback (most recent call last):
...
ValueError: The given polytope is not reflexive!
Polytope: 3-d lattice polytope in 3-d lattice M
poly_x(keys, reduce_dimension=False)[source]#

Run poly.x with given keys on vertices of this polytope.

INPUT:

  • keys – a string of options passed to poly.x. The key “f” is added automatically.

  • reduce_dimension – (default: False) if True and this polytope is not full-dimensional, poly.x will be called for the vertices of this polytope in some basis of the spanned affine space.

OUTPUT: the output of poly.x as a string.

EXAMPLES: This call is used for determining if a polytope is reflexive or not:

sage: o = lattice_polytope.cross_polytope(3)
sage: print(o.poly_x("e"))                                                  # needs palp
8 3  Vertices of P-dual <-> Equations of P
  -1  -1   1
   1  -1   1
  -1   1   1
   1   1   1
  -1  -1  -1
   1  -1  -1
  -1   1  -1
   1   1  -1
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> print(o.poly_x("e"))                                                  # needs palp
8 3  Vertices of P-dual <-> Equations of P
  -1  -1   1
   1  -1   1
  -1   1   1
   1   1   1
  -1  -1  -1
   1  -1  -1
  -1   1  -1
   1   1  -1

Since PALP has limits on different parameters determined during compilation, the following code is likely to fail, unless you change default settings of PALP:

sage: BIG = lattice_polytope.cross_polytope(7)
sage: BIG
7-d reflexive polytope in 7-d lattice M
sage: BIG.poly_x("e")                                                       # needs palp
Traceback (most recent call last):
...
ValueError: Error executing 'poly.x -fe' for the given polytope!
Output:
Please increase POLY_Dmax to at least 7
>>> from sage.all import *
>>> BIG = lattice_polytope.cross_polytope(Integer(7))
>>> BIG
7-d reflexive polytope in 7-d lattice M
>>> BIG.poly_x("e")                                                       # needs palp
Traceback (most recent call last):
...
ValueError: Error executing 'poly.x -fe' for the given polytope!
Output:
Please increase POLY_Dmax to at least 7

You cannot call poly.x for polytopes that don’t span the space (if you could, it would crush anyway):

sage: p = LatticePolytope([(1,0,0), (0,1,0), (-1,0,0), (0,-1,0)])
sage: p.poly_x("e")                                                         # needs palp
Traceback (most recent call last):
...
ValueError: Cannot run PALP for a 2-dimensional polytope in a 3-dimensional space!
>>> from sage.all import *
>>> p = LatticePolytope([(Integer(1),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0)), (-Integer(1),Integer(0),Integer(0)), (Integer(0),-Integer(1),Integer(0))])
>>> p.poly_x("e")                                                         # needs palp
Traceback (most recent call last):
...
ValueError: Cannot run PALP for a 2-dimensional polytope in a 3-dimensional space!

But if you know what you are doing, you can call it for the polytope in some basis of the spanned space:

sage: print(p.poly_x("e", reduce_dimension=True))                           # needs palp
4 2  Equations of P
  -1   1     0
   1   1     2
  -1  -1     0
   1  -1     2
>>> from sage.all import *
>>> print(p.poly_x("e", reduce_dimension=True))                           # needs palp
4 2  Equations of P
  -1   1     0
   1   1     2
  -1  -1     0
   1  -1     2
polyhedron(**kwds)[source]#

Return the Polyhedron object determined by this polytope’s vertices.

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(2)
sage: o.polyhedron()
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(2))
>>> o.polyhedron()
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices
show3d()[source]#

Show a 3d picture of the polytope with default settings and without axes or frame.

See self.plot3d? for more details.

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(3)
sage: o.show3d()                                                            # needs palp sage.plot
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.show3d()                                                            # needs palp sage.plot
skeleton()[source]#

Return the graph of the one-skeleton of this polytope.

EXAMPLES:

sage: d = lattice_polytope.cross_polytope(2)
sage: g = d.skeleton(); g                                                   # needs palp sage.graphs
Graph on 4 vertices
sage: g.edges(sort=True)                                                    # needs palp sage.graphs
[(0, 1, None), (0, 3, None), (1, 2, None), (2, 3, None)]
>>> from sage.all import *
>>> d = lattice_polytope.cross_polytope(Integer(2))
>>> g = d.skeleton(); g                                                   # needs palp sage.graphs
Graph on 4 vertices
>>> g.edges(sort=True)                                                    # needs palp sage.graphs
[(0, 1, None), (0, 3, None), (1, 2, None), (2, 3, None)]
skeleton_points(k=1)[source]#

Return the increasing list of indices of lattice points in k-skeleton of the polytope (k is 1 by default).

EXAMPLES: We compute all skeleton points for the cube:

sage: o = lattice_polytope.cross_polytope(3)
sage: c = o.polar()
sage: c.skeleton_points()                                                   # needs palp sage.graphs
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 15, 19, 21, 22, 23, 25, 26]
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> c = o.polar()
>>> c.skeleton_points()                                                   # needs palp sage.graphs
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 15, 19, 21, 22, 23, 25, 26]

The default was 1-skeleton:

sage: c.skeleton_points(k=1)                                                # needs palp sage.graphs
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 15, 19, 21, 22, 23, 25, 26]
>>> from sage.all import *
>>> c.skeleton_points(k=Integer(1))                                                # needs palp sage.graphs
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 15, 19, 21, 22, 23, 25, 26]

0-skeleton just lists all vertices:

sage: c.skeleton_points(k=0)                                                # needs palp sage.graphs
[0, 1, 2, 3, 4, 5, 6, 7]
>>> from sage.all import *
>>> c.skeleton_points(k=Integer(0))                                                # needs palp sage.graphs
[0, 1, 2, 3, 4, 5, 6, 7]

2-skeleton lists all points except for the origin (point #17):

sage: c.skeleton_points(k=2)                                                # needs palp sage.graphs
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
 18, 19, 20, 21, 22, 23, 24, 25, 26]
>>> from sage.all import *
>>> c.skeleton_points(k=Integer(2))                                                # needs palp sage.graphs
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
 18, 19, 20, 21, 22, 23, 24, 25, 26]

3-skeleton includes all points:

sage: c.skeleton_points(k=3)                                                # needs palp
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
 18, 19, 20, 21, 22, 23, 24, 25, 26]
>>> from sage.all import *
>>> c.skeleton_points(k=Integer(3))                                                # needs palp
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
 18, 19, 20, 21, 22, 23, 24, 25, 26]

It is OK to compute higher dimensional skeletons - you will get the list of all points:

sage: c.skeleton_points(k=100)                                              # needs palp
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
 18, 19, 20, 21, 22, 23, 24, 25, 26]
>>> from sage.all import *
>>> c.skeleton_points(k=Integer(100))                                              # needs palp
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
 18, 19, 20, 21, 22, 23, 24, 25, 26]
skeleton_show(normal=None)[source]#

Show the graph of one-skeleton of this polytope. Works only for polytopes in a 3-dimensional space.

INPUT:

  • normal – a 3-dimensional vector (can be given as a list), which should be perpendicular to the screen. If not given, will be selected randomly (new each time and it may be far from “nice”).

EXAMPLES: Show a pretty picture of the octahedron:

sage: o = lattice_polytope.cross_polytope(3)
sage: o.skeleton_show([1,2,4])                                              # needs palp sage.plot
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.skeleton_show([Integer(1),Integer(2),Integer(4)])                                              # needs palp sage.plot

Does not work for a diamond at the moment:

sage: d = lattice_polytope.cross_polytope(2)
sage: d.skeleton_show()
Traceback (most recent call last):
...
NotImplementedError: skeleton view is implemented only in 3-d space
>>> from sage.all import *
>>> d = lattice_polytope.cross_polytope(Integer(2))
>>> d.skeleton_show()
Traceback (most recent call last):
...
NotImplementedError: skeleton view is implemented only in 3-d space
traverse_boundary()[source]#

Return a list of indices of vertices of a 2-dimensional polytope in their boundary order.

Needed for plot3d function of polytopes.

EXAMPLES:

sage: p = lattice_polytope.cross_polytope(2).polar()
sage: p.traverse_boundary()                                                 # needs sage.graphs
[3, 0, 1, 2]
>>> from sage.all import *
>>> p = lattice_polytope.cross_polytope(Integer(2)).polar()
>>> p.traverse_boundary()                                                 # needs sage.graphs
[3, 0, 1, 2]
vertex(i)[source]#

Return the i-th vertex of this polytope, i.e. the i-th column of the matrix returned by vertices().

EXAMPLES: Note that numeration starts with zero:

sage: o = lattice_polytope.cross_polytope(3)
sage: o.vertices()
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1)
in 3-d lattice M
sage: o.vertex(3)
M(-1, 0, 0)
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.vertices()
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1)
in 3-d lattice M
>>> o.vertex(Integer(3))
M(-1, 0, 0)
vertex_facet_pairing_matrix()[source]#

Return the vertex facet pairing matrix \(PM\).

Return a matrix whose the \(i, j^\text{th}\) entry is the height of the \(j^\text{th}\) vertex over the \(i^\text{th}\) facet. The ordering of the vertices and facets is as in vertices() and facets().

EXAMPLES:

sage: L = lattice_polytope.cross_polytope(3)
sage: L.vertex_facet_pairing_matrix()
[2 0 0 0 2 2]
[2 2 0 0 0 2]
[2 2 2 0 0 0]
[2 0 2 0 2 0]
[0 0 2 2 2 0]
[0 0 0 2 2 2]
[0 2 0 2 0 2]
[0 2 2 2 0 0]
>>> from sage.all import *
>>> L = lattice_polytope.cross_polytope(Integer(3))
>>> L.vertex_facet_pairing_matrix()
[2 0 0 0 2 2]
[2 2 0 0 0 2]
[2 2 2 0 0 0]
[2 0 2 0 2 0]
[0 0 2 2 2 0]
[0 0 0 2 2 2]
[0 2 0 2 0 2]
[0 2 2 2 0 0]
vertices(*args, **kwds)[source]#

Return vertices of self.

INPUT:

  • any arguments given will be passed on to the returned object.

OUTPUT:

EXAMPLES:

Vertices of the octahedron and its polar cube are in dual lattices:

sage: o = lattice_polytope.cross_polytope(3)
sage: o.vertices()
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1)
in 3-d lattice M
sage: cube = o.polar()
sage: cube.vertices()
N( 1, -1, -1),
N( 1,  1, -1),
N( 1,  1,  1),
N( 1, -1,  1),
N(-1, -1,  1),
N(-1, -1, -1),
N(-1,  1, -1),
N(-1,  1,  1)
in 3-d lattice N
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o.vertices()
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1)
in 3-d lattice M
>>> cube = o.polar()
>>> cube.vertices()
N( 1, -1, -1),
N( 1,  1, -1),
N( 1,  1,  1),
N( 1, -1,  1),
N(-1, -1,  1),
N(-1, -1, -1),
N(-1,  1, -1),
N(-1,  1,  1)
in 3-d lattice N
class sage.geometry.lattice_polytope.NefPartition(data, Delta_polar, check=True)[source]#

Bases: SageObject, Hashable

Create a nef-partition.

INPUT:

  • data – a list of integers, the \(i\)-th element of this list must be the part of the \(i\)-th vertex of Delta_polar in this nef-partition;

  • Delta_polar – a lattice polytope;

  • check – by default the input will be checked for correctness, i.e. that data indeed specify a nef-partition. If you are sure that the input is correct, you can speed up construction via check=False option.

OUTPUT:

  • a nef-partition of Delta_polar.

Let \(M\) and \(N\) be dual lattices. Let \(\Delta \subset M_\RR\) be a reflexive polytope with polar \(\Delta^\circ \subset N_\RR\). Let \(X_\Delta\) be the toric variety associated to the normal fan of \(\Delta\). A nef-partition is a decomposition of the vertex set \(V\) of \(\Delta^\circ\) into a disjoint union \(V = V_0 \sqcup V_1 \sqcup \dots \sqcup V_{k-1}\) such that divisors \(E_i = \sum_{v\in V_i} D_v\) are Cartier (here \(D_v\) are prime torus-invariant Weil divisors corresponding to vertices of \(\Delta^\circ\)). Equivalently, let \(\nabla_i \subset N_\RR\) be the convex hull of vertices from \(V_i\) and the origin. These polytopes form a nef-partition if their Minkowski sum \(\nabla \subset N_\RR\) is a reflexive polytope.

The dual nef-partition is formed by polytopes \(\Delta_i \subset M_\RR\) of \(E_i\), which give a decomposition of the vertex set of \(\nabla^\circ \subset M_\RR\) and their Minkowski sum is \(\Delta\), i.e. the polar duality of reflexive polytopes switches convex hull and Minkowski sum for dual nef-partitions:

\[\begin{split}\Delta^\circ &= \mathrm{Conv} \left(\nabla_0, \nabla_1, \dots, \nabla_{k-1}\right), \\ \nabla^{\phantom{\circ}} &= \nabla_0 + \nabla_1 + \dots + \nabla_{k-1}, \\ & \\ \Delta^{\phantom{\circ}} &= \Delta_0 + \Delta_1 + \dots + \Delta_{k-1}, \\ \nabla^\circ &= \mathrm{Conv} \left(\Delta_0, \Delta_1, \dots, \Delta_{k-1}\right).\end{split}\]

One can also interpret the duality of nef-partitions as the duality of the associated cones. Below \(\overline{M} = M \times \ZZ^k\) and \(\overline{N} = N \times \ZZ^k\) are dual lattices.

The Cayley polytope \(P \subset \overline{M}_\RR\) of a nef-partition is given by \(P = \mathrm{Conv}(\Delta_0 \times e_0, \Delta_1 \times e_1, \ldots, \Delta_{k-1} \times e_{k-1})\), where \(\{e_i\}_{i=0}^{k-1}\) is the standard basis of \(\ZZ^k\). The dual Cayley polytope \(P^* \subset \overline{N}_\RR\) is the Cayley polytope of the dual nef-partition.

The Cayley cone \(C \subset \overline{M}_\RR\) of a nef-partition is the cone spanned by its Cayley polytope. The dual Cayley cone \(C^\vee \subset \overline{M}_\RR\) is the usual dual cone of \(C\). It turns out, that \(C^\vee\) is spanned by \(P^*\).

It is also possible to go back from the Cayley cone to the Cayley polytope, since \(C\) is a reflexive Gorenstein cone supported by \(P\): primitive integral ray generators of \(C\) are contained in an affine hyperplane and coincide with vertices of \(P\).

See Section 4.3.1 in [CK1999] and references therein for further details, or [BN2008] for a purely combinatorial approach.

EXAMPLES:

It is very easy to create a nef-partition for the octahedron, since for this polytope any decomposition of vertices is a nef-partition. We create a 3-part nef-partition with the 0-th and 1-st vertices belonging to the 0-th part (recall that numeration in Sage starts with 0), the 2-nd and 5-th vertices belonging to the 1-st part, and 3-rd and 4-th vertices belonging to the 2-nd part:

sage: o = lattice_polytope.cross_polytope(3)
sage: np = NefPartition([0,0,1,2,2,1], o)
sage: np
Nef-partition {0, 1} ⊔ {2, 5} ⊔ {3, 4}
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> np = NefPartition([Integer(0),Integer(0),Integer(1),Integer(2),Integer(2),Integer(1)], o)
>>> np
Nef-partition {0, 1} ⊔ {2, 5} ⊔ {3, 4}

The octahedron plays the role of \(\Delta^\circ\) in the above description:

sage: np.Delta_polar() is o
True
>>> from sage.all import *
>>> np.Delta_polar() is o
True

The dual nef-partition (corresponding to the “mirror complete intersection”) gives decomposition of the vertex set of \(\nabla^\circ\):

sage: np.dual()
Nef-partition {0, 1, 2} ⊔ {3, 4} ⊔ {5, 6, 7}
sage: np.nabla_polar().vertices()
N(-1, -1,  0),
N(-1,  0,  0),
N( 0, -1,  0),
N( 0,  0, -1),
N( 0,  0,  1),
N( 1,  0,  0),
N( 0,  1,  0),
N( 1,  1,  0)
in 3-d lattice N
>>> from sage.all import *
>>> np.dual()
Nef-partition {0, 1, 2} ⊔ {3, 4} ⊔ {5, 6, 7}
>>> np.nabla_polar().vertices()
N(-1, -1,  0),
N(-1,  0,  0),
N( 0, -1,  0),
N( 0,  0, -1),
N( 0,  0,  1),
N( 1,  0,  0),
N( 0,  1,  0),
N( 1,  1,  0)
in 3-d lattice N

Of course, \(\nabla^\circ\) is \(\Delta^\circ\) from the point of view of the dual nef-partition:

sage: np.dual().Delta_polar() is np.nabla_polar()
True
sage: np.Delta(1).vertices()
N(0, 0, -1),
N(0, 0,  1)
in 3-d lattice N
sage: np.dual().nabla(1).vertices()
N(0, 0, -1),
N(0, 0,  1)
in 3-d lattice N
>>> from sage.all import *
>>> np.dual().Delta_polar() is np.nabla_polar()
True
>>> np.Delta(Integer(1)).vertices()
N(0, 0, -1),
N(0, 0,  1)
in 3-d lattice N
>>> np.dual().nabla(Integer(1)).vertices()
N(0, 0, -1),
N(0, 0,  1)
in 3-d lattice N

Instead of constructing nef-partitions directly, you can request all 2-part nef-partitions of a given reflexive polytope (they will be computed using nef.x program from PALP):

sage: o.nef_partitions()                                                        # needs palp
[
Nef-partition {0, 1, 3} ⊔ {2, 4, 5},
Nef-partition {0, 1, 3, 4} ⊔ {2, 5} (direct product),
Nef-partition {0, 1, 2} ⊔ {3, 4, 5},
Nef-partition {0, 1, 2, 3} ⊔ {4, 5},
Nef-partition {0, 1, 2, 3, 4} ⊔ {5} (projection)
]
>>> from sage.all import *
>>> o.nef_partitions()                                                        # needs palp
[
Nef-partition {0, 1, 3} ⊔ {2, 4, 5},
Nef-partition {0, 1, 3, 4} ⊔ {2, 5} (direct product),
Nef-partition {0, 1, 2} ⊔ {3, 4, 5},
Nef-partition {0, 1, 2, 3} ⊔ {4, 5},
Nef-partition {0, 1, 2, 3, 4} ⊔ {5} (projection)
]
Delta(i=None)[source]#

Return the polytope \(\Delta\) or \(\Delta_i\) corresponding to self.

INPUT:

  • i – an integer. If not given, \(\Delta\) will be returned.

OUTPUT:

See nef-partition class documentation for definitions and notation.

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(3)
sage: np = NefPartition([0, 0, 1, 0, 1, 1], o); np
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
sage: np.Delta().polar() is o
True
sage: np.Delta().vertices()
N( 1, -1, -1),
N( 1,  1, -1),
N( 1,  1,  1),
N( 1, -1,  1),
N(-1, -1,  1),
N(-1, -1, -1),
N(-1,  1, -1),
N(-1,  1,  1)
in 3-d lattice N
sage: np.Delta(0).vertices()
N(-1, -1, 0),
N(-1,  0, 0),
N( 1,  0, 0),
N( 1, -1, 0)
in 3-d lattice N
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> np = NefPartition([Integer(0), Integer(0), Integer(1), Integer(0), Integer(1), Integer(1)], o); np
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
>>> np.Delta().polar() is o
True
>>> np.Delta().vertices()
N( 1, -1, -1),
N( 1,  1, -1),
N( 1,  1,  1),
N( 1, -1,  1),
N(-1, -1,  1),
N(-1, -1, -1),
N(-1,  1, -1),
N(-1,  1,  1)
in 3-d lattice N
>>> np.Delta(Integer(0)).vertices()
N(-1, -1, 0),
N(-1,  0, 0),
N( 1,  0, 0),
N( 1, -1, 0)
in 3-d lattice N
Delta_polar()[source]#

Return the polytope \(\Delta^\circ\) corresponding to self.

OUTPUT:

See nef-partition class documentation for definitions and notation.

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(3)
sage: np = NefPartition([0, 0, 1, 0, 1, 1], o); np
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
sage: np.Delta_polar() is o
True
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> np = NefPartition([Integer(0), Integer(0), Integer(1), Integer(0), Integer(1), Integer(1)], o); np
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
>>> np.Delta_polar() is o
True
Deltas()[source]#

Return the polytopes \(\Delta_i\) corresponding to self.

OUTPUT:

See nef-partition class documentation for definitions and notation.

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(3)
sage: np = NefPartition([0, 0, 1, 0, 1, 1], o); np
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
sage: np.Delta().vertices()
N( 1, -1, -1),
N( 1,  1, -1),
N( 1,  1,  1),
N( 1, -1,  1),
N(-1, -1,  1),
N(-1, -1, -1),
N(-1,  1, -1),
N(-1,  1,  1)
in 3-d lattice N
sage: [Delta_i.vertices() for Delta_i in np.Deltas()]
[N(-1, -1, 0),
 N(-1,  0, 0),
 N( 1,  0, 0),
 N( 1, -1, 0)
 in 3-d lattice N,
 N(0, 0, -1),
 N(0, 1,  1),
 N(0, 0,  1),
 N(0, 1, -1)
 in 3-d lattice N]
sage: np.nabla_polar().vertices()
N(-1, -1,  0),
N( 1, -1,  0),
N( 1,  0,  0),
N(-1,  0,  0),
N( 0,  1, -1),
N( 0,  1,  1),
N( 0,  0,  1),
N( 0,  0, -1)
in 3-d lattice N
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> np = NefPartition([Integer(0), Integer(0), Integer(1), Integer(0), Integer(1), Integer(1)], o); np
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
>>> np.Delta().vertices()
N( 1, -1, -1),
N( 1,  1, -1),
N( 1,  1,  1),
N( 1, -1,  1),
N(-1, -1,  1),
N(-1, -1, -1),
N(-1,  1, -1),
N(-1,  1,  1)
in 3-d lattice N
>>> [Delta_i.vertices() for Delta_i in np.Deltas()]
[N(-1, -1, 0),
 N(-1,  0, 0),
 N( 1,  0, 0),
 N( 1, -1, 0)
 in 3-d lattice N,
 N(0, 0, -1),
 N(0, 1,  1),
 N(0, 0,  1),
 N(0, 1, -1)
 in 3-d lattice N]
>>> np.nabla_polar().vertices()
N(-1, -1,  0),
N( 1, -1,  0),
N( 1,  0,  0),
N(-1,  0,  0),
N( 0,  1, -1),
N( 0,  1,  1),
N( 0,  0,  1),
N( 0,  0, -1)
in 3-d lattice N
dual()[source]#

Return the dual nef-partition.

OUTPUT:

See the class documentation for the definition.

ALGORITHM:

See Proposition 3.19 in [BN2008].

Note

Automatically constructed dual nef-partitions will be ordered, i.e. vertex partition of \(\nabla\) will look like \(\{0, 1, 2\} \sqcup \{3, 4, 5, 6\} \sqcup \{7, 8\}\).

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(3)
sage: np = NefPartition([0, 0, 1, 0, 1, 1], o); np
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
sage: np.dual()
Nef-partition {0, 1, 2, 3} ⊔ {4, 5, 6, 7}
sage: np.dual().Delta() is np.nabla()
True
sage: np.dual().nabla(0) is np.Delta(0)
True
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> np = NefPartition([Integer(0), Integer(0), Integer(1), Integer(0), Integer(1), Integer(1)], o); np
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
>>> np.dual()
Nef-partition {0, 1, 2, 3} ⊔ {4, 5, 6, 7}
>>> np.dual().Delta() is np.nabla()
True
>>> np.dual().nabla(Integer(0)) is np.Delta(Integer(0))
True
hodge_numbers()[source]#

Return Hodge numbers corresponding to self.

OUTPUT:

  • a tuple of integers (produced by nef.x program from PALP).

EXAMPLES:

Currently, you need to request Hodge numbers when you compute nef-partitions:

sage: # long time, needs palp
sage: p = lattice_polytope.cross_polytope(5)
sage: np = p.nef_partitions()[0]                    # 4s on sage.math, 2011
sage: np.hodge_numbers()
Traceback (most recent call last):
...
NotImplementedError: use nef_partitions(hodge_numbers=True)!
sage: np = p.nef_partitions(hodge_numbers=True)[0]  # 13s on sage.math, 2011
sage: np.hodge_numbers()
(19, 19)
>>> from sage.all import *
>>> # long time, needs palp
>>> p = lattice_polytope.cross_polytope(Integer(5))
>>> np = p.nef_partitions()[Integer(0)]                    # 4s on sage.math, 2011
>>> np.hodge_numbers()
Traceback (most recent call last):
...
NotImplementedError: use nef_partitions(hodge_numbers=True)!
>>> np = p.nef_partitions(hodge_numbers=True)[Integer(0)]  # 13s on sage.math, 2011
>>> np.hodge_numbers()
(19, 19)
nabla(i=None)[source]#

Return the polytope \(\nabla\) or \(\nabla_i\) corresponding to self.

INPUT:

  • i – an integer. If not given, \(\nabla\) will be returned.

OUTPUT:

See nef-partition class documentation for definitions and notation.

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(3)
sage: np = NefPartition([0, 0, 1, 0, 1, 1], o); np
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
sage: np.Delta_polar().vertices()
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1)
in 3-d lattice M
sage: np.nabla(0).vertices()
M(-1, 0, 0),
M( 1, 0, 0),
M( 0, 1, 0)
in 3-d lattice M
sage: np.nabla().vertices()
M(-1,  0,  1),
M(-1,  0, -1),
M( 1,  0,  1),
M( 1,  0, -1),
M( 0,  1,  1),
M( 0,  1, -1),
M( 1, -1,  0),
M(-1, -1,  0)
in 3-d lattice M
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> np = NefPartition([Integer(0), Integer(0), Integer(1), Integer(0), Integer(1), Integer(1)], o); np
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
>>> np.Delta_polar().vertices()
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1)
in 3-d lattice M
>>> np.nabla(Integer(0)).vertices()
M(-1, 0, 0),
M( 1, 0, 0),
M( 0, 1, 0)
in 3-d lattice M
>>> np.nabla().vertices()
M(-1,  0,  1),
M(-1,  0, -1),
M( 1,  0,  1),
M( 1,  0, -1),
M( 0,  1,  1),
M( 0,  1, -1),
M( 1, -1,  0),
M(-1, -1,  0)
in 3-d lattice M
nabla_polar()[source]#

Return the polytope \(\nabla^\circ\) corresponding to self.

OUTPUT:

See nef-partition class documentation for definitions and notation.

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(3)
sage: np = NefPartition([0, 0, 1, 0, 1, 1], o); np
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
sage: np.nabla_polar().vertices()
N(-1, -1,  0),
N( 1, -1,  0),
N( 1,  0,  0),
N(-1,  0,  0),
N( 0,  1, -1),
N( 0,  1,  1),
N( 0,  0,  1),
N( 0,  0, -1)
in 3-d lattice N
sage: np.nabla_polar() is np.dual().Delta_polar()
True
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> np = NefPartition([Integer(0), Integer(0), Integer(1), Integer(0), Integer(1), Integer(1)], o); np
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
>>> np.nabla_polar().vertices()
N(-1, -1,  0),
N( 1, -1,  0),
N( 1,  0,  0),
N(-1,  0,  0),
N( 0,  1, -1),
N( 0,  1,  1),
N( 0,  0,  1),
N( 0,  0, -1)
in 3-d lattice N
>>> np.nabla_polar() is np.dual().Delta_polar()
True
nablas()[source]#

Return the polytopes \(\nabla_i\) corresponding to self.

OUTPUT:

See nef-partition class documentation for definitions and notation.

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(3)
sage: np = NefPartition([0, 0, 1, 0, 1, 1], o); np
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
sage: np.Delta_polar().vertices()
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1)
in 3-d lattice M
sage: [nabla_i.vertices() for nabla_i in np.nablas()]
[M(-1, 0, 0),
 M( 1, 0, 0),
 M( 0, 1, 0)
 in 3-d lattice M,
 M(0, -1,  0),
 M(0,  0, -1),
 M(0,  0,  1)
 in 3-d lattice M]
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> np = NefPartition([Integer(0), Integer(0), Integer(1), Integer(0), Integer(1), Integer(1)], o); np
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
>>> np.Delta_polar().vertices()
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1)
in 3-d lattice M
>>> [nabla_i.vertices() for nabla_i in np.nablas()]
[M(-1, 0, 0),
 M( 1, 0, 0),
 M( 0, 1, 0)
 in 3-d lattice M,
 M(0, -1,  0),
 M(0,  0, -1),
 M(0,  0,  1)
 in 3-d lattice M]
nparts()[source]#

Return the number of parts in self.

OUTPUT:

  • an integer.

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(3)
sage: np = NefPartition([0, 0, 1, 0, 1, 1], o); np
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
sage: np.nparts()
2
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> np = NefPartition([Integer(0), Integer(0), Integer(1), Integer(0), Integer(1), Integer(1)], o); np
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
>>> np.nparts()
2
part(i, all_points=False)[source]#

Return the i-th part of self.

INPUT:

  • i – an integer

  • all_points – (default: False) whether to list all lattice points or just vertices

OUTPUT:

  • a tuple of integers, indices of vertices (or all lattice points) of \(\Delta^\circ\) belonging to \(V_i\).

See nef-partition class documentation for definitions and notation.

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(3)
sage: np = NefPartition([0, 0, 1, 0, 1, 1], o); np
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
sage: np.part(0)
(0, 1, 3)
sage: np.part(0, all_points=True)                                           # needs palp
(0, 1, 3)
sage: np.dual().part(0)
(0, 1, 2, 3)
sage: np.dual().part(0, all_points=True)                                    # needs palp
(0, 1, 2, 3, 8)
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> np = NefPartition([Integer(0), Integer(0), Integer(1), Integer(0), Integer(1), Integer(1)], o); np
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
>>> np.part(Integer(0))
(0, 1, 3)
>>> np.part(Integer(0), all_points=True)                                           # needs palp
(0, 1, 3)
>>> np.dual().part(Integer(0))
(0, 1, 2, 3)
>>> np.dual().part(Integer(0), all_points=True)                                    # needs palp
(0, 1, 2, 3, 8)
part_of(i)[source]#

Return the index of the part containing the i-th vertex.

INPUT:

  • i – an integer.

OUTPUT:

  • an integer \(j\) such that the i-th vertex of \(\Delta^\circ\) belongs to \(V_j\).

See nef-partition class documentation for definitions and notation.

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(3)
sage: np = NefPartition([0, 0, 1, 0, 1, 1], o); np
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
sage: np.part_of(3)
0
sage: np.part_of(2)
1
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> np = NefPartition([Integer(0), Integer(0), Integer(1), Integer(0), Integer(1), Integer(1)], o); np
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
>>> np.part_of(Integer(3))
0
>>> np.part_of(Integer(2))
1
part_of_point(i)[source]#

Return the index of the part containing the i-th point.

INPUT:

  • i – an integer.

OUTPUT:

  • an integer \(j\) such that the i-th point of \(\Delta^\circ\) belongs to \(\nabla_j\).

Note

Since a nef-partition induces a partition on the set of boundary lattice points of \(\Delta^\circ\), the value of \(j\) is well-defined for all \(i\) but the one that corresponds to the origin, in which case this method will raise a ValueError exception. (The origin always belongs to all \(\nabla_j\).)

See nef-partition class documentation for definitions and notation.

EXAMPLES:

We consider a relatively complicated reflexive polytope #2252 (easily accessible in Sage as ReflexivePolytope(3, 2252), we create it here explicitly to avoid loading the whole database):

sage: p = LatticePolytope([(1,0,0), (0,1,0), (0,0,1), (0,1,-1),
....:         (0,-1,1), (-1,1,0), (0,-1,-1), (-1,-1,0), (-1,-1,2)])
sage: np = p.nef_partitions()[0]; np                                        # needs palp
Nef-partition {1, 2, 5, 7, 8} ⊔ {0, 3, 4, 6}
sage: p.nvertices()
9
sage: p.npoints()                                                           # needs palp
15
>>> from sage.all import *
>>> p = LatticePolytope([(Integer(1),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0)), (Integer(0),Integer(0),Integer(1)), (Integer(0),Integer(1),-Integer(1)),
...         (Integer(0),-Integer(1),Integer(1)), (-Integer(1),Integer(1),Integer(0)), (Integer(0),-Integer(1),-Integer(1)), (-Integer(1),-Integer(1),Integer(0)), (-Integer(1),-Integer(1),Integer(2))])
>>> np = p.nef_partitions()[Integer(0)]; np                                        # needs palp
Nef-partition {1, 2, 5, 7, 8} ⊔ {0, 3, 4, 6}
>>> p.nvertices()
9
>>> p.npoints()                                                           # needs palp
15

We see that the polytope has 6 more points in addition to vertices. One of them is the origin:

sage: p.origin()                                                            # needs palp
14
sage: np.part_of_point(14)                                                  # needs palp
Traceback (most recent call last):
...
ValueError: the origin belongs to all parts!
>>> from sage.all import *
>>> p.origin()                                                            # needs palp
14
>>> np.part_of_point(Integer(14))                                                  # needs palp
Traceback (most recent call last):
...
ValueError: the origin belongs to all parts!

But the remaining 5 are partitioned by np:

sage: [n for n in range(p.npoints())                                        # needs palp
....:    if p.origin() != n and np.part_of_point(n) == 0]
[1, 2, 5, 7, 8, 9, 11, 13]
sage: [n for n in range(p.npoints())                                        # needs palp
....:    if p.origin() != n and np.part_of_point(n) == 1]
[0, 3, 4, 6, 10, 12]
>>> from sage.all import *
>>> [n for n in range(p.npoints())                                        # needs palp
...    if p.origin() != n and np.part_of_point(n) == Integer(0)]
[1, 2, 5, 7, 8, 9, 11, 13]
>>> [n for n in range(p.npoints())                                        # needs palp
...    if p.origin() != n and np.part_of_point(n) == Integer(1)]
[0, 3, 4, 6, 10, 12]
parts(all_points=False)[source]#

Return all parts of self.

INPUT:

  • all_points – (default: False) whether to list all lattice points or just vertices

OUTPUT:

  • a tuple of tuples of integers. The \(i\)-th tuple contains indices of vertices (or all lattice points) of \(\Delta^\circ\) belonging to \(V_i\)

See nef-partition class documentation for definitions and notation.

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(3)
sage: np = NefPartition([0, 0, 1, 0, 1, 1], o); np
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
sage: np.parts()
((0, 1, 3), (2, 4, 5))
sage: np.parts(all_points=True)                                             # needs palp
((0, 1, 3), (2, 4, 5))
sage: np.dual().parts()
((0, 1, 2, 3), (4, 5, 6, 7))
sage: np.dual().parts(all_points=True)                                      # needs palp
((0, 1, 2, 3, 8), (4, 5, 6, 7, 10))
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> np = NefPartition([Integer(0), Integer(0), Integer(1), Integer(0), Integer(1), Integer(1)], o); np
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
>>> np.parts()
((0, 1, 3), (2, 4, 5))
>>> np.parts(all_points=True)                                             # needs palp
((0, 1, 3), (2, 4, 5))
>>> np.dual().parts()
((0, 1, 2, 3), (4, 5, 6, 7))
>>> np.dual().parts(all_points=True)                                      # needs palp
((0, 1, 2, 3, 8), (4, 5, 6, 7, 10))
sage.geometry.lattice_polytope.ReflexivePolytope(dim, n)[source]#

Return the \(n\)-th 2- or 3-dimensional reflexive polytope.

Note

  1. Numeration starts with zero: \(0 \leq n \leq 15\) for \({\rm dim} = 2\) and \(0 \leq n \leq 4318\) for \({\rm dim} = 3\).

  2. During the first call, all reflexive polytopes of requested dimension are loaded and cached for future use, so the first call for 3-dimensional polytopes can take several seconds, but all consecutive calls are fast.

  3. Equivalent to ReflexivePolytopes(dim)[n] but checks bounds first.

EXAMPLES:

The 3rd 2-dimensional polytope is “the diamond”:

sage: ReflexivePolytope(2, 3)
2-d reflexive polytope #3 in 2-d lattice M
sage: lattice_polytope.ReflexivePolytope(2,3).vertices()
M( 1,  0),
M( 0,  1),
M( 0, -1),
M(-1,  0)
in 2-d lattice M
>>> from sage.all import *
>>> ReflexivePolytope(Integer(2), Integer(3))
2-d reflexive polytope #3 in 2-d lattice M
>>> lattice_polytope.ReflexivePolytope(Integer(2),Integer(3)).vertices()
M( 1,  0),
M( 0,  1),
M( 0, -1),
M(-1,  0)
in 2-d lattice M

There are 16 reflexive polygons and numeration starts with 0:

sage: ReflexivePolytope(2,16)
Traceback (most recent call last):
...
ValueError: there are only 16 reflexive polygons!
>>> from sage.all import *
>>> ReflexivePolytope(Integer(2),Integer(16))
Traceback (most recent call last):
...
ValueError: there are only 16 reflexive polygons!

It is not possible to load a 4-dimensional polytope in this way:

sage: ReflexivePolytope(4,16)
Traceback (most recent call last):
...
NotImplementedError: only 2- and 3-dimensional reflexive polytopes are available!
>>> from sage.all import *
>>> ReflexivePolytope(Integer(4),Integer(16))
Traceback (most recent call last):
...
NotImplementedError: only 2- and 3-dimensional reflexive polytopes are available!
sage.geometry.lattice_polytope.ReflexivePolytopes(dim)[source]#

Return the sequence of all 2- or 3-dimensional reflexive polytopes.

Note

During the first call the database is loaded and cached for future use, so repetitive calls will return the same object in memory.

Parameters:

dim (2 or 3) – dimension of required reflexive polytopes

Return type:

list of lattice polytopes

EXAMPLES:

There are 16 reflexive polygons:

sage: len(ReflexivePolytopes(2))
16
>>> from sage.all import *
>>> len(ReflexivePolytopes(Integer(2)))
16

It is not possible to load 4-dimensional polytopes in this way:

sage: ReflexivePolytopes(4)
Traceback (most recent call last):
...
NotImplementedError: only 2- and 3-dimensional reflexive polytopes are available!
>>> from sage.all import *
>>> ReflexivePolytopes(Integer(4))
Traceback (most recent call last):
...
NotImplementedError: only 2- and 3-dimensional reflexive polytopes are available!
class sage.geometry.lattice_polytope.SetOfAllLatticePolytopesClass[source]#

Bases: Set_generic

sage.geometry.lattice_polytope.all_cached_data(polytopes)[source]#

Compute all cached data for all given polytopes and their polars.

This functions does it MUCH faster than member functions of LatticePolytope during the first run. So it is recommended to use this functions if you work with big sets of data. None of the polytopes in the given sequence should be constructed as the polar polytope to another one.

INPUT: a sequence of lattice polytopes.

EXAMPLES: This function has no output, it is just a fast way to work with long sequences of polytopes. Of course, you can use short sequences as well:

sage: o = lattice_polytope.cross_polytope(3)
sage: lattice_polytope.all_cached_data([o])                                     # needs palp
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> lattice_polytope.all_cached_data([o])                                     # needs palp
sage.geometry.lattice_polytope.all_facet_equations(polytopes)[source]#

Compute polar polytopes for all reflexive and equations of facets for all non-reflexive polytopes.

all_facet_equations and all_polars are synonyms.

This functions does it MUCH faster than member functions of LatticePolytope during the first run. So it is recommended to use this functions if you work with big sets of data.

INPUT: a sequence of lattice polytopes.

EXAMPLES: This function has no output, it is just a fast way to work with long sequences of polytopes. Of course, you can use short sequences as well:

sage: o = lattice_polytope.cross_polytope(3)
sage: lattice_polytope.all_polars([o])                                          # needs palp
sage: o.polar()                                                                 # needs palp
3-d reflexive polytope in 3-d lattice N
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> lattice_polytope.all_polars([o])                                          # needs palp
>>> o.polar()                                                                 # needs palp
3-d reflexive polytope in 3-d lattice N
sage.geometry.lattice_polytope.all_nef_partitions(polytopes, keep_symmetric=False)[source]#

Compute nef-partitions for all given polytopes.

This functions does it MUCH faster than member functions of LatticePolytope during the first run. So it is recommended to use this functions if you work with big sets of data.

Note: member function is_reflexive will be called separately for each polytope. It is strictly recommended to call all_polars on the sequence of polytopes before using this function.

INPUT: a sequence of lattice polytopes.

EXAMPLES: This function has no output, it is just a fast way to work with long sequences of polytopes. Of course, you can use short sequences as well:

sage: o = lattice_polytope.cross_polytope(3)
sage: lattice_polytope.all_nef_partitions([o])                                  # needs palp
sage: o.nef_partitions()                                                        # needs palp
[
Nef-partition {0, 1, 3} ⊔ {2, 4, 5},
Nef-partition {0, 1, 3, 4} ⊔ {2, 5} (direct product),
Nef-partition {0, 1, 2} ⊔ {3, 4, 5},
Nef-partition {0, 1, 2, 3} ⊔ {4, 5},
Nef-partition {0, 1, 2, 3, 4} ⊔ {5} (projection)
]
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> lattice_polytope.all_nef_partitions([o])                                  # needs palp
>>> o.nef_partitions()                                                        # needs palp
[
Nef-partition {0, 1, 3} ⊔ {2, 4, 5},
Nef-partition {0, 1, 3, 4} ⊔ {2, 5} (direct product),
Nef-partition {0, 1, 2} ⊔ {3, 4, 5},
Nef-partition {0, 1, 2, 3} ⊔ {4, 5},
Nef-partition {0, 1, 2, 3, 4} ⊔ {5} (projection)
]

You cannot use this function for non-reflexive polytopes:

sage: p = LatticePolytope([(1,0,0), (0,1,0), (0,0,2),
....:                      (-1,0,0), (0,-1,0), (0,0,-1)])
sage: lattice_polytope.all_nef_partitions([o, p])                               # needs palp
Traceback (most recent call last):
...
ValueError: nef-partitions can be computed for reflexive polytopes only
>>> from sage.all import *
>>> p = LatticePolytope([(Integer(1),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0)), (Integer(0),Integer(0),Integer(2)),
...                      (-Integer(1),Integer(0),Integer(0)), (Integer(0),-Integer(1),Integer(0)), (Integer(0),Integer(0),-Integer(1))])
>>> lattice_polytope.all_nef_partitions([o, p])                               # needs palp
Traceback (most recent call last):
...
ValueError: nef-partitions can be computed for reflexive polytopes only
sage.geometry.lattice_polytope.all_points(polytopes)[source]#

Compute lattice points for all given polytopes.

This functions does it MUCH faster than member functions of LatticePolytope during the first run. So it is recommended to use this functions if you work with big sets of data.

INPUT: a sequence of lattice polytopes.

EXAMPLES: This function has no output, it is just a fast way to work with long sequences of polytopes. Of course, you can use short sequences as well:

sage: o = lattice_polytope.cross_polytope(3)
sage: lattice_polytope.all_points([o])                                          # needs palp
sage: o.points()                                                                # needs palp
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1),
M( 0,  0,  0)
in 3-d lattice M
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> lattice_polytope.all_points([o])                                          # needs palp
>>> o.points()                                                                # needs palp
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1),
M( 0,  0,  0)
in 3-d lattice M
sage.geometry.lattice_polytope.all_polars(polytopes)[source]#

Compute polar polytopes for all reflexive and equations of facets for all non-reflexive polytopes.

all_facet_equations and all_polars are synonyms.

This functions does it MUCH faster than member functions of LatticePolytope during the first run. So it is recommended to use this functions if you work with big sets of data.

INPUT: a sequence of lattice polytopes.

EXAMPLES: This function has no output, it is just a fast way to work with long sequences of polytopes. Of course, you can use short sequences as well:

sage: o = lattice_polytope.cross_polytope(3)
sage: lattice_polytope.all_polars([o])                                          # needs palp
sage: o.polar()                                                                 # needs palp
3-d reflexive polytope in 3-d lattice N
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> lattice_polytope.all_polars([o])                                          # needs palp
>>> o.polar()                                                                 # needs palp
3-d reflexive polytope in 3-d lattice N
sage.geometry.lattice_polytope.convex_hull(points)[source]#

Compute the convex hull of the given points.

Note

points might not span the space. Also, it fails for large numbers of vertices in dimensions 4 or greater

INPUT:

  • points – a list that can be converted into vectors of the same dimension over ZZ.

OUTPUT:

list of vertices of the convex hull of the given points (as vectors).

EXAMPLES: Let’s compute the convex hull of several points on a line in the plane:

sage: lattice_polytope.convex_hull([[1,2],[3,4],[5,6],[7,8]])
[(1, 2), (7, 8)]
>>> from sage.all import *
>>> lattice_polytope.convex_hull([[Integer(1),Integer(2)],[Integer(3),Integer(4)],[Integer(5),Integer(6)],[Integer(7),Integer(8)]])
[(1, 2), (7, 8)]
sage.geometry.lattice_polytope.cross_polytope(dim)[source]#

Return a cross-polytope of the given dimension.

INPUT:

  • dim – an integer.

OUTPUT:

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(3)
sage: o
3-d reflexive polytope in 3-d lattice M
sage: o.vertices()
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1)
in 3-d lattice M
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> o
3-d reflexive polytope in 3-d lattice M
>>> o.vertices()
M( 1,  0,  0),
M( 0,  1,  0),
M( 0,  0,  1),
M(-1,  0,  0),
M( 0, -1,  0),
M( 0,  0, -1)
in 3-d lattice M
sage.geometry.lattice_polytope.is_LatticePolytope(x)[source]#

Check if x is a lattice polytope.

INPUT:

  • x – anything.

OUTPUT:

EXAMPLES:

sage: from sage.geometry.lattice_polytope import is_LatticePolytope
sage: is_LatticePolytope(1)
doctest:warning...
DeprecationWarning: is_LatticePolytope is deprecated, use isinstance instead
See https://github.com/sagemath/sage/issues/34307 for details.
False
sage: p = LatticePolytope([(1,0), (0,1), (-1,-1)])
sage: p                                                                         # needs palp
2-d reflexive polytope #0 in 2-d lattice M
sage: is_LatticePolytope(p)
True
>>> from sage.all import *
>>> from sage.geometry.lattice_polytope import is_LatticePolytope
>>> is_LatticePolytope(Integer(1))
doctest:warning...
DeprecationWarning: is_LatticePolytope is deprecated, use isinstance instead
See https://github.com/sagemath/sage/issues/34307 for details.
False
>>> p = LatticePolytope([(Integer(1),Integer(0)), (Integer(0),Integer(1)), (-Integer(1),-Integer(1))])
>>> p                                                                         # needs palp
2-d reflexive polytope #0 in 2-d lattice M
>>> is_LatticePolytope(p)
True
sage.geometry.lattice_polytope.is_NefPartition(x)[source]#

Check if x is a nef-partition.

INPUT:

  • x – anything.

OUTPUT:

EXAMPLES:

sage: from sage.geometry.lattice_polytope import NefPartition
sage: isinstance(1, NefPartition)
False
sage: o = lattice_polytope.cross_polytope(3)
sage: np = o.nef_partitions()[0]; np                                            # needs palp
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
sage: isinstance(np, NefPartition)                                                       # needs palp
True
>>> from sage.all import *
>>> from sage.geometry.lattice_polytope import NefPartition
>>> isinstance(Integer(1), NefPartition)
False
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> np = o.nef_partitions()[Integer(0)]; np                                            # needs palp
Nef-partition {0, 1, 3} ⊔ {2, 4, 5}
>>> isinstance(np, NefPartition)                                                       # needs palp
True
sage.geometry.lattice_polytope.minkowski_sum(points1, points2)[source]#

Compute the Minkowski sum of two convex polytopes.

Note

Polytopes might not be of maximal dimension.

INPUT:

  • points1, points2 – lists of objects that can be converted into vectors of the same dimension, treated as vertices of two polytopes.

OUTPUT: list of vertices of the Minkowski sum, given as vectors.

EXAMPLES: Let’s compute the Minkowski sum of two line segments:

sage: lattice_polytope.minkowski_sum([[1,0],[-1,0]],[[0,1],[0,-1]])
[(1, 1), (1, -1), (-1, 1), (-1, -1)]
>>> from sage.all import *
>>> lattice_polytope.minkowski_sum([[Integer(1),Integer(0)],[-Integer(1),Integer(0)]],[[Integer(0),Integer(1)],[Integer(0),-Integer(1)]])
[(1, 1), (1, -1), (-1, 1), (-1, -1)]
sage.geometry.lattice_polytope.positive_integer_relations(points)[source]#

Return relations between given points.

INPUT:

  • points – lattice points given as columns of a matrix

OUTPUT:

matrix of relations between given points with non-negative integer coefficients

EXAMPLES: This is a 3-dimensional reflexive polytope:

sage: p = LatticePolytope([(1,0,0), (0,1,0),
....:                      (-1,-1,0), (0,0,1), (-1,0,-1)])
sage: p.points()                                                                # needs palp
M( 1,  0,  0),
M( 0,  1,  0),
M(-1, -1,  0),
M( 0,  0,  1),
M(-1,  0, -1),
M( 0,  0,  0)
in 3-d lattice M
>>> from sage.all import *
>>> p = LatticePolytope([(Integer(1),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0)),
...                      (-Integer(1),-Integer(1),Integer(0)), (Integer(0),Integer(0),Integer(1)), (-Integer(1),Integer(0),-Integer(1))])
>>> p.points()                                                                # needs palp
M( 1,  0,  0),
M( 0,  1,  0),
M(-1, -1,  0),
M( 0,  0,  1),
M(-1,  0, -1),
M( 0,  0,  0)
in 3-d lattice M

We can compute linear relations between its points in the following way:

sage: p.points().matrix().kernel().echelonized_basis_matrix()                   # needs palp
[ 1  0  0  1  1  0]
[ 0  1  1 -1 -1  0]
[ 0  0  0  0  0  1]
>>> from sage.all import *
>>> p.points().matrix().kernel().echelonized_basis_matrix()                   # needs palp
[ 1  0  0  1  1  0]
[ 0  1  1 -1 -1  0]
[ 0  0  0  0  0  1]

However, the above relations may contain negative and rational numbers. This function transforms them in such a way, that all coefficients are non-negative integers:

sage: points = p.points().column_matrix()
sage: lattice_polytope.positive_integer_relations(points)                       # needs palp
[1 0 0 1 1 0]
[1 1 1 0 0 0]
[0 0 0 0 0 1]

sage: cm = ReflexivePolytope(2,1).vertices().column_matrix()
sage: lattice_polytope.positive_integer_relations(cm)
[2 1 1]
>>> from sage.all import *
>>> points = p.points().column_matrix()
>>> lattice_polytope.positive_integer_relations(points)                       # needs palp
[1 0 0 1 1 0]
[1 1 1 0 0 0]
[0 0 0 0 0 1]

>>> cm = ReflexivePolytope(Integer(2),Integer(1)).vertices().column_matrix()
>>> lattice_polytope.positive_integer_relations(cm)
[2 1 1]
sage.geometry.lattice_polytope.read_all_polytopes(file_name)[source]#

Read all polytopes from the given file.

INPUT:

  • file_name – a string with the name of a file with VERTICES of polytopes.

OUTPUT:

  • a sequence of polytopes.

EXAMPLES:

We use poly.x to compute two polar polytopes and read them:

sage: # needs palp
sage: d = lattice_polytope.cross_polytope(2)
sage: o = lattice_polytope.cross_polytope(3)
sage: result_name = lattice_polytope._palp("poly.x -fe", [d, o])
sage: with open(result_name) as f:
....:     print(f.read())
4 2  Vertices of P-dual <-> Equations of P
  -1   1
   1   1
  -1  -1
   1  -1
8 3  Vertices of P-dual <-> Equations of P
  -1  -1   1
   1  -1   1
  -1   1   1
   1   1   1
  -1  -1  -1
   1  -1  -1
  -1   1  -1
   1   1  -1
sage: lattice_polytope.read_all_polytopes(result_name)
[2-d reflexive polytope #14 in 2-d lattice M,
 3-d reflexive polytope in 3-d lattice M]
sage: os.remove(result_name)
>>> from sage.all import *
>>> # needs palp
>>> d = lattice_polytope.cross_polytope(Integer(2))
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> result_name = lattice_polytope._palp("poly.x -fe", [d, o])
>>> with open(result_name) as f:
...     print(f.read())
4 2  Vertices of P-dual <-> Equations of P
  -1   1
   1   1
  -1  -1
   1  -1
8 3  Vertices of P-dual <-> Equations of P
  -1  -1   1
   1  -1   1
  -1   1   1
   1   1   1
  -1  -1  -1
   1  -1  -1
  -1   1  -1
   1   1  -1
>>> lattice_polytope.read_all_polytopes(result_name)
[2-d reflexive polytope #14 in 2-d lattice M,
 3-d reflexive polytope in 3-d lattice M]
>>> os.remove(result_name)
sage.geometry.lattice_polytope.read_palp_matrix(data, permutation=False)[source]#

Read and return an integer matrix from a string or an opened file.

First input line must start with two integers m and n, the number of rows and columns of the matrix. The rest of the first line is ignored. The next m lines must contain n numbers each.

If m>n, returns the transposed matrix. If the string is empty or EOF is reached, returns the empty matrix, constructed by matrix().

INPUT:

  • data – Either a string containing the filename or the file itself

    containing the output by PALP.

  • permutation – (default: False) If True, try to retrieve the permutation output by PALP. This parameter makes sense only when PALP computed the normal form of a lattice polytope.

OUTPUT:

A matrix or a tuple of a matrix and a permutation.

EXAMPLES:

sage: lattice_polytope.read_palp_matrix("2 3 comment \n 1 2 3 \n 4 5 6")
[1 2 3]
[4 5 6]
sage: lattice_polytope.read_palp_matrix("3 2 Will be transposed \n 1 2 \n 3 4 \n 5 6")
[1 3 5]
[2 4 6]
>>> from sage.all import *
>>> lattice_polytope.read_palp_matrix("2 3 comment \n 1 2 3 \n 4 5 6")
[1 2 3]
[4 5 6]
>>> lattice_polytope.read_palp_matrix("3 2 Will be transposed \n 1 2 \n 3 4 \n 5 6")
[1 3 5]
[2 4 6]
sage.geometry.lattice_polytope.set_palp_dimension(d)[source]#

Set the dimension for PALP calls to d.

INPUT:

  • d – an integer from the list [4,5,6,11] or None.

OUTPUT:

  • none.

PALP has many hard-coded limits, which must be specified before compilation, one of them is dimension. Sage includes several versions with different dimension settings (which may also affect other limits and enable certain features of PALP). You can change the version which will be used by calling this function. Such a change is not done automatically for each polytope based on its dimension, since depending on what you are doing it may be necessary to use dimensions higher than that of the input polytope.

EXAMPLES:

Let’s try to work with a 7-dimensional polytope:

sage: p = lattice_polytope.cross_polytope(7)
sage: p._palp("poly.x -fv")                                                     # needs palp
Traceback (most recent call last):
...
ValueError: Error executing 'poly.x -fv' for the given polytope!
Output:
Please increase POLY_Dmax to at least 7
>>> from sage.all import *
>>> p = lattice_polytope.cross_polytope(Integer(7))
>>> p._palp("poly.x -fv")                                                     # needs palp
Traceback (most recent call last):
...
ValueError: Error executing 'poly.x -fv' for the given polytope!
Output:
Please increase POLY_Dmax to at least 7

However, we can work with this polytope by changing PALP dimension to 11:

sage: lattice_polytope.set_palp_dimension(11)
sage: p._palp("poly.x -fv")                                                     # needs palp
'7 14  Vertices of P...'
>>> from sage.all import *
>>> lattice_polytope.set_palp_dimension(Integer(11))
>>> p._palp("poly.x -fv")                                                     # needs palp
'7 14  Vertices of P...'

Let’s go back to default settings:

sage: lattice_polytope.set_palp_dimension(None)
>>> from sage.all import *
>>> lattice_polytope.set_palp_dimension(None)
sage.geometry.lattice_polytope.skip_palp_matrix(data, n=1)[source]#

Skip matrix data in a file.

INPUT:

  • data – opened file with blocks of matrix data in the following format: A block consisting of m+1 lines has the number m as the first element of its first line.

  • n – (default: 1) integer, specifies how many blocks should be skipped

If EOF is reached during the process, raises ValueError exception.

EXAMPLES: We create a file with vertices of the square and the cube, but read only the second set:

sage: # needs palp
sage: d = lattice_polytope.cross_polytope(2)
sage: o = lattice_polytope.cross_polytope(3)
sage: result_name = lattice_polytope._palp("poly.x -fe", [d, o])
sage: with open(result_name) as f:
....:     print(f.read())
4 2  Vertices of P-dual <-> Equations of P
  -1   1
   1   1
  -1  -1
   1  -1
8 3  Vertices of P-dual <-> Equations of P
  -1  -1   1
   1  -1   1
  -1   1   1
   1   1   1
  -1  -1  -1
   1  -1  -1
  -1   1  -1
   1   1  -1
sage: f = open(result_name)
sage: lattice_polytope.skip_palp_matrix(f)
sage: lattice_polytope.read_palp_matrix(f)
[-1  1 -1  1 -1  1 -1  1]
[-1 -1  1  1 -1 -1  1  1]
[ 1  1  1  1 -1 -1 -1 -1]
sage: f.close()
sage: os.remove(result_name)
>>> from sage.all import *
>>> # needs palp
>>> d = lattice_polytope.cross_polytope(Integer(2))
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> result_name = lattice_polytope._palp("poly.x -fe", [d, o])
>>> with open(result_name) as f:
...     print(f.read())
4 2  Vertices of P-dual <-> Equations of P
  -1   1
   1   1
  -1  -1
   1  -1
8 3  Vertices of P-dual <-> Equations of P
  -1  -1   1
   1  -1   1
  -1   1   1
   1   1   1
  -1  -1  -1
   1  -1  -1
  -1   1  -1
   1   1  -1
>>> f = open(result_name)
>>> lattice_polytope.skip_palp_matrix(f)
>>> lattice_polytope.read_palp_matrix(f)
[-1  1 -1  1 -1  1 -1  1]
[-1 -1  1  1 -1 -1  1  1]
[ 1  1  1  1 -1 -1 -1 -1]
>>> f.close()
>>> os.remove(result_name)
sage.geometry.lattice_polytope.write_palp_matrix(m, ofile=None, comment='', format=None)[source]#

Write m into ofile in PALP format.

INPUT:

  • m – a matrix over integers or a point collection.

  • ofile – a file opened for writing (default: stdout)

  • comment – a string (default: empty) see output description

  • format – a format string used to print matrix entries.

OUTPUT:

  • nothing is returned, output written to ofile has the format

    • First line: number_of_rows number_of_columns comment

    • Next number_of_rows lines: rows of the matrix.

EXAMPLES:

sage: o = lattice_polytope.cross_polytope(3)
sage: lattice_polytope.write_palp_matrix(o.vertices(), comment="3D Octahedron")
3 6 3D Octahedron
 1  0  0 -1  0  0
 0  1  0  0 -1  0
 0  0  1  0  0 -1
sage: lattice_polytope.write_palp_matrix(o.vertices(), format="%4d")
3 6
   1    0    0   -1    0    0
   0    1    0    0   -1    0
   0    0    1    0    0   -1
>>> from sage.all import *
>>> o = lattice_polytope.cross_polytope(Integer(3))
>>> lattice_polytope.write_palp_matrix(o.vertices(), comment="3D Octahedron")
3 6 3D Octahedron
 1  0  0 -1  0  0
 0  1  0  0 -1  0
 0  0  1  0  0 -1
>>> lattice_polytope.write_palp_matrix(o.vertices(), format="%4d")
3 6
   1    0    0   -1    0    0
   0    1    0    0   -1    0
   0    0    1    0    0   -1