Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupport News AboutSign UpSign In
| Download
Project: admcycles
Views: 724
Visibility: Unlisted (only visible to those who know the link)
Image: ubuntu2004
1
# This file was *autogenerated* from the file DR.sage
2
from __future__ import absolute_import, print_function
3
import itertools
4
from six.moves import range
5
6
from copy import copy
7
8
from sage.misc.cachefunc import cached_function
9
from sage.rings.all import PolynomialRing, ZZ
10
from sage.rings.rational_field import QQ
11
from sage.arith.misc import factorial, binomial, bernoulli, multinomial
12
from sage.matrix.constructor import matrix, Matrix
13
from sage.combinat.all import Combinations, IntegerVectors, Partitions, Permutations, Subsets
14
from sage.functions.other import floor, ceil
15
from sage.symbolic.ring import SR
16
from sage.modules.free_module_element import vector
17
from sage.modules.free_module import span
18
from sage.misc.getusage import get_memory_usage
19
20
# Sage code by Aaron Pixton ([email protected])
21
#
22
# This file contains code to compute the double ramification cycle and simplify
23
# it using the 3-spin tautological relations.
24
#
25
# The functions are nearly all undocumented. The main function to use to
26
# compute DR cycles is the following:
27
# - DR_compute(g,r,n,dvector,kval=0,moduli_type=MODULI_ST):
28
# g = genus
29
# r = cohomological degree (set to g for the actual DR cycle, should give
30
# tautological relations for r > g)
31
# n = number of marked points
32
# dvector = vector of weights to place on the marked points, should have
33
# sum zero in the case of the DR cycle
34
# kval: 0 by default (DR cycles), can be set to other integers to twist
35
# by copies of the log canonical bundle (e.g. 1 for differentials)
36
# moduli_type: MODULI_ST by default (moduli of stable curves), can be
37
# set to moduli spaces containing less of the boundary (e.g.
38
# MODULI_CT for compact type) to compute there instead
39
#
40
# Example: to compute the genus 1 DR cycle with weight vector (2,-2):
41
#
42
# sage: DR_compute(1,1,2,(2,-2))
43
# (0, 2, 2, 0, -1/24)
44
#
45
# The function returns a vector of rational numbers giving the coefficients
46
# of the standard tautological generators (xi_Gamma)_*(kappa-psi monomial).
47
# To see how the generators are ordered, you can view a list of them by the
48
# command:
49
#
50
# sage: list_strata(1,1,2)
51
# generator 0
52
# [ -1 1 2]
53
# [X + 1 1 1]
54
# -------------------------
55
# generator 1
56
# [ -1 1 2]
57
# [ 1 X + 1 1]
58
# -------------------------
59
# generator 2
60
# [ -1 1 2]
61
# [ 1 1 X + 1]
62
# -------------------------
63
# generator 3
64
# [-1 1 2 0]
65
# [ 0 1 1 1]
66
# [ 1 0 0 1]
67
# -------------------------
68
# generator 4
69
# [-1 0 1 2]
70
# [ 0 2 1 1]
71
# -------------------------
72
#
73
# Here each generator is represented by an augmented vertex-edge incidence
74
# matrix:
75
# - each row after the first one corresponds to a vertex
76
# - each column after the first one corresponds to an edge or leg
77
# - the first column gives the genera of the vertices
78
# - the first row gives the markings on the legs
79
# - the other cells are 0 if the vertex and edge/leg are not incident
80
# 1 if the vertex and edge/leg are incident once
81
# 2 if the edge is a loop at the vertex
82
# - entries with polynomials in X describe kappa/psi decorations:
83
# - in the first column, each X^n term corresponds to a kappa_n at that
84
# vertex
85
# - in other locations, each X term corresponds to a psi at that half-edge
86
# - at loops, 2 + aX + bX^2 corresponds to having psi^a on one side of the
87
# loop and psi^b on the other
88
#
89
# In the example above, the five classes described on R^1(Mbar_{1,2}) are:
90
# kappa_1, psi_1, psi_2, delta_{12}, delta_{irr}.
91
# (Here by delta_{irr} we mean the pushforward of 1 under the gluing map
92
# Mbar_{0,4} -> Mbar_{1,2}, so twice the class of the physical locus.)
93
#
94
#
95
# There are a couple other functions that might be convenient when computing
96
# DR cycles:
97
# - DR_sparse: same as DR_compute except that it returns the answer as a
98
# sparse vector, e.g.
99
# sage: DR_sparse(1,1,2,(2,-2))
100
# [[1, 2], [2, 2], [4, -1/24]]
101
# - DR_reduced: same as DR_compute except that it only requires two arguments
102
# (g and dvector) and simplifies the answer using the 3-spin
103
# tautological relations, e.g.
104
# sage: DR_reduced(1,(2,-2))
105
# (0, 0, 0, 4, 1/8)
106
107
108
R = PolynomialRing(ZZ, 1, order='lex', names=('X',))
109
X = R.gen()
110
A_list = [ZZ(factorial(6*n))/(factorial(3*n)*factorial(2*n))
111
for n in range(100)]
112
B_list = [ZZ(factorial(6*n+1))/((6*n-1)*factorial(3*n)*factorial(2*n))
113
for n in range(100)]
114
MODULI_SMALL = -1
115
MODULI_SM = 0
116
MODULI_RT = 1
117
MODULI_CT = 2
118
MODULI_ST = 3
119
ENABLE_DPRINT = True
120
ENABLE_DSAVE = False
121
122
123
def dprint(string, *args):
124
if ENABLE_DPRINT:
125
print(string % args)
126
127
128
def dsave(string, *args):
129
if ENABLE_DSAVE:
130
save(0, string % args)
131
132
133
class Graph:
134
def __init__(self, M=None, genus_list=None):
135
if M:
136
self.M = copy(M)
137
elif genus_list:
138
self.M = matrix(R, len(genus_list)+1,
139
1, [-1]+genus_list)
140
else:
141
self.M = matrix(R, 1, 1, -1)
142
143
def num_vertices(self):
144
return self.M.nrows() - 1
145
146
def num_edges(self):
147
return self.M.ncols() - 1
148
149
def h1(self):
150
return self.M.ncols() - self.M.nrows() + 1
151
152
def add_vertex(self, g):
153
self.M = self.M.stack(matrix(1, self.M.ncols()))
154
self.M[-1, 0] = g
155
156
def add_edge(self, i1, i2, marking=0):
157
self.M = self.M.augment(matrix(self.M.nrows(), 1))
158
self.M[0, -1] = marking
159
if i1 > 0:
160
self.M[i1, -1] += 1
161
if i2 > 0:
162
self.M[i2, -1] += 1
163
164
def del_vertex(self, i):
165
self.M = self.M[:i].stack(self.M[(i + 1):])
166
167
def del_edge(self, i):
168
if i == self.num_edges():
169
self.M = self.M[0:, :i]
170
else:
171
self.M = self.M[0:, :i].augment(self.M[0:, (i + 1):])
172
173
def compute_degree_vec(self):
174
self.degree_vec = [0 for i in range(1, self.M.nrows())]
175
for i in range(1, self.M.nrows()):
176
for j in range(1, self.M.ncols()):
177
self.degree_vec[i - 1] += self.M[i, j][0]
178
179
def degree(self, i):
180
return self.degree_vec[i - 1]
181
182
def split_vertex(self, i, row1, row2):
183
self.M = self.M.stack(matrix(2, self.M.ncols(), row1+row2))
184
self.add_edge(self.M.nrows()-2,
185
self.M.nrows()-1)
186
self.del_vertex(i)
187
188
def set_target_parity(self):
189
self.target_parity = 0
190
for i in range(1, self.M.nrows()):
191
local_parity = 1 + self.M[i, 0][0]
192
for j in range(1, self.M.ncols()):
193
local_parity += self.M[i, j][1] + self.M[i, j][2]
194
for j in range(1, self.M[i, 0].degree()+1):
195
local_parity += j*self.M[i, 0][j]
196
local_parity %= 2
197
self.target_parity += (local_parity << (i-1))
198
199
def replace_vertex_with_graph(self, i, G):
200
nv = self.num_vertices()
201
ne = self.num_edges()
202
# i should have degree d, there should be no classes near i, and G should have markings 1,...,d and genus equal to the genus of i
203
hedge_list = []
204
for k in range(1, self.M.ncols()):
205
for j in range(self.M[i, k][0]):
206
hedge_list.append(k)
207
self.del_vertex(i)
208
for j in range(G.num_edges() - len(hedge_list)):
209
self.add_edge(0, 0)
210
for j in range(G.num_vertices()):
211
self.add_vertex(G.M[j+1, 0])
212
col = ne+1
213
for k in range(1, G.M.ncols()):
214
if G.M[0, k] > 0:
215
mark = ZZ(G.M[0, k])
216
for j in range(G.num_vertices()):
217
if self.M[nv+j, hedge_list[mark-1]] == 0:
218
self.M[nv+j, hedge_list[mark-1]] = G.M[j+1, k]
219
elif G.M[j+1, k] != 0:
220
a = self.M[nv+j, hedge_list[mark-1]][1]
221
b = G.M[j+1, k][1]
222
self.M[nv+j, hedge_list[mark-1]] = 2 + \
223
max(a, b)*X + min(a, b)*X**2
224
else:
225
for j in range(G.num_vertices()):
226
self.M[nv+j, col] = G.M[j+1, k]
227
col += 1
228
229
def compute_invariant(self):
230
nr, nc = self.M.nrows(), self.M.ncols()
231
self.invariant = [[self.M[i, 0], [], [], [
232
[] for j in range(1, nr)]] for i in range(1, nr)]
233
for k in range(1, nc):
234
L = [i for i in range(1, nr)
235
if self.M[i, k] != 0]
236
if len(L) == 1:
237
if self.M[0, k] != 0:
238
self.invariant[L[0] -
239
1][2].append((self.M[0, k], self.M[L[0], k]))
240
else:
241
self.invariant[L[0]-1][1].append(self.M[L[0], k])
242
else:
243
self.invariant[L[0]-1][3][L[1]-1].append((self.M[L[0], k],
244
self.M[L[1], k]))
245
self.invariant[L[1]-1][3][L[0]-1].append((self.M[L[1], k],
246
self.M[L[0], k]))
247
for i in range(1, nr):
248
self.invariant[i - 1][3] = [term for term in self.invariant[i - 1][3]
249
if len(term)]
250
for k in range(len(self.invariant[i - 1][3])):
251
self.invariant[i - 1][3][k].sort()
252
self.invariant[i -
253
1][3][k] = tuple(self.invariant[i - 1][3][k])
254
self.invariant[i - 1][3].sort()
255
self.invariant[i - 1][3] = tuple(self.invariant[i - 1][3])
256
self.invariant[i - 1][2].sort()
257
self.invariant[i - 1][2] = tuple(self.invariant[i - 1][2])
258
self.invariant[i - 1][1].sort()
259
self.invariant[i - 1][1] = tuple(self.invariant[i - 1][1])
260
self.invariant[i - 1] = tuple(self.invariant[i - 1])
261
vertex_invariants = [[i, self.invariant[i-1]]
262
for i in range(1, nr)]
263
self.invariant.sort()
264
self.invariant = tuple(self.invariant)
265
vertex_invariants.sort(key=lambda x: x[1])
266
self.vertex_groupings = []
267
for i in range(nr-1):
268
if i == 0 or vertex_invariants[i][1] != vertex_invariants[i-1][1]:
269
self.vertex_groupings.append([])
270
self.vertex_groupings[-1].append(
271
vertex_invariants[i][0])
272
273
def purify(self):
274
for i in range(self.M.nrows()):
275
for j in range(self.M.ncols()):
276
self.M[i, j] = R(self.M[i, j][0])
277
278
def contract(self, i, vlist, elist):
279
# assumes graph is undecorated
280
if self.M[0, i] != 0:
281
print("ERROR: cannot contract a marking")
282
return
283
S = [row for row in range(
284
1, self.M.nrows()) if self.M[row, i] != 0]
285
if len(S) == 1:
286
self.M[S[0], 0] += 1
287
self.del_edge(i)
288
elist = elist[:(i-1)] + elist[i:]
289
else:
290
self.del_edge(i)
291
elist = elist[:(i-1)] + elist[i:]
292
self.add_vertex(0)
293
self.M[-1] += self.M[S[0]]
294
self.M[-1] += self.M[S[1]]
295
self.del_vertex(S[1])
296
self.del_vertex(S[0])
297
vlist = vlist[:(S[0]-1)] + vlist[S[0]:(S[1]-1)] + \
298
vlist[S[1]:] + [vlist[S[0]-1] + vlist[S[1]-1]]
299
return vlist, elist
300
301
302
def graph_isomorphic(G1, G2):
303
if G1.invariant != G2.invariant:
304
return False
305
else:
306
return isomorphic(G1.M, G2.M, G1.vertex_groupings, G2.vertex_groupings)
307
308
309
def isomorphic(M1, M2, group1, group2):
310
nr, nc = M1.nrows(), M1.ncols()
311
PermList = [Permutations(list(range(len(group)))) for group in group1]
312
for sigma_data in itertools.product(*PermList):
313
sigma = [0 for i in range(nr - 1)]
314
for i in range(len(group1)):
315
for j in range(len(group1[i])):
316
sigma[group1[i][j] - 1] = group2[i][sigma_data[i][j]]
317
good = True
318
for i in range(1, nr):
319
ii = sigma[i-1]
320
for j in range(1, i):
321
jj = sigma[j-1]
322
L1 = []
323
for k in range(1, nc):
324
if M1[i, k] != 0 and M1[j, k] != 0:
325
L1.append([M1[i, k], M1[j, k]])
326
L1.sort()
327
L2 = []
328
for k in range(1, nc):
329
if M2[ii, k] != 0 and M2[jj, k] != 0:
330
L2.append([M2[ii, k], M2[jj, k]])
331
L2.sort()
332
if L1 != L2:
333
good = False
334
break
335
if good is False:
336
break
337
if good:
338
return True
339
return False
340
341
342
def graph_count_automorphisms(G, vertex_orbits=False):
343
return count_automorphisms(G.M, G.vertex_groupings, vertex_orbits)
344
345
346
def count_automorphisms(M, grouping, vertex_orbits=False):
347
nr, nc = M.nrows(), M.ncols()
348
count = ZZ.zero()
349
PermList = [Permutations(list(range(len(group)))) for group in grouping]
350
if vertex_orbits:
351
isom_list = []
352
for sigma_data in itertools.product(*PermList):
353
sigma = [0 for i in range(nr-1)]
354
for i in range(len(grouping)):
355
for j in range(len(grouping[i])):
356
sigma[grouping[i][j]-1] = grouping[i][sigma_data[i][j]]
357
good = True
358
for i in range(1, nr):
359
ii = sigma[i-1]
360
for j in range(1, i):
361
jj = sigma[j-1]
362
L1 = []
363
for k in range(1, nc):
364
if M[i, k] != 0 and M[j, k] != 0:
365
L1.append([M[i, k], M[j, k]])
366
L1.sort()
367
L2 = []
368
for k in range(1, nc):
369
if M[ii, k] != 0 and M[jj, k] != 0:
370
L2.append([M[ii, k], M[jj, k]])
371
L2.sort()
372
if L1 != L2:
373
good = False
374
break
375
if good is False:
376
break
377
if good:
378
count += 1
379
if vertex_orbits:
380
isom_list.append(sigma)
381
382
if vertex_orbits:
383
orbit_list = []
384
vertices_used = []
385
while len(vertices_used) < nr - 1:
386
i = [ii for ii in range(1, nr) if ii not in vertices_used][0]
387
orbit = []
388
for sigma in isom_list:
389
if sigma[i - 1] not in orbit:
390
orbit.append(sigma[i - 1])
391
vertices_used.append(sigma[i - 1])
392
orbit.sort()
393
orbit_list.append(orbit)
394
return orbit_list
395
396
for i in range(1, nr):
397
for k in range(1, nc):
398
if M[i, k][0] == 2 and M[i, k][1] == M[i, k][2]:
399
count *= 2
400
L = []
401
for k in range(1, nc):
402
if M[i, k] != 0:
403
if sum(1 for j in range(1, nr) if M[j, k] != 0) == 1:
404
L.append([M[0, k], M[i, k]])
405
count *= aut(L)
406
407
for j in range(1, i):
408
L = []
409
for k in range(1, nc):
410
if M[i, k] != 0 and M[j, k] != 0:
411
L.append([M[i, k], M[j, k]])
412
count *= aut(L)
413
return count
414
415
416
def graph_list_isomorphisms(G1, G2, only_one=False):
417
if G1.invariant != G2.invariant:
418
return []
419
else:
420
return list_isomorphisms(G1.M, G2.M, G1.vertex_groupings, G2.vertex_groupings, only_one)
421
422
423
def list_isomorphisms(M1, M2, group1, group2, only_one=False):
424
# Warning: does not count loops!
425
# If this is too slow, we can probably improve by caching a list of automorphisms and applying those to the first isom found.
426
nr, nc = M1.nrows(), M2.ncols()
427
PermList = [Permutations(list(range(len(group)))) for group in group1]
428
isom_list = []
429
for sigma_data in itertools.product(*PermList):
430
sigma = [0 for i in range(nr - 1)]
431
for i in range(len(group1)):
432
for j in range(len(group1[i])):
433
sigma[group1[i][j] - 1] = group2[i][sigma_data[i][j]]
434
good = True
435
for i in range(1, nr):
436
ii = sigma[i-1]
437
for j in range(1, i):
438
jj = sigma[j-1]
439
L1 = []
440
for k in range(1, nc):
441
if M1[i, k] != 0 and M1[j, k] != 0:
442
L1.append([M1[i, k], M1[j, k]])
443
L1.sort()
444
L2 = []
445
for k in range(1, nc):
446
if M2[ii, k] != 0 and M2[jj, k] != 0:
447
L2.append([M2[ii, k], M2[jj, k]])
448
L2.sort()
449
if L1 != L2:
450
good = False
451
break
452
if not good:
453
break
454
if good:
455
cols1 = [[M1[i, j] for i in range(nr)] for j in range(1, nc)]
456
cols2 = [[M2[0, j]] + [M2[sigma[i - 1], j] for i in range(1, nr)]
457
for j in range(1, nc)]
458
edge_group1 = []
459
edge_group2 = []
460
used1 = []
461
for j in range(1, nc):
462
if j not in used1:
463
edge_group1.append([])
464
edge_group2.append([])
465
for k in range(1, nc):
466
if cols1[k-1] == cols1[j-1]:
467
edge_group1[-1].append(k)
468
used1.append(k)
469
if cols2[k-1] == cols1[j-1]:
470
edge_group2[-1].append(k)
471
edge_PermList = [Permutations(
472
list(range(len(edge_group)))) for edge_group in edge_group1]
473
for edge_sigma_data in itertools.product(*edge_PermList):
474
edge_sigma = [0 for i in range(nc - 1)]
475
for i in range(len(edge_group1)):
476
for j in range(len(edge_group1[i])):
477
edge_sigma[edge_group1[i][j] -
478
1] = edge_group2[i][edge_sigma_data[i][j]]
479
isom_list.append([sigma, edge_sigma])
480
if only_one:
481
return isom_list
482
return isom_list
483
484
485
def aut(L):
486
"""
487
Return the cardinality of the automorphism group of the list ``L``.
488
489
EXAMPLES::
490
491
sage: from admcycles.DR import aut
492
sage: aut([])
493
1
494
sage: aut([4,1,3,2])
495
1
496
sage: aut([4,5,6,5,4,4,6])
497
24
498
"""
499
if not L:
500
return ZZ.one()
501
L.sort()
502
total = ZZ.one()
503
n = 1
504
last = L[0]
505
for l in L[1:]:
506
if l == last:
507
n += 1
508
total *= n
509
else:
510
n = 1
511
last = l
512
return total
513
514
515
def degenerate(G_list, moduli_type=MODULI_ST):
516
mod_size = moduli_type + 1
517
if moduli_type == MODULI_SMALL:
518
mod_size = MODULI_SM + 1
519
G_list_new = [[] for i in range(mod_size)]
520
for which_type in range(mod_size):
521
for G in G_list[which_type]:
522
for i in range(1, G.num_vertices()+1):
523
row = list(G.M[i])
524
m = row[0] + sum(row)
525
if m < 4:
526
continue
527
row1 = [0 for j in range(len(row))]
528
while [2 * x for x in row1] <= row:
529
if row1[0] == 1 and moduli_type <= MODULI_RT:
530
break
531
if row1[0] + sum(row1) >= 2 and row1[0] + sum(row1) <= m - 2:
532
row2 = [row[j] - row1[j] for j in range(len(row))]
533
G_copy = Graph(G.M)
534
G_copy.split_vertex(i, row1, row2)
535
new_type = which_type
536
if new_type == MODULI_SM:
537
new_type = MODULI_RT
538
if new_type == MODULI_RT and row1[0] > 0:
539
new_type = MODULI_CT
540
G_list_new[new_type].append(G_copy)
541
row1[-1] += 1
542
for j in range(1, len(row)):
543
if row1[-j] <= row[-j]:
544
break
545
row1[-j] = 0
546
row1[-j-1] += 1
547
for i in range(mod_size):
548
G_list_new[i] = remove_isomorphic(G_list_new[i])
549
return G_list_new
550
551
552
def dim_form(g, n, moduli_type=MODULI_ST):
553
g = ZZ(g)
554
n = ZZ(n)
555
if moduli_type == MODULI_ST:
556
return 3 * g - 3 + n
557
if moduli_type == MODULI_CT:
558
return 2 * g - 3 + n
559
if moduli_type == MODULI_RT:
560
if g > 0:
561
return g - 2 + n
562
else:
563
return n - 3
564
if moduli_type == MODULI_SM:
565
if n == 0:
566
return g - 2
567
elif g >= 1:
568
return g - 1
569
else:
570
return ZZ.zero()
571
if moduli_type == MODULI_SMALL:
572
return ZZ(1000)
573
return 3 * g - 3 + n
574
575
576
def decorate(G_list, r, moduli_type=MODULI_ST):
577
mod_size = moduli_type + 1
578
if moduli_type == MODULI_SMALL:
579
mod_size = MODULI_SM + 1
580
G_list_new = [[] for i in range(mod_size)]
581
for which_type in range(mod_size):
582
for G in G_list[which_type]:
583
G_deco = [[] for i in range(mod_size)]
584
G.compute_degree_vec()
585
nr, nc = G.M.nrows(), G.M.ncols()
586
two_list = []
587
one_list = []
588
for i in range(1, nr):
589
for j in range(1, nc):
590
if G.M[i, j] == 2:
591
two_list.append([i, j])
592
elif G.M[i, j] == 1:
593
one_list.append([i, j])
594
a = nr-1
595
b = len(two_list)
596
c = len(one_list)
597
dims = [[dim_form(G.M[i + 1, 0][0], G.degree(i+1), mod_type)
598
for i in range(a)] for mod_type in range(mod_size)]
599
for vec in IntegerVectors(r, a+b+c):
600
new_type = which_type
601
if moduli_type > MODULI_SMALL:
602
test_dims = vec[:a]
603
for i in range(b):
604
test_dims[two_list[i][0] - 1] += vec[a + i]
605
for i in range(c):
606
test_dims[one_list[i][0] - 1] += vec[a + b + i]
607
for mod_type in range(which_type, mod_size):
608
for i in range(a):
609
if test_dims[i] > dims[mod_type][i]:
610
new_type = mod_type + 1
611
break
612
if new_type > moduli_type:
613
continue
614
S_list = []
615
for i in range(a):
616
S_list.append(Partitions(vec[i]))
617
for i in range(a, a+b):
618
S_list.append(
619
[[vec[i]-j, j] for j in range(floor(vec[i]/ZZ(2) + 1))])
620
S = itertools.product(*S_list)
621
for vec2 in S:
622
G_copy = Graph(G.M)
623
for i in range(a):
624
for j in vec2[i]:
625
G_copy.M[i + 1, 0] += X**j
626
for i in range(a, a+b):
627
G_copy.M[two_list[i-a][0], two_list[i-a]
628
[1]] += vec2[i][0]*X + vec2[i][1]*X**2
629
for i in range(c):
630
G_copy.M[one_list[i][0],
631
one_list[i][1]] += vec[i+a+b]*X
632
G_deco[new_type].append(G_copy)
633
for mod_type in range(mod_size):
634
G_list_new[mod_type] += remove_isomorphic(G_deco[mod_type])
635
return G_list_new
636
637
638
def remove_isomorphic(G_list):
639
G_list_new = []
640
inv_dict = {}
641
count = 0
642
for G1 in G_list:
643
G1.compute_invariant()
644
if G1.invariant not in inv_dict:
645
inv_dict[G1.invariant] = []
646
good = True
647
for i in inv_dict[G1.invariant]:
648
if graph_isomorphic(G1, G_list_new[i]):
649
good = False
650
break
651
if good:
652
G_list_new.append(G1)
653
inv_dict[G1.invariant].append(count)
654
count += 1
655
return G_list_new
656
657
658
def num_strata(g, r, markings=(), moduli_type=MODULI_ST):
659
return len(all_strata(g, r, markings, moduli_type))
660
661
662
def num_pure_strata(g, r, markings=(), moduli_type=MODULI_ST):
663
return len(all_pure_strata(g, r, markings, moduli_type))
664
665
666
def single_stratum(num, g, r, markings=(), moduli_type=MODULI_ST):
667
return all_strata(g, r, markings, moduli_type)[num]
668
669
670
def single_pure_stratum(num, g, r, markings=(), moduli_type=MODULI_ST):
671
return all_pure_strata(g, r, markings, moduli_type)[num]
672
673
674
@cached_function
675
def autom_count(num, g, r, markings=(), moduli_type=MODULI_ST):
676
return graph_count_automorphisms(single_stratum(num, g, r, markings, moduli_type))
677
678
679
@cached_function
680
def pure_strata_autom_count(num, g, r, markings=(), moduli_type=MODULI_ST):
681
return graph_count_automorphisms(single_pure_stratum(num, g, r, markings, moduli_type))
682
683
684
@cached_function
685
def unpurify_map(g, r, markings=(), moduli_type=MODULI_ST):
686
unpurify = {}
687
pure_strata = [all_pure_strata(g, r0, markings, moduli_type)
688
for r0 in range(r+1)]
689
impure_strata = all_strata(g, r, markings, moduli_type)
690
for i, strati in enumerate(impure_strata):
691
G = Graph(strati.M)
692
G.purify()
693
r0 = G.num_edges() - len(markings)
694
found = False
695
for j in range(len(pure_strata[r0])):
696
if G.M == pure_strata[r0][j].M:
697
G_key = (r0, j)
698
found = True
699
break
700
if not found:
701
print("ERROR! Purification failed.")
702
if G_key not in unpurify:
703
unpurify[G_key] = []
704
unpurify[G_key].append(i)
705
return unpurify
706
707
708
@cached_function
709
def all_strata(g, r, markings=(), moduli_type=MODULI_ST):
710
mod_size = moduli_type + 1
711
if moduli_type == MODULI_SMALL:
712
mod_size = MODULI_SM + 1
713
big_list = [[] for i in range(mod_size)]
714
for loops in range(g+1):
715
if loops == 1 and moduli_type <= MODULI_CT:
716
break
717
if loops > r:
718
break
719
for edges in range(r-loops+1):
720
if edges == 1 and moduli_type <= MODULI_SM:
721
break
722
G = Graph()
723
G.add_vertex(g-loops)
724
for k in range(loops):
725
G.add_edge(1, 1)
726
for k in markings:
727
G.add_edge(1, 0, k)
728
GGG = [[] for i in range(mod_size)]
729
if loops == 0:
730
if edges == 0:
731
GGG[MODULI_SM] = [G]
732
else:
733
GGG[MODULI_RT] = [G]
734
else:
735
GGG[MODULI_ST] = [G]
736
for k in range(edges):
737
GGG = degenerate(GGG, moduli_type)
738
GGG = decorate(GGG, r-loops-edges, moduli_type)
739
for i in range(mod_size):
740
big_list[i] += GGG[i]
741
combined_list = []
742
for i in range(mod_size):
743
combined_list += big_list[i]
744
for G in combined_list:
745
G.compute_degree_vec()
746
G.set_target_parity()
747
return combined_list
748
749
750
@cached_function
751
def all_pure_strata(g, r, markings=(), moduli_type=MODULI_ST):
752
big_list = [[] for i in range(moduli_type+1)]
753
for loops in range(g+1):
754
if loops == 1 and moduli_type <= MODULI_CT:
755
break
756
if loops > r:
757
break
758
for edges in range(r-loops, r-loops+1):
759
if edges >= 1 and moduli_type <= MODULI_SM:
760
break
761
G = Graph()
762
G.add_vertex(g-loops)
763
for k in range(loops):
764
G.add_edge(1, 1)
765
for k in markings:
766
G.add_edge(1, 0, k)
767
G.compute_invariant()
768
GGG = [[] for i in range(moduli_type+1)]
769
if loops == 0:
770
if edges == 0:
771
GGG[MODULI_SM] = [G]
772
else:
773
GGG[MODULI_RT] = [G]
774
else:
775
GGG[MODULI_ST] = [G]
776
for k in range(edges):
777
GGG = degenerate(GGG, moduli_type)
778
for i in range(moduli_type+1):
779
big_list[i] += GGG[i]
780
combined_list = []
781
for i in range(moduli_type+1):
782
combined_list += big_list[i]
783
return combined_list
784
785
############################
786
787
788
def C_coeff(m, term):
789
n = term - m // 3
790
if n < 0:
791
return 0
792
if m % 3 == 0:
793
return A_list[n]
794
else:
795
return B_list[n]
796
797
798
@cached_function
799
def dual_C_coeff(i, j, parity):
800
total = ZZ.zero()
801
k = parity % 2
802
while k // 3 <= i:
803
if k % 3 != 2:
804
total += (-1)**(k // 3) * C_coeff(k, i) * C_coeff(-2 - k, j)
805
k += 2
806
return total
807
808
809
def poly_to_partition(F):
810
mmm = F.degree()
811
target_partition = []
812
for i in range(1, mmm+1):
813
for j in range(F[i]):
814
target_partition.append(i)
815
return tuple(target_partition)
816
817
818
@cached_function
819
def kappa_coeff(sigma, kappa_0, target_partition):
820
total = ZZ.zero()
821
num_ones = sum(1 for i in sigma if i == 1)
822
for i in range(0, num_ones+1):
823
for injection in Permutations(list(range(len(target_partition))), len(sigma)-i):
824
term = binomial(num_ones, i)*binomial(kappa_0 +
825
len(target_partition) + i-1, i)*factorial(i)
826
for j in range(len(sigma)-i):
827
term *= C_coeff(sigma[j+i], target_partition[injection[j]])
828
for j in range(len(target_partition)):
829
if j in injection:
830
continue
831
term *= C_coeff(0, target_partition[j])
832
total += term
833
total = (-1)**(len(target_partition)+len(sigma)) * \
834
total/aut(list(target_partition))
835
return total
836
837
838
@cached_function
839
def FZ_kappa_factor(num, sigma, g, r, markings=(), moduli_type=MODULI_ST):
840
G = single_stratum(num, g, r, markings, moduli_type)
841
L = []
842
nv = G.num_vertices()
843
for i in range(1, nv+1):
844
L.append((2 * G.M[i, 0][0] + G.degree(i) -
845
2, G.M[i, 0] - G.M[i, 0][0]))
846
LL = []
847
tau = []
848
for i in range(nv):
849
mini = -1
850
for j in range(nv):
851
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]):
852
mini = j
853
tau.append(mini)
854
LL.append(L[mini])
855
factor_dict = FZ_kappa_factor2(tuple(LL), sigma)
856
factor_vec = [0 for i in range(1 << nv)]
857
for parity_key in factor_dict:
858
parity = 0
859
for i in range(nv):
860
if parity_key[i] == 1:
861
parity += 1 << tau[i]
862
factor_vec[parity] = factor_dict[parity_key]
863
return factor_vec
864
865
866
@cached_function
867
def FZ_marking_factor(num, marking_vec, g, r, markings=(), moduli_type=MODULI_ST):
868
G = single_stratum(num, g, r, markings, moduli_type)
869
nv = G.num_vertices()
870
ne = G.num_edges()
871
num_parities = ZZ(2) ** nv
872
PPP_list = []
873
for marks in marking_vec:
874
PPP_list.append(Permutations(marks[1]))
875
PPP = itertools.product(*PPP_list)
876
marking_factors = [0 for i in range(num_parities)]
877
incident_vertices = []
878
for mark_type in marking_vec:
879
incident_vertices.append([])
880
for k in range(1, ne+1):
881
if G.M[0, k] == mark_type[0]:
882
for i in range(1, nv+1):
883
if G.M[i, k] != 0:
884
incident_vertices[-1].append((i - 1, G.M[i, k][1]))
885
break
886
for perms in PPP:
887
parity = 0
888
marking_factor = 1
889
for marks_index in range(len(marking_vec)):
890
for count in range(len(incident_vertices[marks_index])):
891
marking_factor *= C_coeff(perms[marks_index][count],
892
incident_vertices[marks_index][count][1])
893
parity ^= (perms[marks_index][count] %
894
ZZ(2)) << incident_vertices[marks_index][count][0]
895
marking_factors[parity] += marking_factor
896
return marking_factors
897
898
899
@cached_function
900
def FZ_kappa_factor2(L, sigma):
901
nv = len(L)
902
mmm = max((0,)+sigma)
903
sigma_grouped = [0 for i in range(mmm)]
904
for i in sigma:
905
sigma_grouped[i-1] += 1
906
S_list = []
907
for i in sigma_grouped:
908
S_list.append(IntegerVectors(i, nv))
909
S = itertools.product(*S_list)
910
kappa_factors = {}
911
for parity in itertools.product(*[(0, 1) for i in range(nv)]):
912
kappa_factors[tuple(parity)] = 0
913
for assignment in S:
914
assigned_sigma = [[] for j in range(nv)]
915
for i in range(mmm):
916
for j in range(nv):
917
for k in range(assignment[i][j]):
918
assigned_sigma[j].append(i+1)
919
sigma_auts = 1
920
parity = [0 for i in range(nv)]
921
kappa_factor = ZZ.one()
922
for j in range(nv):
923
sigma_auts *= aut(assigned_sigma[j])
924
parity[j] += sum(assigned_sigma[j])
925
parity[j] %= ZZ(2)
926
kappa_factor *= kappa_coeff(
927
tuple(assigned_sigma[j]), L[j][0], poly_to_partition(L[j][1]))
928
kappa_factors[tuple(parity)] += kappa_factor/sigma_auts
929
return kappa_factors
930
931
932
@cached_function
933
def FZ_hedge_factor(num, g, r, markings=(), moduli_type=MODULI_ST):
934
G = single_stratum(num, g, r, markings, moduli_type)
935
nv = G.num_vertices()
936
num_parities = ZZ(2) ** nv
937
ne = G.num_edges()
938
edge_list = []
939
for k in range(1, ne+1):
940
if G.M[0, k] == 0:
941
edge_list.append([k])
942
for i in range(1, nv+1):
943
if G.M[i, k] != 0:
944
edge_list[-1].append(i)
945
if G.M[i, k][0] == 2:
946
edge_list[-1].append(i)
947
hedge_factors = [0 for i in range(num_parities)]
948
for edge_parities in itertools.product(*[[0, 1] for i in edge_list]):
949
parity = 0
950
for i in range(len(edge_list)):
951
if edge_parities[i] == 1:
952
parity ^= 1 << (edge_list[i][1]-1)
953
parity ^= 1 << (edge_list[i][2]-1)
954
hedge_factor = 1
955
for i in range(len(edge_list)):
956
if edge_list[i][1] == edge_list[i][2]:
957
hedge_factor *= dual_C_coeff(G.M[edge_list[i][1], edge_list[i][0]][1],
958
G.M[edge_list[i][1], edge_list[i][0]][2], edge_parities[i] % ZZ(2))
959
else:
960
hedge_factor *= dual_C_coeff(G.M[edge_list[i][1], edge_list[i][0]][1],
961
G.M[edge_list[i][2], edge_list[i][0]][1], edge_parities[i] % ZZ(2))
962
hedge_factors[parity] += hedge_factor
963
return hedge_factors
964
965
966
@cached_function
967
def FZ_coeff(num, FZ_param, g, r, markings=(), moduli_type=MODULI_ST):
968
sigma = FZ_param[0]
969
marking_vec = FZ_param[1]
970
G = single_stratum(num, g, r, markings, moduli_type)
971
nv = G.num_vertices()
972
graph_auts = autom_count(num, g, r, markings, moduli_type)
973
h1_factor = ZZ(2) ** G.h1()
974
num_parities = ZZ(2) ** nv
975
976
marking_factors = FZ_marking_factor(
977
num, marking_vec, g, r, markings, moduli_type)
978
kappa_factors = FZ_kappa_factor(num, sigma, g, r, markings, moduli_type)
979
hedge_factors = FZ_hedge_factor(num, g, r, markings, moduli_type)
980
981
total = ZZ.zero()
982
for i in range(num_parities):
983
if marking_factors[i] == 0:
984
continue
985
for j in range(num_parities):
986
total += marking_factors[i]*kappa_factors[j] * \
987
hedge_factors[i ^ j ^ G.target_parity]
988
989
total /= h1_factor*graph_auts
990
return total
991
992
993
@cached_function
994
def interior_FZ(g, r, markings=(), moduli_type=MODULI_ST):
995
ngen = num_strata(g, r, markings, moduli_type)
996
relations = []
997
FZpl = FZ_param_list(3 * r - g - 1, markings)
998
for FZ_param in FZpl:
999
relation = [FZ_coeff(i, FZ_param, g, r, markings, moduli_type)
1000
for i in range(ngen)]
1001
relations.append(relation)
1002
return relations
1003
1004
1005
def possibly_new_FZ(g, r, n=0, moduli_type=MODULI_ST):
1006
m = 3 * r - g - 1 - n
1007
if m < 0:
1008
return []
1009
dprint("Start FZ (%s,%s,%s,%s): %s", g, r, n, moduli_type,
1010
floor(get_memory_usage()))
1011
markings = (1,) * n
1012
ngen = num_strata(g, r, markings, moduli_type)
1013
relations = []
1014
for i in range(m + 1):
1015
if m - i % 2:
1016
continue
1017
for sigma in Partitions(i):
1018
# TODO: the line above should iterate directly over the set
1019
# of partitions with all parts of size = 1 (mod 3)
1020
if any(j % 3 != 1 for j in sigma):
1021
continue
1022
if n > 0:
1023
FZ_param = (tuple(sigma), ((1, markings),))
1024
else:
1025
FZ_param = (tuple(sigma), ())
1026
relation = []
1027
for j in range(ngen):
1028
coeff = FZ_coeff(j, FZ_param, g, r, markings, moduli_type)
1029
if coeff != 0:
1030
relation.append([j, coeff])
1031
relations.append(relation)
1032
dprint("End FZ (%s,%s,%s,%s): %s", g, r, n, moduli_type,
1033
floor(get_memory_usage()))
1034
return relations
1035
1036
1037
@cached_function
1038
def boundary_FZ(g, r, markings=(), moduli_type=MODULI_ST):
1039
if moduli_type <= MODULI_SM:
1040
return []
1041
generators = all_strata(g, r, markings, moduli_type)
1042
ngen = len(generators)
1043
relations = []
1044
for r0 in range(1, r):
1045
strata = all_strata(g, r0, markings, moduli_type)
1046
for G in strata:
1047
vertex_orbits = graph_count_automorphisms(G, True)
1048
for i in [orbit[0] for orbit in vertex_orbits]:
1049
good = True
1050
for j in range(G.M.ncols()):
1051
if R(G.M[i, j][0]) != G.M[i, j]:
1052
good = False
1053
break
1054
if good:
1055
g2 = G.M[i, 0][0]
1056
if 3 * (r - r0) < g2 + 1:
1057
continue
1058
d = G.degree(i)
1059
if dim_form(g2, d, moduli_type) < r-r0:
1060
continue
1061
strata2 = all_strata(
1062
g2, r-r0, tuple(range(1, d+1)), moduli_type)
1063
which_gen_list = [-1 for num in range(len(strata2))]
1064
for num in range(len(strata2)):
1065
G_copy = Graph(G.M)
1066
G_copy.replace_vertex_with_graph(i, strata2[num])
1067
which_gen_list[num] = num_of_stratum(
1068
G_copy, g, r, markings, moduli_type)
1069
rFZpl = reduced_FZ_param_list(
1070
G, i, g2, d, 3 * (r - r0) - g2 - 1)
1071
ccccc = 0
1072
for FZ_param in rFZpl:
1073
relation = [0] * ngen
1074
for num in range(len(strata2)):
1075
if which_gen_list[num] != -1:
1076
relation[which_gen_list[num]] += FZ_coeff(num, FZ_param, g2, r-r0, tuple(
1077
range(1, d+1)), moduli_type)
1078
relations.append(relation)
1079
ccccc += 1
1080
return relations
1081
1082
1083
def list_all_FZ(g, r, markings=(), moduli_type=MODULI_ST):
1084
relations = copy(interior_FZ(g, r, markings, moduli_type))
1085
if moduli_type > MODULI_SM:
1086
relations += boundary_FZ(g, r, markings, moduli_type)
1087
if not relations:
1088
ngen = num_strata(g, r, markings, moduli_type)
1089
relations.append([0 for i in range(ngen)])
1090
return relations
1091
1092
1093
def reduced_FZ_param_list(G, v, g, d, n):
1094
params = FZ_param_list(n, tuple(range(1, d+1)))
1095
graph_params = []
1096
M = matrix(R, 2, d + 1)
1097
M[0, 0] = -1
1098
for i in range(1, d+1):
1099
M[0, i] = i
1100
for p in params:
1101
G_copy = Graph(G.M)
1102
M[1, 0] = -g-1
1103
for j in p[0]:
1104
M[1, 0] += X**j
1105
for i in range(1, d + 1):
1106
M[1, i] = 1 + p[1][i-1][1][0]*X
1107
G_p = Graph(M)
1108
G_copy.replace_vertex_with_graph(v, G_p)
1109
graph_params.append([p, G_copy])
1110
params_reduced = []
1111
graphs_seen = []
1112
for x in graph_params:
1113
x[1].compute_invariant()
1114
good = True
1115
for GG in graphs_seen:
1116
if graph_isomorphic(x[1], GG):
1117
good = False
1118
break
1119
if good:
1120
graphs_seen.append(x[1])
1121
params_reduced.append(x[0])
1122
return params_reduced
1123
1124
1125
def FZ_param_list(n, markings=()):
1126
if n < 0:
1127
return []
1128
final_list = []
1129
mmm = max((0,)+markings)
1130
markings_grouped = [0 for i in range(mmm)]
1131
for i in markings:
1132
markings_grouped[i-1] += 1
1133
markings_best = []
1134
for i in range(mmm):
1135
if markings_grouped[i] > 0:
1136
markings_best.append([i+1, markings_grouped[i]])
1137
for j in range(floor(n/ZZ(2) + 1)):
1138
for n_vec in IntegerVectors(n-2 * j, 1 + len(markings_best)):
1139
S_list = [[list(sigma) for sigma in Partitions(n_vec[0])
1140
if not any(l % 3 == 2 for l in sigma)]]
1141
# TODO: the line above should iterate directly over the set
1142
# of partitions with no parts of size = 2 (mod 3)
1143
for i in range(len(markings_best)):
1144
S_list.append(Partitions(
1145
n_vec[i+1]+markings_best[i][1], length=markings_best[i][1]).list())
1146
S_list[-1] = [[k - 1 for k in sigma] for sigma in S_list[-1]
1147
if sum(1 for l in sigma if (l % 3) == 0) == 0]
1148
for S in itertools.product(*S_list):
1149
final_list.append((tuple(S[0]), tuple([(markings_best[k][0], tuple(
1150
S[k+1])) for k in range(len(markings_best))])))
1151
return final_list
1152
1153
1154
def FZ_matrix(g, r, markings=(), moduli_type=MODULI_ST):
1155
return matrix(list_all_FZ(g, r, markings, moduli_type))
1156
1157
#####################################
1158
1159
1160
@cached_function
1161
def contraction_table(g, r, markings=(), moduli_type=MODULI_ST):
1162
contraction_dict = {}
1163
pure_strata = [all_pure_strata(g, r0, markings, moduli_type)
1164
for r0 in range(r+1)]
1165
for r0 in range(r+1):
1166
for ii in range(len(pure_strata[r0])):
1167
G = pure_strata[r0][ii]
1168
S = [j for j in range(1, G.M.ncols()) if G.M[0, j] == 0]
1169
contractions = {}
1170
for edge_subset in itertools.product(*[[0, 1] for i in range(r0)]):
1171
key = tuple(i for i in range(
1172
r0) if edge_subset[i] == 0)
1173
A = [S[i] for i in key]
1174
A.reverse()
1175
vlist = [[i] for i in range(1, G.M.nrows())]
1176
elist = list(range(1, G.M.ncols()))
1177
Gcopy = Graph(G.M)
1178
for i in A:
1179
vlist, elist = Gcopy.contract(i, vlist, elist)
1180
Gcopy.compute_invariant()
1181
rnew = r0 - len(A)
1182
contraction_result = []
1183
for i in range(len(pure_strata[rnew])):
1184
L = graph_list_isomorphisms(
1185
pure_strata[rnew][i], Gcopy, True)
1186
if L:
1187
contraction_result.append((rnew, i))
1188
contraction_result.append(L[0])
1189
break
1190
contraction_result.append((vlist, elist))
1191
contractions[key] = contraction_result
1192
1193
for edge_assignment in itertools.product(*[[0, 1, 2] for i in range(r0)]):
1194
if sum(1 for i in edge_assignment if i == 1) > r-r0:
1195
continue
1196
key1 = tuple(i for i in range(
1197
r0) if edge_assignment[i] == 0)
1198
B = [S[i]
1199
for i in range(r0) if edge_assignment[i] == 1]
1200
key2 = tuple(i for i in range(
1201
r0) if edge_assignment[i] == 2)
1202
if key1 > key2:
1203
continue
1204
contract1 = contractions[key1]
1205
contract2 = contractions[key2]
1206
dict_key = [contract1[0], contract2[0]]
1207
dict_entry = [contract1[1], contract2[1]]
1208
if dict_key[0] > dict_key[1]:
1209
dict_key.reverse()
1210
dict_entry.reverse()
1211
dict_entry = [(r0, ii), B, contract2[2],
1212
contract1[2]] + dict_entry
1213
else:
1214
dict_entry = [(r0, ii), B, contract1[2],
1215
contract2[2]] + dict_entry
1216
dict_key = tuple(dict_key)
1217
if dict_key not in contraction_dict:
1218
contraction_dict[dict_key] = []
1219
contraction_dict[dict_key].append(dict_entry)
1220
if dict_key[0] == dict_key[1]:
1221
contraction_dict[dict_key].append(
1222
dict_entry[:2] + [dict_entry[3], dict_entry[2], dict_entry[5], dict_entry[4]])
1223
return contraction_dict
1224
1225
1226
@cached_function
1227
def multiply(r1, i1, r2, i2, g, rmax, markings=(), moduli_type=MODULI_ST):
1228
unpurify = unpurify_map(g, r1+r2, markings, moduli_type)
1229
gens = all_strata(g, r1+r2, markings, moduli_type)
1230
ngens = num_strata(g, r1+r2, markings, moduli_type)
1231
answer = [0 for i in range(ngens)]
1232
pure_strata = [all_pure_strata(g, r, markings, moduli_type)
1233
for r in range(rmax+1)]
1234
contraction_dict = contraction_table(g, rmax, markings, moduli_type)
1235
G1 = single_stratum(i1, g, r1, markings, moduli_type)
1236
G2 = single_stratum(i2, g, r2, markings, moduli_type)
1237
G1copy = Graph(G1.M)
1238
G2copy = Graph(G2.M)
1239
G1copy.purify()
1240
G2copy.purify()
1241
pure_r1 = G1copy.num_edges() - len(markings)
1242
pure_r2 = G2copy.num_edges() - len(markings)
1243
found = False
1244
for i in range(len(pure_strata[pure_r1])):
1245
if G1copy.M == pure_strata[pure_r1][i].M:
1246
G1_key = (pure_r1, i)
1247
found = True
1248
break
1249
if not found:
1250
print("ERROR! Purification failed.")
1251
found = False
1252
for i in range(len(pure_strata[pure_r2])):
1253
if G2copy.M == pure_strata[pure_r2][i].M:
1254
G2_key = (pure_r2, i)
1255
found = True
1256
break
1257
if not found:
1258
print("ERROR! Purification failed.")
1259
if G1_key > G2_key:
1260
return multiply(r2, i2, r1, i1, g, rmax, markings, moduli_type)
1261
1262
if (G1_key, G2_key) not in contraction_dict:
1263
return answer
1264
for L in contraction_dict[(G1_key, G2_key)]:
1265
H = pure_strata[L[0][0]][L[0][1]]
1266
Hloops = []
1267
if moduli_type > MODULI_CT:
1268
for i in range(1, H.M.nrows()):
1269
for j in range(1, H.M.ncols()):
1270
if H.M[i, j][0] == 2:
1271
Hloops.append((i, j))
1272
auts = pure_strata_autom_count(
1273
L[0][1], g, L[0][0], markings, moduli_type)
1274
B = L[1]
1275
if len(B) == pure_r1 and len(B) == pure_r2:
1276
auts *= 2
1277
aut_cosets1 = automorphism_cosets(i1, g, r1, markings, moduli_type)
1278
aut_cosets2 = automorphism_cosets(i2, g, r2, markings, moduli_type)
1279
auts /= aut_cosets1[0]*aut_cosets2[0]
1280
for isom1 in aut_cosets1[1]:
1281
for isom2 in aut_cosets2[1]:
1282
Hcopy = Graph(H.M)
1283
vmap1 = [0 for i in range(G1.M.nrows())]
1284
for i in range(1, G1.M.nrows()):
1285
vmap1[i] = L[2][0][L[4][0][isom1[0]
1286
[i-1]-1]-1]
1287
emap1 = [0 for i in range(G1.M.ncols())]
1288
for i in range(1, G1.M.ncols()):
1289
emap1[i] = L[2][1][L[4][1][isom1[1]
1290
[i-1]-1]-1]
1291
vmap2 = [0 for i in range(G2.M.nrows())]
1292
for i in range(1, G2.M.nrows()):
1293
vmap2[i] = L[3][0][L[5][0][isom2[0]
1294
[i-1]-1]-1]
1295
emap2 = [0 for i in range(G2.M.ncols())]
1296
for i in range(1, G2.M.ncols()):
1297
emap2[i] = L[3][1][L[5][1][isom2[1]
1298
[i-1]-1]-1]
1299
1300
psilooplist = []
1301
psiindexlist = []
1302
loop_factor = ZZ.one()
1303
for i in range(1, G1.M.nrows()):
1304
for j in range(1, G1.M.ncols()):
1305
if G1.M[i, j][0] != 0:
1306
if G1.M[i, j][0] == 1:
1307
if G1.M[i, j][1] != 0:
1308
jj = emap1[j]
1309
for ii in vmap1[i]:
1310
if H.M[ii, jj] != 0:
1311
Hcopy.M[ii, jj] += G1.M[i, j][1]*X
1312
break
1313
elif G1.M[i, j][1] == 0:
1314
loop_factor *= 2
1315
else:
1316
jj = emap1[j]
1317
psilooplist.append([[G1.M[i, j][1], G1.M[i, j][2]], [
1318
G1.M[i, j][2], G1.M[i, j][1]]])
1319
psiindexlist.append([jj])
1320
for ii in vmap1[i]:
1321
for k in range(H.M[ii, jj][0]):
1322
psiindexlist[-1].append(ii)
1323
for i in range(1, G2.M.nrows()):
1324
for j in range(1, G2.M.ncols()):
1325
if G2.M[i, j][0] != 0:
1326
if G2.M[i, j][0] == 1:
1327
if G2.M[i, j][1] != 0:
1328
if G2.M[i, j][0] == 1:
1329
jj = emap2[j]
1330
for ii in vmap2[i]:
1331
if H.M[ii, jj] != 0:
1332
Hcopy.M[ii,
1333
jj] += G2.M[i, j][1]*X
1334
break
1335
elif G2.M[i, j][1] == 0:
1336
loop_factor *= 2
1337
else:
1338
jj = emap2[j]
1339
psilooplist.append([[G2.M[i, j][1], G2.M[i, j][2]], [
1340
G2.M[i, j][2], G2.M[i, j][1]]])
1341
psiindexlist.append([jj])
1342
for ii in vmap2[i]:
1343
for k in range(H.M[ii, jj][0]):
1344
psiindexlist[-1].append(ii)
1345
1346
Klocationlist = []
1347
Kindexlist = []
1348
for i in range(1, G1.M.nrows()):
1349
for r in range(1, rmax+1):
1350
for k in range(G1.M[i, 0][r]):
1351
Klocationlist.append(vmap1[i])
1352
Kindexlist.append(r)
1353
for i in range(1, G2.M.nrows()):
1354
for r in range(1, rmax+1):
1355
for k in range(G2.M[i, 0][r]):
1356
Klocationlist.append(vmap2[i])
1357
Kindexlist.append(r)
1358
1359
psilist = []
1360
for j in B:
1361
S = [i for i in range(1, H.M.nrows()) if H.M[i, j][0] != 0]
1362
if len(S) == 2:
1363
psilist.append([[S[0], j], [S[1], j]])
1364
else:
1365
psilooplist.append([[0, 1], [1, 0]])
1366
psiindexlist.append([j, S[0], S[0]])
1367
1368
for psiloopvals in itertools.product(*psilooplist):
1369
for Klocs in itertools.product(*Klocationlist):
1370
for psilocs in itertools.product(*psilist):
1371
Hcopycopy = Graph(Hcopy.M)
1372
for i in range(len(psiindexlist)):
1373
Hcopycopy.M[psiindexlist[i][1],
1374
psiindexlist[i][0]] += psiloopvals[i][0]*X
1375
if psiindexlist[i][1] == psiindexlist[i][2]:
1376
Hcopycopy.M[psiindexlist[i][1],
1377
psiindexlist[i][0]] += psiloopvals[i][1]*X**2
1378
else:
1379
Hcopycopy.M[psiindexlist[i][2],
1380
psiindexlist[i][0]] += psiloopvals[i][1]*X
1381
for i in range(len(Kindexlist)):
1382
Hcopycopy.M[Klocs[i],
1383
0] += X**Kindexlist[i]
1384
for i in psilocs:
1385
Hcopycopy.M[i[0], i[1]] += X
1386
for k in Hloops:
1387
if Hcopycopy.M[k][2] > Hcopycopy.M[k][1]:
1388
Hcopycopy.M[k] += (Hcopycopy.M[k]
1389
[2] - Hcopycopy.M[k][1])*(X - X**2)
1390
Hcopycopy.compute_invariant()
1391
for which_gen in unpurify[L[0]]:
1392
if graph_isomorphic(Hcopycopy, gens[which_gen]):
1393
answer[which_gen] += (-1)**len(B) * \
1394
loop_factor/auts
1395
break
1396
return answer
1397
1398
1399
def check_associativity(g, r1, r2, r3, markings=(), moduli_type=MODULI_ST):
1400
ngens1 = num_strata(g, r1, markings, moduli_type)
1401
ngens2 = num_strata(g, r2, markings, moduli_type)
1402
ngens3 = num_strata(g, r3, markings, moduli_type)
1403
print("%s*%s*%s = %s associators to compute:" %
1404
(ngens1, ngens2, ngens3, ngens1*ngens2*ngens3))
1405
count = 0
1406
for i1 in range(ngens1):
1407
for i2 in range(ngens2):
1408
for i3 in range(ngens3):
1409
a = multiply(r1, i1, r2, i2, g, r1+r2 +
1410
r3, markings, moduli_type)
1411
answer1 = vector(
1412
[0 for i in range(num_strata(g, r1+r2+r3, markings, moduli_type))])
1413
for j in range(num_strata(g, r1+r2, markings, moduli_type)):
1414
if a[j] == 0:
1415
continue
1416
answer1 += a[j]*vector(multiply(r1+r2, j, r3,
1417
i3, g, r1+r2+r3, markings, moduli_type))
1418
a = multiply(r1, i1, r3, i3, g, r1+r2 +
1419
r3, markings, moduli_type)
1420
answer2 = vector(
1421
[0 for i in range(num_strata(g, r1+r2+r3, markings, moduli_type))])
1422
for j in range(num_strata(g, r1+r3, markings, moduli_type)):
1423
if a[j] == 0:
1424
continue
1425
answer2 += a[j]*vector(multiply(r1+r3, j, r2,
1426
i2, g, r1+r2+r3, markings, moduli_type))
1427
if answer1 != answer2:
1428
print("Error: %s %s %s" % (i1, i2, i3))
1429
count += 1
1430
if count % 100 == 0:
1431
print("%s done" % count)
1432
1433
1434
def gorenstein_precompute(g, r1, markings=(), moduli_type=MODULI_ST):
1435
r3 = dim_form(g, len(markings), moduli_type)
1436
r2 = r3 - r1
1437
all_strata(g, r1, markings, moduli_type)
1438
all_strata(g, r2, markings, moduli_type)
1439
contraction_table(g, r3, markings, moduli_type)
1440
unpurify_map(g, r3, markings, moduli_type)
1441
1442
1443
def pairing_matrix(g, r1, markings=(), moduli_type=MODULI_ST):
1444
r3 = dim_form(g, len(markings), moduli_type)
1445
r2 = r3-r1
1446
ngens1 = num_strata(g, r1, markings, moduli_type)
1447
ngens2 = num_strata(g, r2, markings, moduli_type)
1448
ngens3 = num_strata(g, r3, markings, moduli_type)
1449
socle_evaluations = [socle_evaluation(
1450
i, g, markings, moduli_type) for i in range(ngens3)]
1451
pairings = [[0 for i2 in range(ngens2)] for i1 in range(ngens1)]
1452
sym = bool(r1 == r2)
1453
for i1 in range(ngens1):
1454
for i2 in range(ngens2):
1455
if sym and i1 > i2:
1456
pairings[i1][i2] = pairings[i2][i1]
1457
continue
1458
L = multiply(r1, i1, r2, i2, g, r3, markings, moduli_type)
1459
pairings[i1][i2] = sum([L[k]*socle_evaluations[k]
1460
for k in range(ngens3)])
1461
return pairings
1462
1463
1464
@cached_function
1465
def pairing_submatrix(S1, S2, g, r1, markings=(), moduli_type=MODULI_ST):
1466
r3 = dim_form(g, len(markings), moduli_type)
1467
r2 = r3-r1
1468
# ngens1 = num_strata(g, r1, markings, moduli_type)
1469
# ngens2 = num_strata(g, r2, markings, moduli_type)
1470
ngens3 = num_strata(g, r3, markings, moduli_type)
1471
socle_evaluations = [socle_evaluation(
1472
i, g, markings, moduli_type) for i in range(ngens3)]
1473
pairings = [[0 for i2 in S2] for i1 in S1]
1474
sym = bool(r1 == r2 and S1 == S2)
1475
for i1 in range(len(S1)):
1476
for i2 in range(len(S2)):
1477
if sym and i1 > i2:
1478
pairings[i1][i2] = pairings[i2][i1]
1479
continue
1480
L = multiply(r1, S1[i1], r2, S2[i2], g, r3, markings, moduli_type)
1481
pairings[i1][i2] = sum([L[k]*socle_evaluations[k]
1482
for k in range(ngens3)])
1483
return pairings
1484
1485
#####################################
1486
1487
1488
def betti(g, r, marked_points=(), moduli_type=MODULI_ST):
1489
"""
1490
This function returns the predicted rank of the codimension r grading
1491
of the tautological ring of the moduli space of stable genus g curves
1492
with marked points labeled by the multiset marked_points.
1493
1494
g and r should be nonnegative integers and marked_points should be a
1495
tuple of positive integers.
1496
1497
The parameter moduli_type determines which moduli space to use:
1498
- MODULI_ST: all stable curves (this is the default)
1499
- MODULI_CT: curves of compact type
1500
- MODULI_RT: curves with rational tails
1501
- MODULI_SM: smooth curves
1502
1503
EXAMPLES::
1504
1505
sage: from admcycles.DR import betti
1506
1507
Check rank R^3(bar{M}_2) = 1::
1508
1509
sage: betti(2,3)
1510
1
1511
1512
Check rank R^2(bar{M}_{2,3}) = 44::
1513
1514
sage: betti(2,2,(1,2,3))
1515
44
1516
1517
Check rank R^2(bar{M}_{2,3})^{S_3} = 20::
1518
1519
sage: betti(2,2,(1,1,1))
1520
20
1521
1522
Check rank R^2(bar{M}_{2,3})^{S_2} = 32 (S_2 interchanging markings 1 and 2)::
1523
1524
sage: betti(2,2,(1,1,2))
1525
32
1526
1527
Check rank R^2(M^c_4) = rank R^3(M^c_4) = 6::
1528
1529
sage: from admcycles.DR import MODULI_CT, MODULI_RT, MODULI_SM
1530
sage: betti(4,2,(),MODULI_CT)
1531
6
1532
sage: betti(4,3,(),MODULI_CT)
1533
6
1534
1535
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.
1536
Indeed, betti(17,8,(1,1),MODULI_RT) = 122 and betti(17,9,(1,1),MODULI_RT)=123
1537
Similarly, we have rank R^9(M_{20,1}) = 75 < rank R^10(M_{20,1}) = 76
1538
Indeed, betti(20,9,(1,),MODULI_SM) = 75 and betti(20,10,(1,),MODULI_SM) = 76.
1539
"""
1540
L = list_all_FZ(g, r, marked_points, moduli_type)
1541
L.reverse()
1542
return (len(L[0]) - compute_rank(L))
1543
1544
1545
def gorenstein(g, r, marked_points=(), moduli_type=MODULI_ST):
1546
"""
1547
This function returns the rank of the codimension r grading of the
1548
Gorenstein quotient of the tautological ring of the moduli space of genus g
1549
curves with marked points labeled by the multiset marked_points.
1550
1551
g and r should be nonnegative integers and marked_points should be a
1552
tuple of positive integers.
1553
1554
The parameter moduli_type determines which moduli space to use:
1555
- MODULI_ST: all stable curves (this is the default)
1556
- MODULI_CT: curves of compact type
1557
- MODULI_RT: curves with rational tails
1558
1559
EXAMPLES::
1560
1561
sage: from admcycles.DR import gorenstein
1562
1563
Check rank Gor^3(bar{M}_{3}) = 10::
1564
1565
sage: gorenstein(3,3)
1566
10
1567
1568
Check rank Gor^2(bar{M}_{2,2}) = 14::
1569
1570
sage: gorenstein(2,2,(1,2))
1571
14
1572
1573
Check rank Gor^2(bar{M}_{2,2})^{S_2} = 11::
1574
1575
sage: gorenstein(2,2,(1,1))
1576
11
1577
1578
Check rank Gor^2(M^c_{4}) = 6::
1579
1580
sage: from admcycles.DR import MODULI_CT, MODULI_RT
1581
1582
sage: gorenstein(4,2,(),MODULI_CT)
1583
6
1584
1585
Check rank Gor^4(M^rt_{8,2}) = 22::
1586
1587
sage: gorenstein(8,4,(1,2),MODULI_RT)
1588
22
1589
"""
1590
gorenstein_precompute(g, r, marked_points, moduli_type)
1591
r3 = dim_form(g, len(marked_points), moduli_type)
1592
r2 = r3-r
1593
S1 = good_generator_list(g, r, marked_points, moduli_type)
1594
S2 = good_generator_list(g, r2, marked_points, moduli_type)
1595
M = pairing_submatrix(tuple(S1), tuple(S2), g, r,
1596
marked_points, moduli_type)
1597
return matrix(M).rank()
1598
1599
#####################################
1600
1601
1602
def compute_rank(L):
1603
count = 0
1604
for i in range(len(L)):
1605
S = [j for j in range(len(L[0])) if L[i][j] != 0]
1606
if not S:
1607
continue
1608
count += 1
1609
j = S[0]
1610
T = [ii for ii in range(i+1, len(L))
1611
if L[ii][j] != 0]
1612
for k in S[1:]:
1613
rat = L[i][k]/L[i][j]
1614
for ii in T:
1615
L[ii][k] -= rat*L[ii][j]
1616
for ii in range(i+1, len(L)):
1617
L[ii][j] = 0
1618
return count
1619
1620
1621
def choose_orders_sparse(D, nrows, ncols):
1622
row_nums = [0 for i in range(nrows)]
1623
col_nums = [0 for j in range(ncols)]
1624
for key in D:
1625
row_nums[key[0]] += 1
1626
col_nums[key[1]] += 1
1627
row_order = list(range(nrows))
1628
col_order = list(range(ncols))
1629
row_order.sort(key=lambda x: row_nums[x])
1630
col_order.sort(key=lambda x: col_nums[x])
1631
return row_order, col_order
1632
1633
1634
def choose_orders(L):
1635
rows = len(L)
1636
if rows == 0:
1637
return [], []
1638
cols = len(L[0])
1639
row_nums = [0 for i in range(rows)]
1640
col_nums = [0 for j in range(cols)]
1641
for i in range(rows):
1642
for j in range(cols):
1643
if L[i][j] != 0:
1644
row_nums[i] += 1
1645
col_nums[j] += 1
1646
row_order = list(range(rows))
1647
col_order = list(range(cols))
1648
row_order.sort(key=lambda x: row_nums[x])
1649
col_order.sort(key=lambda x: col_nums[x])
1650
return row_order, col_order
1651
1652
1653
def compute_rank2(L, row_order, col_order):
1654
count = 0
1655
for irow in range(len(row_order)):
1656
i = row_order[irow]
1657
S = [j for j in col_order if L[i][j] != 0]
1658
if not S:
1659
continue
1660
count += 1
1661
j = S[0]
1662
T = [ii for ii in row_order[irow+1:]
1663
if L[ii][j] != 0]
1664
for k in S[1:]:
1665
rat = L[i][k]/L[i][j]
1666
for ii in T:
1667
L[ii][k] -= rat*L[ii][j]
1668
for ii in T:
1669
L[ii][j] = 0
1670
return count
1671
1672
####################################
1673
1674
1675
def socle_evaluation(num, g, markings=(), moduli_type=MODULI_ST):
1676
answer = 1
1677
G = single_stratum(num, g, dim_form(
1678
g, len(markings), moduli_type), markings, moduli_type)
1679
for i in range(1, G.M.nrows()):
1680
g0 = G.M[i, 0][0]
1681
psilist = []
1682
for j in range(1, G.M.ncols()):
1683
if G.M[i, j][0] > 0:
1684
psilist.append(G.M[i, j][1])
1685
if G.M[i, j][0] == 2:
1686
psilist.append(G.M[i, j][2])
1687
n0 = len(psilist)
1688
dim0 = dim_form(g0, n0, moduli_type)
1689
kappalist = []
1690
for j in range(1, dim0+1):
1691
for k in range(G.M[i, 0][j]):
1692
kappalist.append(j)
1693
if sum(psilist)+sum(kappalist) != dim0:
1694
print("ERROR: wrong dim")
1695
return
1696
answer *= socle_formula(g0, psilist, kappalist, moduli_type)
1697
return answer
1698
1699
1700
def socle_formula(g, psilist, kappalist, moduli_type=MODULI_ST):
1701
if moduli_type == MODULI_CT or g == 0:
1702
return CTconst(g)*CTsum(psilist, kappalist)
1703
if moduli_type <= MODULI_SM or moduli_type == MODULI_RT:
1704
return RTsum(g, psilist, kappalist)
1705
if moduli_type == MODULI_ST:
1706
return STsum(psilist, kappalist)
1707
1708
1709
def setparts_recur(symlist, progress):
1710
if not symlist:
1711
return [progress]
1712
l = []
1713
for i in Combinations(symlist[1:]):
1714
j = [symlist[0]]+i
1715
if progress and j < progress[-1]:
1716
continue
1717
cur = 0
1718
new_symlist = []
1719
for k in range(len(symlist)):
1720
if cur < len(j) and symlist[k] == j[cur]:
1721
cur += 1
1722
else:
1723
new_symlist.append(symlist[k])
1724
l += setparts_recur(new_symlist, progress+[j])
1725
return l
1726
1727
1728
def setparts_with_auts(symlist):
1729
l = setparts_recur(symlist, [])
1730
a = aut(symlist)
1731
ll = []
1732
for i in l:
1733
b = aut(i)
1734
for j in i:
1735
b *= aut(j)
1736
ll.append([i, a/b])
1737
return ll
1738
1739
1740
def multi2(g, sigma):
1741
sigma.sort()
1742
if sigma[0] == 0:
1743
total = ZZ.zero()
1744
for i in range(len(sigma) - 1):
1745
sigmacopy = sigma[1:]
1746
if sigmacopy[i] > 0:
1747
sigmacopy[i] -= 1
1748
total += multi2(g, sigmacopy)
1749
return total
1750
term = factorial(2 * g - 3 + len(sigma))
1751
term *= ZZ(2 * g - 1).multifactorial(2)
1752
term /= factorial(2 * g - 1)
1753
for i in sigma:
1754
term /= ZZ(2 * i - 1).multifactorial(2)
1755
return term
1756
1757
1758
def STsum(psilist, kappalist):
1759
kappalist.sort()
1760
total = 0
1761
for i in setparts_with_auts(kappalist):
1762
total += (-1)**(len(i[0])) * i[1] * \
1763
STrecur([1 + sum(j) for j in i[0]] + psilist)
1764
return total*(-1)**(len(kappalist))
1765
1766
1767
def RTsum(g, psilist, kappalist):
1768
kappalist.sort()
1769
total = ZZ.zero()
1770
for i0, i1 in setparts_with_auts(kappalist):
1771
total += (-1) ** len(i0) * i1 * \
1772
multi2(g, [1 + sum(j) for j in i0] + psilist)
1773
return total * (-1)**(len(kappalist))
1774
1775
1776
def CTsum(psilist, kappalist):
1777
kappalist.sort()
1778
total = ZZ.zero()
1779
for i0, i1 in setparts_with_auts(kappalist):
1780
total += (-1)**len(i0) * i1 * \
1781
multinomial([1 + sum(j) for j in i0] + psilist)
1782
return total * (-1)**len(kappalist)
1783
1784
1785
def CTconst(g):
1786
return abs((ZZ(2) ** (ZZ(2) * g-1)-1)*bernoulli(ZZ(2) * g))/(ZZ(2) ** (ZZ(2) * g-1)*factorial(ZZ(2) * g))
1787
1788
1789
def STrecur(psi):
1790
return STrecur_calc(tuple(sorted(psi)))
1791
1792
1793
@cached_function
1794
def STrecur_calc(psi):
1795
"""
1796
INPUT: a sorted tuple of (nonegative ?) integers
1797
1798
OUTPUT: a rational
1799
"""
1800
if not psi:
1801
return ZZ.one()
1802
s = sum(psi)
1803
n = len(psi)
1804
if (s - n) % 3:
1805
return ZZ.zero()
1806
if psi[0] == 0:
1807
if s == 0 and n == 3:
1808
return ZZ.one()
1809
total = ZZ.zero()
1810
psi_end = list(psi[1:])
1811
for i in range(n - 1):
1812
psicopy = list(psi_end)
1813
if psicopy[i] > 0:
1814
psicopy[i] -= 1
1815
total += STrecur(psicopy)
1816
return total
1817
1818
g = ZZ(s - n) // 3 + 1 # in ZZ
1819
d = ZZ(psi[-1])
1820
total = ZZ.zero()
1821
1822
psicopy = [0, 0, 0, 0] + list(psi)
1823
psicopy[-1] += 1
1824
total += (2 * d + 3) / 12 * STrecur(psicopy)
1825
1826
psicopy = [0, 0, 0] + list(psi)
1827
total -= (2 * g + n - 1) / 6 * STrecur(psicopy)
1828
1829
for I in Subsets(list(range(n - 1))):
1830
psi3 = [0, 0] + [psi[i] for i in I]
1831
x = STrecur(psi3)
1832
if x == 0:
1833
continue
1834
psi1 = [0, 0] + [psi[i] for i in range(n - 1) if i not in I] + [d + 1]
1835
psi2 = list(psi1[1:])
1836
psi2[-1] = d
1837
total += ((2 * d + 3)*STrecur(psi1) -
1838
(2 * g + n - 1)*STrecur(psi2)) * x
1839
total /= (2 * g + n - 1) * (2 * g + n - 2)
1840
return total
1841
1842
1843
@cached_function
1844
def good_generator_list(g, r, markings=(), moduli_type=MODULI_ST):
1845
gens = all_strata(g, r, markings, moduli_type)
1846
good_gens = []
1847
ngens = len(gens)
1848
for num in range(ngens):
1849
G = gens[num]
1850
good = True
1851
for i in range(1, G.M.nrows()):
1852
g = G.M[i, 0][0]
1853
codim = 0
1854
for d in range(1, r + 1):
1855
if G.M[i, 0][d] != 0:
1856
if 3 * d > g:
1857
good = False
1858
break
1859
codim += d*G.M[i, 0][d]
1860
if not good:
1861
break
1862
for j in range(1, G.M.ncols()):
1863
codim += G.M[i, j][1]
1864
codim += G.M[i, j][2]
1865
if codim > 0 and codim >= g:
1866
good = False
1867
break
1868
if good:
1869
good_gens.append(num)
1870
return good_gens
1871
1872
1873
@cached_function
1874
def automorphism_cosets(num, g, r, markings=(), moduli_type=MODULI_ST):
1875
G = single_stratum(num, g, r, markings, moduli_type)
1876
pureG = Graph(G.M)
1877
pureG.purify()
1878
pureG.compute_invariant()
1879
pure_auts = graph_list_isomorphisms(pureG, pureG)
1880
num_pure = len(pure_auts)
1881
impure_auts = graph_list_isomorphisms(G, G)
1882
num_impure = len(impure_auts)
1883
chosen_auts = []
1884
used_auts = []
1885
v = G.num_vertices()
1886
e = G.num_edges()
1887
for i in range(num_pure):
1888
if i not in used_auts:
1889
chosen_auts.append(pure_auts[i])
1890
for g in impure_auts:
1891
sigma = [[pure_auts[i][0][g[0][k]-1]
1892
for k in range(v)], [pure_auts[i][1][g[1][k]-1] for k in range(e)]]
1893
for ii in range(num_pure):
1894
if pure_auts[ii] == sigma:
1895
used_auts.append(ii)
1896
break
1897
return [num_impure, chosen_auts]
1898
1899
#############################################
1900
1901
1902
def FZ_rels(g, r, markings=(), moduli_type=MODULI_ST):
1903
return span(FZ_matrix(g, r, markings, moduli_type)).basis()
1904
1905
1906
def goren_rels(g, r, markings=(), moduli_type=MODULI_ST):
1907
gorenstein_precompute(g, r, markings, moduli_type)
1908
r3 = dim_form(g, len(markings), moduli_type)
1909
r2 = r3-r
1910
S1 = list(range(num_strata(g, r, markings, moduli_type)))
1911
S2 = good_generator_list(g, r2, markings, moduli_type)
1912
M = pairing_submatrix(S1, S2, g, r, markings, moduli_type)
1913
return Matrix(M).kernel().basis()
1914
1915
1916
@cached_function
1917
def kappa_conversion(sigma):
1918
answer = []
1919
for spart in setparts_with_auts(list(sigma)):
1920
coeff = spart[1]
1921
poly = R(0)
1922
for part in spart[0]:
1923
coeff *= factorial(len(part) - 1)
1924
poly += X**sum(part)
1925
answer.append([poly, coeff])
1926
return answer
1927
1928
1929
@cached_function
1930
def kappa_conversion_inverse(sigma):
1931
answer = []
1932
for spart in setparts_with_auts(list(sigma)):
1933
coeff = spart[1]*(-1)**(len(sigma)-len(spart[0]))
1934
poly = R(0)
1935
for part in spart[0]:
1936
poly += X**sum(part)
1937
answer.append([poly, coeff])
1938
return answer
1939
1940
1941
@cached_function
1942
def convert_to_monomial_basis(num, g, r, markings=(), moduli_type=MODULI_ST):
1943
answer = []
1944
G = single_stratum(num, g, r, markings, moduli_type)
1945
genus_vec = []
1946
kappa_vec = []
1947
for i in range(1, G.M.nrows()):
1948
genus_vec.append(G.M[i, 0][0])
1949
kappa_vec.append([])
1950
for j in range(1, r+1):
1951
for k in range(G.M[i, 0][j]):
1952
kappa_vec[-1].append(j)
1953
kappa_vec[-1] = kappa_conversion(
1954
tuple(kappa_vec[-1]))
1955
for choice in itertools.product(*kappa_vec):
1956
coeff = 1
1957
GG = Graph(G.M)
1958
for i in range(1, G.M.nrows()):
1959
GG.M[i, 0] = genus_vec[i - 1] + choice[i - 1][0]
1960
coeff *= choice[i - 1][1]
1961
answer.append((num_of_stratum(GG, g, r, markings, moduli_type), coeff))
1962
return answer
1963
1964
1965
@cached_function
1966
def convert_to_pushforward_basis(num, g, r, markings=(), moduli_type=MODULI_ST):
1967
answer = []
1968
G = single_stratum(num, g, r, markings, moduli_type)
1969
genus_vec = []
1970
kappa_vec = []
1971
for i in range(1, G.M.nrows()):
1972
genus_vec.append(G.M[i, 0][0])
1973
kappa_vec.append([])
1974
for j in range(1, r+1):
1975
for k in range(G.M[i, 0][j]):
1976
kappa_vec[-1].append(j)
1977
kappa_vec[-1] = kappa_conversion_inverse(
1978
tuple(kappa_vec[-1]))
1979
for choice in itertools.product(*kappa_vec):
1980
coeff = 1
1981
GG = Graph(G.M)
1982
for i in range(1, G.M.nrows()):
1983
GG.M[i, 0] = genus_vec[i-1] + \
1984
choice[i-1][0]
1985
coeff *= choice[i-1][1]
1986
answer.append((num_of_stratum(GG, g, r, markings, moduli_type), coeff))
1987
return answer
1988
1989
1990
def convert_vector_to_monomial_basis(vec, g, r, markings=(), moduli_type=MODULI_ST):
1991
l = len(vec)
1992
vec2 = [0 for i in range(l)]
1993
for i in range(l):
1994
if vec[i] != 0:
1995
for x in convert_to_monomial_basis(i, g, r, markings, moduli_type):
1996
vec2[x[0]] += x[1]*vec[i]
1997
return vec2
1998
1999
2000
def convert_vector_to_pushforward_basis(vec, g, r, markings=(), moduli_type=MODULI_ST):
2001
l = len(vec)
2002
vec2 = [0 for i in range(l)]
2003
for i in range(l):
2004
if vec[i] != 0:
2005
for x in convert_to_pushforward_basis(i, g, r, markings, moduli_type):
2006
vec2[x[0]] += x[1]*vec[i]
2007
return vec2
2008
2009
2010
def kappa_multiple(vec, which_kappa, g, r, n=0, moduli_type=MODULI_ST):
2011
vec2 = []
2012
for x in vec:
2013
for y in single_kappa_multiple(x[0], which_kappa, g, r, n, moduli_type):
2014
vec2.append([y[0], x[1]*y[1]])
2015
vec2 = simplify_sparse(vec2)
2016
return vec2
2017
2018
2019
def psi_multiple(vec, which_psi, g, r, n=0, moduli_type=MODULI_ST):
2020
vec2 = []
2021
for x in vec:
2022
for y in single_psi_multiple(x[0], which_psi, g, r, n, moduli_type):
2023
vec2.append([y[0], x[1]*y[1]])
2024
vec2 = simplify_sparse(vec2)
2025
return vec2
2026
2027
2028
def insertion_pullback(vec, g, r, n=0, new_mark=1, moduli_type=MODULI_ST):
2029
vec2 = []
2030
for x in vec:
2031
for y in single_insertion_pullback(x[0], g, r, n, new_mark, moduli_type):
2032
vec2.append([y[0], x[1]*y[1]])
2033
vec2 = simplify_sparse(vec2)
2034
return vec2
2035
2036
2037
def insertion_pullback2(vec, g, r, n=0, new_mark=1, moduli_type=MODULI_ST):
2038
vec2 = []
2039
for x in vec:
2040
for y in single_insertion_pullback2(x[0], g, r, n, new_mark, moduli_type):
2041
vec2.append([y[0], x[1]*y[1]])
2042
vec2 = simplify_sparse(vec2)
2043
return vec2
2044
2045
2046
@cached_function
2047
def strata_invariant_lookup(g, r, markings=(), moduli_type=MODULI_ST):
2048
inv_dict = {}
2049
L = all_strata(g, r, markings, moduli_type)
2050
for i in range(len(L)):
2051
if L[i].invariant not in inv_dict:
2052
inv_dict[L[i].invariant] = []
2053
inv_dict[L[i].invariant].append(i)
2054
return inv_dict
2055
2056
2057
@cached_function
2058
def num_of_stratum(G, g, r, markings=(), moduli_type=MODULI_ST):
2059
G.compute_invariant()
2060
L = all_strata(g, r, markings, moduli_type)
2061
LL = strata_invariant_lookup(g, r, markings, moduli_type)
2062
2063
# for debugging purposes
2064
try:
2065
x = LL[G.invariant]
2066
except KeyError:
2067
print("num_of_stratum(G={}, g={}, r={}, markings={}, moduli_type={}".format(
2068
G, g, r, markings, moduli_type))
2069
print("G.invariant = {}".format(G.invariant))
2070
print("LL.keys() = ")
2071
for l in LL:
2072
print(l)
2073
raise
2074
2075
if len(x) == 1:
2076
return x[0]
2077
for i in x:
2078
if graph_isomorphic(G, L[i]):
2079
return i
2080
print("ERROR")
2081
print((g, r, markings, moduli_type))
2082
print(G.M)
2083
2084
2085
@cached_function
2086
def single_psi_multiple(num, which_psi, g, r, n=0, moduli_type=MODULI_ST):
2087
markings = tuple(range(1, n+1))
2088
G = single_stratum(num, g, r, markings, moduli_type)
2089
answer = []
2090
for j in range(1, G.M.ncols()):
2091
if G.M[0, j] == which_psi:
2092
good_j = j
2093
break
2094
for i in range(1, G.M.nrows()):
2095
if G.M[i, good_j] != 0:
2096
deg = 0
2097
dim_used = 0
2098
for j in range(1, r+1):
2099
dim_used += j*G.M[i, 0][j]
2100
for j in range(1, G.M.ncols()):
2101
dim_used += G.M[i, j][1] + G.M[i, j][2]
2102
deg += G.M[i, j][0]
2103
if dim_used < dim_form(G.M[i, 0][0], deg, moduli_type):
2104
GG = Graph(G.M)
2105
GG.M[i, good_j] += X
2106
answer.append(
2107
(num_of_stratum(GG, g, r+1, markings, moduli_type), 1))
2108
break
2109
return answer
2110
2111
2112
@cached_function
2113
def single_kappa_multiple(num, which_kappa, g, r, n=0, moduli_type=MODULI_ST):
2114
markings = tuple(range(1, n+1))
2115
G = single_stratum(num, g, r, markings, moduli_type)
2116
answer = []
2117
for i in range(1, G.M.nrows()):
2118
deg = 0
2119
dim_used = 0
2120
for j in range(1, r+1):
2121
dim_used += j*G.M[i, 0][j]
2122
for j in range(1, G.M.ncols()):
2123
dim_used += G.M[i, j][1] + G.M[i, j][2]
2124
deg += G.M[i, j][0]
2125
if dim_used + which_kappa <= dim_form(G.M[i, 0][0], deg, moduli_type):
2126
GG = Graph(G.M)
2127
GG.M[i, 0] += X**which_kappa
2128
answer.append((num_of_stratum(GG, g, r+which_kappa,
2129
markings, moduli_type), 1))
2130
for j in range(1, r+1):
2131
if G.M[i, 0][j] > 0:
2132
GG = Graph(G.M)
2133
GG.M[i, 0] += X**(j+which_kappa)
2134
GG.M[i, 0] -= X**j
2135
answer.append((num_of_stratum(
2136
GG, g, r+which_kappa, markings, moduli_type), -G.M[i, 0][j]))
2137
return answer
2138
2139
# Only doing this for markings=list(range(1,n+1)) right now.
2140
# Also, this function uses the monomial basis.
2141
2142
2143
def single_kappa_psi_multiple(num, kappa_partition, psi_exps, g, r, n=0, moduli_type=MODULI_ST):
2144
markings = tuple(range(1, n+1))
2145
G = single_stratum(num, g, r, markings, moduli_type)
2146
GG = Graph(G.M)
2147
for j in range(1, G.M.ncols()):
2148
if GG.M[0, j] != 0:
2149
for i in range(1, G.M.nrows()):
2150
if GG.M[i, j] != 0:
2151
GG.M[i, j] += psi_exps[GG.M[0, j][0]-1]*X
2152
break
2153
rnew = r+sum(kappa_partition)+sum(psi_exps)
2154
answer = []
2155
kappa_options = [list(range(1, G.M.nrows()))
2156
for i in range(len(kappa_partition))]
2157
for kappa_distrib in itertools.product(*kappa_options):
2158
GGG = Graph(GG.M)
2159
for i in range(len(kappa_partition)):
2160
GGG.M[kappa_distrib[i], 0] += X**(kappa_partition[i])
2161
is_bad = False
2162
for i in range(1, GGG.M.nrows()):
2163
deg = 0
2164
dim_used = 0
2165
for j in range(1, rnew+1):
2166
dim_used += j*GGG.M[i, 0][j]
2167
for j in range(1, GGG.M.ncols()):
2168
dim_used += GGG.M[i, j][1] + GGG.M[i, j][2]
2169
deg += GGG.M[i, j][0]
2170
if dim_used > dim_form(GGG.M[i, 0][0], deg, moduli_type):
2171
is_bad = True
2172
break
2173
if is_bad:
2174
continue
2175
answer.append(
2176
(num_of_stratum(GGG, g, rnew, markings, moduli_type), 1))
2177
return answer
2178
2179
2180
@cached_function
2181
def single_insertion_pullback(num, g, r, n=0, new_mark=1, moduli_type=MODULI_ST):
2182
markings = tuple(range(1, n+1))
2183
new_markings = tuple(range(1, n+2))
2184
G = single_stratum(num, g, r, markings, moduli_type)
2185
answer = []
2186
for i in range(1, G.M.nrows()):
2187
GG = Graph(G.M)
2188
for j in range(1, G.M.ncols()):
2189
if GG.M[0, j][0] >= new_mark:
2190
GG.M[0, j] += 1
2191
GG.add_edge(i, 0, new_mark)
2192
answer.append(
2193
(num_of_stratum(GG, g, r, new_markings, moduli_type), 1))
2194
for j in range(1, r+1):
2195
for k in range(GG.M[i, 0][j]):
2196
GGG = Graph(GG.M)
2197
GGG.M[i, 0] -= X**j
2198
GGG.M[i, -1] += j*X
2199
answer.append(
2200
(num_of_stratum(GGG, g, r, new_markings, moduli_type), -1))
2201
if moduli_type <= MODULI_SM:
2202
continue
2203
for j in range(1, G.M.ncols()):
2204
if G.M[i, j][0] == 1:
2205
if G.M[i, j][1] >= 1:
2206
x = G.M[i, j][1]
2207
GGG = Graph(GG.M)
2208
row1 = [GG.M[i, k] for k in range(GG.M.ncols())]
2209
row2 = [0 for k in range(GG.M.ncols())]
2210
row1[j] = 0
2211
row1[-1] = 0
2212
row2[j] = 1
2213
row2[-1] = 1
2214
GGG.split_vertex(i, row1, row2)
2215
GGG.M[-2, -
2216
1] += (x-1)*X
2217
answer.append(
2218
(num_of_stratum(GGG, g, r, new_markings, moduli_type), -1))
2219
if G.M[i, j][0] == 2:
2220
if G.M[i, j][1] >= 1 or G.M[i, j][2] >= 1:
2221
x = G.M[i, j][1]
2222
y = G.M[i, j][2]
2223
row1 = [GG.M[i, k] for k in range(GG.M.ncols())]
2224
row2 = [0 for k in range(GG.M.ncols())]
2225
row1[j] = 0
2226
row1[-1] = 0
2227
row2[j] = 1
2228
row2[-1] = 1
2229
if y >= 1:
2230
row1[j] = 1 + x*X
2231
GGG = Graph(GG.M)
2232
GGG.split_vertex(i, row1, row2)
2233
GGG.M[-2, -
2234
1] += (y-1)*X
2235
answer.append(
2236
(num_of_stratum(GGG, g, r, new_markings, moduli_type), -1))
2237
if x >= 1:
2238
row1[j] = 1 + y*X
2239
GGG = Graph(GG.M)
2240
GGG.split_vertex(i, row1, row2)
2241
GGG.M[-2, -
2242
1] += (x-1)*X
2243
answer.append(
2244
(num_of_stratum(GGG, g, r, new_markings, moduli_type), -1))
2245
return answer
2246
2247
2248
@cached_function
2249
def single_insertion_pullback2(num, g, r, n=0, new_mark=1, moduli_type=MODULI_ST):
2250
markings = tuple(range(1, n+1))
2251
new_markings = tuple(range(1, n+2))
2252
G = single_stratum(num, g, r, markings, MODULI_SMALL)
2253
answer = []
2254
for i in range(1, G.M.nrows()):
2255
GG = Graph(G.M)
2256
for j in range(1, G.M.ncols()):
2257
if GG.M[0, j][0] >= new_mark:
2258
GG.M[0, j] += 1
2259
GG.add_edge(i, 0, new_mark)
2260
answer.append(
2261
(num_of_stratum(GG, g, r, new_markings, moduli_type), 1))
2262
for j in range(1, r+1):
2263
for k in range(GG.M[i, 0][j]):
2264
GGG = Graph(GG.M)
2265
GGG.M[i, 0] -= X**j
2266
GGG.M[i, -1] += j*X
2267
answer.append(
2268
(num_of_stratum(GGG, g, r, new_markings, moduli_type), -1))
2269
if moduli_type <= MODULI_SM:
2270
continue
2271
for j in range(1, G.M.ncols()):
2272
if G.M[i, j][0] == 1:
2273
if G.M[i, j][1] >= 1:
2274
x = G.M[i, j][1]
2275
GGG = Graph(GG.M)
2276
row1 = [GG.M[i, k] for k in range(GG.M.ncols())]
2277
row2 = [0 for k in range(GG.M.ncols())]
2278
row1[j] = 0
2279
row1[-1] = 0
2280
row2[j] = 1
2281
row2[-1] = 1
2282
GGG.split_vertex(i, row1, row2)
2283
GGG.M[-2, -
2284
1] += (x-1)*X
2285
answer.append(
2286
(num_of_stratum(GGG, g, r, new_markings, moduli_type), -1))
2287
return answer
2288
2289
2290
def num_new_rels(g, r, n=0, moduli_type=MODULI_ST):
2291
return len(choose_basic_rels(g, r, n, moduli_type))
2292
2293
2294
@cached_function
2295
def choose_basic_rels(g, r, n=0, moduli_type=MODULI_ST):
2296
if 3 * r < g + n + 1:
2297
return []
2298
sym_ngen = num_strata(g, r, tuple([1 for i in range(n)]), moduli_type)
2299
if moduli_type == MODULI_SMALL and r > dim_form(g, n, MODULI_SM):
2300
sym_possible_rels = [[[i, 1]] for i in range(sym_ngen)]
2301
else:
2302
sym_possible_rels = possibly_new_FZ(g, r, n, moduli_type)
2303
if not sym_possible_rels:
2304
return []
2305
dprint("Start basic_rels (%s,%s,%s,%s): %s", g, r,
2306
n, moduli_type, floor(get_memory_usage()))
2307
previous_rels = derived_rels(g, r, n, moduli_type)
2308
nrels = len(previous_rels)
2309
dprint("%s gens, %s oldrels", sym_ngen, nrels)
2310
D = {}
2311
for i in range(nrels):
2312
for x in previous_rels[i]:
2313
D[i, x[0]] = x[1]
2314
if nrels > 0:
2315
row_order, col_order = choose_orders_sparse(D, nrels, sym_ngen)
2316
previous_rank = compute_rank_sparse(D, row_order, col_order)
2317
dprint("rank %s", previous_rank)
2318
else:
2319
previous_rank = 0
2320
row_order = []
2321
col_order = list(range(sym_ngen))
2322
answer = []
2323
for j in range(len(sym_possible_rels)):
2324
for x in sym_possible_rels[j]:
2325
D[nrels, x[0]] = x[1]
2326
row_order.append(nrels)
2327
nrels += 1
2328
if compute_rank_sparse(D, row_order, col_order) > previous_rank:
2329
answer.append(unsymmetrize_vec(sym_possible_rels[j], g, r, tuple(
2330
range(1, n+1)), moduli_type))
2331
previous_rank += 1
2332
dprint("rank %s", previous_rank)
2333
dprint("End basic_rels (%s,%s,%s,%s): %s", g, r,
2334
n, moduli_type, floor(get_memory_usage()))
2335
dprint("%s,%s,%s,%s: rank %s", g, r, n,
2336
moduli_type, sym_ngen-previous_rank)
2337
if moduli_type > -1:
2338
dsave("sparse-%s,%s,%s,%s|%s,%s,%s", g, r, n, moduli_type,
2339
len(answer), sym_ngen-previous_rank, floor(get_memory_usage()))
2340
# if moduli_type >= 0 and sym_ngen-previous_rank != betti(g,r,tuple([1 for i in range(n)]),moduli_type):
2341
# dprint("ERROR: %s,%s,%s,%s",g,r,n,moduli_type)
2342
# return
2343
return answer
2344
2345
2346
def recursive_betti(g, r, markings=(), moduli_type=MODULI_ST):
2347
"""
2348
EXAMPLES::
2349
2350
sage: from admcycles.DR import recursive_betti
2351
sage: recursive_betti(2, 3) # not tested
2352
?
2353
"""
2354
dprint("Start recursive_betti (%s,%s,%s,%s): %s", g, r,
2355
markings, moduli_type, floor(get_memory_usage()))
2356
n = len(markings)
2357
if r > dim_form(g, n, moduli_type):
2358
return 0
2359
ngen = num_strata(g, r, markings, moduli_type)
2360
dprint("%s gens", ngen)
2361
relations = []
2362
partial_sym_map = partial_symmetrize_map(g, r, markings, moduli_type)
2363
for rel in choose_basic_rels(g, r, n, moduli_type):
2364
rel2 = []
2365
for x in rel:
2366
rel2.append([partial_sym_map[x[0]], x[1]])
2367
rel2 = simplify_sparse(rel2)
2368
relations.append(rel2)
2369
for rel in interior_derived_rels(g, r, n, moduli_type):
2370
rel2 = []
2371
for x in rel:
2372
rel2.append([partial_sym_map[x[0]], x[1]])
2373
rel2 = simplify_sparse(rel2)
2374
relations.append(rel2)
2375
dprint("%s gens, %s rels so far", ngen, len(relations))
2376
dprint("Middle recursive_betti (%s,%s,%s,%s): %s", g, r,
2377
markings, moduli_type, floor(get_memory_usage()))
2378
if moduli_type > MODULI_SM:
2379
for r0 in range(1, r):
2380
strata = all_strata(g, r0, markings, moduli_type)
2381
for G in strata:
2382
vertex_orbits = graph_count_automorphisms(G, True)
2383
for i in [orbit[0] for orbit in vertex_orbits]:
2384
good = True
2385
for j in range(G.M.ncols()):
2386
if R(G.M[i, j][0]) != G.M[i, j]:
2387
good = False
2388
break
2389
if good:
2390
g2 = G.M[i, 0][0]
2391
if 3 * (r - r0) < g2 + 1:
2392
continue
2393
d = G.degree(i)
2394
if dim_form(g2, d, moduli_type) < r-r0:
2395
continue
2396
strata2 = all_strata(
2397
g2, r-r0, tuple(range(1, d+1)), moduli_type)
2398
which_gen_list = [
2399
-1 for num in range(len(strata2))]
2400
for num in range(len(strata2)):
2401
G_copy = Graph(G.M)
2402
G_copy.replace_vertex_with_graph(i, strata2[num])
2403
which_gen_list[num] = num_of_stratum(
2404
G_copy, g, r, markings, moduli_type)
2405
rel_list = copy(choose_basic_rels(
2406
g2, r-r0, d, moduli_type))
2407
rel_list += interior_derived_rels(g2,
2408
r-r0, d, moduli_type)
2409
for rel0 in rel_list:
2410
relation = []
2411
for x in rel0:
2412
num = x[0]
2413
if which_gen_list[num] != -1:
2414
relation.append(
2415
[which_gen_list[num], x[1]])
2416
relation = simplify_sparse(relation)
2417
relations.append(relation)
2418
dprint("%s gens, %s rels", ngen, len(relations))
2419
dsave("sparse-%s-gens-%s-rels", ngen, len(relations))
2420
dprint("Middle recursive_betti (%s,%s,%s,%s): %s", g, r,
2421
markings, moduli_type, floor(get_memory_usage()))
2422
relations = remove_duplicates(relations)
2423
nrels = len(relations)
2424
dprint("%s gens, %s distinct rels", ngen, nrels)
2425
dsave("sparse-%s-distinct-rels", nrels)
2426
rank = 0
2427
D = {}
2428
for i, reli in enumerate(relations):
2429
for x in reli:
2430
D[i, x[0]] = x[1]
2431
if nrels:
2432
row_order, col_order = choose_orders_sparse(D, nrels, ngen)
2433
rank = compute_rank_sparse(D, row_order, col_order)
2434
dsave("sparse-answer-%s", ngen-rank)
2435
return ngen - rank
2436
2437
2438
def subsequences(seq, l):
2439
"""
2440
Return all subsequences of length ``l`` a list ``seq``.
2441
2442
EXAMPLES::
2443
2444
sage: from admcycles.DR import subsequences
2445
sage: subsequences([2,3,5,7],2)
2446
[[2, 3], [2, 5], [2, 7], [3, 5], [3, 7], [5, 7]]
2447
sage: subsequences([4,3,2,1],2)
2448
[[4, 3], [4, 2], [4, 1], [3, 2], [3, 1], [2, 1]]
2449
"""
2450
S = tuple(range(len(seq)))
2451
return [[seq[i] for i in subset] for subset in Subsets(S, l)]
2452
2453
2454
@cached_function
2455
def pullback_derived_rels(g, r, n=0, moduli_type=MODULI_ST):
2456
if r == 0:
2457
return []
2458
dprint("Start pullback_derived (%s,%s,%s,%s): %s", g,
2459
r, n, moduli_type, floor(get_memory_usage()))
2460
answer = []
2461
for n0 in range(n):
2462
if dim_form(g, n0, moduli_type) >= r:
2463
basic_rels = choose_basic_rels(g, r, n0, moduli_type)
2464
for rel in basic_rels:
2465
for vec in subsequences(list(range(1, n+1)), n-n0):
2466
rel2 = copy(rel)
2467
for i in range(n-n0):
2468
rel2 = insertion_pullback(
2469
rel2, g, r, n0+i, vec[i], moduli_type)
2470
answer.append(rel2)
2471
else:
2472
basic_rels = choose_basic_rels(g, r, n0, MODULI_SMALL)
2473
k = r - dim_form(g, n0, moduli_type)
2474
for rel in basic_rels:
2475
for vec in subsequences(list(range(1, n0+k-1 + 1)), k-1):
2476
for vec2 in subsequences(list(range(1, n+1)), n-n0-k+1):
2477
rel2 = copy(rel)
2478
for i in range(k-1):
2479
rel2 = insertion_pullback(
2480
rel2, g, r, n0+i, vec[i], MODULI_SMALL)
2481
rel2 = insertion_pullback2(
2482
rel2, g, r, n0+k-1, vec2[0], moduli_type)
2483
for i in range(n-n0-k):
2484
rel2 = insertion_pullback(
2485
rel2, g, r, n0+k+i, vec2[i+1], moduli_type)
2486
answer.append(rel2)
2487
dprint("End pullback_derived (%s,%s,%s,%s): %s", g,
2488
r, n, moduli_type, floor(get_memory_usage()))
2489
return answer
2490
2491
2492
@cached_function
2493
def interior_derived_rels(g, r, n=0, moduli_type=MODULI_ST):
2494
dprint("Start interior_derived (%s,%s,%s,%s): %s", g,
2495
r, n, moduli_type, floor(get_memory_usage()))
2496
answer = copy(pullback_derived_rels(g, r, n, moduli_type))
2497
for r0 in range(r):
2498
pullback_rels = copy(choose_basic_rels(g, r0, n, moduli_type))
2499
pullback_rels += pullback_derived_rels(g, r0, n, moduli_type)
2500
for rel in pullback_rels:
2501
for i in range(r-r0+1):
2502
for sigma in Partitions(i):
2503
for tau in IntegerVectors(r-r0-i, n):
2504
rel2 = copy(rel)
2505
rcur = r0
2506
for m in range(n):
2507
for mm in range(tau[m]):
2508
rel2 = psi_multiple(
2509
rel2, m+1, g, rcur, n, moduli_type)
2510
rcur += 1
2511
for m in sigma:
2512
rel2 = kappa_multiple(
2513
rel2, m, g, rcur, n, moduli_type)
2514
rcur += m
2515
answer.append(rel2)
2516
dprint("End interior_derived (%s,%s,%s,%s): %s", g,
2517
r, n, moduli_type, floor(get_memory_usage()))
2518
return answer
2519
2520
2521
@cached_function
2522
def symmetrize_map(g, r, markings=(), moduli_type=MODULI_ST):
2523
markings2 = tuple([1 for i in markings])
2524
gens = all_strata(g, r, markings, moduli_type)
2525
map = []
2526
for G in gens:
2527
GG = Graph(G.M)
2528
for i in range(1, GG.M.ncols()):
2529
if GG.M[0, i][0] > 0:
2530
GG.M[0, i] = R(1)
2531
map.append(num_of_stratum(GG, g, r, markings2, moduli_type))
2532
return map
2533
2534
2535
@cached_function
2536
def partial_symmetrize_map(g, r, markings=(), moduli_type=MODULI_ST):
2537
markings1 = tuple(range(1, len(markings)+1))
2538
gens = all_strata(g, r, markings1, moduli_type)
2539
map = []
2540
for G in gens:
2541
GG = Graph(G.M)
2542
for i in range(1, GG.M.ncols()):
2543
if GG.M[0, i][0] > 0:
2544
GG.M[0, i] = R(markings[GG.M[0, i][0]-1])
2545
map.append(num_of_stratum(GG, g, r, markings, moduli_type))
2546
return map
2547
2548
2549
@cached_function
2550
def unsymmetrize_map(g, r, markings=(), moduli_type=MODULI_ST):
2551
markings2 = tuple([1 for i in markings])
2552
sym_map = symmetrize_map(g, r, markings, moduli_type)
2553
map = [[] for i in range(num_strata(g, r, markings2, moduli_type))]
2554
for i in range(len(sym_map)):
2555
map[sym_map[i]].append(i)
2556
return map
2557
2558
2559
def unsymmetrize_vec(vec, g, r, markings=(), moduli_type=MODULI_ST):
2560
unsym_map = unsymmetrize_map(g, r, markings, moduli_type)
2561
vec2 = []
2562
for x in vec:
2563
aut = len(unsym_map[x[0]])
2564
for j in unsym_map[x[0]]:
2565
vec2.append([j, QQ((x[1], aut))])
2566
vec2 = simplify_sparse(vec2)
2567
return vec2
2568
2569
2570
def derived_rels(g, r, n=0, moduli_type=MODULI_ST):
2571
dprint("Start derived (%s,%s,%s,%s): %s", g, r,
2572
n, moduli_type, floor(get_memory_usage()))
2573
markings = tuple([1 for _ in range(n)])
2574
# generators = all_strata(g, r, markings, moduli_type)
2575
sym_map = symmetrize_map(g, r, tuple(range(1, n+1)), moduli_type)
2576
answer = []
2577
for rel in interior_derived_rels(g, r, n, moduli_type):
2578
rel2 = []
2579
for x in rel:
2580
rel2.append([sym_map[x[0]], x[1]])
2581
rel2 = simplify_sparse(rel2)
2582
answer.append(rel2)
2583
if moduli_type <= MODULI_SM:
2584
return answer
2585
for r0 in range(1, r):
2586
strata = all_strata(g, r0, markings, moduli_type)
2587
for G in strata:
2588
vertex_orbits = graph_count_automorphisms(G, True)
2589
for i in [orbit[0] for orbit in vertex_orbits]:
2590
good = True
2591
for j in range(G.M.ncols()):
2592
if R(G.M[i, j][0]) != G.M[i, j]:
2593
good = False
2594
break
2595
if good:
2596
g2 = G.M[i, 0][0]
2597
if 3 * (r - r0) < g2 + 1:
2598
continue
2599
d = G.degree(i)
2600
if dim_form(g2, d, moduli_type) < r-r0:
2601
continue
2602
strata2 = all_strata(
2603
g2, r-r0, tuple(range(1, d+1)), moduli_type)
2604
which_gen_list = [
2605
-1 for num in range(len(strata2))]
2606
for num in range(len(strata2)):
2607
G_copy = Graph(G.M)
2608
G_copy.replace_vertex_with_graph(i, strata2[num])
2609
which_gen_list[num] = num_of_stratum(
2610
G_copy, g, r, markings, moduli_type)
2611
rel_list = copy(choose_basic_rels(
2612
g2, r-r0, d, moduli_type))
2613
rel_list += interior_derived_rels(g2, r-r0, d, moduli_type)
2614
for rel0 in rel_list:
2615
relation = []
2616
for x0, x1 in rel0:
2617
if which_gen_list[x0] != -1:
2618
relation.append([which_gen_list[x0], x1])
2619
relation = simplify_sparse(relation)
2620
answer.append(relation)
2621
answer = remove_duplicates(answer)
2622
dprint("End derived (%s,%s,%s,%s): %s", g, r, n,
2623
moduli_type, floor(get_memory_usage()))
2624
return answer
2625
2626
2627
def remove_duplicates(L):
2628
"""
2629
Remove duplicate elements in a list ``L``.
2630
2631
One cannot use ``set(L)`` because the elements of ``L`` are not hashable.
2632
2633
INPUT:
2634
2635
- ``L`` -- a list
2636
2637
OUTPUT:
2638
2639
a list
2640
2641
EXAMPLES::
2642
2643
sage: from admcycles.DR import remove_duplicates
2644
sage: remove_duplicates([4,7,6,4,3,3,4,2,2,1])
2645
[1, 2, 3, 4, 6, 7]
2646
"""
2647
if not L:
2648
return L
2649
L.sort()
2650
LL = [L[0]]
2651
for i, Li in enumerate(L[1:]):
2652
if Li != L[i]:
2653
LL.append(Li)
2654
return LL
2655
2656
2657
def simplify_sparse(vec):
2658
vec.sort()
2659
vec2 = []
2660
last_index = None
2661
for x in vec:
2662
if x[0] == last_index:
2663
if vec2[-1][1] == -x[1]:
2664
vec2 = vec2[:-1]
2665
last_index = None
2666
else:
2667
vec2[-1][1] += x[1]
2668
else:
2669
vec2.append(x)
2670
last_index = x[0]
2671
return vec2
2672
2673
2674
def compute_rank_sparse(D, row_order, col_order):
2675
count = 0
2676
nrows = len(row_order)
2677
ncols = len(col_order)
2678
row_order_rank = [-1 for i in range(nrows)]
2679
col_order_rank = [-1 for i in range(ncols)]
2680
for i in range(nrows):
2681
row_order_rank[row_order[i]] = i
2682
for i in range(ncols):
2683
col_order_rank[col_order[i]] = i
2684
2685
row_contents = [set() for i in range(nrows)]
2686
col_contents = [set() for i in range(ncols)]
2687
for x in D:
2688
row_contents[x[0]].add(x[1])
2689
col_contents[x[1]].add(x[0])
2690
2691
for i in row_order:
2692
S = []
2693
for j in row_contents[i]:
2694
S.append(j)
2695
if not S:
2696
continue
2697
count += 1
2698
S.sort(key=lambda x: col_order_rank[x])
2699
j = S[0]
2700
T = []
2701
for ii in col_contents[j]:
2702
if row_order_rank[ii] > row_order_rank[i]:
2703
T.append(ii)
2704
for k in S[1:]:
2705
rat = QQ((D[i, k], D[i, j]))
2706
for ii in T:
2707
if (ii, k) not in D:
2708
D[ii, k] = 0
2709
row_contents[ii].add(k)
2710
col_contents[k].add(ii)
2711
D[ii, k] -= rat*D[ii, j]
2712
if D[ii, k] == 0:
2713
D.pop((ii, k))
2714
row_contents[ii].remove(k)
2715
col_contents[k].remove(ii)
2716
for ii in T:
2717
D.pop((ii, j))
2718
row_contents[ii].remove(j)
2719
col_contents[j].remove(ii)
2720
return count
2721
2722
##########################################
2723
2724
2725
def veto_for_DR(num, g, r, markings=(), moduli_type=MODULI_ST):
2726
G = single_stratum(num, g, r, markings, moduli_type)
2727
nr = G.M.nrows()
2728
nc = G.M.ncols()
2729
marked_vertices = []
2730
for i in range(1, nr):
2731
if G.M[i, 0] != R(G.M[i, 0][0]):
2732
return True
2733
for j in range(1, nc):
2734
if G.M[i, j][0] == 2:
2735
return True
2736
if G.M[0, j] != 0 and G.M[i, j] != 0:
2737
marked_vertices.append(i)
2738
for ii in range(1, nr):
2739
S = set(marked_vertices)
2740
S.add(ii)
2741
did_something = True
2742
while did_something:
2743
did_something = False
2744
for i in tuple(S):
2745
if i == ii:
2746
continue
2747
for j in range(1, nc):
2748
if G.M[i, j] != 0:
2749
for i2 in range(1, nr):
2750
if G.M[i2, j] != 0 and i2 not in S:
2751
S.add(i2)
2752
did_something = True
2753
if len(S) < nr-1:
2754
return True
2755
return False
2756
2757
2758
def find_nonsep_pairs(num, g, r, markings=(), moduli_type=MODULI_ST):
2759
G = single_stratum(num, g, r, markings, moduli_type)
2760
nr = G.M.nrows()
2761
nc = G.M.ncols()
2762
answer = []
2763
for i1 in range(1, nr):
2764
for i2 in range(1, i1):
2765
found_edge = False
2766
for j in range(1, nc):
2767
if G.M[i1, j] != 0 and G.M[i2, j] != 0:
2768
found_edge = True
2769
break
2770
if not found_edge:
2771
continue
2772
S = set([i1])
2773
did_something = True
2774
while did_something:
2775
did_something = False
2776
for i3 in tuple(S):
2777
for j in range(1, nc):
2778
if G.M[i3, j] != 0:
2779
for i4 in range(1, nr):
2780
if G.M[i4, j] != 0 and i4 not in S and (i3 != i1 or i4 != i2):
2781
S.add(i4)
2782
did_something = True
2783
if i2 in S:
2784
answer.append([i2, i1])
2785
return answer
2786
2787
# assumes r <= 4 for now
2788
2789
2790
def DR_coeff_is_known(num, g, r, markings=(), moduli_type=MODULI_ST):
2791
return r <= 4
2792
2793
# old stuff
2794
G = single_stratum(num, g, r, markings, moduli_type)
2795
nr = G.M.nrows()
2796
nc = G.M.ncols()
2797
nonsep_pairs = find_nonsep_pairs(num, g, r, markings, moduli_type)
2798
if len(nonsep_pairs) > 3:
2799
return False
2800
if len(nonsep_pairs) == 3:
2801
i1 = nonsep_pairs[0][0]
2802
i2 = nonsep_pairs[0][1]
2803
i3 = nonsep_pairs[2][1]
2804
jlist = []
2805
for j in range(1, nc):
2806
if len([1 for i in [i1, i2, i3] if G.M[i, j] != 0]) == 2:
2807
jlist.append(j)
2808
if len(jlist) > 3:
2809
return False
2810
for j in jlist:
2811
for i in [i1, i2, i3]:
2812
if G.M[i, j][1] != 0:
2813
return False
2814
2815
return True
2816
2817
# this stuff is old, keeping it around for now just in case
2818
for j in range(1, nc):
2819
ilist = []
2820
for i in range(1, nr):
2821
if G.M[i, j] != 0:
2822
ilist.append(i)
2823
if len(ilist) == 1:
2824
continue
2825
ii1 = ilist[0]
2826
ii2 = ilist[1]
2827
jlist = []
2828
for jj in range(1, nc):
2829
if G.M[ii1, jj] != 0 and G.M[ii2, jj] != 0:
2830
jlist.append(jj)
2831
if len(jlist) == 1 or jlist[0] != j:
2832
continue
2833
count = len(jlist)
2834
for ii in [ii1, ii2]:
2835
for jj in jlist:
2836
count += G.M[ii, jj][1]
2837
if count > 3:
2838
return False
2839
return True
2840
2841
# next function doesn't work on arbitrary graphs yet, see above function
2842
2843
2844
def DR_coeff(num, g, r, n=0, dvector=(), moduli_type=MODULI_ST):
2845
markings = tuple(range(1, n+1))
2846
G = single_stratum(num, g, r, markings, moduli_type)
2847
nr = G.M.nrows()
2848
nc = G.M.ncols()
2849
answer = QQ((1, autom_count(num, g, r, markings, moduli_type)))
2850
2851
def balance(ilist):
2852
bal = [0 for i1 in ilist]
2853
for j1 in range(1, nc):
2854
if G.M[0, j1] != 0:
2855
for i3 in range(1, nr):
2856
if G.M[i3, j1] != 0:
2857
S = set([i3])
2858
did_something = True
2859
while did_something:
2860
did_something = False
2861
for i4 in tuple(S):
2862
for j2 in range(1, nc):
2863
if G.M[i4, j2] != 0:
2864
for i5 in range(1, nr):
2865
if G.M[i5, j2] != 0 and i5 not in S and (i4 not in ilist or i5 not in ilist):
2866
S.add(i5)
2867
did_something = True
2868
for kk, ikk in enumerate(ilist):
2869
if ikk in S:
2870
bal[kk] += dvector[G.M[0, j1][0] - 1]
2871
return bal
2872
2873
def poly4(a, b, c):
2874
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))
2875
2876
nonsep_pairs = find_nonsep_pairs(num, g, r, markings, moduli_type)
2877
if len(nonsep_pairs) == 4:
2878
cycle = [nonsep_pairs[0][0], nonsep_pairs[0]
2879
[1], 0, 0]
2880
for i in range(1, 4):
2881
for j in range(2):
2882
if nonsep_pairs[i][j] == cycle[0]:
2883
cycle[3] = nonsep_pairs[i][1 - j]
2884
if nonsep_pairs[i][j] == cycle[1]:
2885
cycle[2] = nonsep_pairs[i][1 - j]
2886
dbal = balance(cycle)
2887
a = dbal[0]
2888
b = dbal[0] + dbal[1]
2889
c = dbal[0] + dbal[1] + dbal[2]
2890
answer *= poly4(a, b, c)
2891
elif len(nonsep_pairs) == 3:
2892
i1 = nonsep_pairs[0][0]
2893
i2 = nonsep_pairs[0][1]
2894
i3 = nonsep_pairs[2][1]
2895
cycle = [i1, i2, i3]
2896
cycle4 = cycle + [cycle[0]]
2897
dbal = balance(cycle)
2898
dbal4 = dbal + [dbal[0]]
2899
done = False
2900
for k in range(3):
2901
jlist = [j for j in range(1, nc) if (
2902
G.M[cycle4[k], j] != 0 and G.M[cycle4[k+1], j] != 0)]
2903
if len(jlist) == 2:
2904
answer *= -ZZ.one() / 6 * \
2905
poly4(0, dbal4[k], -dbal4[k+1])
2906
done = True
2907
elif len(jlist) == 1:
2908
if G.M[cycle4[k], jlist[0]][1] + G.M[cycle4[k+1], jlist[0]][1] > 0:
2909
answer *= -ZZ.one() / 2 * \
2910
poly4(0, dbal4[k], -dbal4[k+1])
2911
done = True
2912
if not done:
2913
answer *= (dbal[0]**6 + dbal[1]**6 + dbal[2]**6 -
2914
10 * dbal[0]**2 * dbal[1]**2 * dbal[2]**2)/ZZ(30)
2915
2916
for j in range(1, nc):
2917
ilist = []
2918
for i in range(1, nr):
2919
if G.M[i, j] != 0:
2920
ilist.append(i)
2921
if len(ilist) == 1:
2922
x = G.M[ilist[0], j][1]
2923
answer /= factorial(x)
2924
answer *= dvector[G.M[0, j][0] -
2925
1]**(ZZ(2) * x)
2926
continue
2927
if ilist in nonsep_pairs:
2928
continue
2929
ii1 = ilist[0]
2930
ii2 = ilist[1]
2931
jlist = []
2932
for jj in range(1, nc):
2933
if G.M[ii1, jj] != 0 and G.M[ii2, jj] != 0:
2934
jlist.append(jj)
2935
if len(jlist) == 1:
2936
x1 = G.M[ii1, j][1]
2937
x2 = G.M[ii2, j][1]
2938
answer *= -1
2939
answer /= factorial(x1)
2940
answer /= factorial(x2)
2941
answer /= x1+x2+1
2942
answer *= balance([ii1, ii2])[0]**(ZZ(2) *
2943
(x1+x2+1))
2944
continue
2945
elif len(jlist) == 2:
2946
if jlist[0] != j:
2947
continue
2948
xvec = [G.M[ii1, jlist[0]][1], G.M[ii1, jlist[1]][1],
2949
G.M[ii2, jlist[0]][1], G.M[ii2, jlist[1]][1]]
2950
x = sum(xvec)
2951
if x == 0:
2952
answer *= -ZZ.one() / 6
2953
answer *= balance([ii1, ii2])[0]**4
2954
elif x == 1:
2955
answer *= -ZZ.one() / 30
2956
answer *= balance([ii1, ii2])[0]**6
2957
elif x == 2:
2958
if xvec in [[2, 0, 0, 0], [0, 2, 0, 0], [0, 0, 2, 0], [0, 0, 0, 2]]:
2959
answer *= -ZZ.one() / 168
2960
elif xvec in [[1, 1, 0, 0], [0, 0, 1, 1], [1, 0, 0, 1], [0, 1, 1, 0]]:
2961
answer *= -ZZ.one() / 280
2962
elif xvec in [[1, 0, 1, 0], [0, 1, 0, 1]]:
2963
answer *= -ZZ.one() / 84
2964
answer *= balance([ii1, ii2])[0]**8
2965
continue
2966
elif len(jlist) == 3:
2967
if jlist[0] != j:
2968
continue
2969
xvec = [G.M[ii1, jlist[0]][1], G.M[ii1, jlist[1]][1], G.M[ii1, jlist[2]]
2970
[1], G.M[ii2, jlist[0]][1], G.M[ii2, jlist[1]][1], G.M[ii2, jlist[2]][1]]
2971
x = sum(xvec)
2972
if x == 0:
2973
answer *= -ZZ.one() / 90
2974
answer *= balance([ii1, ii2])[0]**6
2975
elif x == 1:
2976
answer *= -ZZ.one() / 840
2977
answer *= balance([ii1, ii2])[0]**8
2978
continue
2979
elif len(jlist) == 4:
2980
if jlist[0] != j:
2981
continue
2982
answer *= -ZZ.one() / 2520
2983
answer *= balance([ii1, ii2])[0]**8
2984
return answer
2985
2986
2987
def DR_uncomputed(g, r, markings, moduli_type=MODULI_ST):
2988
#markings = tuple(range(1,n+1))
2989
for i in range(num_strata(g, r, markings, moduli_type)):
2990
if not veto_for_DR(i, g, r, markings, moduli_type) and not DR_coeff_is_known(i, g, r, markings, moduli_type):
2991
print(i)
2992
print(single_stratum(i, g, r, markings, moduli_type).M)
2993
print("---------------------")
2994
2995
2996
def reduce_with_rels(B, vec):
2997
vec2 = copy(vec)
2998
for row in B:
2999
for i in range(len(row)):
3000
if row[i] != 0:
3001
if vec2[i] != 0:
3002
vec2 -= QQ((vec2[i], row[i]))*row
3003
break
3004
return vec2
3005
3006
3007
def DR_coeff_setup(num, g, r, n=0, dvector=(), kval=0, moduli_type=MODULI_ST):
3008
markings = tuple(range(1, n+1))
3009
G = single_stratum(num, g, r, markings, moduli_type)
3010
nr = G.M.nrows()
3011
nc = G.M.ncols()
3012
edge_list = []
3013
exp_list = []
3014
scalar_factor = ZZ.one() / \
3015
autom_count(num, g, r, markings, moduli_type)
3016
for i in range(1, nr):
3017
for j in range(1, G.M[i, 0].degree()+1):
3018
scalar_factor /= factorial(G.M[i, 0][j])
3019
scalar_factor /= factorial(j)**G.M[i, 0][j]
3020
scalar_factor *= (-1)**G.M[i, 0][j]
3021
scalar_factor *= (kval**2)**(j * G.M[i, 0][j])
3022
given_weights = [-kval*(2*G.M[i+1, 0][0] - 2 + sum([G.M[i+1][j][0]
3023
for j in range(1, nc)])) for i in range(nr-1)]
3024
for j in range(1, nc):
3025
ilist = [i for i in range(1, nr)
3026
if G.M[i, j] != 0]
3027
if G.M[0, j] == 0:
3028
if len(ilist) == 1:
3029
i1 = ilist[0]
3030
i2 = ilist[0]
3031
exp1 = G.M[i1, j][1]
3032
exp2 = G.M[i1, j][2]
3033
else:
3034
i1 = ilist[0]
3035
i2 = ilist[1]
3036
exp1 = G.M[i1, j][1]
3037
exp2 = G.M[i2, j][1]
3038
edge_list.append([i1-1, i2-1])
3039
exp_list.append(exp1 + exp2 + 1)
3040
scalar_factor /= - \
3041
factorial(exp1)*factorial(exp2)*(exp1+exp2+1)
3042
else:
3043
exp1 = G.M[ilist[0], j][1]
3044
scalar_factor *= dvector[G.M[0, j][0] -
3045
1]**(ZZ(2) * exp1)/factorial(exp1)
3046
given_weights[ilist[0] -
3047
1] += dvector[G.M[0, j][0] - 1]
3048
return edge_list, exp_list, given_weights, scalar_factor
3049
3050
3051
def DR_coeff_new(num, g, r, n=0, dvector=(), kval=0, moduli_type=MODULI_ST):
3052
markings = tuple(range(1, n+1))
3053
G = single_stratum(num, g, r, markings, moduli_type)
3054
nr = G.M.nrows()
3055
nc = G.M.ncols()
3056
edge_list, exp_list, given_weights, scalar_factor = DR_coeff_setup(
3057
num, g, r, n, dvector, kval, moduli_type)
3058
m0 = ceil(sum([abs(i) for i in dvector])/ZZ(2)) + g*abs(kval)
3059
h0 = nc - nr - n + 1
3060
deg = 2 * sum(exp_list)
3061
mrange = list(range(m0 + 1, m0 + deg + 2))
3062
mvalues = []
3063
for m in mrange:
3064
total = 0
3065
for weight_data in itertools.product(*[list(range(m)) for i in range(len(edge_list))]):
3066
vertex_weights = copy(given_weights)
3067
for i in range(len(edge_list)):
3068
vertex_weights[edge_list[i][0]] += weight_data[i]
3069
vertex_weights[edge_list[i][1]] -= weight_data[i]
3070
if any(i % m for i in vertex_weights):
3071
continue
3072
term = 1
3073
for i in range(len(edge_list)):
3074
term *= weight_data[i]**(ZZ(2) * exp_list[i])
3075
total += term
3076
mvalues.append(QQ((total, m**h0)))
3077
mpoly = interpolate(mrange, mvalues)
3078
return mpoly.subs(X=0)*scalar_factor
3079
3080
3081
def DR_coeff_setup_m(m, num, g, r, n=0, dvector=(), kval=0, moduli_type=MODULI_ST):
3082
markings = tuple(range(1, n+1))
3083
G = single_stratum(num, g, r, markings, moduli_type)
3084
nr = G.M.nrows()
3085
nc = G.M.ncols()
3086
edge_list = []
3087
exp_list = []
3088
scalar_factor = ZZ.one() / \
3089
autom_count(num, g, r, markings, moduli_type)
3090
for i in range(1, nr):
3091
for j in range(1, G.M[i, 0].degree()+1):
3092
scalar_factor /= factorial(G.M[i, 0][j])
3093
scalar_factor /= factorial(j)**G.M[i, 0][j]
3094
scalar_factor *= (-1)**G.M[i, 0][j]
3095
scalar_factor *= (kval**2 - kval*m + m**2 /
3096
ZZ(6))**(j*G.M[i, 0][j])
3097
given_weights = [-kval*(ZZ(2) * G.M[i+1, 0][0] - ZZ(2) + sum(
3098
[G.M[i+1][j][0] for j in range(1, nc)])) for i in range(nr-1)]
3099
for j in range(1, nc):
3100
ilist = [i for i in range(1, nr)
3101
if G.M[i, j] != 0]
3102
if G.M[0, j] == 0:
3103
if len(ilist) == 1:
3104
i1 = ilist[0]
3105
i2 = ilist[0]
3106
exp1 = G.M[i1, j][1]
3107
exp2 = G.M[i1, j][2]
3108
else:
3109
i1 = ilist[0]
3110
i2 = ilist[1]
3111
exp1 = G.M[i1, j][1]
3112
exp2 = G.M[i2, j][1]
3113
edge_list.append([i1-1, i2-1])
3114
exp_list.append(exp1 + exp2 + 1)
3115
scalar_factor /= - \
3116
factorial(exp1)*factorial(exp2)*(exp1+exp2+1)
3117
else:
3118
exp1 = G.M[ilist[0], j][1]
3119
dval = dvector[G.M[0, j][0] - 1]
3120
if dval < 0:
3121
dval = dval + m
3122
scalar_factor *= (dval**2 - dval*m + m**2 /
3123
ZZ(6))**(exp1)/factorial(exp1)
3124
given_weights[ilist[0] -
3125
1] += dvector[G.M[0, j][0] - 1]
3126
return edge_list, exp_list, given_weights, scalar_factor
3127
3128
3129
def DR_coeff_m(m, num, g, r, n=0, dvector=(), kval=0, moduli_type=MODULI_ST):
3130
markings = tuple(range(1, n+1))
3131
G = single_stratum(num, g, r, markings, moduli_type)
3132
nr = G.M.nrows()
3133
nc = G.M.ncols()
3134
edge_list, exp_list, given_weights, scalar_factor = DR_coeff_setup_m(
3135
m, num, g, r, n, dvector, kval, moduli_type)
3136
h0 = nc - nr - n + 1
3137
total = ZZ.zero()
3138
for weight_data in itertools.product(*[list(range(m)) for i in range(len(edge_list))]):
3139
vertex_weights = copy(given_weights)
3140
for i in range(len(edge_list)):
3141
vertex_weights[edge_list[i][0]] += weight_data[i]
3142
vertex_weights[edge_list[i][1]] -= weight_data[i]
3143
if any(i % m for i in vertex_weights):
3144
continue
3145
term = 1
3146
for i in range(len(edge_list)):
3147
dval = weight_data[i]
3148
term *= (dval**2 - dval*m + m**2 / ZZ(6))**exp_list[i]
3149
total += term
3150
total /= m**h0
3151
total *= scalar_factor
3152
return total
3153
3154
3155
def interpolate(A, B):
3156
X = SR.var('X')
3157
l = len(A)
3158
poly = 0
3159
M = []
3160
for i in range(l):
3161
M.append(vector([a**i for a in A]))
3162
M.append(vector(B))
3163
ker = Matrix(M).kernel().basis()[0]
3164
for i in range(l):
3165
poly += (QQ(-ker[i])/QQ(ker[-1]))*X**i
3166
return poly
3167
3168
3169
def DR_compute(g, r, n=0, dvector=(), kval=0, moduli_type=MODULI_ST):
3170
answer = []
3171
markings = tuple(range(1, n+1))
3172
for i in range(num_strata(g, r, markings, moduli_type)):
3173
answer.append(DR_coeff_new(i, g, r, n, dvector, kval, moduli_type))
3174
return vector(answer)/ZZ(2) ** r
3175
3176
3177
def DR_compute_m(m, g, r, n=0, dvector=(), kval=0, moduli_type=MODULI_ST):
3178
answer = []
3179
markings = tuple(range(1, n+1))
3180
for i in range(num_strata(g, r, markings, moduli_type)):
3181
answer.append(DR_coeff_m(m, i, g, r, n, dvector, kval, moduli_type))
3182
return vector(answer)
3183
3184
3185
def DR_sparse(g, r, n=0, dvector=(), kval=0, moduli_type=MODULI_ST):
3186
"""
3187
Same as :func:`DR_compute` except that it returns the answer as a
3188
sparse vector.
3189
3190
EXAMPLES::
3191
3192
sage: from admcycles.DR import DR_sparse
3193
sage: DR_sparse(1,1,2,(2,-2))
3194
[[1, 2], [2, 2], [4, -1/24]]
3195
"""
3196
vec = DR_compute(g, r, n, dvector, kval, moduli_type)
3197
return [[i, veci] for i, veci in enumerate(vec) if veci != 0]
3198
3199
3200
def DR_psi_check(g, n, dvector, which_psi):
3201
vec = DR_sparse(g, g, n, dvector, 0)
3202
r = g
3203
while r < 3 * g - 3 + n:
3204
vec = psi_multiple(vec, which_psi, g, r, n)
3205
r += 1
3206
total = ZZ.zero()
3207
for a, b in vec:
3208
total += b * socle_evaluation(a, g, tuple(range(1, n + 1)))
3209
return total / 2**g
3210
3211
3212
def DR_reduced(g, dvector=()):
3213
"""
3214
Same as :func:`DR_compute` except that it only requires two arguments
3215
(g and dvector) and simplifies the answer using the 3-spin
3216
tautological relations.
3217
3218
EXAMPLES::
3219
3220
sage: from admcycles.DR import DR_reduced
3221
sage: DR_reduced(1,(2,-2))
3222
(0, 0, 0, 4, 1/8)
3223
"""
3224
g = ZZ(g)
3225
n = len(dvector)
3226
r = g
3227
kval = ZZ(sum(dvector) / (2 * g - 2 + n))
3228
vec = DR_compute(g, r, n, dvector, kval)
3229
vec = vector(QQ, (QQ(a) for a in vec))
3230
rels = FZ_rels(g, r, tuple(range(1, n + 1)))
3231
return reduce_with_rels(rels, vec)
3232
3233
3234
def list_strata(g, r, n=0, moduli_type=MODULI_ST):
3235
"""
3236
Displays the list of all strata.
3237
3238
EXAMPLES::
3239
3240
sage: from admcycles.DR import list_strata
3241
sage: list_strata(1,1,2)
3242
generator 0
3243
[ -1 1 2]
3244
[X + 1 1 1]
3245
------------------------------
3246
generator 1
3247
[ -1 1 2]
3248
[ 1 X + 1 1]
3249
------------------------------
3250
generator 2
3251
[ -1 1 2]
3252
[ 1 1 X + 1]
3253
------------------------------
3254
generator 3
3255
[-1 1 2 0]
3256
[ 0 1 1 1]
3257
[ 1 0 0 1]
3258
------------------------------
3259
generator 4
3260
[-1 0 1 2]
3261
[ 0 2 1 1]
3262
------------------------------
3263
"""
3264
L = all_strata(g, r, tuple(range(1, n + 1)), moduli_type)
3265
for i, Li in enumerate(L):
3266
print("generator %s" % i)
3267
print(Li.M)
3268
print("-" * 30)
3269
3270