Skip to main content

Tutorial

note

This tutorial is available as a Jupyter notebook that you can download at this link so that you can follow along.

Let us look at the basics of the CYTools package. The starting objects for most computations are the Polytope and Cone classes. These can be imported as follows.

from cytools import Polytope, Cone

Other important classes in this package are Triangulation, ToricVariety, and CalabiYau. These should generally not be directly constructed by the user, and instead they are constructed by designated functions.

Let us take a brief look at each of the classes.

Polytopes

First, let's take a look at the Polytope class. A Polytope object can be created by specifying a list of points defining the convex hull. Note that CYTools only supports lattice polytopes so any floating point numbers will be truncated to integers.

p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1],[-1,-1,-1,-1]])

We can print some information about the polytope as follows.

print(p)
# A 4-dimensional reflexive lattice polytope in ZZ^4

The list of lattice points, boundary points, interior points, etc., can be computed using self-explanatory functions.

pts = p.points()
pts
# array([[ 0, 0, 0, 0],
# [-1, -1, -1, -1],
# [ 0, 0, 0, 1],
# [ 0, 0, 1, 0],
# [ 0, 1, 0, 0],
# [ 1, 0, 0, 0]])

We can compute information relevant to Batyrev's construction of Calabi-Yau hypersurfaces when the polytope is reflexive and 4-dimensional.

info

To avoid ambiguity, one must specify if the polytope should be viewed as living in the MM lattice or the NN lattice.

p.h11(lattice="N"), p.h21(lattice="N")
# (1, 101)
tip

To see the full list of available functions you can type the name of the polytope (in this case p) followed by a period and then press tab. This works for any kind of object! So if you want to see the available functions for ToricVariety, CalabiYau, or Cone objects you can do the same thing.

You can find the full documentation of the Polytope class here.

Using the Kreuzer-Skarke database

CYTools provides two useful functions to work with the Kreuzer-Skarke (KS) database. We can import them as follows.

from cytools import read_polytopes, fetch_polytopes

The first function takes a file name as input and reads all polytopes specified in the format used in the KS database. The second file directly fetches the polytopes from the database. For example let's fetch 100 polytopes with h2,1=7h^{2,1}=7.

g = fetch_polytopes(h21=7, lattice="N", limit=100)
print(g)
# <generator object polytope_generator at 0x7f306eacaeb0>

As you can see above, these functions return generator objects that give one polytope at a time. To get the polytopes we can use

p1 = next(g)
p2 = next(g)

Or to get the full list of polytopes we can use

l = list(g)
print(len(l))
# 98

In this example generator had a limit of 100 polytopes, but since it had already generated two of them it produced a list of only 98.

info

If you are not familiar with Python, it is worth noting that generators raise an exception once they reach the end. For this reason, if you are using the next function in your code it is usually necessary to wrap this with try-except statements as in the following example.

g = fetch_polytopes(h21=2, lattice="N", limit=100)
for i in range(100):
try:
p = next(g)
print(f"Fetched polytope {i}")
except StopIteration:
print(f"Iteration stopped at number {i}")
break

Alternatively, one can conveniently use generators in the following way.

g = fetch_polytopes(h21=2, lattice="N", limit=100)
for p in g:
# Do something
tip

The fetch_polytopes function can take many different parameters, and can even fetch 5D polytopes from the Schöller-Skarke database. To see more information about a function, you can write the name of the function and end it with a question mark (?), as follows. (This only works on Jupyter notebooks.)

fetch_polytopes?

Triangulations

Let us now look at how we can triangulate the polytopes. We start with the following polytope

p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[-1,1,1,0],[0,-1,-1,0],[0,0,0,1],[1,-2,1,1],[-2,2,0,-1],[1,0,-1,-1]])

We can obtain a triangulation simply by using

t = p.triangulate()

and print information about the triangulation as follows

print(t)
# A fine, regular, star triangulation of a 4-dimensional point configuration with 12 points in ZZ^4

For 4-dimensional reflexive polytopes it defaults to finding an FRST of the points not interior to facets. Other options such as heights, whether to make it a star, the backend, etc., can be inputted as well.

t = p.triangulate(heights=[0,3,7,1,9,1,1,1,3,2,2,2], make_star=True, backend="cgal")

Various properties of the triangulation can be accessed by self-explanatory functions.

simps = t.simplices()

Some of these require additional software that are included in the Docker image. For example, finding triangulations that differ by a bistellar flip requires TOPCOM.

triangs = t.neighbor_triangulations()

If one wants to generate random triangulations, one for example can pick random heights around the Delaunay triangulation. This can be done with the random_triangulations_fast function. Note that this function is primarily designed to work with large polytopes where it is impossible to find all triangulations.

triangs_gen = p.random_triangulations_fast(N=100)
tip

The above method to find triangulations is fast, but does not produce a fair sampling of triangulations. This can be done with the random_triangulations_fair function. Again, this function is primarily designed for large polytopes.

You can find the full documentation of the Triangulation class here.

Toric Varieties

We can interpret star triangulations as defining a toric fan, and construct the associated toric variety. We can do this as follows.

p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[-1,1,1,0],[0,-1,-1,0],[0,0,0,1],[1,-2,1,1],[-2,2,0,-1],[1,0,-1,-1]])
t = p.triangulate()
v = t.get_toric_variety()

Basic information can be printed as follows.

print(v)
# A simplicial compact 4-dimensional toric variety with 31 affine patches

Various properties of the toric variety can be accessed by self-explanatory functions. For example, intersection numbers and Mori cone can be computed as follows.

intnums = v.intersection_numbers()
mori_cone = v.mori_cone()

You can find the full documentation of the ToricVariety class here.

Calabi-Yaus

Let's now get to the class of most interest. A CalabiYau object can be obtained from a triangulation or from a toric variety as follows.

p = Polytope([[1,0,0,0],[0,1,0,0],[0,0,1,0],[-1,1,1,0],[0,-1,-1,0],[0,0,0,1],[1,-2,1,1],[-2,2,0,-1],[1,0,-1,-1]])
t = p.triangulate()
v = t.get_toric_variety()
cy = v.get_cy()
cy = t.get_cy() # This is equivalent to the line above, but you can get it directly from the triangulation

Basic information can be printed as follows.

print(cy)
# A Calabi-Yau 3-fold hypersurface with h11=7 and h21=23 in a 4-dimensional toric variety

Various properties of the CY can be accessed by self-explanatory functions. For example, intersection numbers and the inferred Mori cone from toric geometry can be computed as follows.

intnums = cy.intersection_numbers()
mori_cone = cy.toric_mori_cone()

You can find the full documentation of the CalabiYau class here.

Cones

Lastly, let's briefly look at cones. These can be constructed by specifying a set of rays or normals to hyperplanes.

c1 = Cone([[0,1],[1,1]])
c2 = Cone(hyperplanes=[[0,1],[1,1]])

Let us look at the (toric) Mori cone of the above Calabi-Yau.

mc = cy.toric_mori_cone()

We can print some information about it as follows.

print(mc)
# A 7-dimensional rational polyhedral cone in RR^12 generated by 36 rays

Note that by default the Mori cone is given in a basis-independent way, as an h1,1h^{1,1} dimensional cone in an h1,1+dV+1h^{1,1}+d_V+1-dimensional lattice. We can tell CYTools to use a basis of curves with the in_basis=True parameter. (For more information of how to set a basis of curves or divisors see set_divisor_basis).

The Kähler cone can be computed from the designated function, or by taking the dual of the Mori cone in a basis of curves.

kc = cy.kahler_cone()
kc = cy.toric_mori_cone(in_basis=True).dual() # This line is equivalent to the previous, but it is less direct

CYTools uses a lazy duality where no computation is done and instead the definition of the cone is dualized. This can be seen by printing the information.

print(kc)
# A rational polyhedral cone in RR^7 defined by 36 hyperplanes.

However, we can still do the hard duality computation if desired.

kc_rays = kc.rays()

And then the definition of the cone is updated.

print(kc)
# A 7-dimensional rational polyhedral cone in RR^7 generated by 18 rays

We can compute the location of the tip of the stretched Kähler cone as follows.

tip = kc.tip_of_stretched_cone(1)

You can find the full documentation of the Cone class here.

This concludes the brief tutorial. We have some additional advanced usage instructions for people who intend to perform large-scale computations with CYTools or who want to tinker with the Docker image. For a full list of available classes and functions please visit the documentation tab.