Skip to content

Polytope

Polytope

Polytope(points, labels=None, backend=None, deterministic_glsm_basis=False)

A lattice polytope.

This class handles all computations relating to lattice polytopes, such as the computation of lattice points and faces. When using reflexive polytopes, it also allows the computation of topological properties of the arising Calabi-Yau hypersurfaces that only depend on the polytope.

The polytope is the convex hull of the input points. All lattice points of the polytope are computed on construction and are labelled. Input points keep the labels given in labels (if any), and every other lattice point receives an integer label that does not clash with the given ones. Most methods accept and return labels, and the helpers points_to_labels and points_to_indices convert between points, labels, and indices.

The Polytope class is also exported at the root of the package, so it can be imported from either cytools.polytope or cytools.

Parameters:

  • points (ArrayLike) –

    The lattice points whose convex hull defines the polytope. CYTools only supports lattice polytopes, so the points must have integer coordinates. They must all be distinct, but they do not need to be vertices.

  • labels (ArrayLike | None, default: None ) –

    Labels for the input points, i.e. points[i] is labelled labels[i]. The labels must be distinct and hashable, and there must be one per input point. If not specified, all points are given integer labels following the default point ordering (see points).

  • backend (str | None, default: None ) –

    The backend used to construct the convex hull. The options are "ppl", "qhull", or "palp". If not specified, PPL is used for polytopes of dimension 1 to 4, and PALP otherwise. 0-dimensional polytopes always use PALP.

  • deterministic_glsm_basis (bool, default: False ) –

    Whether to choose the GLSM (and thus divisor) basis in a deterministic manner, so that it is consistent across different machines. The basis chosen with this flag may differ from the default one.

Raises:

  • ValueError –

    If points is a string, if the points or labels are not unique, if the number of labels does not match the number of points, or if backend is not one of the allowed options.

Examples:

We construct two polytopes from lists of points.

>>> from cytools import Polytope
>>> p1 = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-1,-1]])
>>> print(p1)
A 4-dimensional reflexive lattice polytope in ZZ^4
>>> p2 = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[-1,-1,-1,0]])
>>> print(p2)
A 3-dimensional reflexive lattice polytope in ZZ^4

Custom labels can be given to the input points.

>>> p3 = Polytope([[1,0],[0,1],[-1,-1]], labels=["a","b","c"])
>>> p3.labels
(0, 'c', 'b', 'a')

Methods:

  • all_triangulations –

    Compute all triangulations of the polytope using TOPCOM.

  • ambient_dimension –

    Return the dimension of the ambient lattice.

  • automorphisms –

    Return the \(GL(d,\mathbb{Z})\) matrices that leave the polytope invariant.

  • chi –

    Compute the Euler characteristic of the associated Calabi-Yau.

  • clear_cache –

    Clear the cached results of any previous computation.

  • dimension –

    Return the dimension of the polytope.

  • dual_polytope –

    Return the dual polytope (also called polar polytope).

  • faces –

    Compute the faces of the polytope.

  • facets –

    Return the facets (codimension-1 faces) of the polytope.

  • find_2d_reflexive_subpolytopes –

    Find the 2-dimensional reflexive subpolytopes of a 4D polytope.

  • glsm_basis –

    Compute a basis of columns of the GLSM charge matrix.

  • glsm_charge_matrix –

    Compute the GLSM charge matrix of the theory arising from the polytope.

  • glsm_linear_relations –

    Compute the linear relations of the columns of the GLSM charge matrix.

  • h11 –

    Return the Hodge number \(h^{1,1}\) of the associated Calabi-Yau.

  • h12 –

    Return the Hodge number \(h^{1,2}\) of the associated Calabi-Yau.

  • h13 –

    Return the Hodge number \(h^{1,3}\) of the associated Calabi-Yau.

  • h22 –

    Return the Hodge number \(h^{2,2}\) of the associated Calabi-Yau.

  • hpq –

    Return the Hodge number \(h^{p,q}\) of the associated Calabi-Yau.

  • inequalities –

    Return the inequalities giving the hyperplane representation.

  • inequivalent_Z2_actions –

    Enumerate the inequivalent toric \(\mathbb{Z}_2\) actions.

  • is_affinely_equivalent –

    Return whether the polytopes are related by an integral affine map.

  • is_favorable –

    Return whether the associated Calabi-Yau hypersurface is favorable.

  • is_linearly_equivalent –

    Return whether the polytopes are related by a \(GL(d,\mathbb{Z})\) map.

  • is_reflexive –

    Return whether the polytope is reflexive.

  • is_solid –

    Return whether the polytope is solid (i.e. full-dimensional).

  • is_trilayer –

    Return whether the polytope is "trilayer".

  • minkowski_sum –

    Return the Minkowski sum of the two polytopes.

  • nef_partitions –

    Compute the nef partitions of the polytope using PALP.

  • normal_form –

    Return the normal form of the polytope as defined by Kreuzer-Skarke.

  • points –

    Return the lattice points of the polytope.

  • points_to_indices –

    Return the indices corresponding to the given points.

  • points_to_labels –

    Return the labels corresponding to the given points.

  • pts_bdry –

    Return the boundary lattice points of the polytope.

  • pts_codim2 –

    Return the boundary lattice points not interior to facets.

  • pts_facet –

    Return the lattice points interior to facets.

  • pts_int –

    Return the interior lattice points of the polytope.

  • pts_not_facets –

    Return the lattice points not interior to facets.

  • random_triangulations_fair –

    Construct pseudorandom fine, regular triangulations of the polytope.

  • random_triangulations_fast –

    Construct pseudorandom regular triangulations of the polytope.

  • random_triangulations_gnn –

    Construct random NTFE FR(S)Ts of a reflexive 4-dimensional polytope.

  • triangulate –

    Return a single regular triangulation of the polytope.

  • vertices –

    Return the vertices of the polytope.

  • volume –

    Return the normalized volume of the polytope.

Attributes:

  • backend (str) –

    Return the backend used to compute the convex hull.

  • label_origin –

    Return the label of the origin.

  • labels –

    Return the labels of all lattice points, in order.

  • labels_bdry –

    Return the labels of the boundary points.

  • labels_codim2 –

    Return the labels of the boundary points not interior to facets.

  • labels_facet –

    Return the labels of the points interior to facets.

  • labels_int –

    Return the labels of the interior points.

  • labels_not_facet –

    Return the labels of the points not interior to facets.

  • labels_vertices –

    Return the labels of the vertices, sorted.

backend property

backend

Return the backend used to compute the convex hull.

Returns:

  • str –

    The backend, one of "ppl", "qhull", or "palp".

Examples:

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-1,-1]])
>>> p.backend
'ppl'

label_origin property

label_origin

Return the label of the origin.

Returns:

  • Hashable | None –

    The label of the origin, or None if the origin is not a lattice point of the polytope.

labels property

labels

Return the labels of all lattice points, in order.

The order matches the order of the points returned by points.

Returns:

  • tuple –

    The labels of the lattice points.

Examples:

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-6,-9]])
>>> p.labels
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

labels_bdry property

labels_bdry

Return the labels of the boundary points.

Returns:

  • tuple –

    The labels of the boundary points, ordered by decreasing number of saturated inequalities.

labels_codim2 property

labels_codim2

Return the labels of the boundary points not interior to facets.

These are the points that saturate at least two inequalities, i.e. the ones lying in faces of codimension 2 or higher.

Returns:

  • tuple –

    The labels of the boundary points not interior to facets.

labels_facet property

labels_facet

Return the labels of the points interior to facets.

These are the boundary points that saturate exactly one inequality.

Returns:

  • tuple –

    The labels of the points interior to facets.

labels_int property

labels_int

Return the labels of the interior points.

Interior points are those that do not saturate any inequality of the polytope, i.e. the ones in the relative interior.

Returns:

  • tuple –

    The labels of the interior points.

labels_not_facet property

labels_not_facet

Return the labels of the points not interior to facets.

These are the interior points followed by the boundary points not interior to facets.

Returns:

  • tuple –

    The labels of the points not interior to facets.

labels_vertices property

labels_vertices

Return the labels of the vertices, sorted.

The vertices are computed (and cached) if needed.

Returns:

  • tuple –

    The sorted labels of the vertices of the polytope.

Examples:

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-6,-9]])
>>> p.labels_vertices
(1, 2, 3, 4, 5)

all_triangulations

all_triangulations(points=None, only_fine=True, only_regular=True, only_star=None, star_origin=None, include_points_interior_to_facets=None, backend=None, as_list=False, raw_output=False)

Compute all triangulations of the polytope using TOPCOM.

There is the option to only compute fine, regular, or star triangulations. Regularity is checked separately for each triangulation, since checking it with TOPCOM is much slower.

If the polytope is a simplex, its unique triangulation is returned and the restriction flags are ignored.

Warning

Polytopes with more than around 15 points usually have too many triangulations, so this function may take too long or run out of memory. A warning is issued when 17 or more points are used.

Parameters:

  • points (ArrayLike | None, default: None ) –

    The labels of the points to be used. If specified, include_points_interior_to_facets is ignored.

  • only_fine (bool, default: True ) –

    Whether to restrict to fine triangulations.

  • only_regular (bool, default: True ) –

    Whether to restrict to regular triangulations. This is ignored if raw_output is true.

  • only_star (bool | None, default: None ) –

    Whether to restrict to star triangulations. If not specified, it is set to True for reflexive polytopes (without translations) and False otherwise.

  • star_origin (Hashable | None, default: None ) –

    The label of the point that will be used as the star origin. If not specified and the polytope is reflexive (without translations), the origin is used. Otherwise, it must be specified when only_star is true.

  • include_points_interior_to_facets (bool | None, default: None ) –

    Whether to include points interior to facets in the triangulation. If not specified, it is set to False for reflexive polytopes and True otherwise.

  • backend (str | None, default: None ) –

    The optimizer used to check regularity. The available options are the backends of Cone.is_solid. If not specified, it is picked automatically.

  • as_list (bool, default: False ) –

    Whether to return the full list of triangulations at once instead of a generator. By default a generator is returned, which is usually preferred for efficiency.

  • raw_output (bool, default: False ) –

    Whether to return each triangulation as an array of simplices, each specified by point labels, instead of as a Triangulation object. In this case regularity is not checked.

Returns:

  • Iterator | list –

    A generator of Triangulation objects (or of arrays of simplices if raw_output is true), or a list of them if as_list is true.

Raises:

  • ValueError –

    If only_star is true, star_origin is not specified, and the polytope is not reflexive (without translations).

Examples:

We construct a polytope and find all of its triangulations. We try picking different restrictions and see how the number of triangulations changes.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],
...               [-1,-2,-1,-1],[-2,-1,-1,-1]])
>>> g = p.all_triangulations()
>>> next(g)  # takes some time while TOPCOM finds all triangulations
A fine, regular, star triangulation of a 4-dimensional point...
>>> next(g)  # produces the next triangulation immediately
A fine, regular, star triangulation of a 4-dimensional point...
>>> len(p.all_triangulations(as_list=True))  # number of FRSTs
2

The number of triangulations, no matter if they are fine, regular, or star, is larger.

>>> len(p.all_triangulations(only_regular=False, only_star=False,
...                          only_fine=False, as_list=True))
6

ambient_dimension

ambient_dimension()

Return the dimension of the ambient lattice.

Also available as ambient_dim.

Returns:

  • int | None –

    The dimension of the ambient lattice, or None if the polytope was constructed from an empty list of points.

Examples:

We construct a polytope and check the dimension of the ambient lattice.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[-1,-1,-1,0]])
>>> p.ambient_dimension()
4

automorphisms

automorphisms(square_to_one=False, action='right', as_dictionary=False)

Return the \(GL(d,\mathbb{Z})\) matrices that leave the polytope invariant.

These are the integer matrices with determinant \(\pm 1\) (i.e. elements of \(SL^{\pm}(d,\mathbb{Z})\)) that map the polytope to itself. By default, they act on the points (as row vectors) by multiplication on the right. The results are cached.

Parameters:

  • square_to_one (bool, default: False ) –

    Whether to restrict to the matrices that square to the identity.

  • action (str, default: 'right' ) –

    Whether the returned matrices act on the "left" or on the "right". The matrices that act on the left are the transposes of the ones that act on the right. This option is ignored when as_dictionary is true.

  • as_dictionary (bool, default: False ) –

    Whether to return each automorphism as a dictionary that describes its action on the indices of the points. For an automorphism matrix a (acting on the right), the dictionary d satisfies points()[d[i]] @ a == points()[i] for every index i, i.e. it maps each index to the index of its preimage.

Returns:

  • ndarray | list[dict[int, int]] –

    The automorphism matrices, as an array of shape (n_autos, d, d), or a list of dictionaries if as_dictionary is true. In both cases the automorphisms are given in the same order.

Raises:

Examples:

We construct a polytope, and find its automorphisms. We also check that one of the non-trivial automorphisms is indeed an automorphism by checking that it acts as a permutation on the vertices.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-6,-9]])
>>> autos = p.automorphisms()
>>> a = autos[1]
>>> a
array([[ 1,  0,  0,  0],
       [-1, -1, -6, -9],
       [ 0,  0,  1,  0],
       [ 0,  0,  0,  1]])
>>> p.vertices()
array([[-1, -1, -6, -9],
       [ 0,  0,  0,  1],
       [ 0,  0,  1,  0],
       [ 0,  1,  0,  0],
       [ 1,  0,  0,  0]])
>>> p.vertices() @ a
array([[ 0,  1,  0,  0],
       [ 0,  0,  0,  1],
       [ 0,  0,  1,  0],
       [-1, -1, -6, -9],
       [ 1,  0,  0,  0]])

We can restrict to automorphisms that square to the identity.

>>> autos2 = p.automorphisms(square_to_one=True)
>>> a2 = autos2[1]
>>> a2 @ a2
array([[1, 0, 0, 0],
       [0, 1, 0, 0],
       [0, 0, 1, 0],
       [0, 0, 0, 1]])

We can also get matrices that act on the left, which are simply the transposes, so that the points are now columns.

>>> autos_left = p.automorphisms(square_to_one=True, action="left")
>>> autos_left[1] @ p.vertices().T
array([[ 0,  0,  0, -1,  1],
       [ 1,  0,  0, -1,  0],
       [ 0,  0,  1, -6,  0],
       [ 0,  1,  0, -9,  0]])

Finally, we get dictionaries that describe how the indices of the points transform.

>>> autos_dict = p.automorphisms(as_dictionary=True)
>>> autos_dict[1]
{0: 0, 1: 4, 2: 2, 3: 3, 4: 1, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}

chi

chi(lattice)

Compute the Euler characteristic of the associated Calabi-Yau.

The Calabi-Yau is the anticanonical hypersurface in the toric variety given by a desingularization of the face fan (if lattice="N") or the normal fan (if lattice="M") of the polytope. The Euler characteristic is computed from the Hodge numbers, and for the "M" lattice it is computed from the dual polytope. The result is cached.

Note

Only reflexive polytopes of dimension 2 to 5 are currently supported.

Parameters:

  • lattice (str) –

    The lattice on which the polytope is defined. The options are "N" and "M".

Returns:

  • int –

    The Euler characteristic of the arising Calabi-Yau manifold.

Raises:

  • ValueError –

    If the polytope is not reflexive or its dimension is not between 2 and 5, or if lattice is not "N" or "M".

Examples:

We construct a polytope and compute the Euler characteristic of the associated hypersurfaces.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-6,-9]])
>>> p.chi(lattice="N")
-540
>>> p.chi(lattice="M")
540

clear_cache

clear_cache()

Clear the cached results of any previous computation.

This clears cached quantities such as the vertices, faces, dual polytope, automorphisms, normal forms, Euler characteristic, favorability, GLSM data, and nef partitions. The results of hpq are cached separately and are not cleared. The lattice points, their labels, and the hyperplane representation are computed on construction and are not cleared either.

Examples:

We compute the faces of a polytope, which are cached, and then clear the cache.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-1,-1]])
>>> f = p.faces()  # computed and cached
>>> f = p.faces()  # returned from the cache
>>> p.clear_cache()  # clear the results of previous computations
>>> f = p.faces()  # computed again

dimension

dimension()

Return the dimension of the polytope.

Also available as dim.

Returns:

  • int –

    The dimension of the polytope. It is -1 for the empty polytope.

Examples:

We construct a polytope and check its dimension.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[-1,-1,-1,0]])
>>> p.dimension()
3

dual_polytope

dual_polytope()

Return the dual polytope (also called polar polytope).

The dual polytope of a polytope \(\Delta \subset \mathbb{R}^n\) is

\[\Delta^\circ = \{y\in \mathbb{R}^n \mid x\cdot y \geq -1 \text{ for all } x \in \Delta\}.\]

A lattice polytope is reflexive if its dual is also a lattice polytope. Only lattice polytopes are currently supported, so only duals of reflexive polytopes (with the origin as their unique interior point) can be computed. The vertices of the dual polytope are the normal vectors of the inequalities returned by inequalities.

The result is cached, and the dual of the dual polytope is the original Polytope object.

Also available as dual, polar_polytope, and polar.

Returns:

Raises:

Examples:

We construct a reflexive polytope and find its dual. We then verify that the dual of the dual is the original polytope.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-1,-1]])
>>> p_dual = p.dual_polytope()
>>> print(p_dual)
A 4-dimensional reflexive lattice polytope in ZZ^4
>>> p_dual_dual = p_dual.dual_polytope()
>>> p_dual_dual is p
True

faces

faces(d=None)

Compute the faces of the polytope.

The faces of each dimension are sorted lexicographically by their labels, and the result is cached. If the faces of the dual polytope have already been computed, they are obtained by duality. Otherwise, when the polytope is 4-dimensional, the slightly more optimized _faces4d function is used.

Parameters:

  • d (int | None, default: None ) –

    The dimension of the desired faces. If not specified, the faces of all dimensions are returned.

Returns:

  • tuple –

    If d is specified, a tuple of PolytopeFace objects of dimension d. Otherwise, a tuple of tuples of PolytopeFace objects organized in ascending dimension, so that the \(i\)-th entry contains the \(i\)-dimensional faces. The last entry contains the polytope itself as a face.

Raises:

Examples:

We show that this function returns a tuple of 2-faces if d is set to 2. Otherwise, the function returns all faces in tuples organized in ascending dimension. We verify that the first element in the tuple of 2-faces is the same as the first element in the corresponding subtuple in the tuple of all faces.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-1,-1]])
>>> faces2d = p.faces(2)
>>> allfaces = p.faces()
>>> print(faces2d[0])  # print the first 2-face
A 2-dimensional face of a 4-dimensional polytope in ZZ^4
>>> faces2d[0] is allfaces[2][0]
True

facets

facets()

Return the facets (codimension-1 faces) of the polytope.

Returns:

Examples:

We construct a polytope and find its facets.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-1,-1]])
>>> facets = p.facets()
>>> len(facets)
5

find_2d_reflexive_subpolytopes

find_2d_reflexive_subpolytopes()

Find the 2-dimensional reflexive subpolytopes of a 4D polytope.

This uses the algorithm by Huang and Taylor described in arXiv:1907.09482. Each subpolytope is the intersection of the polytope with a 2-dimensional plane through the origin, and it is returned as a new Polytope (with default labels) in the ambient lattice of this polytope. The subpolytopes are not returned in any particular order.

Returns:

  • list[Polytope] –

    The 2-dimensional reflexive subpolytopes.

Raises:

Examples:

We construct a polytope and find its 2D reflexive subpolytopes.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-6,-9]])
>>> subpolys = p.find_2d_reflexive_subpolytopes()
>>> len(subpolys)
1
>>> print(subpolys[0])
A 2-dimensional reflexive lattice polytope in ZZ^4

glsm_basis

glsm_basis(include_origin=True, include_points_interior_to_facets=False, points=None, integral=True)

Compute a basis of columns of the GLSM charge matrix.

The basis is computed (and cached) together with the charge matrix by glsm_charge_matrix, and the corresponding columns of the charge matrix form an identity matrix (or a diagonal matrix, if integral is false). The chosen basis can be made machine-independent with the deterministic_glsm_basis option of the constructor.

Parameters:

  • include_origin (bool, default: True ) –

    Whether to include the origin in the calculation. This corresponds to the inclusion of the canonical divisor. If False, the returned indices are shifted so that they refer to the charge matrix without the origin column.

  • include_points_interior_to_facets (bool, default: False ) –

    Whether to also use the points interior to facets. By default only the origin and the boundary points not interior to facets are used.

  • points (ArrayLike | None, default: None ) –

    The indices of the points that will be used. If specified, include_origin and include_points_interior_to_facets are ignored, and the origin (index 0) is always included. Note that the returned indices then refer to the columns of the corresponding charge matrix, i.e. to the positions in the sorted list of point indices.

  • integral (bool, default: True ) –

    Whether to find an integral basis for the columns of the GLSM charge matrix, i.e. a basis such that the remaining columns can be written as integer linear combinations of the basis columns.

Returns:

  • ndarray –

    The indices of the columns that form a basis.

Raises:

  • ValueError –

    If any of the indices in points is out of range, or if the polytope is not reflexive.

Examples:

We construct a polytope, find its GLSM charge matrix and a basis of columns.

>>> import numpy as np
>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-6,-9]])
>>> p.glsm_basis()
array([5, 6])
>>> glsm = p.glsm_charge_matrix()
>>> glsm[:,p.glsm_basis()]  # the basis columns form an identity matrix
array([[1, 0],
       [0, 1]])

glsm_charge_matrix

glsm_charge_matrix(include_origin=True, include_points_interior_to_facets=False, points=None, integral=True)

Compute the GLSM charge matrix of the theory arising from the polytope.

Each column of the GLSM charge matrix corresponds to a lattice point of the polytope (i.e. to a prime toric divisor), and the rows span the integer linear relations among the points, so that the product of the charge matrix with the matrix of points (one per row) vanishes. The column of the origin is minus the sum of the other columns, so that each row sums to zero. The matrix is chosen such that the columns given by glsm_basis form an identity matrix (or a diagonal matrix, if integral is false). The results are cached.

Parameters:

  • include_origin (bool, default: True ) –

    Whether to include the origin in the calculation. This corresponds to the inclusion of the canonical divisor.

  • include_points_interior_to_facets (bool, default: False ) –

    Whether to also use the points interior to facets. By default only the origin and the boundary points not interior to facets are used.

  • points (ArrayLike | None, default: None ) –

    The indices of the points that will be used. If specified, include_origin and include_points_interior_to_facets are ignored, and the origin (index 0) is always included as the first column.

  • integral (bool, default: True ) –

    Whether to find an integral basis for the columns of the GLSM charge matrix, i.e. a basis such that the remaining columns can be written as integer linear combinations of the basis columns. If an integral basis cannot be found, a warning is issued and a non-integral one is computed.

Returns:

  • ndarray –

    The GLSM charge matrix, with one column per point.

Raises:

  • ValueError –

    If the polytope is not reflexive, or if any of the indices in points is out of range.

Examples:

We construct a polytope and find the GLSM charge matrix with different parameters.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-6,-9]])
>>> p.glsm_charge_matrix()
array([[-18,   1,   9,   6,   1,   1,   0],
       [ -6,   0,   3,   2,   0,   0,   1]])

By definition, this product must be zero.

>>> p.glsm_charge_matrix().dot(p.points_not_interior_to_facets())
array([[0, 0, 0, 0],
       [0, 0, 0, 0]])

We can exclude the origin (i.e. the canonical divisor), or include points interior to facets.

>>> p.glsm_charge_matrix(include_origin=False)
array([[1, 9, 6, 1, 1, 0],
       [0, 3, 2, 0, 0, 1]])
>>> p.glsm_charge_matrix(include_points_interior_to_facets=True)
array([[ 0,  1,  0,  6,  1,  1,  0,  0,  0, -9],
       [-2,  0,  1,  0,  0,  0,  0,  0,  0,  1],
       [ 0,  0,  0,  2,  0,  0,  1,  0,  0, -3],
       [ 0,  0,  0,  1,  0,  0,  0,  1,  0, -2],
       [-1,  0,  0,  1,  0,  0,  0,  0,  1, -1]])

glsm_linear_relations

glsm_linear_relations(include_origin=True, include_points_interior_to_facets=False, points=None, integral=True)

Compute the linear relations of the columns of the GLSM charge matrix.

The rows of the returned matrix span the linear relations among the columns of the GLSM charge matrix, so that the product of the two (with one of them transposed) vanishes. When the origin is included, one of the relations states that the rows of the charge matrix sum to zero. They are computed (and cached) together with the charge matrix by glsm_charge_matrix.

Parameters:

  • include_origin (bool, default: True ) –

    Whether to include the origin in the calculation. This corresponds to the inclusion of the canonical divisor. If False, the first row and column are dropped.

  • include_points_interior_to_facets (bool, default: False ) –

    Whether to also use the points interior to facets. By default only the origin and the boundary points not interior to facets are used.

  • points (ArrayLike | None, default: None ) –

    The indices of the points that will be used. If specified, include_origin and include_points_interior_to_facets are ignored, and the origin (index 0) is always included.

  • integral (bool, default: True ) –

    Whether to find an integral basis for the columns of the GLSM charge matrix, i.e. a basis such that the remaining columns can be written as integer linear combinations of the basis columns.

Returns:

  • ndarray –

    The matrix of linear relations of the columns of the GLSM charge matrix, one per row.

Raises:

  • ValueError –

    If any of the indices in points is out of range, or if the polytope is not reflexive.

Examples:

We construct a polytope and find its linear relations with different parameters.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-6,-9]])
>>> p.glsm_linear_relations()
array([[ 1,  0,  0,  0,  0, 18,  6],
       [ 0,  1,  0,  0,  0, -1,  0],
       [ 0,  0,  1,  0,  0, -9, -3],
       [ 0,  0,  0,  1,  0, -6, -2],
       [ 0,  0,  0,  0,  1, -1,  0]])

By definition, this product must be zero.

>>> p.glsm_linear_relations().dot(p.glsm_charge_matrix().T)
array([[0, 0],
       [0, 0],
       [0, 0],
       [0, 0],
       [0, 0]])

We can exclude the origin (i.e. the canonical divisor).

>>> p.glsm_linear_relations(include_origin=False)
array([[ 1,  0,  0,  0, -1,  0],
       [ 0,  1,  0,  0, -9, -3],
       [ 0,  0,  1,  0, -6, -2],
       [ 0,  0,  0,  1, -1,  0]])

h11

h11(lattice='N')

Return the Hodge number \(h^{1,1}\) of the associated Calabi-Yau.

This is a shortcut for hpq(1, 1, lattice), where the conventions are explained.

Parameters:

  • lattice (str, default: 'N' ) –

    The lattice on which the polytope is defined. The options are "N" and "M".

Returns:

  • int –

    The Hodge number \(h^{1,1}\) of the arising Calabi-Yau manifold.

Examples:

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-6,-9]])
>>> p.h11(lattice="N")
2
>>> p.h11(lattice="M")
272

h12

h12(lattice='N')

Return the Hodge number \(h^{1,2}\) of the associated Calabi-Yau.

This is a shortcut for hpq(1, 2, lattice), where the conventions are explained. Note that lattice="M" currently gives incorrect results for 4- and 5-dimensional polytopes (see the warning in hpq).

Also available as h21.

Parameters:

  • lattice (str, default: 'N' ) –

    The lattice on which the polytope is defined. The options are "N" and "M".

Returns:

  • int –

    The Hodge number \(h^{1,2}\) of the arising Calabi-Yau manifold.

Examples:

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-6,-9]])
>>> p.h12(lattice="N")
272

h13

h13(lattice='N')

Return the Hodge number \(h^{1,3}\) of the associated Calabi-Yau.

This is a shortcut for hpq(1, 3, lattice), where the conventions are explained. It is only non-zero for Calabi-Yau fourfolds, i.e. for 5-dimensional polytopes. Note that lattice="M" currently gives incorrect results for 5-dimensional polytopes (see the warning in hpq).

Also available as h31.

Parameters:

  • lattice (str, default: 'N' ) –

    The lattice on which the polytope is defined. The options are "N" and "M".

Returns:

  • int –

    The Hodge number \(h^{1,3}\) of the arising Calabi-Yau manifold.

Examples:

>>> p = Polytope([[1,0,0,0,0],[0,1,0,0,0],[0,0,1,0,0],[0,0,0,1,0],
...               [0,0,0,0,1],[-1,-1,-1,-1,-1]])
>>> p.h13(lattice="N")
426

h22

h22(lattice='N')

Return the Hodge number \(h^{2,2}\) of the associated Calabi-Yau.

This is a shortcut for hpq(2, 2, lattice), where the conventions are explained. Note that it is currently only computed correctly for Calabi-Yau fourfolds, i.e. for 5-dimensional polytopes (see the warning in hpq).

Parameters:

  • lattice (str, default: 'N' ) –

    The lattice on which the polytope is defined. The options are "N" and "M".

Returns:

  • int –

    The Hodge number \(h^{2,2}\) of the arising Calabi-Yau manifold.

Examples:

>>> p = Polytope([[1,0,0,0,0],[0,1,0,0,0],[0,0,1,0,0],[0,0,0,1,0],
...               [0,0,0,0,1],[-1,-1,-1,-1,-1]])
>>> p.h22(lattice="N")
1752

hpq

hpq(p, q, lattice='N')

Return the Hodge number \(h^{p,q}\) of the associated Calabi-Yau.

The Calabi-Yau is the anticanonical hypersurface in the toric variety given by a desingularization of the face fan (if lattice="N") or the normal fan (if lattice="M") of the polytope. The Hodge numbers are computed combinatorially (Batyrev's formulas), and the results are cached.

Note

Only reflexive polytopes of dimension 2 to 5 are currently supported.

Warning

Hodge numbers \(h^{p,q}\) with \(p+q\) larger than the complex dimension of the Calabi-Yau (after mapping to the \(N\) lattice with \(p \to d-1-p\) when lattice="M") are currently returned as 0, even when they are not zero (e.g. \(h^{2,2}=h^{1,1}\) for Calabi-Yau threefolds). In particular, h12(lattice="M") for 4- and 5-dimensional polytopes, h13(lattice="M") for 5-dimensional polytopes, and h22 for 3- and 4-dimensional polytopes are affected. Using the dual polytope with lattice="N" gives the correct values for the "M" lattice.

Parameters:

  • p (int) –

    The holomorphic index of the Dolbeault cohomology of interest.

  • q (int) –

    The anti-holomorphic index of the Dolbeault cohomology of interest.

  • lattice (str, default: 'N' ) –

    The lattice on which the polytope is defined. The options are "N" and "M".

Returns:

  • int –

    The Hodge number \(h^{p,q}\) of the arising Calabi-Yau manifold.

Raises:

  • ValueError –

    If the polytope is not reflexive or its dimension is not between 2 and 5, or if lattice is not "N" or "M".

Examples:

We construct a polytope and check some Hodge numbers of the associated hypersurface.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-6,-9]])
>>> p.hpq(0,0,lattice="N")
1
>>> p.hpq(0,1,lattice="N")
0
>>> p.hpq(1,1,lattice="N")
2
>>> p.hpq(1,2,lattice="N")
272

inequalities

inequalities()

Return the inequalities giving the hyperplane representation.

Each row \((c_0, \ldots, c_{d-1}, c_d)\) encodes the inequality

\[c_0x_0 + \cdots + c_{d-1}x_{d-1} + c_d \geq 0.\]

For polytopes that are not full-dimensional, the equalities defining the affine hull of the polytope are not included, so the inequalities only define the polytope when restricted to its affine hull.

Returns:

  • ndarray –

    The inequalities defining the polytope, one per row, as an integer array of shape (n_ineqs, ambient_dim + 1).

Raises:

Examples:

We construct a polytope and find the defining inequalities.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-1,-1]])
>>> p.inequalities()
array([[ 4, -1, -1, -1,  1],
       [-1,  4, -1, -1,  1],
       [-1, -1,  4, -1,  1],
       [-1, -1, -1,  4,  1],
       [-1, -1, -1, -1,  1]])

inequivalent_Z2_actions

inequivalent_Z2_actions()

Enumerate the inequivalent toric \(\mathbb{Z}_2\) actions.

A toric \(\mathbb{Z}_2\) action is specified by a half-integral point \(q/2\), where \(q \in \{0,1\}^d\) is non-zero. Two such actions are considered equivalent if their vectors \(q\) are related (modulo 2) by a lattice automorphism of the polytope (see automorphisms). The result is cached.

Returns:

  • ndarray –

    The inequivalent representatives \(q\), one per row, sorted lexicographically. Each one is the lexicographically smallest vector in its equivalence class.

Raises:

Examples:

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-1,-1]])
>>> p.inequivalent_Z2_actions()
array([[0, 0, 0, 1],
       [0, 0, 1, 1]])

is_affinely_equivalent

is_affinely_equivalent(other, backend='palp')

Return whether the polytopes are related by an integral affine map.

Two polytopes are affinely equivalent if they can be transformed into each other by a \(GL(d,\mathbb{Z})\) transformation followed by an integral translation. This is checked by comparing their affine normal forms (see normal_form).

Parameters:

  • other (Polytope) –

    The other polytope being compared.

  • backend (str, default: 'palp' ) –

    The backend used to compute the normal form. The options are "native", which uses native Python code, or "palp", which uses PALP for the computation.

Returns:

  • bool –

    True if the polytopes are affinely equivalent, False otherwise.

Examples:

We construct two polytopes and check if they are affinely equivalent.

>>> p1 = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-1,-1]])
>>> p2 = Polytope([[1,0,0,1],[0,1,0,1],[0,0,1,1],[0,0,0,2],[-1,-1,-1,0]])
>>> p1.is_affinely_equivalent(p2)
True

is_favorable

is_favorable(lattice)

Return whether the associated Calabi-Yau hypersurface is favorable.

A Calabi-Yau hypersurface is favorable if all of its Kähler forms descend from Kähler forms on the ambient toric variety. This is checked by verifying that the number of lattice points not interior to facets equals \(h^{1,1} + d + 1\). For the "M" lattice, the dual polytope is used. The result is cached.

Note

Only reflexive polytopes of dimension 2 to 5 are currently supported.

Parameters:

  • lattice (str) –

    The lattice on which the polytope is defined. The options are "N" and "M".

Returns:

  • bool –

    True if the hypersurface is favorable, False otherwise.

Raises:

  • ValueError –

    If lattice is not "N" or "M", or if the polytope is not reflexive or its dimension is not between 2 and 5.

Examples:

We construct two reflexive polytopes and find whether they are favorable when considered in the N lattice.

>>> p1 = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-1,-1]])
>>> p2 = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-3,-6]])
>>> p1.is_favorable(lattice="N")
True
>>> p2.is_favorable(lattice="N")
False

is_linearly_equivalent

is_linearly_equivalent(other, backend='palp')

Return whether the polytopes are related by a \(GL(d,\mathbb{Z})\) map.

Two polytopes are linearly equivalent if they can be transformed into each other by a \(GL(d,\mathbb{Z})\) (i.e. \(SL^{\pm}(d,\mathbb{Z})\)) transformation. This is checked by comparing their normal forms (see normal_form).

Parameters:

  • other (Polytope) –

    The other polytope being compared.

  • backend (str, default: 'palp' ) –

    The backend used to compute the normal form. The options are "native", which uses native Python code, or "palp", which uses PALP for the computation.

Returns:

  • bool –

    True if the polytopes are linearly equivalent, False otherwise.

Examples:

We construct two polytopes and check if they are linearly equivalent.

>>> p1 = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-1,-1]])
>>> p2 = Polytope([[-1,0,0,0],[0,-1,0,0],[0,0,-1,0],[0,0,0,-1],[1,1,1,1]])
>>> p1.is_linearly_equivalent(p2)
True

is_reflexive

is_reflexive(allow_translations=True)

Return whether the polytope is reflexive.

A lattice polytope is reflexive if it has a unique interior lattice point and every facet is at lattice distance 1 from it. Polytopes that are not full-dimensional are checked relative to the lattice of their affine hull. Polytopes of dimension 0 or lower are never considered reflexive.

The result is cached.

Parameters:

  • allow_translations (bool, default: True ) –

    Whether to allow the polytope to be translated. If True, the polytope is checked for reflexivity after translating its unique interior point to the origin. If False, the origin must be the unique interior point (and, for polytopes that are not full-dimensional, the affine hull must contain the origin).

Returns:

  • bool –

    True if the polytope is reflexive, False otherwise.

Examples:

We construct a polytope and check if it is reflexive.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-6,-9]])
>>> p.is_reflexive()
True

A translated reflexive polytope is only reflexive if translations are allowed.

>>> p = Polytope([[2,1],[1,2],[0,0]])
>>> p.is_reflexive()
True
>>> p.is_reflexive(allow_translations=False)
False

is_solid

is_solid()

Return whether the polytope is solid (i.e. full-dimensional).

Returns:

  • bool –

    True if the dimension of the polytope equals the dimension of the ambient lattice, False otherwise.

Examples:

We construct a polytope and check if it is solid.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[-1,-1,-1,0]])
>>> p.is_solid()
False

is_trilayer

is_trilayer(return_anticanon=False)

Return whether the polytope is "trilayer".

This computes the GLSM charges of the vertices (a basis of the integer linear relations among the vertices) and the anticanonical class, given by the sum of the charges of all vertices. The polytope is considered trilayer if the anticanonical class is divisible by 2 and some vertex has charges equal to half of it. The polytope is assumed to be full-dimensional.

Parameters:

  • return_anticanon (bool, default: False ) –

    Whether to also return the charges of the anticanonical class.

Returns:

  • bool | tuple[bool, ndarray] –

    True if the polytope is trilayer, False otherwise. If return_anticanon is true, a tuple with this value and the charges of the anticanonical class is returned instead.

Examples:

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-6,-9]])
>>> p.is_trilayer()
True
>>> p.is_trilayer(return_anticanon=True)
(True, array([18]))

minkowski_sum

minkowski_sum(other)

Return the Minkowski sum of the two polytopes.

The sum is computed as the convex hull of all pairwise sums of the vertices of the two polytopes, so both must lie in lattices of the same dimension. The resulting polytope uses the default backend.

Also available through the + operator.

Parameters:

  • other (Polytope) –

    The other polytope used for the Minkowski sum.

Returns:

Examples:

We construct two polytopes and compute their Minkowski sum.

>>> p1 = Polytope([[1,0,0],[0,1,0],[-1,-1,0]])
>>> p2 = Polytope([[0,0,1],[0,0,-1]])
>>> print(p1.minkowski_sum(p2))
A 3-dimensional reflexive lattice polytope in ZZ^3

nef_partitions

nef_partitions(keep_symmetric=False, keep_products=False, keep_projections=False, codim=2, compute_hodge_numbers=True, return_hodge_numbers=False)

Compute the nef partitions of the polytope using PALP.

A nef partition is a partition of the vertices of the polytope into codim parts, which defines a complete intersection Calabi-Yau (CICY). Each part is given as a tuple of vertex indices, where the vertices are ordered as by PALP. For polytopes with default labels, this matches the ordering of vertices. The results are cached.

Note

This is currently an experimental feature and may change significantly in future versions. It requires the experimental features to be enabled with enable_experimental_features.

Parameters:

  • keep_symmetric (bool, default: False ) –

    Whether to keep symmetric partitions related by lattice automorphisms.

  • keep_products (bool, default: False ) –

    Whether to keep product partitions, corresponding to complete intersections that are direct products.

  • keep_projections (bool, default: False ) –

    Whether to keep projection partitions, i.e. partitions where one of the parts consists of a single vertex.

  • codim (int, default: 2 ) –

    The number of parts in the partition or, equivalently, the codimension of the complete intersection Calabi-Yau.

  • compute_hodge_numbers (bool, default: True ) –

    Whether to compute the Hodge numbers of the CICYs. This is always done if return_hodge_numbers is true.

  • return_hodge_numbers (bool, default: False ) –

    Whether to return the Hodge numbers along with the nef partitions. For each nef partition, the Hodge numbers are given as a tuple of tuples whose \((p,q)\) entry is \(h^{p,q}\).

Returns:

  • list | tuple[list, list] –

    The list of nef partitions of the polytope. If return_hodge_numbers is true, a tuple with the list of nef partitions and the list of the corresponding Hodge numbers is returned instead.

Raises:

  • Exception –

    If the experimental features are not enabled.

  • ValueError –

    If the polytope is not reflexive.

Examples:

We construct a tesseract and find the 2- and 3-part nef partitions.

>>> from cytools import config
>>> config.enable_experimental_features()
>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],
...               [-1,0,0,0],[0,-1,0,0],[0,0,-1,0],[0,0,0,-1]])
>>> nef_part_2 = p.nef_partitions()  # default codimension is 2
>>> nef_part_2[0]  # the first of the nef partitions
((0, 1, 2, 5), (3, 4, 6, 7))
>>> nef_part_3 = p.nef_partitions(codim=3)
>>> nef_part_3[0]
((0, 1, 6), (2, 3, 4), (5, 7))

We can also get the Hodge numbers of the CICYs, which in this case are K3 surfaces.

>>> nef_parts, hodge_nums = p.nef_partitions(return_hodge_numbers=True)
>>> hodge_nums[0]
((1, 0, 1), (0, 20, 0), (1, 0, 1))

normal_form

normal_form(affine_transform=False, backend='palp')

Return the normal form of the polytope as defined by Kreuzer-Skarke.

The normal form is a canonical choice of the vertices such that two polytopes have the same normal form if and only if they are related by a \(GL(d,\mathbb{Z})\) transformation (or also a translation, if affine_transform is true). The results are cached.

Parameters:

  • affine_transform (bool, default: False ) –

    Whether to also allow translations, in addition to \(GL(d,\mathbb{Z})\) transformations.

  • backend (str, default: 'palp' ) –

    The backend to use. The options are "native", which uses native Python code, or "palp", which uses PALP for the computation. The native algorithm and PALP use different conventions for affine normal forms, and PALP generally works better. PALP only supports full-dimensional polytopes, so for other polytopes a warning is issued and the native backend is used instead.

Returns:

  • ndarray –

    The vertices in normal form, one per row.

Raises:

Examples:

We construct a polytope, and find its linear and affine normal forms.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-6,-9]])
>>> p.normal_form()
array([[ 1,  0,  0,  0],
       [ 0,  1,  0,  0],
       [ 0,  0,  1,  0],
       [ 0,  0,  0,  1],
       [-9, -6, -1, -1]])
>>> p.normal_form(affine_transform=True)
array([[ 1,  0,  0,  0],
       [ 0,  1,  0,  0],
       [ 0,  0,  1,  0],
       [12, 17, 17, 18],
       [ 0,  0,  0,  0]])

points

points(which=None, optimal=False, as_indices=False)

Return the lattice points of the polytope.

Points are sorted so that interior points come first, and then the rest are arranged by decreasing number of saturated inequalities, with ties broken lexicographically by their coordinates. For reflexive polytopes this is useful since the origin will be at index 0 and the boundary points interior to facets will be last.

Also available as pts.

Parameters:

  • which (Hashable | list | ndarray | None, default: None ) –

    The label, or list of labels, of the points to return. Note that these are labels and not indices. If not specified, all points are returned.

  • optimal (bool, default: False ) –

    Whether to return the points in their optimal coordinates (i.e. translated and LLL-reduced, and with the dimension reduced if the polytope is not full-dimensional).

  • as_indices (bool, default: False ) –

    Whether to return the indices of the points in the full list of points instead of their coordinates.

Returns:

  • ndarray | list[int] –

    The lattice points, one per row. If as_indices is true, a list of indices is returned instead.

Examples:

We construct a polytope and compute the lattice points. One can verify that the first point is the only interior point, and the last three points are the ones interior to facets. Thus it follows the aforementioned ordering.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-6,-9]])
>>> p.points()
array([[ 0,  0,  0,  0],
       [-1, -1, -6, -9],
       [ 0,  0,  0,  1],
       [ 0,  0,  1,  0],
       [ 0,  1,  0,  0],
       [ 1,  0,  0,  0],
       [ 0,  0, -2, -3],
       [ 0,  0, -1, -2],
       [ 0,  0, -1, -1],
       [ 0,  0,  0, -1]])

Specific points can be selected by their labels.

>>> p.points(which=[3,1])
array([[ 0,  0,  1,  0],
       [-1, -1, -6, -9]])

points_to_indices

points_to_indices(points, is_optimal=False)

Return the indices corresponding to the given points.

The indices refer to the list of points returned by points. It also accepts a single point, in which case it returns the corresponding index.

Parameters:

  • points (ArrayLike) –

    A point or a list of points.

  • is_optimal (bool, default: False ) –

    Whether the points are given in the optimal (True) or input (False) coordinates.

Returns:

  • list[int] | int –

    The list of indices corresponding to the given points, or the index of the point if only one is given. An empty integer array is returned if points is empty.

Raises:

  • KeyError –

    If any of the points is not a lattice point of the polytope.

Examples:

We construct a polytope and find the indices of some of its points. For a single point, a single index is returned.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-6,-9]])
>>> p.points_to_indices([-1,-1,-6,-9])
1

For a list of points, a list of indices is returned.

>>> p.points_to_indices([[-1,-1,-6,-9],[0,0,0,0],[0,0,1,0]])
[1, 0, 3]

points_to_labels

points_to_labels(points, is_optimal=False)

Return the labels corresponding to the given points.

It also accepts a single point, in which case it returns the corresponding label.

Parameters:

  • points (ArrayLike) –

    A point or a list of points.

  • is_optimal (bool, default: False ) –

    Whether the points are given in the optimal (True) or input (False) coordinates.

Returns:

  • list | Hashable –

    The list of labels corresponding to the given points, or the label of the point if only one is given. An empty list is returned if points is empty.

Raises:

  • KeyError –

    If any of the points is not a lattice point of the polytope.

Examples:

We construct a polytope with custom labels and find the labels of some of its points.

>>> p = Polytope([[1,0],[0,1],[-1,-1]], labels=["a","b","c"])
>>> p.points_to_labels([0,1])
'b'
>>> p.points_to_labels([[0,0],[-1,-1]])
[0, 'c']

pts_bdry

pts_bdry(as_indices=False)

Return the boundary lattice points of the polytope.

The points are ordered by decreasing number of saturated inequalities, as in points.

Also available as boundary_points and bdry_points.

Parameters:

  • as_indices (bool, default: False ) –

    Whether to return the indices of the points in the full list of points instead of their coordinates.

Returns:

  • ndarray | list[int] –

    The boundary points, one per row, or their indices if as_indices is true.

Examples:

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-6,-9]])
>>> p.boundary_points(as_indices=True)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

pts_codim2

pts_codim2(as_indices=False)

Return the boundary lattice points not interior to facets.

These are the points that saturate at least two inequalities, i.e. those that lie in faces of codimension 2 or higher.

Also available as boundary_points_not_interior_to_facets and boundary_pts_not_interior_to_facets.

Parameters:

  • as_indices (bool, default: False ) –

    Whether to return the indices of the points in the full list of points instead of their coordinates.

Returns:

  • ndarray | list[int] –

    The boundary points not interior to facets, one per row, or their indices if as_indices is true.

Examples:

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-6,-9]])
>>> p.boundary_points_not_interior_to_facets(as_indices=True)
[1, 2, 3, 4, 5, 6]

pts_facet

pts_facet(as_indices=False)

Return the lattice points interior to facets.

These are the boundary points that saturate exactly one inequality.

Also available as points_interior_to_facets and pts_interior_to_facets.

Parameters:

  • as_indices (bool, default: False ) –

    Whether to return the indices of the points in the full list of points instead of their coordinates.

Returns:

  • ndarray | list[int] –

    The points interior to facets, one per row, or their indices if as_indices is true.

Examples:

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-6,-9]])
>>> p.points_interior_to_facets()
array([[ 0,  0, -1, -2],
       [ 0,  0, -1, -1],
       [ 0,  0,  0, -1]])

pts_int

pts_int(as_indices=False)

Return the interior lattice points of the polytope.

These are the points in the relative interior of the polytope. The ordering is the same as in points.

Also available as interior_points and interior_pts.

Parameters:

  • as_indices (bool, default: False ) –

    Whether to return the indices of the points in the full list of points instead of their coordinates.

Returns:

  • ndarray | list[int] –

    The interior points, one per row, or their indices if as_indices is true.

Examples:

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-6,-9]])
>>> p.interior_points()
array([[0, 0, 0, 0]])

pts_not_facets

pts_not_facets(as_indices=False)

Return the lattice points not interior to facets.

These are the interior points followed by the boundary points not interior to facets. For reflexive polytopes, these are the points typically used to construct triangulations, since the points interior to facets correspond to divisors that do not intersect a generic Calabi-Yau hypersurface.

Also available as points_not_interior_to_facets and pts_not_interior_to_facets.

Parameters:

  • as_indices (bool, default: False ) –

    Whether to return the indices of the points in the full list of points instead of their coordinates.

Returns:

  • ndarray | list[int] –

    The points not interior to facets, one per row, or their indices if as_indices is true.

Examples:

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-6,-9]])
>>> p.points_not_interior_to_facets(as_indices=True)
[0, 1, 2, 3, 4, 5, 6]

random_triangulations_fair

random_triangulations_fair(N=None, n_walk=None, n_flip=None, initial_walk_steps=None, walk_step_size=0.01, max_steps_to_wall=25, fine_tune_steps=8, max_retries=50, make_star=None, include_points_interior_to_facets=None, points=None, backend='cgal', as_list=False, progress_bar=True, seed=None)

Construct pseudorandom fine, regular triangulations of the polytope.

The triangulations are fine and regular, and optionally star. This function implements Algorithm #3 from the paper Bounding the Kreuzer-Skarke Landscape by Mehmet Demirtas, Liam McAllister, and Andres Rios-Tascon (arXiv:2008.01730).

This is a Markov chain Monte Carlo algorithm that involves taking random walks inside the subset of the secondary fan corresponding to fine triangulations and performing random flips. For details, please see Section 4.1 in the paper. Only distinct triangulations are returned.

Note
  • By default this function tries to guess reasonable parameters for the polytope that is being used. However, it may take some trial-and-error to find optimal parameters that produce a good sampling and prevent the algorithm from getting stuck.
  • This function is designed mainly for large polytopes where sampling triangulations is challenging. When small polytopes are used it is likely to get stuck or not produce an optimal set.
  • The time it takes to obtain each triangulation varies very significantly with the parameters used, so it is best to adjust them to the desired balance between speed and fairness of the sampling.

Parameters:

  • N (int | None, default: None ) –

    The number of desired distinct triangulations. If not specified, it will generate as many triangulations as it can find until it fails to obtain a new triangulation max_retries consecutive times. This parameter is required when as_list is true.

  • n_walk (int | None, default: None ) –

    The number of hit-and-run steps per triangulation. If not specified, it is set to n_points//10 + 10, where n_points is the total number of lattice points of the polytope.

  • n_flip (int | None, default: None ) –

    The number of random flips performed per triangulation. If not specified, it is set to n_points//10 + 10.

  • initial_walk_steps (int | None, default: None ) –

    The number of hit-and-run steps to take before starting to record triangulations. Small values may result in a bias towards Delaunay-like triangulations. If not specified, it is set to 2*n_points//10 + 10.

  • walk_step_size (float, default: 0.01 ) –

    The size of the random steps taken in the secondary fan. The algorithm may stall if it is too small.

  • max_steps_to_wall (int, default: 25 ) –

    The maximum number of steps to take towards a wall of the subset of the secondary fan that corresponds to fine triangulations. If a wall is not found, a new random direction is selected. Setting this to be very large (>100) reduces performance. If it is too low, the algorithm may stall.

  • fine_tune_steps (int, default: 8 ) –

    The number of steps used to determine the location of a wall. Decreasing it improves performance, but might result in biased samples.

  • max_retries (int, default: 50 ) –

    The maximum number of consecutive failed attempts to obtain a new triangulation before the process is terminated. It is also the maximum number of random directions tried when searching for a wall.

  • make_star (bool | None, default: None ) –

    Whether to convert the obtained triangulations into star triangulations. If not specified, it is set to True for reflexive polytopes (without translations) and False otherwise. It is always set to False if the origin is not one of the points.

  • include_points_interior_to_facets (bool | None, default: None ) –

    Whether to include points interior to facets in the triangulation. If not specified, it is set to False for reflexive polytopes and True otherwise.

  • points (ArrayLike | None, default: None ) –

    The labels of the points to be used. If specified, include_points_interior_to_facets is ignored.

  • backend (str, default: 'cgal' ) –

    The backend used to compute the triangulations. The options are "cgal" and "qhull".

  • as_list (bool, default: False ) –

    Whether to return the full list of triangulations at once instead of a generator. By default a generator is returned, which is usually preferred for efficiency.

  • progress_bar (bool, default: True ) –

    Whether to show a progress bar with the number of triangulations obtained. This option only has an effect when as_list is true.

  • seed (int | None, default: None ) –

    A seed for the random number generator. This can be used to obtain reproducible results.

Returns:

Raises:

Examples:

We construct a polytope and find some random triangulations. The computation takes considerable time, but they should be a fair sample from the full set of triangulations (if the parameters are chosen correctly). For (some) machine learning purposes or when the fairness of the sample is not crucial, random_triangulations_fast should be used instead.

>>> p = Polytope(
...     [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-1,-1]]
... ).dual()
>>> g = p.random_triangulations_fair()
>>> next(g)  # takes a long time
A fine, regular, star triangulation of a 4-dimensional point...
>>> next(g)  # takes a similar amount of time
A fine, regular, star triangulation of a 4-dimensional point...

We can also get a list of 10 triangulations, which takes a while.

>>> rand_triangs = p.random_triangulations_fair(N=10, as_list=True)

random_triangulations_fast

random_triangulations_fast(N=None, c=0.2, max_retries=500, make_star=True, only_fine=True, include_points_interior_to_facets=None, points=None, backend='cgal', as_list=False, progress_bar=True, seed=None)

Construct pseudorandom regular triangulations of the polytope.

The triangulations are regular, and optionally fine and star. They are obtained by picking random heights around the Delaunay heights from a Gaussian distribution. Only distinct triangulations are returned.

Warning

This function produces random triangulations very quickly, but it does not produce a fair sample. When a fair sampling is required, random_triangulations_fair should be used.

Parameters:

  • N (int | None, default: None ) –

    The number of desired distinct triangulations. If not specified, it will generate as many triangulations as it can find until it fails to obtain a new triangulation max_retries consecutive times. This parameter is required when as_list is true.

  • c (float, default: 0.2 ) –

    The standard deviation of the Gaussian distribution used to pick the heights. A larger c results in a wider range of possible triangulations, but with a larger fraction of them being non-fine, which slows down the process when only_fine is true.

  • max_retries (int, default: 500 ) –

    The maximum number of consecutive failed attempts to obtain a new triangulation before the process is terminated.

  • make_star (bool | None, default: True ) –

    Whether to convert the obtained triangulations into star triangulations. If set to None, it is set to True for reflexive polytopes (without translations) and False otherwise. It is always set to False if the origin is not one of the points.

  • only_fine (bool, default: True ) –

    Whether to restrict to fine triangulations.

  • include_points_interior_to_facets (bool | None, default: None ) –

    Whether to include points interior to facets in the triangulation. If not specified, it is set to False for reflexive polytopes and True otherwise.

  • points (ArrayLike | None, default: None ) –

    The labels of the points to be used. If specified, include_points_interior_to_facets is ignored.

  • backend (str, default: 'cgal' ) –

    The backend used to compute the triangulations. The options are "cgal" and "qhull".

  • as_list (bool, default: False ) –

    Whether to return the full list of triangulations at once instead of a generator. By default a generator is returned, which is usually preferred for efficiency.

  • progress_bar (bool, default: True ) –

    Whether to show a progress bar with the number of triangulations obtained. This option only has an effect when as_list is true.

  • seed (int | None, default: None ) –

    A seed for the random number generator. This can be used to obtain reproducible results.

Returns:

Raises:

  • ValueError –

    If as_list is true but N is not specified.

Examples:

We construct a polytope and find some random triangulations. The triangulations are obtained very quickly, but they are not a fair sample of the space of triangulations. For a fair sample, random_triangulations_fair should be used.

>>> p = Polytope(
...     [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-1,-1]]
... ).dual()
>>> g = p.random_triangulations_fast()
>>> next(g)  # runs very quickly
A fine, regular, star triangulation of a 4-dimensional point...
>>> next(g)  # keeps producing triangulations until it can't find more
A fine, regular, star triangulation of a 4-dimensional point...

We can also get a list of 10 triangulations, which is produced very quickly.

>>> rand_triangs = p.random_triangulations_fast(N=10, as_list=True)

random_triangulations_gnn

random_triangulations_gnn(N, make_star=True, max_npts=0, N_face_triangs=1000, as_heights=False, as_generator=False, seed=None, verbosity=0)

Construct random NTFE FR(S)Ts of a reflexive 4-dimensional polytope.

The fine, regular (and optionally star) triangulations (FR(S)Ts) are built by sampling fine, regular triangulations (FRTs) of the 2-faces with the dualGNN graph neural network (arXiv:2605.27770) and extending them via the NTFE algorithm (arXiv:2309.10855). Each attempt draws one FRT per 2-face uniformly from the sampled pools, and it is accepted if the combination extends to a triangulation of the full polytope (i.e. if its expanded secondary cone is solid). Only distinct NTFEs are returned.

This function provides a convenient interface for this use case. When more control is needed (e.g. to provide precomputed face triangulations, possibly sampled with a custom GNN checkpoint), ntfe_frts with triang_method="dualgnn" can be used directly.

This sampler is much closer to uniform over the set of FRSTs than random_triangulations_fast, while being far faster than random_triangulations_fair at large \(h^{1,1}\).

Note

This function requires the optional dualgnn package (which depends on PyTorch). It can be installed with pip install cytools[gnn] or pip install dualgnn.

Parameters:

  • N (int) –

    The number of NTFEs to sample. Since not every combination of 2-face FRTs glues into an NTFE (acceptance can be ~1% for polytopes with large 2-faces, e.g. the \(h^{1,1}=86\) benchmark of arXiv:2605.27770), draws are retried until N distinct NTFEs are found. Fewer are returned only if the attempt budget (1000*N draws) is exhausted or every combination has been checked.

  • make_star (bool, default: True ) –

    Whether to convert the NTFE FRTs into FRSTs (i.e. to make them star).

  • max_npts (int, default: 0 ) –

    The maximum number of points of 2-faces for which all FRTs are enumerated. The GNN samples the FRTs of 2-faces with more points. The default of 0 means that the GNN samples every 2-face, since enumerating the FRTs of a face can be slow even when only a handful of triangulations are wanted.

  • N_face_triangs (int, default: 1000 ) –

    The number of FRTs the GNN samples per (large) 2-face. Every sampled NTFE is assembled from these per-face pools, so the pool size sets the support of the sampler: small pools restrict (and so bias) which NTFEs can appear, while the cost of building the pools is proportional to their size. Lower it (e.g. to 50) for quick exploratory draws on polytopes with large 2-faces, and raise it when coverage of the NTFE space matters.

  • as_heights (bool, default: False ) –

    Whether to return the height vectors realizing each NTFE instead of Triangulation objects. This is cheaper when the Triangulation objects are not needed.

  • as_generator (bool, default: False ) –

    Whether to return a generator instead of a list. Use generators if memory is a concern.

  • seed (int | None, default: None ) –

    A seed for the random number generator. This can be used to obtain reproducible results.

  • verbosity (int, default: 0 ) –

    The verbosity level. Higher means more verbose.

Returns:

  • list | Iterator –

    A list of Triangulation objects (or of height vectors if as_heights is true), or a generator of them if as_generator is true.

Raises:

  • NotImplementedError –

    If the polytope is not a reflexive 4-dimensional polytope.

  • ImportError –

    If the optional dualgnn package is not installed.

Examples:

We construct a polytope and sample some random triangulations. This polytope has a single NTFE, so a single triangulation is returned regardless of N.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],
...               [0,0,0,-1],[-1,-1,-6,-9]])
>>> p.random_triangulations_gnn(N=4, seed=0)
[A fine, regular, star triangulation of a 4-dimensional point...]

For large polytopes, the GNN-sampled 2-face FRT pools can be built once with face_triangs and reused across NTFE draws with ntfe_frts. Since only ~1% of the sampled expanded secondary cones may be solid, request many more than the desired count.

>>> verts = [[-1,-1,-1,-1],[-1,-1,-1,3],[-1,-1,3,-1],[-1,3,-1,-1],
...          [ 1,-1,-1,-1],[ 1,-1,-1,3],[ 1,-1,3,-1],[ 1,3,-1,-1]]
>>> p = Polytope(verts)  # h11=86
>>> pools = p.face_triangs(
...     dim=2, triang_method="dualgnn", max_npts=0, N_face_triangs=50,
...     seed=0,
... )  # the expensive step (~10s)
>>> frsts = p.ntfe_frts(N=300, face_triangs=pools, make_star=True,
...                     seed=0)  # cheap (~2s)

triangulate

triangulate(*, include_points_interior_to_facets=None, points=None, make_star=None, simplices=None, check_input_simplices=True, heights=None, check_heights=True, backend='cgal', verbosity=1, seed=None)

Return a single regular triangulation of the polytope.

When reflexive polytopes are used, it defaults to returning a fine, regular, star triangulation. The points of the triangulation are ordered by their labels. All arguments are keyword-only.

Parameters:

  • include_points_interior_to_facets (bool | None, default: None ) –

    Whether to include points interior to facets in the triangulation. If not specified, it is set to False for reflexive polytopes and True otherwise.

  • points (ArrayLike | None, default: None ) –

    The labels of the points to be used. If specified, include_points_interior_to_facets is ignored.

  • make_star (bool | None, default: None ) –

    Whether to turn the triangulation into a star triangulation with respect to the origin, either by deleting internal lines and connecting all points to the origin, or equivalently by decreasing the height of the origin to be much lower than the rest. If not specified, it is set to True if the polytope is reflexive (without translations) and neither heights nor simplices are given, and to False otherwise. It is always set to False if the polytope is not reflexive and the origin is not one of the points.

  • simplices (ArrayLike | None, default: None ) –

    The simplices specifying the triangulation, each given as a list of point labels. This is useful when a triangulation was previously computed and needs to be used again.

  • check_input_simplices (bool, default: True ) –

    Whether to check that the input simplices define a valid triangulation.

  • heights (ArrayLike | None, default: None ) –

    The heights specifying the regular triangulation. They can be given either for all the points of the polytope, in which case only the heights of the points used in the triangulation are kept, or for the points used in the triangulation, ordered by their labels. Heights can only be specified when using CGAL or QHull as the backend. If not specified, the triangulation depends on the backend: CGAL constructs the Delaunay triangulation, QHull uses random heights near the Delaunay heights, and TOPCOM constructs a placing triangulation.

  • check_heights (bool, default: True ) –

    Whether to check that the input or default heights define a unique triangulation.

  • backend (str, default: 'cgal' ) –

    The backend used to compute the triangulation. The options are "cgal", "qhull", and "topcom". CGAL is the default as it is very fast and robust.

  • verbosity (int, default: 1 ) –

    The verbosity level.

  • seed (int | None, default: None ) –

    Seed for the height perturbation used by the QHull backend. Only that backend perturbs its heights, so this has no effect for CGAL or TOPCOM. If not specified, the perturbation is drawn afresh each time and the triangulation is not reproducible.

Returns:

Raises:

  • ValueError –

    If the input simplices use more points than the ones selected for the triangulation. Further errors may be raised by the Triangulation constructor.

Examples:

We construct a triangulation of a reflexive polytope and check that by default it is a fine, regular, star triangulation.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-6,-9]])
>>> t = p.triangulate()
>>> t.is_fine(), t.is_regular(), t.is_star()
(True, True, True)
>>> len(t.points())
7

By default the points interior to facets are not included, but we can include them, or specify the points to use.

>>> len(p.triangulate(include_points_interior_to_facets=True).points())
10
>>> p.triangulate(points=[1,2,3,4,5]).is_star()
False

We also construct triangulations with heights, input simplices, and using the other backends.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],
...               [-1,-2,-1,-1],[-2,-1,-1,-1]])
>>> t = p.triangulate(heights=[-30,5,5,24,-19,-14,29])
>>> t.simplices()
array([[0, 1, 2, 3, 4],
       [0, 1, 2, 3, 5],
       [0, 1, 2, 4, 6],
       [0, 1, 2, 5, 6],
       [0, 1, 3, 4, 5],
       [0, 1, 4, 5, 6],
       [0, 2, 3, 4, 5],
       [0, 2, 4, 5, 6]], dtype=uint8)
>>> t2 = p.triangulate(simplices=t.simplices())
>>> t2 == t
True
>>> t3 = p.triangulate(backend="qhull")
>>> t4 = p.triangulate(backend="topcom")

vertices

vertices(optimal=False, as_indices=False)

Return the vertices of the polytope.

The vertices are sorted by their labels, and the result is cached.

Parameters:

  • optimal (bool, default: False ) –

    Whether to return the vertices in their optimal coordinates.

  • as_indices (bool, default: False ) –

    Whether to return the indices of the vertices in the full list of points instead of their coordinates.

Returns:

  • ndarray | list[int] –

    The vertices of the polytope, one per row, or their indices if as_indices is true.

Examples:

We construct a polytope and find its vertices. We can see that they match the points that we used to construct the polytope.

>>> p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-1,-1]])
>>> p.vertices()
array([[-1, -1, -1, -1],
       [ 0,  0,  0,  1],
       [ 0,  0,  1,  0],
       [ 0,  1,  0,  0],
       [ 1,  0,  0,  0]])

volume

volume()

Return the normalized volume of the polytope.

The volume is computed relative to the lattice of the affine hull of the polytope, so it is well defined for polytopes that are not full-dimensional. The result is cached.

Warning

By convention, the standard simplex has unit volume. To get the more typical Euclidean volume it must be divided by \(d!\).

Returns:

  • int –

    The normalized volume of the polytope. It is 0 for 0-dimensional polytopes.

Examples:

We construct a standard simplex and a cube, and find their volumes.

>>> p1 = Polytope([[1,0,0],[0,1,0],[0,0,1],[0,0,0]])
>>> p2 = Polytope([[1,0,0],[0,1,0],[0,0,1],[0,0,0],
...                [0,1,1],[1,0,1],[1,1,0],[1,1,1]])
>>> p1.volume()
1
>>> p2.volume()
6