Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
181 views
unlisted
ubuntu2004
1
# -*- coding: utf-8 -*-
2
r"""
3
Double ramification cycle
4
"""
5
6
import itertools
7
from copy import copy
8
9
from sage.combinat.integer_vector import IntegerVectors
10
from sage.combinat.combinat import bernoulli_polynomial
11
from sage.arith.all import factorial
12
from sage.functions.log import exp
13
from sage.rings.all import QQ
14
from sage.rings.power_series_ring import PowerSeriesRing
15
16
from admcycles.admcycles import list_strata
17
from admcycles.stable_graph import StableGraph
18
from admcycles.double_ramification_cycle import DR_cycle
19
from .tautological_ring import TautologicalRing
20
21
# S.<x0,x1>=PowerSeriesRing(QQ,'x0,x1',default_prec=14)
22
23
24
def graph_sum(g, n, decgraphs=None, globalfact=None, vertterm=None, legterm=None, edgeterm=None, maxdeg=None, deg=None, termsout=False):
25
r"""Returns the (possibly mixed-degree) tautological class obtained by summing over graphs gamma,
26
inserting vertex-, leg- and edgeterms.
27
28
INPUT:
29
30
- ``decgraphs`` -- list or generator; entries of decgraphs are pairs (gamma,dec) of a StableGraph
31
gamma and some additional combinatorial structure dec associated to gamma
32
33
- ``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 1
34
35
- ``vertterm`` -- function; ``vertterm(gv,nv,maxdeg, **kwargs)`` takes arguments local genus gv and number of legs nv
36
and maxdeg gets handed the parameters gamma,dec,v as optional keyworded arguments and gives out a
37
tautological class on Mbar_{gv,nv}; the class is assumed to be of degree at most maxdeg,
38
if deg is given, the class is exactly of degree deg
39
40
- ``legterm`` -- function; ``legterm(gv,nv,i,maxdeg, **kwargs)`` similar to vertterm, except input is
41
gv,nv,i,maxdeg where i is number of marking on Mbar_{gv,nv} associated to leg
42
gamma, dec and origleg (number of leg in outer graph) given as keyworded arguments
43
44
- ``edgeterm`` -- function; edgeterm(maxdeg,**kwargs) takes keyworded arguments gamma,dec,e,maxdeg
45
it gives a generating series s in x0,x1 such that the insertion at edge
46
e=(h0,h1) is given by s(psi_h0, psi_h1)
47
48
- ``termsout`` -- parameter; if termsout=False, return sum of all terms
49
if termsout = 'coarse', return tuple of terms, one for each pair (gamma,dec)
50
if termsout = 'fine', return tuple of terms, one for each pair (gamma,dec) and each distribution
51
of cohomological degrees to vertices and half-edges
52
"""
53
if maxdeg is None:
54
maxdeg = 3 * g - 3 + n if deg is None else deg
55
if decgraphs is None:
56
decgraphs = [(gr, None) for ednum in range(3 * g - 3 + n + 1)
57
for gr in list_strata(g, n, ednum)]
58
if globalfact is None:
59
def globalfact(a, b):
60
return 1
61
if vertterm is None:
62
def vertterm(gv, nv, maxdeg, **kwargs):
63
return TautologicalRing(gv, nv).fundamental_class()
64
if legterm is None:
65
def legterm(gv, nv, i, maxdeg, **kwargs):
66
return TautologicalRing(gv, nv).fundamental_class()
67
if edgeterm is None:
68
def edgeterm(maxdeg, **kwargs):
69
S = PowerSeriesRing(QQ, 'x0,x1', default_prec=maxdeg + 1)
70
return S.one()
71
72
termlist = []
73
74
for (gamma, dec) in decgraphs:
75
restdeg = maxdeg - len(gamma.edges())
76
if restdeg < 0:
77
continue
78
79
gammadectermlist = []
80
81
numvert = gamma.numvert()
82
gnvect = [(gamma.genera(i), len(gamma.legs(i))) for i in range(numvert)]
83
dimvect = [3 * g - 3 + n for (g, n) in gnvect]
84
markings = gamma.list_markings()
85
vertdic = {l: v for v in range(numvert) for l in gamma.legs(v)}
86
indexdic = {l: j + 1 for v in range(numvert) for j, l in enumerate(gamma.legs(v))}
87
# ex_dimvect=[dimvect[vertdic[l]] for l in halfedges] # list of dimensions of spaces adjacent to half-edges
88
89
# Pre-compute all vertex-, leg- and edgeterms
90
vterms = {v: vertterm(gnvect[v][0], gnvect[v][1], restdeg, gamma=gamma, dec=dec, v=v) for v in range(numvert)}
91
lterms = {i: legterm(gnvect[vertdic[i]][0], gnvect[vertdic[i]][1], indexdic[i],
92
restdeg, gamma=gamma, dec=dec, origleg=i) for i in markings}
93
eterms = {e: edgeterm(restdeg, gamma=gamma, dec=dec, e=e) for e in gamma.edges()}
94
varlist = {(h0, h1): eterms[h0, h1].parent().gens() for h0, h1 in gamma.edges()}
95
eterms = {e: eterms[e].coefficients() for e in eterms}
96
varx = {h0: varlist[h0, h1][0] for h0, h1 in gamma.edges()}
97
varx.update({h1: varlist[h0, h1][1] for h0, h1 in gamma.edges()})
98
99
if deg is None:
100
rdlis = range(restdeg + 1) # terms of all degrees up to restdeg must be computed
101
else:
102
rdlis = [deg - len(gamma.edges())]
103
for rdeg in rdlis:
104
# distribute the remaining degree rdeg to vertices
105
for degdist in IntegerVectors(rdeg, numvert, outer=dimvect):
106
# now for each vertex, split degree to vertex- and leg/half-edge terms
107
vertchoices = [IntegerVectors(degdist[v], len(gamma.legs(v)) + 1) for v in range(numvert)]
108
for choice in itertools.product(*vertchoices):
109
vdims = []
110
ldims = {}
111
for v in range(numvert):
112
vdims.append(choice[v][0])
113
ldims.update({l: choice[v][i + 1] for i, l in enumerate(gamma.legs(v))})
114
115
effvterms = [vterms[v].degree_part(vdims[v]) for v in vterms]
116
efflterms = {i: lterms[i].degree_part(ldims[i]) for i in lterms}
117
for i in efflterms:
118
effvterms[vertdic[i]] *= efflterms[i] # multiply contributions from legs to vertexterms
119
for h0, h1 in gamma.edges():
120
# TODO: optimization potential here by multiplying kppolys directly
121
gv0, nv0 = gnvect[vertdic[h0]]
122
R0 = TautologicalRing(gv0, nv0)
123
effvterms[vertdic[h0]] *= eterms[(h0, h1)].get(varx[h0]**ldims[h0]
124
* varx[h1]**ldims[h1], 0) * R0.psi(indexdic[h0])**ldims[h0]
125
gv1, nv1 = gnvect[vertdic[h1]]
126
R1 = TautologicalRing(gv1, nv1)
127
effvterms[vertdic[h1]] *= R1.psi(indexdic[h1])**ldims[h1]
128
for t in effvterms:
129
t.simplify()
130
# print(gamma)
131
# print(rdeg)
132
# print(degdist)
133
# print(choice)
134
# print(effvterms)
135
# print(eterms)
136
# print(indexdic)
137
# print('\n')
138
tempres = gamma.boundary_pushforward(effvterms)
139
tempres.simplify()
140
if not tempres.is_empty():
141
tempres *= globalfact(gamma, dec)
142
gammadectermlist.append(tempres)
143
# print(termlist)
144
if termsout == 'coarse':
145
termlist.append(sum(gammadectermlist))
146
else:
147
termlist += gammadectermlist
148
if termsout:
149
return termlist
150
else:
151
return sum(termlist)
152
153
############
154
#
155
# Useful functions and examples
156
#
157
############
158
159
###
160
# Example 1 : Conjectural graph sum for DR_g(1,-1)
161
###
162
163
# Generating functions for graphs
164
165
166
def DR11_tree_test(gr):
167
return gr.vertex(1) == gr.vertex(2)
168
169
170
def DR11_trees(g, maxdeg):
171
return [gr for n in range(1, g + 1) for e in range(min(n, maxdeg - n + 1))
172
for gr in list_strata(0, 2 + n, e) if DR11_tree_test(gr)]
173
174
175
def DR11_graphs(g, maxdeg=None):
176
if maxdeg is None:
177
maxdeg = 3 * g - 3 + 2
178
result = []
179
for gr in DR11_trees(g, maxdeg):
180
n = len(gr.list_markings()) - 2
181
maxleg = max([max(j + [0]) for j in gr.legs])
182
grlist = []
183
for gdist in IntegerVectors(g, n, min_part=1):
184
genera = copy(gr.genera) + list(gdist)
185
legs = copy(gr.legs) + [[j] for j in range(maxleg + 1, maxleg + n + 1)]
186
edges = copy(gr.edges) + [(j - maxleg + 2, j) for j in range(maxleg + 1, maxleg + n + 1)]
187
grlist.append(StableGraph(genera, legs, edges))
188
result += [(gam, None) for gam in grlist]
189
removedups(result, lambda a, b: a[0].is_isomorphic(b[0]))
190
return result
191
192
193
def removedups(li, comp=None):
194
"""
195
Remove duplicates in a list ``li`` according to a comparison function.
196
197
This works inplace and modifies ``li``.
198
199
EXAMPLES::
200
201
sage: from admcycles.graph_sum import removedups
202
sage: L = [4,6,3,2,4,99,1,3,2]
203
sage: removedups(L)
204
sage: L
205
[6, 4, 99, 1, 3, 2]
206
"""
207
if comp is None:
208
def comp(a, b):
209
return a == b
210
n = len(li)
211
currn = len(li)
212
for i in range(n, -1, -1):
213
if any(comp(li[i], li[j]) for j in range(i + 1, currn)):
214
li.pop(i)
215
currn -= 1
216
217
# Global factor = 1/|Aut(Gamma)|
218
219
220
def divbyaut(gamma, dec):
221
return QQ(1) / gamma.automorphism_number()
222
223
# Vertex- and edgeterms for DR11
224
225
226
def DR11_vterm(gv, nv, maxdeg, **kwargs):
227
R = TautologicalRing(gv, nv)
228
if gv == 0:
229
gamma = kwargs['gamma']
230
v = kwargs['v']
231
f = R.fundamental_class()
232
if 1 not in gamma.legs[v]:
233
# we are in genus zero vertex not equal to base
234
return -nv * f
235
else:
236
# we are at the base vertex
237
return f
238
else:
239
return sum([(-1)**j * R.lambdaclass(j) for j in range(maxdeg + 1)])
240
241
242
def DR11_eterm(maxdeg, **kwargs): # smarter: edterm(maxdeg=None,gamma=None,dec=None,e=None, *args, *kwds)
243
S = PowerSeriesRing(QQ, 'x0,x1', default_prec=maxdeg + 1)
244
x0, x1 = S.gens()
245
return 1 / (1 - x0 - x1 + x0 * x1)
246
247
# Final conjectural graph sum expressing DR_g(1,-1)
248
249
250
def DR11_sum(g, deg=None, **kwds):
251
if deg is None:
252
deg = g
253
return graph_sum(g, 2, decgraphs=DR11_graphs(g, maxdeg=deg), globalfact=divbyaut, vertterm=DR11_vterm, edgeterm=DR11_eterm, deg=deg, **kwds)
254
255
256
def DR11_decgraphs(g, maxdeg=None):
257
"""
258
Decorate the graphs of DR11_graphs(g) with a choice of half-edge
259
at each non-root vertex.
260
"""
261
for gr0, _ in DR11_graphs(g, maxdeg):
262
L = [v for v in gr0.legs if 1 not in v]
263
for choice in itertools.product(*L):
264
yield (gr0, choice)
265
266
267
def divbyaut_new(gamma, dec):
268
zeroverts = gamma.genera.count(0)
269
return factorial(zeroverts - 1) / gamma.automorphism_number()
270
271
# Vertex- and edgeterms for DR11
272
273
274
def DR11_vterm_new(gv, nv, maxdeg, **kwargs):
275
R = TautologicalRing(gv, nv)
276
if gv == 0:
277
gamma = kwargs['gamma']
278
v = kwargs['v']
279
f = R.fundamental_class()
280
if 1 not in gamma.legs[v]:
281
# we are in genus zero vertex not equal to base
282
return -1 * f
283
else:
284
# we are at the base vertex
285
return f
286
else:
287
return sum([(-1)**j * R.lambdaclass(j) for j in range(maxdeg + 1)])
288
289
290
def DR11_eterm_new(maxdeg, **kwargs): # smarter: edterm(maxdeg=None,gamma=None,dec=None,e=None, *args, *kwds)
291
S = PowerSeriesRing(QQ, 'x0,x1', default_prec=maxdeg + 1)
292
x0, x1 = S.gens()
293
e = kwargs['e']
294
dec = kwargs['dec']
295
ex = 0
296
for i in e:
297
if i in dec:
298
ex += 1
299
return 1 / ((1 - x0 - x1)**ex)
300
301
# Final conjectural graph sum expressing DR_g(1,-1)
302
303
304
def DR11_sum_new(g, deg=None, **kwds):
305
if deg is None:
306
deg = g
307
return graph_sum(g, 2, decgraphs=DR11_decgraphs(g, maxdeg=deg), globalfact=divbyaut_new, vertterm=DR11_vterm_new, edgeterm=DR11_eterm_new, deg=deg, **kwds)
308
309
310
###
311
# Example 2 : Chiodo's formula from [JPPZ, Corollary 4]
312
###
313
def dicunion(d1, d2):
314
r"""
315
Computes the union of dictionaries d1, d2.
316
317
EXAMPLES::
318
319
sage: from admcycles.graph_sum import dicunion
320
sage: d1 = {1:2, 3:4}; d2 = {1:2, 4:5};
321
sage: dicunion(d1, d2)
322
{1: 2, 3: 4, 4: 5}
323
"""
324
d3 = copy(d1)
325
d3.update(d2)
326
return d3
327
328
329
def GammaWlist(g, Avector, k, r):
330
r"""
331
Returns a generator of pairs (Gamma, w) of pairs of stable graphs Gamma in genus g
332
and admissible k-weightings w mod r on Gamma for the weight vector Avector.
333
334
EXAMPLES::
335
336
sage: from admcycles.graph_sum import GammaWlist
337
sage: list(GammaWlist(0, (4,-1,-2,-3), 1, 2))
338
[([0] [[1, 2, 3, 4]] [], {1: 4, 2: -1, 3: -2, 4: -3}),
339
([0, 0] [[1, 2, 5], [3, 4, 6]] [(5, 6)],
340
{1: 4, 2: -1, 3: -2, 4: -3, 5: 0, 6: 0}),
341
([0, 0] [[1, 3, 5], [2, 4, 6]] [(5, 6)],
342
{1: 4, 2: -1, 3: -2, 4: -3, 5: 1, 6: 1}),
343
([0, 0] [[1, 4, 5], [2, 3, 6]] [(5, 6)],
344
{1: 4, 2: -1, 3: -2, 4: -3, 5: 0, 6: 0})]
345
"""
346
n = len(Avector)
347
stdic = {i + 1: Avector[i] for i in range(n)}
348
for e in range(3 * g - 3 + n + 1): # number of edges"""
349
for gamma in list_strata(g, n, e):
350
edges = gamma.edges()
351
for w in itertools.product(*[list(range(r)) for h in edges]):
352
dic = dicunion(stdic, {edges[i][0]: w[i] for i in range(len(edges))})
353
dic.update({edges[i][1]: (r - w[i]) % r for i in range(len(edges))})
354
if 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())):
355
yield (gamma, dic)
356
357
358
def Chiodo_GF(g, r):
359
def GF(gamma, dic):
360
h1 = gamma.num_edges() - gamma.num_verts() + 1
361
return r**(2 * g - 1 - h1) / gamma.automorphism_number()
362
return GF
363
364
365
def expclass(x, g=None, n=None):
366
r"""
367
Given a tautological class x on Mbar_{g,n} of degree at least 1, returns
368
the exponential exp(x) = 1 + x + 1/2*x^2 + ... of x.
369
370
EXAMPLES::
371
372
sage: from admcycles.graph_sum import expclass
373
sage: from admcycles import TautologicalRing
374
sage: R = TautologicalRing(1, 2)
375
sage: expclass(R.psi(1))
376
doctest:...: DeprecationWarning: expclass is deprecated. Please use the exp method of TautologicalClass instead
377
See https://gitlab.com/modulispaces/admcycles/-/merge_requests/109 for details.
378
Graph : [1] [[1, 2]] []
379
Polynomial : 1 + psi_1 + 1/2*psi_1^2
380
381
TESTS::
382
383
sage: from admcycles import TautologicalRing
384
sage: R = TautologicalRing(0, 3)
385
sage: expclass(R.zero(), 0, 3)
386
Graph : [0] [[1, 2, 3]] []
387
Polynomial : 1
388
"""
389
from .superseded import deprecation
390
deprecation(109, 'expclass is deprecated. Please use the exp method of TautologicalClass instead')
391
if g is not None:
392
if n is not None:
393
R = TautologicalRing(g, n)
394
else:
395
R = TautologicalRing(g)
396
return R(x).exp()
397
return x.exp()
398
399
400
def Chiodo_vterm(k, r):
401
def vterm(gv, nv, maxdeg, **kwargs):
402
R = TautologicalRing(gv, nv)
403
expo = -sum((-1)**(m - 1) * bernoulli_polynomial(k / r, m + 1) / m / (m + 1) * R.kappa(m)
404
for m in range(1, 3 * gv - 3 + nv + 1))
405
return R(expo).exp()
406
return vterm
407
408
409
def Chiodo_legterm(r):
410
def legterm(gv, nv, i, maxdeg, **kwargs):
411
R = TautologicalRing(gv, nv)
412
dec = kwargs['dec']
413
origleg = kwargs['origleg']
414
ai = dec[origleg]
415
expo = sum((-1)**(m - 1) * bernoulli_polynomial(ai / r, m + 1) / m /
416
(m + 1) * R.psi(i)**m for m in range(1, 3 * gv - 3 + nv + 1))
417
return R(expo).exp()
418
return legterm
419
420
421
def Chiodo_edgeterm(r):
422
def eterm(maxdeg, **kwargs):
423
dec = kwargs['dec']
424
e = kwargs['e']
425
wh = dec[e[0]]
426
427
S = PowerSeriesRing(QQ, 'x0,x1', default_prec=maxdeg + 4)
428
x0, x1 = S.gens()
429
expo = sum((-1)**(m - 1) * bernoulli_polynomial(wh / r, m + 1) / m /
430
(m + 1) * (x0**m - (-x1)**m) for m in range(1, maxdeg + 3))
431
return (1 - exp(expo)) / (x0 + x1)
432
return eterm
433
434
435
def Chiodo_alt(g, Avector, k, r):
436
r"""
437
Computes the mixed-degree class epsilon_* c(-R^* pi_* L) from [JPPZ17]_, Corollary 4.
438
439
This agrees with the corresponding sum of DR_cycles with chiodo_coeff=True, weighted by appropriate powers of r.
440
441
EXAMPLES::
442
443
sage: from admcycles.graph_sum import Chiodo, Chiodo_alt
444
sage: g=1; A=(0,0); k=2; r=2;
445
sage: (Chiodo_alt(g, A, k, r)-Chiodo(g, r, k, A, 1)).simplify()
446
0
447
sage: g=1; A=(1,1); k=4; r=3;
448
sage: (Chiodo_alt(g, A, k, r)-Chiodo(g, r, k, A, 1)).simplify()
449
0
450
"""
451
n = len(Avector)
452
GWlist = GammaWlist(g, Avector, k, r)
453
GF = Chiodo_GF(g, r)
454
vterm = Chiodo_vterm(k, r)
455
lterm = Chiodo_legterm(r)
456
eterm = Chiodo_edgeterm(r)
457
458
return graph_sum(g, n, decgraphs=GWlist, globalfact=GF, vertterm=vterm, legterm=lterm, edgeterm=eterm)
459
460
461
def Chiodo(g, r, k, A, x):
462
n = len(A)
463
return 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))
464
465