| Hosted by CoCalc | Download
Kernel: SageMath 9.2
\def\CC{\bf C} \def\QQ{\bf Q} \def\RR{\bf R} \def\ZZ{\bf Z} \def\NN{\bf N}

admcycles Tutorial

Below we show how to use admcycles for computations in the tautological ring of the moduli space Mg,n\overline{\mathcal{M}}_{g,n} of stable curves. More detailed explanations are available in the preprint [Delecroix-Schmitt-vanZelm].

If you opened this document on share.cocalc.com, you can open an interactive copy by clicking the button Open in CoCalc with one click above. Then you can execute commands and do calculations below by clicking on a box containing code (such as from admcycles import * below) and pressing Shift + Enter. Note that many commands rely on earlier lines having been executed before, in particular the first line from admcycles import * should be executed once at the start.

To insert a new box for writing own commands, you can choose Insert -> Insert cell below in the menu above or click left of the text of a box and press b.

To use admcycles, the first thing you need to do is import it:

from admcycles import *

Then many functions become available. For example you can enter tautological classes as combinations of divisors (here on M3,4\overline{\mathcal{M}}_{3,4}):

t1 = 3*sepbdiv(1,(1,2),3,4)-psiclass(4,3,4)^2

Above, the function sepbdiv(g1,A1,g,n) returns the class of a boundary divisor, the pushforward of the fundamental class under the gluing map Mg1,A1{}×Mgg1,A1c{}Mg,n\overline{\mathcal{M}}_{g_1, A_1 \cup \{\bullet\}} \times \overline{\mathcal{M}}_{g-g_1, A_1^c \cup \{\bullet\}} \to \overline{\mathcal{M}}_{g, n}, and psiclass(i,g,n) returns ψi\psi_i on Mg,n\overline{\mathcal{M}}_{g,n}. To avoid having to type g,n in long formulas, we can use reset_g_n(g,n) to set them once:

reset_g_n(2,1) t2 = -1/3*irrbdiv()*lambdaclass(1)

To get explanations about a function, you can type the name of the function followed by a question mark. E.g. type irrbdiv? below and press Shift+Enter to see the documentation of this function:

Tautological classes

Creating tautological classes

One way to enter a tautological class is to first use the function list_tautgens(g,n,r) to print a list of generators of RH2r(Mg,n)\mathrm{RH}^{2r}(\overline{\mathcal{M}}_{g,n}) :

list_tautgens(2,0,2)

Generators are given by a stable graph, decorated with a monomial in κ\kappa and ψ\psi-classes (see below for an explanation of the representation of stable graphs). One can create a list L of the generators we printed above using tautgens(g,n,r) and compute linear combinations of the elements L[i] of this list:

L = tautgens(2,0,2) t3=2*L[3]+L[4] t3

Stable graphs are represented by three lists:

  • a list genera of the genera gig_i of the vertices,

  • a list legs of lists of legs and half-edges at these vertices,

  • a list edges of pairs (h1,h2) of half-edges forming an edge.

A stable graph can be created manually using StableGraph(genera,legs,edges) by specifying these three lists. Below we create a stable graph with two vertices of genus 1, carrying half-edges 2,3 which form an edge:

G = StableGraph([1,1],[[2],[3]],[(2,3)]); G

Basic operations

Tautological classes can be manipulated using standard arithmetic operations:

s1 = psiclass(3,1,3)^2 # square of psi_3 on \Mbar_{1,3}

They can also be pushed forward under forgetful morphisms, by specifying the list of markings that are forgotten. As an example, we push forward s_1, the class ψ32\psi_3^2 on M1,3\overline{\mathcal{M}}_{1,3}, under the map forgetting marking 33, obtaining the class κ1\kappa_1 on M1,2\overline{\mathcal{M}}_{1,2} as expected:

s1.forgetful_pushforward([3])

Similarly, we can pull back the class ψ2\psi_2 on M1,2\overline{\mathcal{M}}_{1,2} :

s2 = psiclass(2,1,2) s2.forgetful_pullback([3])

Given a tautological class t, the function t.evaluate() computes the integral of t against the fundamental class of Mg,n\overline{\mathcal{M}}_{g,n}, i.e. the degree of the zero-cycle part of t. Below we compute the intersection number M1,3ψ2ψ32\int_{\overline{\mathcal{M}}_{1,3}} \psi_2 \psi_3^2 We check the equality

M1,3ψ2ψ32=M1,2ψ22+ψ1ψ2\int_{\overline{\mathcal{M}}_{1,3}} \psi_2 \psi_3^2 = \int_{\overline{\mathcal{M}}_{1,2}} \psi_2^2 + \psi_1 \psi_2

predicted by the String equation:

s3 = psiclass(2,1,3)*psiclass(3,1,3)^2 s3.evaluate()
s4 = psiclass(2,1,2)^2+psiclass(1,1,2)*psiclass(2,1,2) s4.evaluate()

Using simplify() to reduce number of terms in tautclass:

psisum = psiclass(1,2,1) + 3 * psiclass(1,2,1); psisum
psisimple = psisum.simplify(); psisimple

A basis of the tautological ring and tautological relations

The package can compute the generalized Faber-Zagier relations between the generators above. The function generating_indices(g,n,r) computes a list of indices of tautgens(g,n,r) forming a basis of RH2r(Mg,n)\mathrm{RH}^{2r}(\overline{\mathcal{M}}_{g,n}) :

generating_indices(2,0,2)

Then, the function toTautbasis(g,n,r) can be used to express a tautological class in this basis:

t3.toTautbasis(2,0,2)

This means that the class t3 we defined above as the linear combination t3=2*L[3]+L[4] can be expressed as t3=-48*L[0]+22*L[1] in terms of the basis L[0],L[1] of RH4(M2,0)\mathrm{RH}^{4}(\overline{\mathcal{M}}_{2,0}).

We can also use the function is_zero to check a tautological relation. Below, we verify the divisor relation κψ+δ0=0\kappa - \psi + \delta_0 = 0 on M1,4\overline{\mathcal{M}}_{1,4} :

g=1; n=4 reset_g_n(g,n) bgraphs = [bd for bd in list_strata(g,n,1) if bd.numvert()>1] del0 = sum([bd.to_tautclass() for bd in bgraphs]) # sum of boundary classes with separating node psisum = sum([psiclass(i) for i in range(1,n+1)]) # sum of psi-classes rel = kappaclass(1)-psisum+del0 rel.is_zero()

Comparing classes on open subsets of M1,4\overline{\mathcal{M}}_{1,4} using parameter moduli to be one of 'st', 'tl', 'ct', 'rt' or 'sm' :

kappaclass(1,3,0).toTautbasis(moduli='sm')
lambdaclass(1,3,0).toTautbasis(moduli='sm')
diff = lambdaclass(1,3,0) - (1/12)*kappaclass(1,3,0) diff.is_zero(moduli='sm')

Pulling back tautological classes to a boundary divisor

Below we create a stable graph bdry and compute a pullback of a tautological class under the corresponding boundary gluing map. The result is expressed in terms of a basis of the tautological ring on M2,1×M2,1\overline{\mathcal{M}}_{2,1} \times \overline{\mathcal{M}}_{2,1} :

bdry = StableGraph([2,2],[[1],[2]],[(1,2)]) generator = tautgens(4,0,2)[3] generator
pullback = bdry.boundary_pullback(generator) pullback.totensorTautbasis(2)
pullback.totensorTautbasis(2,vecout=true)

We can see that in the Kunneth decomposition of H4(M2,1×M2,1)\mathrm{H}^4(\overline{\mathcal{M}}_{2,1} \times \overline{\mathcal{M}}_{2,1}) the pullback has no component along H2(M2,1)H2(M2,1)\mathrm{H}^2(\overline{\mathcal{M}}_{2,1}) \otimes \mathrm{H}^2(\overline{\mathcal{M}}_{2,1}) and the contributions to H0(M2,1)H4(M2,1)\mathrm{H}^0(\overline{\mathcal{M}}_{2,1}) \otimes \mathrm{H}^4(\overline{\mathcal{M}}_{2,1}) and H4(M2,1)H0(M2,1)\mathrm{H}^4(\overline{\mathcal{M}}_{2,1}) \otimes \mathrm{H}^0(\overline{\mathcal{M}}_{2,1}) are symmetric, as expected.

Pushing forward classes from the boundary

We can also compute the pushforward of the product of classes under a boundary gluing map:

B = StableGraph([2,1],[[4,1,2],[3,5]],[(4,5)]) Bclass = B.boundary_pushforward() # class of undecorated boundary divisor si1 = B.boundary_pushforward([fundclass(2,3),-psiclass(2,1,2)]); si1
si2 = B.boundary_pushforward([-psiclass(1,2,3),fundclass(1,2)]); si2

si1 is obtained by pushing forward the fundamental class on the genus 2 vertex times ψh-\psi_h on the second vertex (where hh is the half-edge). We can then check the self-intersection formula for the boundary divisor above:

(Bclass*Bclass-si1-si2).is_zero()

Special cycle classes

Double ramification cycles

Double ramification cycles are computed by the function DR_cycle(g,A). Below we verify a multiplicativity relation between DR-cycles from the paper [Holmes-Pixton-Schmitt]:

A = vector((2,4,-6)); B = vector((-3,-1,4)) diff = DR_cycle(1,A)*DR_cycle(1,B)-DR_cycle(1,A)*DR_cycle(1,A+B) diff.is_zero(moduli='tl') # vanishing on treelike locus
diff.is_zero(moduli='st') # does not vanish on locus of all stable curves

Calculating DR-cycles as classes with polynomial coefficients in the input:

R.<a1,a2,a3,b1,b2,b3> = PolynomialRing(QQ,6) A = vector((a1,a2,a3)); B = vector((b1,b2,b3)) diff = DR_cycle(1,A)*DR_cycle(1,B)-DR_cycle(1,A)*DR_cycle(1,A+B) diff.is_zero(moduli='tl')

Checking intersection numbers of DR-cycles with lambdaclass from [Buryak-Rossi]:

intersect = DR_cycle(1,A)*DR_cycle(1,B)*lambdaclass(1,1,3) f = intersect.evaluate(); factor(f)
g = f.subs({a3:-a1-a2,b3:-b1-b2}); factor(g)

Strata of k-differentials

Strata of k-differentials using Strataclass(g,k,mu) with mu vector of zero and pole multiplicities:

L = Strataclass(2,1,(3,-1)); L.is_zero()
L = Strataclass(2,1,(2,)); (L-Hyperell(2,1)).is_zero()

Generalized lambda classes

Computing Chern classes of RπO(D)R \pi_* \mathcal{O}(D) for the universal curve Cg,nMg,n\mathcal{C}_{g,n} \to \overline{\mathcal{M}}_{g,n} using generalized_lambda :

g=3; n=1 l=1; d=[0]; a=[] s = lambdaclass(2,g,n) t = generalized_lambda(2,l,d,a,g,n) (s-t).is_zero()

Admissible cover cycles

Hyperelliptic and bielliptic cycles

Computing the cycle of the hyperelliptic locus in genus 3:

H = Hyperell(3,0,0)

The cycle of hyperelliptic curves of genus 3 with 0 marked fixed points of the involution and 0 marked pairs of conjugate points:

H.toTautbasis()

We compare with the known expression H=9λ1δ03δ1H=9 \cdot \lambda_1-\delta_0-3\cdot \delta_1 :

reset_g_n(3, 0) H2 = 9*lambdaclass(1)-(1/2)*irrbdiv()-3*sepbdiv(1,()) H2.toTautbasis()

Creating and identifying general admissible cover cycles

Below we define the group G=Z/2ZG=\mathbb{Z}/2\mathbb{Z} and ramification data H, specifying that we look at double covers with two points of stabilizer G[1], which is the generator of the group GG :

G = PermutationGroup([(1,2)]) # G=Z/2Z H = HurData(G,[G[1],G[1]])

An example with this ramification behaviour is the locus of bielliptic curves (C,p,q)(C,p,q) in M2,2\overline{\mathcal{M}}_{2,2} of genus 2 curves CC admitting a double cover of an elliptic curve with marked ramification points p,qp,q . The following identifies the class of this locus in terms of the generating set tautgens(2,2,3) of RH6(M2,2)\mathrm{RH}^6(\overline{\mathcal{M}}_{2,2}) :

# The following computation might take very long, and will possibly not finish on the free version of cocalc # Remove the # symbols below and press Shift+Enter to try anyway # vbeta = Hidentify(2,H,vecout=true) # vector(vbeta)

If instead we wanted to specify a locus with two points of generator G[1] and one pair of points with generator G[0], we would consider:

H2 = HurData(G,[G[1],G[1],G[0]])

We can also identify the pushforward of the locus of bielliptic curves (C,p,q)(C,p,q) under the map forgetting both markings, obtaining (a multiple of) the locus of bielliptic curves CC inside M2,0\overline{\mathcal{M}}_{2,0}. For this we use the optional parameter markings to specify that no marking should be remembered:

G = PermutationGroup([(1,2)]) H = HurData(G,[G[1],G[1]]) Biell = Hidentify(2,H,markings=[]) Biell.toTautbasis(2,0,1)
(30, -9)

We can compare this to a known formula [B2]=3/2δirr+3δ1[\overline{B}_2] = 3/2 \delta_{\text{irr}} + 3 \delta_1. When entering this, note that irrbdiv returns two times the class δirr\delta_{\text{irr}} since in general the convention is not to divide by automorphisms of stable graphs:

reset_g_n(2, 0) Biell2 = 3/4*irrbdiv()+ 3*sepbdiv(1,()) Biell2.toTautbasis(2,0,1)

Example: Hurwitz-Hodge integrals

Computing the Hurwitz-Hodge integral B2,2,0λ2\int_{\overline{B}_{2,2,0}} \lambda_2 :

(Biell*lambdaclass(2,2,0)).evaluate()
1/48

Computing Hurwitz-Hodge integral of cyclic triple covers of genus 0 curves against λ1\lambda_1, see [Owens-Somerstep]:

G = PermutationGroup([(1,2,3)]) g1 = G('(1,2,3)') g2 = G('(1,3,2)') H = HurData(G,[g1, g1, g2, g2]) #n=2, m=2 t = Hidentify(2,H,markings=[]) (t*lambdaclass(1,2,0)).evaluate()

Citing admcycles

If you use admcycles in your research, consider citing the preprint [Delecroix-Schmitt-vanZelm].