Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
181 views
unlisted
ubuntu2004
1
r"""
2
Faber-Zagier relations and pairing in tautological ring
3
"""
4
from copy import copy
5
import itertools
6
7
from sage.misc.cachefunc import cached_function
8
from sage.modules.free_module import span
9
from sage.rings.integer_ring import ZZ
10
from sage.rings.rational_field import QQ
11
from sage.matrix.constructor import matrix
12
from sage.functions.other import floor
13
from sage.combinat.partition import Partitions
14
from sage.combinat.integer_vector import IntegerVectors
15
from sage.combinat.permutation import Permutations
16
17
from .algebra import multiply, insertion_pullback, kappa_multiple, psi_multiple, get_marks
18
from .graph import all_strata, num_strata, contraction_table, unpurify_map, single_stratum, autom_count, graph_count_automorphisms, R, X, Graph, num_of_stratum, graph_isomorphic
19
from .evaluation import socle_evaluation
20
from .moduli import MODULI_SM, MODULI_ST, MODULI_SMALL, dim_form
21
from .utils import get_memory_usage, dprint, remove_duplicates, subsequences, A_list, B_list, aut, simplify_sparse
22
23
24
def gorenstein_precompute(g, r1, markings=(), moduli_type=MODULI_ST):
25
r3 = dim_form(g, len(markings), moduli_type)
26
r2 = r3 - r1
27
all_strata(g, r1, markings, moduli_type)
28
all_strata(g, r2, markings, moduli_type)
29
contraction_table(g, r3, markings, moduli_type)
30
unpurify_map(g, r3, markings, moduli_type)
31
32
33
def pairing_matrix(g, r1, markings=(), moduli_type=MODULI_ST):
34
r"""
35
Return the pairing matrix for the given genus, degree, markings and
36
moduli type.
37
38
EXAMPLES::
39
40
sage: from admcycles.DR import pairing_matrix
41
sage: pairing_matrix(1, 1, (1, 1))
42
[[1/4, 1/6, 1/12, 2], [1/6, 1/12, 0, 2], [1/12, 0, -1/12, 2], [2, 2, 2, 0]]
43
"""
44
r3 = dim_form(g, len(markings), moduli_type)
45
r2 = r3 - r1
46
ngens1 = num_strata(g, r1, markings, moduli_type)
47
ngens2 = num_strata(g, r2, markings, moduli_type)
48
ngens3 = num_strata(g, r3, markings, moduli_type)
49
socle_evaluations = [socle_evaluation(
50
i, g, markings, moduli_type) for i in range(ngens3)]
51
pairings = [[0 for i2 in range(ngens2)] for i1 in range(ngens1)]
52
sym = bool(r1 == r2)
53
for i1 in range(ngens1):
54
for i2 in range(ngens2):
55
if sym and i1 > i2:
56
pairings[i1][i2] = pairings[i2][i1]
57
continue
58
L = multiply(r1, i1, r2, i2, g, r3, markings, moduli_type)
59
pairings[i1][i2] = sum([L[k] * socle_evaluations[k]
60
for k in range(ngens3)])
61
return pairings
62
63
64
@cached_function
65
def pairing_submatrix(S1, S2, g, r1, markings=(), moduli_type=MODULI_ST):
66
r3 = dim_form(g, len(markings), moduli_type)
67
r2 = r3 - r1
68
# ngens1 = num_strata(g, r1, markings, moduli_type)
69
# ngens2 = num_strata(g, r2, markings, moduli_type)
70
ngens3 = num_strata(g, r3, markings, moduli_type)
71
socle_evaluations = [socle_evaluation(
72
i, g, markings, moduli_type) for i in range(ngens3)]
73
pairings = [[0 for i2 in S2] for i1 in S1]
74
sym = bool(r1 == r2 and S1 == S2)
75
for i1 in range(len(S1)):
76
for i2 in range(len(S2)):
77
if sym and i1 > i2:
78
pairings[i1][i2] = pairings[i2][i1]
79
continue
80
L = multiply(r1, S1[i1], r2, S2[i2], g, r3, markings, moduli_type)
81
pairings[i1][i2] = sum([L[k] * socle_evaluations[k]
82
for k in range(ngens3)])
83
return pairings
84
85
86
def betti(g, r, marked_points=(), moduli_type=MODULI_ST):
87
"""
88
This function returns the predicted rank of the codimension r grading
89
of the tautological ring of the moduli space of stable genus g curves
90
with marked points labeled by the multiset marked_points.
91
92
g and r should be nonnegative integers and marked_points should be a
93
tuple of positive integers.
94
95
The parameter moduli_type determines which moduli space to use:
96
- MODULI_ST: all stable curves (this is the default)
97
- MODULI_CT: curves of compact type
98
- MODULI_RT: curves with rational tails
99
- MODULI_SM: smooth curves
100
101
EXAMPLES::
102
103
sage: from admcycles.DR import betti
104
105
Check rank R^3(bar{M}_2) = 1::
106
107
sage: betti(2, 3)
108
1
109
110
Check rank R^2(bar{M}_{2,3}) = 44::
111
112
sage: betti(2, 2, (1,2,3)) # long time
113
44
114
115
Check rank R^2(bar{M}_{2,3})^{S_3} = 20::
116
117
sage: betti(2, 2, (1,1,1)) # long time
118
20
119
120
Check rank R^2(bar{M}_{2,3})^{S_2} = 32 (S_2 interchanging markings 1 and 2)::
121
122
sage: betti(2, 2, (1,1,2)) # long time
123
32
124
125
Check rank R^2(M^c_4) = rank R^3(M^c_4) = 6::
126
127
sage: from admcycles.DR import MODULI_CT, MODULI_RT, MODULI_SM
128
sage: betti(4, 2, (), MODULI_CT)
129
6
130
sage: betti(4, 3, (), MODULI_CT) # long time
131
6
132
133
We can use this to check that rank R^8(M^rt_{17,2})^(S_2)=122 < R^9(M^rt_{17,2})^(S_2) = 123.
134
Indeed, betti(17,8,(1,1),MODULI_RT) = 122 and betti(17,9,(1,1),MODULI_RT)=123
135
Similarly, we have rank R^9(M_{20,1}) = 75 < rank R^10(M_{20,1}) = 76
136
Indeed, betti(20,9,(1,),MODULI_SM) = 75 and betti(20,10,(1,),MODULI_SM) = 76.
137
"""
138
L = list_all_FZ(g, r, marked_points, moduli_type)
139
L.reverse()
140
return (len(L[0]) - compute_rank(L))
141
142
143
def gorenstein(g, r, marked_points=(), moduli_type=MODULI_ST):
144
"""
145
This function returns the rank of the codimension r grading of the
146
Gorenstein quotient of the tautological ring of the moduli space of genus g
147
curves with marked points labeled by the multiset marked_points.
148
149
g and r should be nonnegative integers and marked_points should be a
150
tuple of positive integers.
151
152
The parameter moduli_type determines which moduli space to use:
153
- MODULI_ST: all stable curves (this is the default)
154
- MODULI_CT: curves of compact type
155
- MODULI_RT: curves with rational tails
156
157
EXAMPLES::
158
159
sage: from admcycles.DR.relations import gorenstein
160
161
Check rank Gor^3(bar{M}_{3}) = 10::
162
163
sage: gorenstein(3, 3) # long time
164
10
165
166
Check rank Gor^2(bar{M}_{2,2}) = 14::
167
168
sage: gorenstein(2, 2, (1,2)) # long time
169
14
170
171
Check rank Gor^2(bar{M}_{2,2})^{S_2} = 11::
172
173
sage: gorenstein(2, 2, (1,1)) # long time
174
11
175
176
Check rank Gor^2(M^c_{4}) = 6::
177
178
sage: from admcycles.DR import MODULI_CT, MODULI_RT
179
180
sage: gorenstein(4, 2, (), MODULI_CT) # long time
181
6
182
183
Check rank Gor^4(M^rt_{8,2}) = 22::
184
185
sage: gorenstein(8, 4, (1,2), MODULI_RT) # long time
186
22
187
"""
188
gorenstein_precompute(g, r, marked_points, moduli_type)
189
r3 = dim_form(g, len(marked_points), moduli_type)
190
r2 = r3 - r
191
S1 = good_generator_list(g, r, marked_points, moduli_type)
192
S2 = good_generator_list(g, r2, marked_points, moduli_type)
193
M = pairing_submatrix(tuple(S1), tuple(S2), g, r,
194
marked_points, moduli_type)
195
return matrix(M).rank()
196
197
198
def recursive_betti(g, r, markings=(), moduli_type=MODULI_ST):
199
"""
200
EXAMPLES::
201
202
sage: from admcycles.DR.relations import recursive_betti
203
sage: recursive_betti(2, 3) # not tested
204
?
205
"""
206
from .utils import dprint, dsave
207
dprint("Start recursive_betti (%s,%s,%s,%s): %s", g, r,
208
markings, moduli_type, floor(get_memory_usage()))
209
n = len(markings)
210
if r > dim_form(g, n, moduli_type):
211
return 0
212
ngen = num_strata(g, r, markings, moduli_type)
213
dprint("%s gens", ngen)
214
relations = []
215
partial_sym_map = symmetrize_map(g, r, get_marks(n, 0), markings, moduli_type)
216
for rel in [unsymmetrize_vec(br, g, r, get_marks(n, 0), moduli_type)
217
for br in choose_basic_rels(g, r, n, moduli_type)]:
218
rel2 = []
219
for x in rel:
220
rel2.append([partial_sym_map[x[0]], x[1]])
221
rel2 = simplify_sparse(rel2)
222
relations.append(rel2)
223
for rel in interior_derived_rels(g, r, n, 0, moduli_type):
224
rel2 = []
225
for x in rel:
226
rel2.append([partial_sym_map[x[0]], x[1]])
227
rel2 = simplify_sparse(rel2)
228
relations.append(rel2)
229
dprint("%s gens, %s rels so far", ngen, len(relations))
230
dprint("Middle recursive_betti (%s,%s,%s,%s): %s", g, r,
231
markings, moduli_type, floor(get_memory_usage()))
232
if moduli_type > MODULI_SM:
233
for r0 in range(1, r):
234
strata = all_strata(g, r0, markings, moduli_type)
235
for G in strata:
236
vertex_orbits = graph_count_automorphisms(G, True)
237
for i in [orbit[0] for orbit in vertex_orbits]:
238
good = True
239
for j in range(G.M.ncols()):
240
if R(G.M[i, j][0]) != G.M[i, j]:
241
good = False
242
break
243
if good:
244
g2 = G.M[i, 0][0]
245
if 3 * (r - r0) < g2 + 1:
246
continue
247
d = G.degree(i)
248
if dim_form(g2, d, moduli_type) < r - r0:
249
continue
250
strata2 = all_strata(
251
g2, r - r0, tuple(range(1, d + 1)), moduli_type)
252
which_gen_list = [
253
-1 for num in range(len(strata2))]
254
for num in range(len(strata2)):
255
G_copy = Graph(G.M)
256
G_copy.replace_vertex_with_graph(i, strata2[num])
257
which_gen_list[num] = num_of_stratum(
258
G_copy, g, r, markings, moduli_type)
259
rel_list = [unsymmetrize_vec(br, g2, r - r0,
260
get_marks(d, 0), moduli_type) for br in
261
choose_basic_rels(g2, r - r0, d, moduli_type)]
262
rel_list += interior_derived_rels(g2,
263
r - r0, d, 0, moduli_type)
264
for rel0 in rel_list:
265
relation = []
266
for x in rel0:
267
num = x[0]
268
if which_gen_list[num] != -1:
269
relation.append(
270
[which_gen_list[num], x[1]])
271
relation = simplify_sparse(relation)
272
relations.append(relation)
273
dprint("%s gens, %s rels", ngen, len(relations))
274
dsave("sparse-%s-gens-%s-rels", ngen, len(relations))
275
dprint("Middle recursive_betti (%s,%s,%s,%s): %s", g, r,
276
markings, moduli_type, floor(get_memory_usage()))
277
relations = remove_duplicates(relations)
278
nrels = len(relations)
279
dprint("%s gens, %s distinct rels", ngen, nrels)
280
dsave("sparse-%s-distinct-rels", nrels)
281
rank = 0
282
D = {}
283
for i, reli in enumerate(relations):
284
for x in reli:
285
D[i, x[0]] = x[1]
286
if nrels:
287
row_order, col_order = choose_orders_sparse(D, nrels, ngen)
288
rank = compute_rank_sparse(D, row_order, col_order)
289
dsave("sparse-answer-%s", ngen - rank)
290
return ngen - rank
291
292
293
def FZ_rels(g, r, markings=(), moduli_type=MODULI_ST):
294
r"""
295
EXAMPLES::
296
297
sage: from admcycles.DR.relations import FZ_rels
298
sage: FZ_rels(2, 2)
299
[
300
(1, 0, 0, 0, 0, 0, -1/20, -1/480),
301
(0, 1, 0, 0, 0, 0, -5/24, -1/96),
302
(0, 0, 1, 0, 0, 0, -1/24, 0),
303
(0, 0, 0, 1, 0, 0, -1/24, 0),
304
(0, 0, 0, 0, 1, 0, -1, -1/12),
305
(0, 0, 0, 0, 0, 1, -1, -1/24)
306
]
307
"""
308
return span(FZ_matrix(g, r, markings, moduli_type)).basis()
309
310
311
# remove this and use rels_matrix instead?
312
def FZ_matrix(g, r, markings=(), moduli_type=MODULI_ST):
313
r"""
314
EXAMPLES::
315
316
sage: from admcycles.DR.relations import FZ_matrix
317
sage: from admcycles.DR.moduli import MODULI_SM, MODULI_RT, MODULI_CT, MODULI_ST
318
sage: FZ_matrix(2, 2)
319
[ 60 -60 84 0 6 0 0 0]
320
[ 473760 -100512 33984 -10080 6624 -10080 -288 -72]
321
[ 115920 -12240 13536 -5040 1584 -5040 -144 -36]
322
[ 0 0 102 -30 0 0 -3 0]
323
[ 0 0 30 42 0 0 -3 0]
324
[ 0 0 0 0 66 -60 -6 -3]
325
[ 0 0 0 0 15 6 -21 -3/2]
326
327
sage: FZ_matrix(2, 2, (1,), MODULI_CT)
328
[ 30 -30 30 0 42 0 0 0]
329
[ 441000 -78120 61200 -138600 37296 -7920 -42840 1368]
330
[ 204120 -27864 -39312 98280 20304 9072 -37800 -3672]
331
[ 0 0 -30 30 0 42 0 0]
332
[ 71820 -7020 9720 -41580 9288 -3240 -18900 756]
333
[ 13860 -900 -2520 16380 2520 3528 -16380 -1764]
334
[ 0 0 0 0 66 -30 -30 -6]
335
[ 0 0 0 0 15 21 -15 -21]
336
[ 0 0 0 0 15 -15 21 -21]
337
338
sage: FZ_matrix(3, 2, (1, 2), MODULI_RT)
339
[-251370 27162 -34020 -34020 145530 18900 145530 18036 -69930]
340
[ -56700 4860 10584 -7560 -49140 -7560 41580 -8424 34020]
341
[ -56700 4860 -7560 10584 41580 -7560 -49140 -8424 34020]
342
[ -6930 450 1260 1260 -8190 1764 -8190 900 -6930]
343
[ -6930 450 -900 -900 6930 900 6930 900 -6930]
344
345
sage: FZ_matrix(3, 2, (1, 1), MODULI_SM)
346
[-125685 13581 -34020 145530 9450]
347
[ -56700 4860 3024 -7560 -7560]
348
[ -3465 225 1260 -8190 882]
349
[ -3465 225 -900 6930 450]
350
"""
351
return matrix(list_all_FZ(g, r, markings, moduli_type))
352
353
354
def FZ_methods_sanity_check(g, r, n, moduli_type):
355
r"""
356
Check whether computing the FZ-relations via Pixton's 3-spin formula and
357
by using new relations give the same result.
358
359
TESTS::
360
361
sage: import itertools
362
sage: from admcycles.DR import FZ_methods_sanity_check, MODULI_SM, MODULI_RT, MODULI_CT, MODULI_ST
363
sage: gn = [(0, 5), (1, 1), (1, 2), (1, 3), (2, 2), (3, 0), (4, 1)]
364
sage: degrees = [0, 1, 2, 3]
365
sage: moduli = [MODULI_SM, MODULI_RT, MODULI_CT, MODULI_ST]
366
sage: for (g, n), r, moduli_type in itertools.product(gn, degrees, moduli): # long time
367
....: if not FZ_methods_sanity_check(g, r, n, moduli_type):
368
....: print("ERROR: g={}, r={}, n={}, moduli_type={}".format(g, r, n, moduli_type))
369
370
sage: all(FZ_methods_sanity_check(9, i, 0, MODULI_SM) for i in range(10)) # long time
371
True
372
"""
373
def echelon_without_zeros(M):
374
M.echelonize()
375
for i, r in enumerate(M.rows()):
376
if r.is_zero():
377
return M.matrix_from_rows(range(i))
378
return M
379
380
spin3 = rels_matrix(g, r, n, 0, moduli_type, True)
381
newrels = rels_matrix(g, r, n, 0, moduli_type, False)
382
return echelon_without_zeros(spin3) == echelon_without_zeros(newrels)
383
384
385
def list_all_FZ(g, r, markings=(), moduli_type=MODULI_ST):
386
relations = copy(interior_FZ(g, r, markings, moduli_type))
387
if moduli_type > MODULI_SM:
388
relations += boundary_FZ(g, r, markings, moduli_type)
389
if not relations:
390
ngen = num_strata(g, r, markings, moduli_type)
391
relations.append([0 for i in range(ngen)])
392
return relations
393
394
395
def reduced_FZ_param_list(G, v, g, d, n):
396
params = FZ_param_list(n, tuple(range(1, d + 1)))
397
graph_params = []
398
M = matrix(R, 2, d + 1)
399
M[0, 0] = -1
400
for i in range(1, d + 1):
401
M[0, i] = i
402
for p in params:
403
G_copy = Graph(G.M)
404
M[1, 0] = -g - 1
405
for j in p[0]:
406
M[1, 0] += X**j
407
for i in range(1, d + 1):
408
M[1, i] = 1 + p[1][i - 1][1][0] * X
409
G_p = Graph(M)
410
G_copy.replace_vertex_with_graph(v, G_p)
411
graph_params.append([p, G_copy])
412
params_reduced = []
413
graphs_seen = []
414
for x in graph_params:
415
x[1].compute_invariant()
416
good = True
417
for GG in graphs_seen:
418
if graph_isomorphic(x[1], GG):
419
good = False
420
break
421
if good:
422
graphs_seen.append(x[1])
423
params_reduced.append(x[0])
424
return params_reduced
425
426
427
def FZ_param_list(n, markings=()):
428
r"""
429
EXAMPLES::
430
431
sage: from admcycles.DR.relations import FZ_param_list
432
sage: FZ_param_list(3, ())
433
[((3,), ()), ((1, 1, 1), ()), ((1,), ())]
434
"""
435
if n < 0:
436
return []
437
final_list = []
438
mmm = max((0,) + markings)
439
markings_grouped = [0 for i in range(mmm)]
440
for i in markings:
441
markings_grouped[i - 1] += 1
442
markings_best = []
443
for i in range(mmm):
444
if markings_grouped[i] > 0:
445
markings_best.append([i + 1, markings_grouped[i]])
446
for j in range(n // 2 + 1):
447
for n_vec in IntegerVectors(n - 2 * j, 1 + len(markings_best)):
448
S_list = [[list(sigma) for sigma in Partitions(n_vec[0])
449
if not any(l % 3 == 2 for l in sigma)]]
450
# TODO: the line above should iterate directly over the set
451
# of partitions with no parts of size = 2 (mod 3)
452
for i in range(len(markings_best)):
453
S_list.append(Partitions(
454
n_vec[i + 1] + markings_best[i][1], length=markings_best[i][1]).list())
455
S_list[-1] = [[k - 1 for k in sigma] for sigma in S_list[-1]
456
if sum(1 for l in sigma if (l % 3) == 0) == 0]
457
for S in itertools.product(*S_list):
458
final_list.append((tuple(S[0]), tuple([(markings_best[k][0], tuple(
459
S[k + 1])) for k in range(len(markings_best))])))
460
return final_list
461
462
463
def C_coeff(m, term):
464
n = term - m // 3
465
if n < 0:
466
return 0
467
if m % 3 == 0:
468
return A_list[n]
469
else:
470
return B_list[n]
471
472
473
@cached_function
474
def dual_C_coeff(i, j, parity):
475
total = ZZ.zero()
476
k = parity % 2
477
while k // 3 <= i:
478
if k % 3 != 2:
479
total += (-1)**(k // 3) * C_coeff(k, i) * C_coeff(-2 - k, j)
480
k += 2
481
return total
482
483
484
def poly_to_partition(F):
485
mmm = F.degree()
486
target_partition = []
487
for i in range(1, mmm + 1):
488
for j in range(F[i]):
489
target_partition.append(i)
490
return tuple(target_partition)
491
492
493
@cached_function
494
def kappa_coeff(sigma, kappa_0, target_partition):
495
total = ZZ.zero()
496
num_ones = sum(1 for i in sigma if i == 1)
497
for i in range(0, num_ones + 1):
498
for injection in Permutations(list(range(len(target_partition))), len(sigma) - i):
499
term = (ZZ(num_ones).binomial(i) *
500
ZZ(kappa_0 + len(target_partition) + i - 1).binomial(i) *
501
ZZ(i).factorial())
502
for j in range(len(sigma) - i):
503
term *= C_coeff(sigma[j + i], target_partition[injection[j]])
504
for j in range(len(target_partition)):
505
if j in injection:
506
continue
507
term *= C_coeff(0, target_partition[j])
508
total += term
509
total = (-1)**(len(target_partition) + len(sigma)) * \
510
total / aut(list(target_partition))
511
return total
512
513
514
@cached_function
515
def FZ_kappa_factor(num, sigma, g, r, markings=(), moduli_type=MODULI_ST):
516
G = single_stratum(num, g, r, markings, moduli_type)
517
L = []
518
nv = G.num_vertices()
519
for i in range(1, nv + 1):
520
L.append((2 * G.M[i, 0][0] + G.degree(i) -
521
2, G.M[i, 0] - G.M[i, 0][0]))
522
LL = []
523
tau = []
524
for i in range(nv):
525
mini = -1
526
for j in range(nv):
527
if (i == 0 or L[j] > LL[-1] or L[j] == LL[-1] and j > tau[-1]) and (mini == -1 or L[j] < L[mini]):
528
mini = j
529
tau.append(mini)
530
LL.append(L[mini])
531
factor_dict = FZ_kappa_factor2(tuple(LL), sigma)
532
factor_vec = [0 for i in range(1 << nv)]
533
for parity_key in factor_dict:
534
parity = 0
535
for i in range(nv):
536
if parity_key[i] == 1:
537
parity += 1 << tau[i]
538
factor_vec[parity] = factor_dict[parity_key]
539
return factor_vec
540
541
542
@cached_function
543
def FZ_marking_factor(num, marking_vec, g, r, markings=(), moduli_type=MODULI_ST):
544
G = single_stratum(num, g, r, markings, moduli_type)
545
nv = G.num_vertices()
546
ne = G.num_edges()
547
num_parities = ZZ(2) ** nv
548
PPP_list = []
549
for marks in marking_vec:
550
PPP_list.append(Permutations(marks[1]))
551
PPP = itertools.product(*PPP_list)
552
marking_factors = [0 for i in range(num_parities)]
553
incident_vertices = []
554
for mark_type in marking_vec:
555
incident_vertices.append([])
556
for k in range(1, ne + 1):
557
if G.M[0, k] == mark_type[0]:
558
for i in range(1, nv + 1):
559
if G.M[i, k] != 0:
560
incident_vertices[-1].append((i - 1, G.M[i, k][1]))
561
break
562
for perms in PPP:
563
parity = 0
564
marking_factor = 1
565
for marks_index in range(len(marking_vec)):
566
for count in range(len(incident_vertices[marks_index])):
567
marking_factor *= C_coeff(perms[marks_index][count],
568
incident_vertices[marks_index][count][1])
569
parity ^= (perms[marks_index][count] %
570
ZZ(2)) << incident_vertices[marks_index][count][0]
571
marking_factors[parity] += marking_factor
572
return marking_factors
573
574
575
@cached_function
576
def FZ_kappa_factor2(L, sigma):
577
nv = len(L)
578
mmm = max((0,) + sigma)
579
sigma_grouped = [0 for i in range(mmm)]
580
for i in sigma:
581
sigma_grouped[i - 1] += 1
582
S_list = []
583
for i in sigma_grouped:
584
S_list.append(IntegerVectors(i, nv))
585
S = itertools.product(*S_list)
586
kappa_factors = {}
587
for parity in itertools.product(*[(0, 1) for i in range(nv)]):
588
kappa_factors[tuple(parity)] = 0
589
for assignment in S:
590
assigned_sigma = [[] for j in range(nv)]
591
for i in range(mmm):
592
for j in range(nv):
593
for k in range(assignment[i][j]):
594
assigned_sigma[j].append(i + 1)
595
sigma_auts = 1
596
parity = [0 for i in range(nv)]
597
kappa_factor = ZZ.one()
598
for j in range(nv):
599
sigma_auts *= aut(assigned_sigma[j])
600
parity[j] += sum(assigned_sigma[j])
601
parity[j] %= ZZ(2)
602
kappa_factor *= kappa_coeff(
603
tuple(assigned_sigma[j]), L[j][0], poly_to_partition(L[j][1]))
604
kappa_factors[tuple(parity)] += kappa_factor / sigma_auts
605
return kappa_factors
606
607
608
@cached_function
609
def FZ_hedge_factor(num, g, r, markings=(), moduli_type=MODULI_ST):
610
G = single_stratum(num, g, r, markings, moduli_type)
611
nv = G.num_vertices()
612
num_parities = ZZ(2) ** nv
613
ne = G.num_edges()
614
edge_list = []
615
for k in range(1, ne + 1):
616
if G.M[0, k] == 0:
617
edge_list.append([k])
618
for i in range(1, nv + 1):
619
if G.M[i, k] != 0:
620
edge_list[-1].append(i)
621
if G.M[i, k][0] == 2:
622
edge_list[-1].append(i)
623
hedge_factors = [0 for i in range(num_parities)]
624
for edge_parities in itertools.product(*[[0, 1] for i in edge_list]):
625
parity = 0
626
for i in range(len(edge_list)):
627
if edge_parities[i] == 1:
628
parity ^= 1 << (edge_list[i][1] - 1)
629
parity ^= 1 << (edge_list[i][2] - 1)
630
hedge_factor = 1
631
for i in range(len(edge_list)):
632
if edge_list[i][1] == edge_list[i][2]:
633
hedge_factor *= dual_C_coeff(G.M[edge_list[i][1], edge_list[i][0]][1],
634
G.M[edge_list[i][1], edge_list[i][0]][2], edge_parities[i] % ZZ(2))
635
else:
636
hedge_factor *= dual_C_coeff(G.M[edge_list[i][1], edge_list[i][0]][1],
637
G.M[edge_list[i][2], edge_list[i][0]][1], edge_parities[i] % ZZ(2))
638
hedge_factors[parity] += hedge_factor
639
return hedge_factors
640
641
642
@cached_function
643
def FZ_coeff(num, FZ_param, g, r, markings=(), moduli_type=MODULI_ST):
644
r"""
645
EXAMPLES::
646
647
sage: from admcycles.DR.graph import num_strata
648
sage: from admcycles.DR.moduli import MODULI_ST
649
sage: from admcycles.DR.relations import FZ_coeff
650
sage: [FZ_coeff(i, ((3,), ()), 2, 2, (), MODULI_ST) for i in range(num_strata(2, 2))]
651
[60, -60, 84, 0, 6, 0, 0, 0]
652
sage: [FZ_coeff(i, ((1, 1, 1), ()), 2, 2, (), MODULI_ST) for i in range(num_strata(2, 2))]
653
[473760, -100512, 33984, -10080, 6624, -10080, -288, -72]
654
sage: [FZ_coeff(i, ((1,), ()), 2, 2, (), MODULI_ST) for i in range(num_strata(2, 2))]
655
[115920, -12240, 13536, -5040, 1584, -5040, -144, -36]
656
"""
657
sigma = FZ_param[0]
658
marking_vec = FZ_param[1]
659
G = single_stratum(num, g, r, markings, moduli_type)
660
nv = G.num_vertices()
661
graph_auts = autom_count(num, g, r, markings, moduli_type)
662
h1_factor = ZZ(2) ** G.h1()
663
num_parities = ZZ(2) ** nv
664
665
marking_factors = FZ_marking_factor(
666
num, marking_vec, g, r, markings, moduli_type)
667
kappa_factors = FZ_kappa_factor(num, sigma, g, r, markings, moduli_type)
668
hedge_factors = FZ_hedge_factor(num, g, r, markings, moduli_type)
669
670
total = ZZ.zero()
671
for i in range(num_parities):
672
if marking_factors[i] == 0:
673
continue
674
for j in range(num_parities):
675
total += marking_factors[i] * kappa_factors[j] * \
676
hedge_factors[i ^ j ^ G.target_parity]
677
678
total /= h1_factor * graph_auts
679
return total
680
681
682
@cached_function
683
def interior_FZ(g, r, markings=(), moduli_type=MODULI_ST):
684
r"""
685
EXAMPLES::
686
687
sage: from admcycles.DR.relations import interior_FZ
688
sage: interior_FZ(2, 2)
689
[[60, -60, 84, 0, 6, 0, 0, 0],
690
[473760, -100512, 33984, -10080, 6624, -10080, -288, -72],
691
[115920, -12240, 13536, -5040, 1584, -5040, -144, -36]]
692
"""
693
ngen = num_strata(g, r, markings, moduli_type)
694
relations = []
695
FZpl = FZ_param_list(3 * r - g - 1, markings)
696
for FZ_param in FZpl:
697
relation = [FZ_coeff(i, FZ_param, g, r, markings, moduli_type)
698
for i in range(ngen)]
699
relations.append(relation)
700
return relations
701
702
703
def possibly_new_FZ(g, r, n=0, moduli_type=MODULI_ST):
704
m = 3 * r - g - 1 - n
705
if m < 0:
706
return []
707
dprint("Start FZ (%s,%s,%s,%s): %s", g, r, n, moduli_type,
708
floor(get_memory_usage()))
709
markings = (1,) * n
710
ngen = num_strata(g, r, markings, moduli_type)
711
relations = []
712
for i in range(m + 1):
713
if (m - i) % 2:
714
continue
715
for sigma in Partitions(i):
716
# TODO: the line above should iterate directly over the set
717
# of partitions with all parts of size = 1 (mod 3)
718
if any(j % 3 != 1 for j in sigma):
719
continue
720
if n > 0:
721
FZ_param = (tuple(sigma), ((1, markings),))
722
else:
723
FZ_param = (tuple(sigma), ())
724
relation = []
725
for j in range(ngen):
726
coeff = FZ_coeff(j, FZ_param, g, r, markings, moduli_type)
727
if coeff != 0:
728
relation.append([j, coeff])
729
relations.append(relation)
730
dprint("End FZ (%s,%s,%s,%s): %s", g, r, n, moduli_type,
731
floor(get_memory_usage()))
732
return relations
733
734
735
@cached_function
736
def boundary_FZ(g, r, markings=(), moduli_type=MODULI_ST):
737
r"""
738
EXAMPLES::
739
740
sage: from admcycles.DR.relations import boundary_FZ
741
sage: boundary_FZ(2, 2)
742
[[0, 0, 102, -30, 0, 0, -3, 0],
743
[0, 0, 30, 42, 0, 0, -3, 0],
744
[0, 0, 0, 0, 66, -60, -6, -3],
745
[0, 0, 0, 0, 15, 6, -21, -3/2]]
746
"""
747
if moduli_type <= MODULI_SM:
748
return []
749
generators = all_strata(g, r, markings, moduli_type)
750
ngen = len(generators)
751
relations = []
752
for r0 in range(1, r):
753
strata = all_strata(g, r0, markings, moduli_type)
754
for G in strata:
755
vertex_orbits = graph_count_automorphisms(G, True)
756
for i in [orbit[0] for orbit in vertex_orbits]:
757
good = True
758
for j in range(G.M.ncols()):
759
if R(G.M[i, j][0]) != G.M[i, j]:
760
good = False
761
break
762
if good:
763
g2 = G.M[i, 0][0]
764
if 3 * (r - r0) < g2 + 1:
765
continue
766
d = G.degree(i)
767
if dim_form(g2, d, moduli_type) < r - r0:
768
continue
769
strata2 = all_strata(
770
g2, r - r0, tuple(range(1, d + 1)), moduli_type)
771
which_gen_list = [-1 for num in range(len(strata2))]
772
for num in range(len(strata2)):
773
G_copy = Graph(G.M)
774
G_copy.replace_vertex_with_graph(i, strata2[num])
775
which_gen_list[num] = num_of_stratum(
776
G_copy, g, r, markings, moduli_type)
777
rFZpl = reduced_FZ_param_list(
778
G, i, g2, d, 3 * (r - r0) - g2 - 1)
779
ccccc = 0
780
for FZ_param in rFZpl:
781
relation = [0] * ngen
782
for num in range(len(strata2)):
783
if which_gen_list[num] != -1:
784
relation[which_gen_list[num]] += FZ_coeff(num, FZ_param, g2, r - r0, tuple(
785
range(1, d + 1)), moduli_type)
786
relations.append(relation)
787
ccccc += 1
788
return relations
789
790
791
def rels_matrix(g, r, n, symm, moduli_type, usespin):
792
r"""
793
Returns a matrix containing all FZ-relations for given ``g``, ``r``, ``n``, ``moduli_type``.
794
The relations are invariant under the symmetry action on the first ``symm`` points.
795
When ``usespin`` is True the relations are computed using Pixton's 3-spin formula.
796
When ``usespin`` is False the relations are computed by deriving old relations and
797
combining them with (possibly) new relations.
798
799
TESTS::
800
801
sage: from admcycles.DR import rels_matrix
802
sage: sum(rels_matrix(2, 2, 4, 2, 1, False).echelon_form().column(-1)) # long time
803
38
804
sage: from admcycles.DR import rels_matrix, MODULI_RT, betti
805
sage: def betti2(g, n, r):
806
....: M = rels_matrix(g, r, n, n, MODULI_RT, False)
807
....: return M.ncols() - M.rank()
808
sage: [betti2(5, 2, i) for i in range(6)] # long time
809
[1, 3, 6, 6, 3, 1]
810
sage: [betti(5, i, (1, 1), MODULI_RT) for i in range(6)] # long time
811
[1, 3, 6, 6, 3, 1]
812
813
Check for https://gitlab.com/modulispaces/admcycles/-/issues/105::
814
815
sage: from admcycles.DR import MODULI_ST
816
sage: rels_matrix(9, 3, 0, 0, MODULI_ST, False).ncols()
817
356
818
"""
819
# m = get_memory_usage()
820
if usespin:
821
if symm != 0:
822
print("FZ_matrix not implemented with symmetry")
823
return
824
rel = matrix(list_all_FZ(g, r, get_marks(n, symm), moduli_type))
825
# dprint("memory diff matrix computation for g = %s, n = %s, r = %s, moduli_type = %s, 3spin = %s : %s",
826
# g, r, n, moduli_type, usespin, floor(get_memory_usage() - m))
827
return rel
828
drels = derived_rels(g, r, n, symm, moduli_type)
829
pnrels = possibly_new_FZ(g, r, n, moduli_type)
830
if symm != n:
831
if symm == 0:
832
pnrels = [unsymmetrize_vec(p, g, r, get_marks(n, symm),
833
moduli_type) for p in pnrels]
834
else:
835
pnrels = [partial_unsymmetrize_vec(p, g, r, get_marks(n, symm),
836
get_marks(n, n), moduli_type) for p in pnrels]
837
allrels = drels + pnrels
838
ngen = num_strata(g, r, get_marks(n, symm), moduli_type)
839
rel = matrix(QQ, len(allrels), ngen, sparse=True)
840
for i, r in enumerate(allrels):
841
for (j, c) in r:
842
rel[i, j] = c
843
# dprint("memory diff relation matrix computation for g = %s, n = %s, r = %s, moduli_type = %s, 3spin = %s: %s",
844
# g, r, n, moduli_type, usespin, floor(get_memory_usage() - m))
845
return rel
846
847
848
@cached_function
849
def pullback_derived_rels(g, r, n, symm, moduli_type=MODULI_ST):
850
r"""
851
Returns a list of relations that are derived from relations with
852
less points by pulling back along the forgetfulmap that forgets a point.
853
"""
854
if r == 0:
855
return []
856
dprint("Start pullback_derived (%s,%s,%s,%s): %s", g,
857
r, n, moduli_type, floor(get_memory_usage()))
858
answer = []
859
for n0 in range(n):
860
if dim_form(g, n0, moduli_type) >= r:
861
basic_rels = choose_basic_rels(g, r, n0, moduli_type)
862
for rel in basic_rels:
863
for vec in subsequences(n, n - n0, symm):
864
local_symm = max(symm, 1) - vec.count(1)
865
if local_symm < n0:
866
rel2 = partial_unsymmetrize_vec(rel, g, r, get_marks(n0, local_symm),
867
get_marks(n0, n0), moduli_type)
868
else:
869
rel2 = copy(rel)
870
for i in range(n - n0):
871
rel2 = insertion_pullback(
872
rel2, vec[i], g, r, n0 + i, local_symm, moduli_type, False)
873
if vec[i] == 1:
874
local_symm += 1
875
answer.append(simplify_sparse(rel2))
876
else:
877
if moduli_type == MODULI_ST and not ((g == 0 and n0 < 3) or (g == 1 and n0 == 0)):
878
continue
879
basic_rels = choose_basic_rels(g, r, n0, MODULI_SMALL)
880
k = r - dim_form(g, n0, moduli_type)
881
for rel in basic_rels:
882
for vec2 in subsequences(n, n - n0 - k + 1, symm):
883
local_symm2 = max(symm, 1) - vec2.count(1)
884
for vec in subsequences(n0 + k - 1, k - 1, local_symm2):
885
local_symm = max(local_symm2, 1) - vec.count(1)
886
if local_symm < n0:
887
rel2 = partial_unsymmetrize_vec(rel, g, r, get_marks(n0, local_symm),
888
get_marks(n0, n0), MODULI_SMALL)
889
else:
890
rel2 = copy(rel)
891
for i in range(k - 1):
892
rel2 = insertion_pullback(
893
rel2, vec[i], g, r, n0 + i, local_symm, MODULI_SMALL, False)
894
if vec[i] == 1:
895
local_symm += 1
896
local_symm = local_symm2
897
rel2 = insertion_pullback(
898
rel2, vec2[0], g, r, n0 + k - 1, local_symm, moduli_type, True)
899
if vec2[0] == 1:
900
local_symm += 1
901
for i in range(n - n0 - k):
902
rel2 = insertion_pullback(
903
rel2, vec2[i + 1], g, r, n0 + k + i, local_symm, moduli_type, False)
904
if vec2[i + 1] == 1:
905
local_symm += 1
906
answer.append(simplify_sparse(rel2))
907
dprint("End pullback_derived (%s,%s,%s,%s): %s", g,
908
r, n, moduli_type, floor(get_memory_usage()))
909
answer = remove_duplicates(answer)
910
return answer
911
912
913
@cached_function
914
def interior_derived_rels(g, r, n, symm, moduli_type=MODULI_ST):
915
r"""
916
Returns a list of relations that are derived from relations with
917
lower codimension by multlication with psi- and kappa-classes.
918
"""
919
dprint("Start interior_derived (%s,%s,%s,%s): %s", g,
920
r, n, moduli_type, floor(get_memory_usage()))
921
answer = copy(pullback_derived_rels(g, r, n, symm, moduli_type))
922
for r0 in range(r):
923
local_symm = max(symm - (r - r0), 0)
924
if local_symm < symm:
925
symm_map = symmetrize_map(g, r, get_marks(n, local_symm), get_marks(n, symm), moduli_type)
926
pullback_rels = [partial_unsymmetrize_vec(v, g, r0, get_marks(n, local_symm), get_marks(n, n), moduli_type) for v in choose_basic_rels(g, r0, n, moduli_type)]
927
pullback_rels += pullback_derived_rels(g, r0, n, local_symm, moduli_type)
928
for rel in pullback_rels:
929
for i in range(r - r0 + 1):
930
for sigma in Partitions(i):
931
for tau in IntegerVectors(r - r0 - i, n - local_symm):
932
rel2 = copy(rel)
933
rcur = r0
934
for m in range(n - local_symm):
935
for mm in range(tau[m]):
936
rel2 = psi_multiple(
937
rel2, get_marks(n, local_symm)[-m - 1], g, rcur, n, local_symm, moduli_type)
938
rcur += 1
939
for m in sigma:
940
rel2 = kappa_multiple(
941
rel2, m, g, rcur, n, local_symm, moduli_type)
942
rcur += m
943
if local_symm < symm:
944
rel2 = [[symm_map[y[0]], y[1]] for y in rel2]
945
answer.append(simplify_sparse(rel2))
946
dprint("End interior_derived (%s,%s,%s,%s): %s", g,
947
r, n, moduli_type, floor(get_memory_usage()))
948
answer = remove_duplicates(answer)
949
return answer
950
951
952
@cached_function
953
def symmetrize_map(g, r, markings, symm_markings, moduli_type):
954
r"""
955
Returns the map for symmetrizing a stratum by replacing its ``markings`` with ``symm_markings``.
956
Requires ``symm_markings`` to be more symmetrized than ``markings``.
957
"""
958
gens = all_strata(g, r, markings, moduli_type)
959
dif = markings.count(1) - 2
960
symm_map = []
961
for G in gens:
962
GG = Graph(G.M)
963
for i in range(1, GG.M.ncols()):
964
if GG.M[0, i][0] > 0:
965
GG.M[0, i] = R(symm_markings[GG.M[0, i][0] + dif])
966
symm_map.append(num_of_stratum(GG, g, r, symm_markings, moduli_type))
967
return symm_map
968
969
970
@cached_function
971
def partial_unsymmetrize_map(g, r, markings, symm_markings, moduli_type):
972
r"""
973
Returns map that replaces a stratum with ``markings`` by a linear
974
combination of strata with ``symm_markings``.
975
976
This function is an inverse of :func:`symmetrize_map`. The coefficient of
977
each stratum in the output is the size of its orbit under the corresponding
978
symmetric action. Requires ``symm_markings`` to be more symmetrized than
979
``markings``.
980
"""
981
target_symm = markings.count(1)
982
symm = symm_markings.count(1)
983
n = len(markings)
984
if target_symm == symm:
985
print("this should not be happening?")
986
return -1
987
if target_symm + 1 < symm:
988
return [partial_unsymmetrize_vec(us, g, r, markings, get_marks(n, target_symm + 1), moduli_type) for us in partial_unsymmetrize_map(g, r, get_marks(n, target_symm + 1), symm_markings, moduli_type)]
989
gens = all_strata(g, r, markings, moduli_type)
990
orbits = {}
991
for i in range(len(gens)):
992
if i in orbits.keys():
993
continue
994
orbits[i] = 1
995
G = gens[i]
996
for j in range(1, G.M.ncols()):
997
if G.M[0, j][0] == 2:
998
pt2 = j
999
break
1000
for j in range(1, G.M.ncols()):
1001
if G.M[0, j][0] == 1:
1002
GG = Graph(G.M)
1003
GG.M[0, j] += 1
1004
GG.M[0, pt2] -= 1
1005
num = num_of_stratum(GG, g, r, markings, moduli_type)
1006
if num in orbits.keys():
1007
orbits[num] = orbits[num] + 1
1008
else:
1009
orbits[num] = 1
1010
symm_map = symmetrize_map(g, r, markings, symm_markings, moduli_type)
1011
result = [[] for i in range(num_strata(g, r, symm_markings, moduli_type))]
1012
for k in orbits.keys():
1013
result[symm_map[k]].append([k, orbits[k]])
1014
return result
1015
1016
1017
def partial_unsymmetrize_vec(vec, g, r, markings, symm_markings, moduli_type):
1018
r"""
1019
Applies partial_symmetrize_map to a relation to calculate its unsymmetrized version.
1020
"""
1021
if markings == symm_markings:
1022
return vec
1023
unsym_map = partial_unsymmetrize_map(g, r, markings, symm_markings, moduli_type)
1024
vec2 = []
1025
for x in vec:
1026
aut = 0
1027
for j in unsym_map[x[0]]:
1028
aut += j[1]
1029
for j in unsym_map[x[0]]:
1030
vec2.append([j[0], x[1] * j[1] / QQ(aut)])
1031
vec2 = simplify_sparse(vec2)
1032
return vec2
1033
1034
1035
# this is faster than partial_unsymmetrize_map when both are defined
1036
@cached_function
1037
def unsymmetrize_map(g, r, markings=(), moduli_type=MODULI_ST):
1038
r"""
1039
Does the same as partial_unsymmetrize_map but is faster.
1040
Only works for unsymmetrizing from complete symmetry.
1041
"""
1042
markings2 = tuple([1 for i in markings])
1043
sym_map = symmetrize_map(g, r, markings, get_marks(len(markings), len(markings)), moduli_type)
1044
map = [[] for i in range(num_strata(g, r, markings2, moduli_type))]
1045
for i in range(len(sym_map)):
1046
map[sym_map[i]].append(i)
1047
return map
1048
1049
1050
def unsymmetrize_vec(vec, g, r, markings=(), moduli_type=MODULI_ST):
1051
unsym_map = unsymmetrize_map(g, r, markings, moduli_type)
1052
vec2 = []
1053
for x in vec:
1054
aut = len(unsym_map[x[0]])
1055
for j in unsym_map[x[0]]:
1056
vec2.append([j, x[1] / QQ(aut)])
1057
vec2 = simplify_sparse(vec2)
1058
return vec2
1059
1060
1061
def derived_rels(g, r, n, symm, moduli_type=MODULI_ST):
1062
r"""
1063
Returns a list of relations that are derived from relations with
1064
lower genus, codimension, and/or number of points.
1065
This is done by taking a Graph and inserting a relation at a vertex.
1066
"""
1067
dprint("Start derived (%s,%s,%s,%s): %s", g, r,
1068
n, moduli_type, floor(get_memory_usage()))
1069
if dim_form(g, n, moduli_type) < r:
1070
return []
1071
markings = get_marks(n, symm)
1072
answer = copy(interior_derived_rels(g, r, n, symm, moduli_type))
1073
if moduli_type <= MODULI_SM:
1074
return answer
1075
for r0 in range(1, r):
1076
strata = all_strata(g, r0, markings, moduli_type)
1077
for G in strata:
1078
vertex_orbits = graph_count_automorphisms(G, True)
1079
for i in [orbit[0] for orbit in vertex_orbits]:
1080
good = True
1081
for j in range(G.M.ncols()):
1082
if R(G.M[i, j][0]) != G.M[i, j]:
1083
good = False
1084
break
1085
if good:
1086
localsymm = 0
1087
for j in range(G.M.ncols()):
1088
if G.M[i, j][0] == 1 and G.M[0, j][0] == 1:
1089
localsymm += 1
1090
g2 = G.M[i, 0][0]
1091
if 3 * (r - r0) < g2 + 1:
1092
continue
1093
d = G.degree(i)
1094
if dim_form(g2, d, moduli_type) < r - r0:
1095
continue
1096
strata2 = all_strata(
1097
g2, r - r0, get_marks(d, localsymm), moduli_type)
1098
which_gen_list = [
1099
-1 for num in range(len(strata2))]
1100
for num in range(len(strata2)):
1101
G_copy = Graph(G.M)
1102
G_copy.replace_vertex_with_graph(i, strata2[num])
1103
which_gen_list[num] = num_of_stratum(
1104
G_copy, g, r, markings, moduli_type)
1105
rel_list = [partial_unsymmetrize_vec(br, g2, r - r0, get_marks(d, localsymm), get_marks(d, d), moduli_type) for br in choose_basic_rels(g2, r - r0, d, moduli_type)]
1106
rel_list += interior_derived_rels(g2, r - r0, d, localsymm, moduli_type)
1107
for rel0 in rel_list:
1108
relation = []
1109
for x0, x1 in rel0:
1110
if which_gen_list[x0] != -1:
1111
relation.append([which_gen_list[x0], x1])
1112
relation = simplify_sparse(relation)
1113
answer.append(relation)
1114
answer = remove_duplicates(answer)
1115
dprint("End derived (%s,%s,%s,%s): %s", g, r, n,
1116
moduli_type, floor(get_memory_usage()))
1117
return answer
1118
1119
1120
@cached_function
1121
def choose_basic_rels(g, r, n, moduli_type):
1122
r"""
1123
Return a basis of new relations of the tautological ring invariant under symmetry.
1124
1125
EXAMPLES::
1126
1127
sage: from admcycles.DR.moduli import MODULI_SM, MODULI_RT, MODULI_CT, MODULI_ST
1128
sage: from admcycles.DR.relations import choose_basic_rels
1129
sage: choose_basic_rels(2, 2, 0, MODULI_ST)
1130
[[[0, 115920],
1131
[1, -12240],
1132
[2, 13536],
1133
[3, -5040],
1134
[4, 1584],
1135
[5, -5040],
1136
[6, -144],
1137
[7, -36]]]
1138
"""
1139
if 3 * r < g + n + 1:
1140
return []
1141
sym_ngen = num_strata(g, r, tuple([1 for i in range(n)]), moduli_type)
1142
if moduli_type == MODULI_SMALL and r > dim_form(g, n, MODULI_SM):
1143
sym_possible_rels = [[[i, 1]] for i in range(sym_ngen)]
1144
else:
1145
sym_possible_rels = possibly_new_FZ(g, r, n, moduli_type)
1146
if not sym_possible_rels:
1147
return []
1148
dprint("Start basic_rels (%s,%s,%s,%s): %s", g, r,
1149
n, moduli_type, floor(get_memory_usage()))
1150
previous_rels = derived_rels(g, r, n, n, moduli_type)
1151
nrels = len(previous_rels)
1152
dprint("%s gens, %s oldrels", sym_ngen, nrels)
1153
D = {}
1154
for i in range(nrels):
1155
for x in previous_rels[i]:
1156
D[i, x[0]] = x[1]
1157
if nrels > 0:
1158
row_order, col_order = choose_orders_sparse(D, nrels, sym_ngen)
1159
previous_rank = compute_rank_sparse(D, row_order, col_order)
1160
dprint("rank %s", previous_rank)
1161
else:
1162
previous_rank = 0
1163
row_order = []
1164
col_order = list(range(sym_ngen))
1165
answer = []
1166
for j in range(len(sym_possible_rels)):
1167
for x in sym_possible_rels[j]:
1168
D[nrels, x[0]] = x[1]
1169
row_order.append(nrels)
1170
nrels += 1
1171
if compute_rank_sparse(D, row_order, col_order) > previous_rank:
1172
answer.append(sym_possible_rels[j])
1173
previous_rank += 1
1174
dprint("rank %s", previous_rank)
1175
dprint("End basic_rels (%s,%s,%s,%s): %s", g, r,
1176
n, moduli_type, floor(get_memory_usage()))
1177
dprint("%s,%s,%s,%s: rank %s", g, r, n,
1178
moduli_type, sym_ngen - previous_rank)
1179
# if moduli_type > -1:
1180
# dsave("sparse-%s,%s,%s,%s|%s,%s,%s", g, r, n, moduli_type,
1181
# len(answer), sym_ngen - previous_rank, floor(get_memory_usage()))
1182
# if moduli_type >= 0 and sym_ngen-previous_rank != betti(g,r,tuple([1 for i in range(n)]),moduli_type):
1183
# dprint("ERROR: %s,%s,%s,%s",g,r,n,moduli_type)
1184
# return
1185
return answer
1186
1187
1188
# TODO: use matrix(D, sparse=True).rank()
1189
# be careful that building the matrix is what costs most of the time!
1190
def compute_rank_sparse(D, row_order, col_order):
1191
r"""
1192
Return the rank of the sparse matrix ``D`` given as a dictionary.
1193
1194
EXAMPLES::
1195
1196
sage: from admcycles.DR.relations import compute_rank_sparse
1197
sage: compute_rank_sparse({(0, 1): 1, (1, 0): 2}, [0 ,1], [0, 1])
1198
2
1199
"""
1200
count = 0
1201
nrows = len(row_order)
1202
ncols = len(col_order)
1203
row_order_rank = [-1 for i in range(nrows)]
1204
col_order_rank = [-1 for i in range(ncols)]
1205
for i in range(nrows):
1206
row_order_rank[row_order[i]] = i
1207
for i in range(ncols):
1208
col_order_rank[col_order[i]] = i
1209
1210
row_contents = [set() for i in range(nrows)]
1211
col_contents = [set() for i in range(ncols)]
1212
for x in D:
1213
row_contents[x[0]].add(x[1])
1214
col_contents[x[1]].add(x[0])
1215
1216
for i in row_order:
1217
S = []
1218
for j in row_contents[i]:
1219
S.append(j)
1220
if not S:
1221
continue
1222
count += 1
1223
S.sort(key=lambda x: col_order_rank[x])
1224
j = S[0]
1225
T = []
1226
for ii in col_contents[j]:
1227
if row_order_rank[ii] > row_order_rank[i]:
1228
T.append(ii)
1229
for k in S[1:]:
1230
rat = D[i, k] / QQ(D[i, j])
1231
for ii in T:
1232
if (ii, k) not in D:
1233
D[ii, k] = 0
1234
row_contents[ii].add(k)
1235
col_contents[k].add(ii)
1236
D[ii, k] -= rat * D[ii, j]
1237
if D[ii, k] == 0:
1238
D.pop((ii, k))
1239
row_contents[ii].remove(k)
1240
col_contents[k].remove(ii)
1241
for ii in T:
1242
D.pop((ii, j))
1243
row_contents[ii].remove(j)
1244
col_contents[j].remove(ii)
1245
return count
1246
1247
1248
def num_new_rels(g, r, n=0, moduli_type=MODULI_ST):
1249
return len(choose_basic_rels(g, r, n, moduli_type))
1250
1251
1252
def goren_rels(g, r, markings=(), moduli_type=MODULI_ST):
1253
r"""
1254
Return the kernel of the pairing in degree ``r``.
1255
1256
EXAMPLES::
1257
1258
sage: from admcycles.DR.relations import goren_rels
1259
sage: goren_rels(3, 2) # long time
1260
[
1261
(1, 0, 0, -41/21, 4/35, 0, 0, 0, -5/42, 41/504, 1/105, -11/140, 1/5040),
1262
(0, 1, 0, -89/7, -11/35, 0, 0, 0, -5/7, 103/168, -1/7, -47/70, -1/140),
1263
(0, 0, 1, -1, -7/5, 0, 0, 0, 0, 0, -1/10, 0, 0),
1264
(0, 0, 0, 0, 0, 1, 0, 0, 0, -1/24, 0, 0, 0),
1265
(0, 0, 0, 0, 0, 0, 1, 0, 0, -1/24, 0, 0, 0),
1266
(0, 0, 0, 0, 0, 0, 0, 1, -2, 1, -7/5, -7/5, -1/10)
1267
]
1268
"""
1269
gorenstein_precompute(g, r, markings, moduli_type)
1270
r3 = dim_form(g, len(markings), moduli_type)
1271
r2 = r3 - r
1272
S1 = tuple(range(num_strata(g, r, markings, moduli_type)))
1273
S2 = tuple(good_generator_list(g, r2, markings, moduli_type))
1274
M = pairing_submatrix(S1, S2, g, r, markings, moduli_type)
1275
return matrix(M).kernel().basis()
1276
1277
1278
@cached_function
1279
def good_generator_list(g, r, markings=(), moduli_type=MODULI_ST):
1280
r"""
1281
Return a subset of indices whose corresponding strata generate
1282
the ``r``-th degree part of the tautological ring.
1283
1284
EXAMPLES::
1285
1286
sage: from admcycles.DR.moduli import MODULI_SM, MODULI_RT, MODULI_CT, MODULI_ST
1287
sage: from admcycles.DR.relations import good_generator_list
1288
sage: good_generator_list(3, 1, (), MODULI_ST)
1289
[0, 1, 2]
1290
sage: good_generator_list(3, 2, (), MODULI_ST)
1291
[1, 3, 4, 8, 9, 10, 11, 12]
1292
sage: good_generator_list(3, 3, (), MODULI_ST)
1293
[9, 22, 30, 31, 32, 35, 36, 37, 38, 39]
1294
sage: good_generator_list(3, 4, (), MODULI_ST)
1295
[86, 87, 109, 110, 111, 112, 113, 114, 117, 118, 119, 120]
1296
sage: good_generator_list(3, 5, (), MODULI_ST)
1297
[237, 238, 239, 257, 258, 259, 260, 261]
1298
sage: good_generator_list(3, 6, (), MODULI_ST)
1299
[373, 374, 375, 376, 377]
1300
"""
1301
gens = all_strata(g, r, markings, moduli_type)
1302
good_gens = []
1303
ngens = len(gens)
1304
for num in range(ngens):
1305
G = gens[num]
1306
good = True
1307
for i in range(1, G.M.nrows()):
1308
g = G.M[i, 0][0]
1309
codim = 0
1310
for d in range(1, r + 1):
1311
if G.M[i, 0][d] != 0 and 3 * d > g:
1312
good = False
1313
break
1314
codim += d * G.M[i, 0][d]
1315
if not good:
1316
break
1317
for j in range(1, G.M.ncols()):
1318
codim += G.M[i, j][1]
1319
codim += G.M[i, j][2]
1320
if codim > 0 and codim >= g:
1321
good = False
1322
break
1323
if good:
1324
good_gens.append(num)
1325
return good_gens
1326
1327
1328
def choose_orders_sparse(D, nrows, ncols):
1329
row_nums = [0 for i in range(nrows)]
1330
col_nums = [0 for j in range(ncols)]
1331
for key in D:
1332
row_nums[key[0]] += 1
1333
col_nums[key[1]] += 1
1334
row_order = list(range(nrows))
1335
col_order = list(range(ncols))
1336
row_order.sort(key=lambda x: row_nums[x])
1337
col_order.sort(key=lambda x: col_nums[x])
1338
return row_order, col_order
1339
1340
1341
def choose_orders(L):
1342
rows = len(L)
1343
if rows == 0:
1344
return [], []
1345
cols = len(L[0])
1346
row_nums = [0 for i in range(rows)]
1347
col_nums = [0 for j in range(cols)]
1348
for i in range(rows):
1349
for j in range(cols):
1350
if L[i][j] != 0:
1351
row_nums[i] += 1
1352
col_nums[j] += 1
1353
row_order = list(range(rows))
1354
col_order = list(range(cols))
1355
row_order.sort(key=lambda x: row_nums[x])
1356
col_order.sort(key=lambda x: col_nums[x])
1357
return row_order, col_order
1358
1359
1360
# TODO: use matrix(L).rank()
1361
# be careful that building the matrix is what costs most of the time!
1362
def compute_rank(L):
1363
r"""
1364
Return the rank of the matrix ``L`` given as a list of lists.
1365
1366
EXAMPLES::
1367
1368
sage: from admcycles.DR.relations import compute_rank
1369
sage: compute_rank([[1, 2], [3, 4]])
1370
2
1371
sage: compute_rank([[1, 2], [2, 4]])
1372
1
1373
sage: compute_rank([[0, 0], [0, 0]])
1374
0
1375
"""
1376
count = 0
1377
for i in range(len(L)):
1378
S = [j for j in range(len(L[0])) if L[i][j] != 0]
1379
if not S:
1380
continue
1381
count += 1
1382
j = S[0]
1383
T = [ii for ii in range(i + 1, len(L))
1384
if L[ii][j] != 0]
1385
for k in S[1:]:
1386
rat = L[i][k] / L[i][j]
1387
for ii in T:
1388
L[ii][k] -= rat * L[ii][j]
1389
for ii in range(i + 1, len(L)):
1390
L[ii][j] = 0
1391
return count
1392
1393
1394
def compute_rank2(L, row_order, col_order):
1395
count = 0
1396
for irow in range(len(row_order)):
1397
i = row_order[irow]
1398
S = [j for j in col_order if L[i][j] != 0]
1399
if not S:
1400
continue
1401
count += 1
1402
j = S[0]
1403
T = [ii for ii in row_order[irow + 1:]
1404
if L[ii][j] != 0]
1405
for k in S[1:]:
1406
rat = L[i][k] / L[i][j]
1407
for ii in T:
1408
L[ii][k] -= rat * L[ii][j]
1409
for ii in T:
1410
L[ii][j] = 0
1411
return count
1412
1413