unlisted
ubuntu2004# -*- coding: utf-8 -*-1r"""2Double ramification cycle3"""45import itertools6from copy import copy78from sage.combinat.integer_vector import IntegerVectors9from sage.combinat.combinat import bernoulli_polynomial10from sage.arith.all import factorial11from sage.functions.log import exp12from sage.rings.all import QQ13from sage.rings.power_series_ring import PowerSeriesRing1415from admcycles.admcycles import list_strata16from admcycles.stable_graph import StableGraph17from admcycles.double_ramification_cycle import DR_cycle18from .tautological_ring import TautologicalRing1920# S.<x0,x1>=PowerSeriesRing(QQ,'x0,x1',default_prec=14)212223def graph_sum(g, n, decgraphs=None, globalfact=None, vertterm=None, legterm=None, edgeterm=None, maxdeg=None, deg=None, termsout=False):24r"""Returns the (possibly mixed-degree) tautological class obtained by summing over graphs gamma,25inserting vertex-, leg- and edgeterms.2627INPUT:2829- ``decgraphs`` -- list or generator; entries of decgraphs are pairs (gamma,dec) of a StableGraph30gamma and some additional combinatorial structure dec associated to gamma3132- ``globalfact`` -- function; globalfact(gamma,dec) gets handed the parameters gamma,dec as arguments and gives out a number that is multiplied with the corresponding term in the graph sum; default is 13334- ``vertterm`` -- function; ``vertterm(gv,nv,maxdeg, **kwargs)`` takes arguments local genus gv and number of legs nv35and maxdeg gets handed the parameters gamma,dec,v as optional keyworded arguments and gives out a36tautological class on Mbar_{gv,nv}; the class is assumed to be of degree at most maxdeg,37if deg is given, the class is exactly of degree deg3839- ``legterm`` -- function; ``legterm(gv,nv,i,maxdeg, **kwargs)`` similar to vertterm, except input is40gv,nv,i,maxdeg where i is number of marking on Mbar_{gv,nv} associated to leg41gamma, dec and origleg (number of leg in outer graph) given as keyworded arguments4243- ``edgeterm`` -- function; edgeterm(maxdeg,**kwargs) takes keyworded arguments gamma,dec,e,maxdeg44it gives a generating series s in x0,x1 such that the insertion at edge45e=(h0,h1) is given by s(psi_h0, psi_h1)4647- ``termsout`` -- parameter; if termsout=False, return sum of all terms48if termsout = 'coarse', return tuple of terms, one for each pair (gamma,dec)49if termsout = 'fine', return tuple of terms, one for each pair (gamma,dec) and each distribution50of cohomological degrees to vertices and half-edges51"""52if maxdeg is None:53maxdeg = 3 * g - 3 + n if deg is None else deg54if decgraphs is None:55decgraphs = [(gr, None) for ednum in range(3 * g - 3 + n + 1)56for gr in list_strata(g, n, ednum)]57if globalfact is None:58def globalfact(a, b):59return 160if vertterm is None:61def vertterm(gv, nv, maxdeg, **kwargs):62return TautologicalRing(gv, nv).fundamental_class()63if legterm is None:64def legterm(gv, nv, i, maxdeg, **kwargs):65return TautologicalRing(gv, nv).fundamental_class()66if edgeterm is None:67def edgeterm(maxdeg, **kwargs):68S = PowerSeriesRing(QQ, 'x0,x1', default_prec=maxdeg + 1)69return S.one()7071termlist = []7273for (gamma, dec) in decgraphs:74restdeg = maxdeg - len(gamma.edges())75if restdeg < 0:76continue7778gammadectermlist = []7980numvert = gamma.numvert()81gnvect = [(gamma.genera(i), len(gamma.legs(i))) for i in range(numvert)]82dimvect = [3 * g - 3 + n for (g, n) in gnvect]83markings = gamma.list_markings()84vertdic = {l: v for v in range(numvert) for l in gamma.legs(v)}85indexdic = {l: j + 1 for v in range(numvert) for j, l in enumerate(gamma.legs(v))}86# ex_dimvect=[dimvect[vertdic[l]] for l in halfedges] # list of dimensions of spaces adjacent to half-edges8788# Pre-compute all vertex-, leg- and edgeterms89vterms = {v: vertterm(gnvect[v][0], gnvect[v][1], restdeg, gamma=gamma, dec=dec, v=v) for v in range(numvert)}90lterms = {i: legterm(gnvect[vertdic[i]][0], gnvect[vertdic[i]][1], indexdic[i],91restdeg, gamma=gamma, dec=dec, origleg=i) for i in markings}92eterms = {e: edgeterm(restdeg, gamma=gamma, dec=dec, e=e) for e in gamma.edges()}93varlist = {(h0, h1): eterms[h0, h1].parent().gens() for h0, h1 in gamma.edges()}94eterms = {e: eterms[e].coefficients() for e in eterms}95varx = {h0: varlist[h0, h1][0] for h0, h1 in gamma.edges()}96varx.update({h1: varlist[h0, h1][1] for h0, h1 in gamma.edges()})9798if deg is None:99rdlis = range(restdeg + 1) # terms of all degrees up to restdeg must be computed100else:101rdlis = [deg - len(gamma.edges())]102for rdeg in rdlis:103# distribute the remaining degree rdeg to vertices104for degdist in IntegerVectors(rdeg, numvert, outer=dimvect):105# now for each vertex, split degree to vertex- and leg/half-edge terms106vertchoices = [IntegerVectors(degdist[v], len(gamma.legs(v)) + 1) for v in range(numvert)]107for choice in itertools.product(*vertchoices):108vdims = []109ldims = {}110for v in range(numvert):111vdims.append(choice[v][0])112ldims.update({l: choice[v][i + 1] for i, l in enumerate(gamma.legs(v))})113114effvterms = [vterms[v].degree_part(vdims[v]) for v in vterms]115efflterms = {i: lterms[i].degree_part(ldims[i]) for i in lterms}116for i in efflterms:117effvterms[vertdic[i]] *= efflterms[i] # multiply contributions from legs to vertexterms118for h0, h1 in gamma.edges():119# TODO: optimization potential here by multiplying kppolys directly120gv0, nv0 = gnvect[vertdic[h0]]121R0 = TautologicalRing(gv0, nv0)122effvterms[vertdic[h0]] *= eterms[(h0, h1)].get(varx[h0]**ldims[h0]123* varx[h1]**ldims[h1], 0) * R0.psi(indexdic[h0])**ldims[h0]124gv1, nv1 = gnvect[vertdic[h1]]125R1 = TautologicalRing(gv1, nv1)126effvterms[vertdic[h1]] *= R1.psi(indexdic[h1])**ldims[h1]127for t in effvterms:128t.simplify()129# print(gamma)130# print(rdeg)131# print(degdist)132# print(choice)133# print(effvterms)134# print(eterms)135# print(indexdic)136# print('\n')137tempres = gamma.boundary_pushforward(effvterms)138tempres.simplify()139if not tempres.is_empty():140tempres *= globalfact(gamma, dec)141gammadectermlist.append(tempres)142# print(termlist)143if termsout == 'coarse':144termlist.append(sum(gammadectermlist))145else:146termlist += gammadectermlist147if termsout:148return termlist149else:150return sum(termlist)151152############153#154# Useful functions and examples155#156############157158###159# Example 1 : Conjectural graph sum for DR_g(1,-1)160###161162# Generating functions for graphs163164165def DR11_tree_test(gr):166return gr.vertex(1) == gr.vertex(2)167168169def DR11_trees(g, maxdeg):170return [gr for n in range(1, g + 1) for e in range(min(n, maxdeg - n + 1))171for gr in list_strata(0, 2 + n, e) if DR11_tree_test(gr)]172173174def DR11_graphs(g, maxdeg=None):175if maxdeg is None:176maxdeg = 3 * g - 3 + 2177result = []178for gr in DR11_trees(g, maxdeg):179n = len(gr.list_markings()) - 2180maxleg = max([max(j + [0]) for j in gr.legs])181grlist = []182for gdist in IntegerVectors(g, n, min_part=1):183genera = copy(gr.genera) + list(gdist)184legs = copy(gr.legs) + [[j] for j in range(maxleg + 1, maxleg + n + 1)]185edges = copy(gr.edges) + [(j - maxleg + 2, j) for j in range(maxleg + 1, maxleg + n + 1)]186grlist.append(StableGraph(genera, legs, edges))187result += [(gam, None) for gam in grlist]188removedups(result, lambda a, b: a[0].is_isomorphic(b[0]))189return result190191192def removedups(li, comp=None):193"""194Remove duplicates in a list ``li`` according to a comparison function.195196This works inplace and modifies ``li``.197198EXAMPLES::199200sage: from admcycles.graph_sum import removedups201sage: L = [4,6,3,2,4,99,1,3,2]202sage: removedups(L)203sage: L204[6, 4, 99, 1, 3, 2]205"""206if comp is None:207def comp(a, b):208return a == b209n = len(li)210currn = len(li)211for i in range(n, -1, -1):212if any(comp(li[i], li[j]) for j in range(i + 1, currn)):213li.pop(i)214currn -= 1215216# Global factor = 1/|Aut(Gamma)|217218219def divbyaut(gamma, dec):220return QQ(1) / gamma.automorphism_number()221222# Vertex- and edgeterms for DR11223224225def DR11_vterm(gv, nv, maxdeg, **kwargs):226R = TautologicalRing(gv, nv)227if gv == 0:228gamma = kwargs['gamma']229v = kwargs['v']230f = R.fundamental_class()231if 1 not in gamma.legs[v]:232# we are in genus zero vertex not equal to base233return -nv * f234else:235# we are at the base vertex236return f237else:238return sum([(-1)**j * R.lambdaclass(j) for j in range(maxdeg + 1)])239240241def DR11_eterm(maxdeg, **kwargs): # smarter: edterm(maxdeg=None,gamma=None,dec=None,e=None, *args, *kwds)242S = PowerSeriesRing(QQ, 'x0,x1', default_prec=maxdeg + 1)243x0, x1 = S.gens()244return 1 / (1 - x0 - x1 + x0 * x1)245246# Final conjectural graph sum expressing DR_g(1,-1)247248249def DR11_sum(g, deg=None, **kwds):250if deg is None:251deg = g252return graph_sum(g, 2, decgraphs=DR11_graphs(g, maxdeg=deg), globalfact=divbyaut, vertterm=DR11_vterm, edgeterm=DR11_eterm, deg=deg, **kwds)253254255def DR11_decgraphs(g, maxdeg=None):256"""257Decorate the graphs of DR11_graphs(g) with a choice of half-edge258at each non-root vertex.259"""260for gr0, _ in DR11_graphs(g, maxdeg):261L = [v for v in gr0.legs if 1 not in v]262for choice in itertools.product(*L):263yield (gr0, choice)264265266def divbyaut_new(gamma, dec):267zeroverts = gamma.genera.count(0)268return factorial(zeroverts - 1) / gamma.automorphism_number()269270# Vertex- and edgeterms for DR11271272273def DR11_vterm_new(gv, nv, maxdeg, **kwargs):274R = TautologicalRing(gv, nv)275if gv == 0:276gamma = kwargs['gamma']277v = kwargs['v']278f = R.fundamental_class()279if 1 not in gamma.legs[v]:280# we are in genus zero vertex not equal to base281return -1 * f282else:283# we are at the base vertex284return f285else:286return sum([(-1)**j * R.lambdaclass(j) for j in range(maxdeg + 1)])287288289def DR11_eterm_new(maxdeg, **kwargs): # smarter: edterm(maxdeg=None,gamma=None,dec=None,e=None, *args, *kwds)290S = PowerSeriesRing(QQ, 'x0,x1', default_prec=maxdeg + 1)291x0, x1 = S.gens()292e = kwargs['e']293dec = kwargs['dec']294ex = 0295for i in e:296if i in dec:297ex += 1298return 1 / ((1 - x0 - x1)**ex)299300# Final conjectural graph sum expressing DR_g(1,-1)301302303def DR11_sum_new(g, deg=None, **kwds):304if deg is None:305deg = g306return graph_sum(g, 2, decgraphs=DR11_decgraphs(g, maxdeg=deg), globalfact=divbyaut_new, vertterm=DR11_vterm_new, edgeterm=DR11_eterm_new, deg=deg, **kwds)307308309###310# Example 2 : Chiodo's formula from [JPPZ, Corollary 4]311###312def dicunion(d1, d2):313r"""314Computes the union of dictionaries d1, d2.315316EXAMPLES::317318sage: from admcycles.graph_sum import dicunion319sage: d1 = {1:2, 3:4}; d2 = {1:2, 4:5};320sage: dicunion(d1, d2)321{1: 2, 3: 4, 4: 5}322"""323d3 = copy(d1)324d3.update(d2)325return d3326327328def GammaWlist(g, Avector, k, r):329r"""330Returns a generator of pairs (Gamma, w) of pairs of stable graphs Gamma in genus g331and admissible k-weightings w mod r on Gamma for the weight vector Avector.332333EXAMPLES::334335sage: from admcycles.graph_sum import GammaWlist336sage: list(GammaWlist(0, (4,-1,-2,-3), 1, 2))337[([0] [[1, 2, 3, 4]] [], {1: 4, 2: -1, 3: -2, 4: -3}),338([0, 0] [[1, 2, 5], [3, 4, 6]] [(5, 6)],339{1: 4, 2: -1, 3: -2, 4: -3, 5: 0, 6: 0}),340([0, 0] [[1, 3, 5], [2, 4, 6]] [(5, 6)],341{1: 4, 2: -1, 3: -2, 4: -3, 5: 1, 6: 1}),342([0, 0] [[1, 4, 5], [2, 3, 6]] [(5, 6)],343{1: 4, 2: -1, 3: -2, 4: -3, 5: 0, 6: 0})]344"""345n = len(Avector)346stdic = {i + 1: Avector[i] for i in range(n)}347for e in range(3 * g - 3 + n + 1): # number of edges"""348for gamma in list_strata(g, n, e):349edges = gamma.edges()350for w in itertools.product(*[list(range(r)) for h in edges]):351dic = dicunion(stdic, {edges[i][0]: w[i] for i in range(len(edges))})352dic.update({edges[i][1]: (r - w[i]) % r for i in range(len(edges))})353if all((k * (2 * gv - 2 + len(hev)) - sum(dic[h] for h in hev)) % r == 0 for gv, hev in zip(gamma.genera(), gamma.legs())):354yield (gamma, dic)355356357def Chiodo_GF(g, r):358def GF(gamma, dic):359h1 = gamma.num_edges() - gamma.num_verts() + 1360return r**(2 * g - 1 - h1) / gamma.automorphism_number()361return GF362363364def expclass(x, g=None, n=None):365r"""366Given a tautological class x on Mbar_{g,n} of degree at least 1, returns367the exponential exp(x) = 1 + x + 1/2*x^2 + ... of x.368369EXAMPLES::370371sage: from admcycles.graph_sum import expclass372sage: from admcycles import TautologicalRing373sage: R = TautologicalRing(1, 2)374sage: expclass(R.psi(1))375doctest:...: DeprecationWarning: expclass is deprecated. Please use the exp method of TautologicalClass instead376See https://gitlab.com/modulispaces/admcycles/-/merge_requests/109 for details.377Graph : [1] [[1, 2]] []378Polynomial : 1 + psi_1 + 1/2*psi_1^2379380TESTS::381382sage: from admcycles import TautologicalRing383sage: R = TautologicalRing(0, 3)384sage: expclass(R.zero(), 0, 3)385Graph : [0] [[1, 2, 3]] []386Polynomial : 1387"""388from .superseded import deprecation389deprecation(109, 'expclass is deprecated. Please use the exp method of TautologicalClass instead')390if g is not None:391if n is not None:392R = TautologicalRing(g, n)393else:394R = TautologicalRing(g)395return R(x).exp()396return x.exp()397398399def Chiodo_vterm(k, r):400def vterm(gv, nv, maxdeg, **kwargs):401R = TautologicalRing(gv, nv)402expo = -sum((-1)**(m - 1) * bernoulli_polynomial(k / r, m + 1) / m / (m + 1) * R.kappa(m)403for m in range(1, 3 * gv - 3 + nv + 1))404return R(expo).exp()405return vterm406407408def Chiodo_legterm(r):409def legterm(gv, nv, i, maxdeg, **kwargs):410R = TautologicalRing(gv, nv)411dec = kwargs['dec']412origleg = kwargs['origleg']413ai = dec[origleg]414expo = sum((-1)**(m - 1) * bernoulli_polynomial(ai / r, m + 1) / m /415(m + 1) * R.psi(i)**m for m in range(1, 3 * gv - 3 + nv + 1))416return R(expo).exp()417return legterm418419420def Chiodo_edgeterm(r):421def eterm(maxdeg, **kwargs):422dec = kwargs['dec']423e = kwargs['e']424wh = dec[e[0]]425426S = PowerSeriesRing(QQ, 'x0,x1', default_prec=maxdeg + 4)427x0, x1 = S.gens()428expo = sum((-1)**(m - 1) * bernoulli_polynomial(wh / r, m + 1) / m /429(m + 1) * (x0**m - (-x1)**m) for m in range(1, maxdeg + 3))430return (1 - exp(expo)) / (x0 + x1)431return eterm432433434def Chiodo_alt(g, Avector, k, r):435r"""436Computes the mixed-degree class epsilon_* c(-R^* pi_* L) from [JPPZ17]_, Corollary 4.437438This agrees with the corresponding sum of DR_cycles with chiodo_coeff=True, weighted by appropriate powers of r.439440EXAMPLES::441442sage: from admcycles.graph_sum import Chiodo, Chiodo_alt443sage: g=1; A=(0,0); k=2; r=2;444sage: (Chiodo_alt(g, A, k, r)-Chiodo(g, r, k, A, 1)).simplify()4450446sage: g=1; A=(1,1); k=4; r=3;447sage: (Chiodo_alt(g, A, k, r)-Chiodo(g, r, k, A, 1)).simplify()4480449"""450n = len(Avector)451GWlist = GammaWlist(g, Avector, k, r)452GF = Chiodo_GF(g, r)453vterm = Chiodo_vterm(k, r)454lterm = Chiodo_legterm(r)455eterm = Chiodo_edgeterm(r)456457return graph_sum(g, n, decgraphs=GWlist, globalfact=GF, vertterm=vterm, legterm=lterm, edgeterm=eterm)458459460def Chiodo(g, r, k, A, x):461n = len(A)462return sum((r**(2 * g - 2 * d - 1)) * (x**d) * DR_cycle(g, A, d, k, chiodo_coeff=True, r_coeff=r) for d in range(3 * g - 3 + n + 1))463464465