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
The main function to use to compute DR cycles are the following:
6
7
- :func:`DR_compute`
8
- :func:`DR_sparse`: same as ``DR_compute`` except that it returns the answer as a
9
sparse vector
10
- :func:`DR_reduced`: same as ``DR_compute`` except that it only requires two
11
arguments (g and dvector) and simplifies the answer using the 3-spin
12
tautological relations.
13
14
EXAMPLES::
15
16
sage: from admcycles.DR import DR_compute, DR_sparse, DR_reduced
17
18
To compute the genus 1 DR cycle with weight vector (2,-2)::
19
20
sage: DR_compute(1,1,2,(2,-2))
21
(0, 2, 2, 0, -1/24)
22
23
In the example above, the five classes described on R^1(Mbar_{1,2}) are:
24
kappa_1, psi_1, psi_2, delta_{12}, delta_{irr}. (Here by delta_{irr} we mean
25
the pushforward of 1 under the gluing map Mbar_{0,4} -> Mbar_{1,2}, so twice
26
the class of the physical locus.)
27
28
Sparse version::
29
30
sage: DR_sparse(1,1,2,(2,-2))
31
[[1, 2], [2, 2], [4, -1/24]]
32
33
Reduced version::
34
35
sage: DR_reduced(1,(2,-2))
36
(0, 0, 0, 4, 1/8)
37
"""
38
39
from copy import copy
40
import itertools
41
42
from sage.rings.integer_ring import ZZ
43
from sage.rings.rational_field import QQ
44
from sage.functions.other import ceil
45
from sage.modules.free_module_element import vector
46
47
from .moduli import MODULI_ST
48
from .graph import R, num_strata, single_stratum, autom_count
49
from .utils import interpolate
50
from .relations import FZ_rels
51
52
53
def find_nonsep_pairs(num, g, r, markings=(), moduli_type=MODULI_ST):
54
G = single_stratum(num, g, r, markings, moduli_type)
55
nr = G.M.nrows()
56
nc = G.M.ncols()
57
answer = []
58
for i1 in range(1, nr):
59
for i2 in range(1, i1):
60
found_edge = False
61
for j in range(1, nc):
62
if G.M[i1, j] != 0 and G.M[i2, j] != 0:
63
found_edge = True
64
break
65
if not found_edge:
66
continue
67
S = set([i1])
68
did_something = True
69
while did_something:
70
did_something = False
71
for i3 in tuple(S):
72
for j in range(1, nc):
73
if G.M[i3, j] != 0:
74
for i4 in range(1, nr):
75
if G.M[i4, j] != 0 and i4 not in S and (i3 != i1 or i4 != i2):
76
S.add(i4)
77
did_something = True
78
if i2 in S:
79
answer.append([i2, i1])
80
return answer
81
82
# assumes r <= 4 for now
83
84
85
def DR_coeff_is_known(num, g, r, markings=(), moduli_type=MODULI_ST):
86
return r <= 4
87
88
# old stuff
89
G = single_stratum(num, g, r, markings, moduli_type)
90
nr = G.M.nrows()
91
nc = G.M.ncols()
92
nonsep_pairs = find_nonsep_pairs(num, g, r, markings, moduli_type)
93
if len(nonsep_pairs) > 3:
94
return False
95
if len(nonsep_pairs) == 3:
96
i1 = nonsep_pairs[0][0]
97
i2 = nonsep_pairs[0][1]
98
i3 = nonsep_pairs[2][1]
99
jlist = []
100
for j in range(1, nc):
101
if len([1 for i in [i1, i2, i3] if G.M[i, j] != 0]) == 2:
102
jlist.append(j)
103
if len(jlist) > 3:
104
return False
105
for j in jlist:
106
for i in [i1, i2, i3]:
107
if G.M[i, j][1] != 0:
108
return False
109
110
return True
111
112
# this stuff is old, keeping it around for now just in case
113
for j in range(1, nc):
114
ilist = []
115
for i in range(1, nr):
116
if G.M[i, j] != 0:
117
ilist.append(i)
118
if len(ilist) == 1:
119
continue
120
ii1 = ilist[0]
121
ii2 = ilist[1]
122
jlist = []
123
for jj in range(1, nc):
124
if G.M[ii1, jj] != 0 and G.M[ii2, jj] != 0:
125
jlist.append(jj)
126
if len(jlist) == 1 or jlist[0] != j:
127
continue
128
count = len(jlist)
129
for ii in [ii1, ii2]:
130
for jj in jlist:
131
count += G.M[ii, jj][1]
132
if count > 3:
133
return False
134
return True
135
136
# next function doesn't work on arbitrary graphs yet, see above function
137
138
139
def DR_coeff(num, g, r, n=0, dvector=(), moduli_type=MODULI_ST):
140
markings = tuple(range(1, n + 1))
141
G = single_stratum(num, g, r, markings, moduli_type)
142
nr = G.M.nrows()
143
nc = G.M.ncols()
144
answer = QQ((1, autom_count(num, g, r, markings, moduli_type)))
145
146
def balance(ilist):
147
bal = [0 for i1 in ilist]
148
for j1 in range(1, nc):
149
if G.M[0, j1] != 0:
150
for i3 in range(1, nr):
151
if G.M[i3, j1] != 0:
152
S = set([i3])
153
did_something = True
154
while did_something:
155
did_something = False
156
for i4 in tuple(S):
157
for j2 in range(1, nc):
158
if G.M[i4, j2] != 0:
159
for i5 in range(1, nr):
160
if G.M[i5, j2] != 0 and i5 not in S and (i4 not in ilist or i5 not in ilist):
161
S.add(i5)
162
did_something = True
163
for kk, ikk in enumerate(ilist):
164
if ikk in S:
165
bal[kk] += dvector[G.M[0, j1][0] - 1]
166
return bal
167
168
def poly4(a, b, c):
169
return (ZZ.one() / 420) * (-15 * (a**8 + b**8 + c**8) + 40 * (a**7 * b + a**7 * c + b**7 * a + b**7 * c + c**7 * a + c**7 * b) - 28 * (a**6 * b**2 + a**6 * c**2 + b**6 * a**2 + b**6 * c**2 + c**6 * a**2 + c**6 * b**2) - 112 * (a**6 * b * c + b**6 * a * c + c**6 * a * b) + 84 * (a**5 * b**2 * c + a**5 * c**2 * b + b**5 * a**2 * c + b**5 * c**2 * a + c**5 * a**2 * b + c**5 * b**2 * a) - 70 * (a**4 * b**2 * c**2 + b**4 * a**2 * c**2 + c**4 * a**2 * b**2))
170
171
nonsep_pairs = find_nonsep_pairs(num, g, r, markings, moduli_type)
172
if len(nonsep_pairs) == 4:
173
cycle = [nonsep_pairs[0][0], nonsep_pairs[0]
174
[1], 0, 0]
175
for i in range(1, 4):
176
for j in range(2):
177
if nonsep_pairs[i][j] == cycle[0]:
178
cycle[3] = nonsep_pairs[i][1 - j]
179
if nonsep_pairs[i][j] == cycle[1]:
180
cycle[2] = nonsep_pairs[i][1 - j]
181
dbal = balance(cycle)
182
a = dbal[0]
183
b = dbal[0] + dbal[1]
184
c = dbal[0] + dbal[1] + dbal[2]
185
answer *= poly4(a, b, c)
186
elif len(nonsep_pairs) == 3:
187
i1 = nonsep_pairs[0][0]
188
i2 = nonsep_pairs[0][1]
189
i3 = nonsep_pairs[2][1]
190
cycle = [i1, i2, i3]
191
cycle4 = cycle + [cycle[0]]
192
dbal = balance(cycle)
193
dbal4 = dbal + [dbal[0]]
194
done = False
195
for k in range(3):
196
jlist = [j for j in range(1, nc) if (
197
G.M[cycle4[k], j] != 0 and G.M[cycle4[k + 1], j] != 0)]
198
if len(jlist) == 2:
199
answer *= -ZZ.one() / 6 * \
200
poly4(0, dbal4[k], -dbal4[k + 1])
201
done = True
202
elif len(jlist) == 1:
203
if G.M[cycle4[k], jlist[0]][1] + G.M[cycle4[k + 1], jlist[0]][1] > 0:
204
answer *= -ZZ.one() / 2 * \
205
poly4(0, dbal4[k], -dbal4[k + 1])
206
done = True
207
if not done:
208
answer *= (dbal[0]**6 + dbal[1]**6 + dbal[2]**6 -
209
10 * dbal[0]**2 * dbal[1]**2 * dbal[2]**2) / ZZ(30)
210
211
for j in range(1, nc):
212
ilist = []
213
for i in range(1, nr):
214
if G.M[i, j] != 0:
215
ilist.append(i)
216
if len(ilist) == 1:
217
x = G.M[ilist[0], j][1]
218
answer /= ZZ(x).factorial()
219
answer *= dvector[G.M[0, j][0] -
220
1]**(ZZ(2) * x)
221
continue
222
if ilist in nonsep_pairs:
223
continue
224
ii1 = ilist[0]
225
ii2 = ilist[1]
226
jlist = []
227
for jj in range(1, nc):
228
if G.M[ii1, jj] != 0 and G.M[ii2, jj] != 0:
229
jlist.append(jj)
230
if len(jlist) == 1:
231
x1 = G.M[ii1, j][1]
232
x2 = G.M[ii2, j][1]
233
answer *= -1
234
answer /= ZZ(x1).factoial()
235
answer /= ZZ(x2).factorial()
236
answer /= x1 + x2 + 1
237
answer *= balance([ii1, ii2])[0]**(ZZ(2) *
238
(x1 + x2 + 1))
239
continue
240
elif len(jlist) == 2:
241
if jlist[0] != j:
242
continue
243
xvec = [G.M[ii1, jlist[0]][1], G.M[ii1, jlist[1]][1],
244
G.M[ii2, jlist[0]][1], G.M[ii2, jlist[1]][1]]
245
x = sum(xvec)
246
if x == 0:
247
answer *= -ZZ.one() / 6
248
answer *= balance([ii1, ii2])[0]**4
249
elif x == 1:
250
answer *= -ZZ.one() / 30
251
answer *= balance([ii1, ii2])[0]**6
252
elif x == 2:
253
if xvec in [[2, 0, 0, 0], [0, 2, 0, 0], [0, 0, 2, 0], [0, 0, 0, 2]]:
254
answer *= -ZZ.one() / 168
255
elif xvec in [[1, 1, 0, 0], [0, 0, 1, 1], [1, 0, 0, 1], [0, 1, 1, 0]]:
256
answer *= -ZZ.one() / 280
257
elif xvec in [[1, 0, 1, 0], [0, 1, 0, 1]]:
258
answer *= -ZZ.one() / 84
259
answer *= balance([ii1, ii2])[0]**8
260
continue
261
elif len(jlist) == 3:
262
if jlist[0] != j:
263
continue
264
xvec = [G.M[ii1, jlist[0]][1], G.M[ii1, jlist[1]][1], G.M[ii1, jlist[2]]
265
[1], G.M[ii2, jlist[0]][1], G.M[ii2, jlist[1]][1], G.M[ii2, jlist[2]][1]]
266
x = sum(xvec)
267
if x == 0:
268
answer *= -ZZ.one() / 90
269
answer *= balance([ii1, ii2])[0]**6
270
elif x == 1:
271
answer *= -ZZ.one() / 840
272
answer *= balance([ii1, ii2])[0]**8
273
continue
274
elif len(jlist) == 4:
275
if jlist[0] != j:
276
continue
277
answer *= -ZZ.one() / 2520
278
answer *= balance([ii1, ii2])[0]**8
279
return answer
280
281
282
def veto_for_DR(num, g, r, markings=(), moduli_type=MODULI_ST):
283
G = single_stratum(num, g, r, markings, moduli_type)
284
nr = G.M.nrows()
285
nc = G.M.ncols()
286
marked_vertices = []
287
for i in range(1, nr):
288
if G.M[i, 0] != R(G.M[i, 0][0]):
289
return True
290
for j in range(1, nc):
291
if G.M[i, j][0] == 2:
292
return True
293
if G.M[0, j] != 0 and G.M[i, j] != 0:
294
marked_vertices.append(i)
295
for ii in range(1, nr):
296
S = set(marked_vertices)
297
S.add(ii)
298
did_something = True
299
while did_something:
300
did_something = False
301
for i in tuple(S):
302
if i == ii:
303
continue
304
for j in range(1, nc):
305
if G.M[i, j] != 0:
306
for i2 in range(1, nr):
307
if G.M[i2, j] != 0 and i2 not in S:
308
S.add(i2)
309
did_something = True
310
if len(S) < nr - 1:
311
return True
312
return False
313
314
315
def DR_uncomputed(g, r, markings, moduli_type=MODULI_ST):
316
# markings = tuple(range(1,n+1))
317
for i in range(num_strata(g, r, markings, moduli_type)):
318
if not veto_for_DR(i, g, r, markings, moduli_type) and not DR_coeff_is_known(i, g, r, markings, moduli_type):
319
print(i)
320
print(single_stratum(i, g, r, markings, moduli_type).M)
321
print("---------------------")
322
323
324
def reduce_with_rels(B, vec):
325
vec2 = copy(vec)
326
for row in B:
327
for i in range(len(row)):
328
if row[i] != 0:
329
if vec2[i] != 0:
330
vec2 -= QQ((vec2[i], row[i])) * row
331
break
332
return vec2
333
334
335
def DR_coeff_setup(num, g, r, n=0, dvector=(), kval=0, moduli_type=MODULI_ST):
336
markings = tuple(range(1, n + 1))
337
G = single_stratum(num, g, r, markings, moduli_type)
338
nr = G.M.nrows()
339
nc = G.M.ncols()
340
edge_list = []
341
exp_list = []
342
scalar_factor = ZZ.one() / \
343
autom_count(num, g, r, markings, moduli_type)
344
for i in range(1, nr):
345
for j in range(1, G.M[i, 0].degree() + 1):
346
scalar_factor /= ZZ(G.M[i, 0][j]).factorial()
347
scalar_factor /= ZZ(j).factorial()**G.M[i, 0][j]
348
scalar_factor *= (-1)**G.M[i, 0][j]
349
scalar_factor *= (kval**2)**(j * G.M[i, 0][j])
350
given_weights = [-kval * (2 * G.M[i + 1, 0][0] - 2 + sum([G.M[i + 1][j][0]
351
for j in range(1, nc)])) for i in range(nr - 1)]
352
for j in range(1, nc):
353
ilist = [i for i in range(1, nr)
354
if G.M[i, j] != 0]
355
if G.M[0, j] == 0:
356
if len(ilist) == 1:
357
i1 = ilist[0]
358
i2 = ilist[0]
359
exp1 = G.M[i1, j][1]
360
exp2 = G.M[i1, j][2]
361
else:
362
i1 = ilist[0]
363
i2 = ilist[1]
364
exp1 = G.M[i1, j][1]
365
exp2 = G.M[i2, j][1]
366
edge_list.append([i1 - 1, i2 - 1])
367
exp_list.append(exp1 + exp2 + 1)
368
scalar_factor /= - \
369
ZZ(exp1).factorial() * ZZ(exp2).factorial() * (exp1 + exp2 + 1)
370
else:
371
exp1 = G.M[ilist[0], j][1]
372
scalar_factor *= dvector[G.M[0, j][0] -
373
1]**(ZZ(2) * exp1) / ZZ(exp1).factorial()
374
given_weights[ilist[0] -
375
1] += dvector[G.M[0, j][0] - 1]
376
return edge_list, exp_list, given_weights, scalar_factor
377
378
379
def DR_coeff_new(num, g, r, n=0, dvector=(), kval=0, moduli_type=MODULI_ST):
380
markings = tuple(range(1, n + 1))
381
G = single_stratum(num, g, r, markings, moduli_type)
382
nr = G.M.nrows()
383
nc = G.M.ncols()
384
edge_list, exp_list, given_weights, scalar_factor = DR_coeff_setup(
385
num, g, r, n, dvector, kval, moduli_type)
386
m0 = ceil(sum([abs(i) for i in dvector]) / ZZ(2)) + g * abs(kval)
387
h0 = nc - nr - n + 1
388
deg = 2 * sum(exp_list)
389
mrange = range(m0 + 1, m0 + deg + 2)
390
mvalues = []
391
for m in mrange:
392
total = 0
393
for weight_data in itertools.product(*[list(range(m)) for i in range(len(edge_list))]):
394
vertex_weights = copy(given_weights)
395
for i in range(len(edge_list)):
396
vertex_weights[edge_list[i][0]] += weight_data[i]
397
vertex_weights[edge_list[i][1]] -= weight_data[i]
398
if any(i % m for i in vertex_weights):
399
continue
400
term = 1
401
for i in range(len(edge_list)):
402
term *= weight_data[i]**(ZZ(2) * exp_list[i])
403
total += term
404
mvalues.append(QQ((total, m**h0)))
405
mpoly = interpolate(mrange, mvalues)
406
return mpoly.subs(x=0) * scalar_factor
407
408
409
def DR_coeff_setup_m(m, num, g, r, n=0, dvector=(), kval=0, moduli_type=MODULI_ST):
410
markings = tuple(range(1, n + 1))
411
G = single_stratum(num, g, r, markings, moduli_type)
412
nr = G.M.nrows()
413
nc = G.M.ncols()
414
edge_list = []
415
exp_list = []
416
scalar_factor = ZZ.one() / \
417
autom_count(num, g, r, markings, moduli_type)
418
for i in range(1, nr):
419
for j in range(1, G.M[i, 0].degree() + 1):
420
scalar_factor /= ZZ(G.M[i, 0][j]).factorial()
421
scalar_factor /= ZZ(j).factorial()**G.M[i, 0][j]
422
scalar_factor *= (-1)**G.M[i, 0][j]
423
scalar_factor *= (kval**2 - kval * m + m**2 /
424
ZZ(6))**(j * G.M[i, 0][j])
425
given_weights = [-kval * (ZZ(2) * G.M[i + 1, 0][0] - ZZ(2) + sum(
426
[G.M[i + 1][j][0] for j in range(1, nc)])) for i in range(nr - 1)]
427
for j in range(1, nc):
428
ilist = [i for i in range(1, nr)
429
if G.M[i, j] != 0]
430
if G.M[0, j] == 0:
431
if len(ilist) == 1:
432
i1 = ilist[0]
433
i2 = ilist[0]
434
exp1 = G.M[i1, j][1]
435
exp2 = G.M[i1, j][2]
436
else:
437
i1 = ilist[0]
438
i2 = ilist[1]
439
exp1 = G.M[i1, j][1]
440
exp2 = G.M[i2, j][1]
441
edge_list.append([i1 - 1, i2 - 1])
442
exp_list.append(exp1 + exp2 + 1)
443
scalar_factor /= - \
444
ZZ(exp1).factorial() * ZZ(exp2).factorial() * (exp1 + exp2 + 1)
445
else:
446
exp1 = G.M[ilist[0], j][1]
447
dval = dvector[G.M[0, j][0] - 1]
448
if dval < 0:
449
dval = dval + m
450
scalar_factor *= (dval**2 - dval * m + m**2 /
451
ZZ(6))**(exp1) / ZZ(exp1).factorial()
452
given_weights[ilist[0] -
453
1] += dvector[G.M[0, j][0] - 1]
454
return edge_list, exp_list, given_weights, scalar_factor
455
456
457
def DR_coeff_m(m, num, g, r, n=0, dvector=(), kval=0, moduli_type=MODULI_ST):
458
markings = tuple(range(1, n + 1))
459
G = single_stratum(num, g, r, markings, moduli_type)
460
nr = G.M.nrows()
461
nc = G.M.ncols()
462
edge_list, exp_list, given_weights, scalar_factor = DR_coeff_setup_m(
463
m, num, g, r, n, dvector, kval, moduli_type)
464
h0 = nc - nr - n + 1
465
total = ZZ.zero()
466
for weight_data in itertools.product(*[list(range(m)) for i in range(len(edge_list))]):
467
vertex_weights = copy(given_weights)
468
for i in range(len(edge_list)):
469
vertex_weights[edge_list[i][0]] += weight_data[i]
470
vertex_weights[edge_list[i][1]] -= weight_data[i]
471
if any(i % m for i in vertex_weights):
472
continue
473
term = 1
474
for i in range(len(edge_list)):
475
dval = weight_data[i]
476
term *= (dval**2 - dval * m + m**2 / ZZ(6))**exp_list[i]
477
total += term
478
total /= m**h0
479
total *= scalar_factor
480
return total
481
482
483
def DR_compute(g, r, n=0, dvector=(), kval=0, moduli_type=MODULI_ST):
484
r"""
485
INPUT:
486
487
g : integer
488
the genus
489
r : integer
490
cohomological degree (set to g for the actual DR cycle, should give
491
tautological relations for r > g)
492
n : integer
493
number of marked points
494
dvector : integer vector
495
weights to place on the marked points, should have sum zero in the case
496
of the DR cycle
497
kval : integer (0 by default)
498
if set to something else than 0 then the result is the twisted DR
499
cycle by copies of the log canonical bundle (e.g. 1 for differentials)
500
moduli_type : a moduli type
501
MODULI_ST by default (moduli of stable curves), can be set to moduli
502
spaces containing less of the boundary (e.g. MODULI_CT for compact type)
503
to compute there instead
504
"""
505
answer = []
506
markings = tuple(range(1, n + 1))
507
for i in range(num_strata(g, r, markings, moduli_type)):
508
answer.append(DR_coeff_new(i, g, r, n, dvector, kval, moduli_type))
509
return vector(answer) / ZZ(2) ** r
510
511
512
def DR_compute_m(m, g, r, n=0, dvector=(), kval=0, moduli_type=MODULI_ST):
513
answer = []
514
markings = tuple(range(1, n + 1))
515
for i in range(num_strata(g, r, markings, moduli_type)):
516
answer.append(DR_coeff_m(m, i, g, r, n, dvector, kval, moduli_type))
517
return vector(answer)
518
519
520
def DR_sparse(g, r, n=0, dvector=(), kval=0, moduli_type=MODULI_ST):
521
"""
522
Same as :func:`DR_compute` except that it returns the answer as a
523
sparse vector.
524
525
EXAMPLES::
526
527
sage: from admcycles.DR import DR_sparse
528
sage: DR_sparse(1,1,2,(2,-2))
529
[[1, 2], [2, 2], [4, -1/24]]
530
"""
531
vec = DR_compute(g, r, n, dvector, kval, moduli_type)
532
return [[i, veci] for i, veci in enumerate(vec) if veci != 0]
533
534
535
def DR_psi_check(g, n, dvector, which_psi):
536
from .algebra import psi_multiple
537
from .evaluation import socle_evaluation
538
vec = DR_sparse(g, g, n, dvector, 0)
539
r = g
540
while r < 3 * g - 3 + n:
541
vec = psi_multiple(vec, which_psi, g, r, n)
542
r += 1
543
total = ZZ.zero()
544
for a, b in vec:
545
total += b * socle_evaluation(a, g, tuple(range(1, n + 1)))
546
return total / 2**g
547
548
549
def DR_reduced(g, dvector=()):
550
"""
551
Same as :func:`DR_compute` except that it only requires two arguments
552
(g and dvector) and simplifies the answer using the 3-spin
553
tautological relations.
554
555
EXAMPLES::
556
557
sage: from admcycles.DR import DR_reduced
558
sage: DR_reduced(1,(2,-2))
559
(0, 0, 0, 4, 1/8)
560
"""
561
g = ZZ(g)
562
n = len(dvector)
563
r = g
564
kval = ZZ(sum(dvector) / (2 * g - 2 + n))
565
vec = DR_compute(g, r, n, dvector, kval)
566
vec = vector(QQ, (QQ(a) for a in vec))
567
rels = FZ_rels(g, r, tuple(range(1, n + 1)))
568
return reduce_with_rels(rels, vec)
569
570