Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
181 views
unlisted
ubuntu2004
1
r"""
2
Spin Stratum: A stratum with signature of even type will has spin structure, i.e.
3
its connected components can be partitioned to two non-empty sets.
4
5
cf. [KZ03]_ and [Boi15]_
6
7
"""
8
9
from copy import deepcopy
10
import sage.misc.persist
11
12
from sage.rings.all import QQ
13
from sage.matrix.constructor import matrix, vector
14
from sage.misc.cachefunc import cached_function
15
from sage.combinat.all import SetPartitions
16
17
import admcycles.admcycles
18
import admcycles.stratarecursion
19
20
import admcycles.diffstrata.levelgraph
21
import admcycles.diffstrata.bic
22
import admcycles.diffstrata.stratatautring
23
import admcycles.diffstrata.embeddedlevelgraph
24
25
from admcycles.stable_graph import StableGraph
26
from admcycles.diffstrata.generalisedstratum import GeneralisedStratum
27
from admcycles.diffstrata.additivegenerator import AdditiveGenerator
28
from admcycles.diffstrata.elgtautclass import ELGTautClass
29
from admcycles.diffstrata.sig import Signature
30
31
from admcycles.identify_classes import SolveMultLin
32
33
34
def Spin_strataclass(sig, res_cond=[]):
35
r"""
36
Compute the spin stratum class of given signature of even type (may
37
have paired simple poles) and residue conditions.
38
39
INPUT:
40
41
- sig (tuple)
42
- res_cond (list): It is a list of lists corresponding to residue
43
conditions. For example, sig=(4,-2,-1,-1) with the
44
simple poles being paired. Then
45
res_cond=[[(0,2), (0,3)]]. Here the zero indicate
46
the components of a generalized stratum.
47
48
EXAMPLES::
49
50
sage: from admcycles.diffstrata.spinstratum import Spin_strataclass
51
sage: Spin_strataclass((2,2)).basis_vector()
52
(159/4, -179/48, -7/24, -7/24, -131/48, -23/24, -131/48, 149/24, -83/16, 271/48, 77/48, 77/48, -193/8, -185/48, 127/48, 395/48, 221/48, -73/8, -185/48, 127/48, 395/48, 221/48, -73/8, -185/48, -41/48, 389/48, 389/48, -23/8, 51/8, -139/16, -323/16, 1/8, -25/4, -11/4, -11/4, -23/4, 973/48, 37/96, -5/2, 23/96, 23/96, -25/32)
53
54
55
"""
56
X = SpinStratum([Signature(sig)], res_cond=res_cond)
57
A = AG_with_spin(X, ((), 0))
58
E = ELGT_with_spin(X, [(1, A)])
59
return E.to_prodtautclass_spin().pushforward()
60
61
62
#######################################################################
63
#######################################################################
64
# Define the class of spin strata #####################################
65
#######################################################################
66
67
class SpinStratum(GeneralisedStratum):
68
69
def __init__(self, sig_list, res_cond=None, pair_res_cond=None):
70
r"""
71
A stratum can have spin structure if there are pairs of simple poles,
72
such that their residues r_i + r_i+1 = 0. In this code, we only handle the
73
cases of at most 1 pair of simple poles.
74
75
INPUT:
76
77
- sig_list (list): a list of tuples indicating the signatures
78
of the components of a generalised stratum
79
80
- res_cond (list): a list whose entries are lists corresponding
81
to residue conditions
82
83
EXAMPLES::
84
85
sage: from admcycles.diffstrata.spinstratum import SpinStratum
86
sage: from admcycles.diffstrata.sig import Signature
87
sage: X = SpinStratum([Signature((4,-2,-1,-1))],
88
....: res_cond=[[(0,2),(0,3)]])
89
90
"""
91
92
super().__init__(sig_list, res_cond)
93
94
if res_cond:
95
self._res_cond = res_cond
96
else:
97
self._res_cond = []
98
if pair_res_cond:
99
self.pair_res_cond = pair_res_cond
100
else:
101
self.pair_res_cond = []
102
self.odd_sing_list = []
103
for i in range(0, len(self._sig_list)):
104
for j in range(0, len(sig_list[i].sig)):
105
if sig_list[i].sig[j] % 2 != 0:
106
self.odd_sing_list.append((i, j))
107
108
self.sim_pole_list = list(self.simple_poles())
109
self.sim_pole_list.sort()
110
self.hor_edge = []
111
self.eff_hor_edge = []
112
self.eff_pair_res = []
113
114
if self.odd_sing_list != [] and pair_res_cond is None:
115
self.is_legal()
116
117
def __repr__(self):
118
return "SpinStratum(sig_list=%r,res_cond=%r)" % (
119
self._sig_list, self._res_cond)
120
121
def __str__(self):
122
rep = ''
123
if self._h0 > 1:
124
rep += 'Product of Strata with spin:\n'
125
else:
126
rep += 'Spin Stratum: '
127
for sig in self._sig_list:
128
rep += repr(sig.sig) + '\n'
129
rep += 'with residue conditions: '
130
if not self._res_cond:
131
rep += repr([]) + '\n'
132
for res in self._res_cond:
133
rep += repr(res) + '\n'
134
return rep
135
136
def is_legal(self):
137
r"""
138
This is just a method for initializing a spin stratum, checking whether
139
the stratum really has spin structure.
140
141
EXAMPLES::
142
143
sage: from admcycles.diffstrata.spinstratum import SpinStratum
144
sage: from admcycles.diffstrata.sig import Signature
145
sage: X = SpinStratum([Signature((4,-2,-2))])
146
sage: X = SpinStratum([Signature((4,-3,-1))])
147
Traceback (most recent call last):
148
...
149
ValueError: The Stratum does not have spin components
150
sage: X = SpinStratum([Signature((4,-2,-1,-1))])
151
sage: X = SpinStratum([Signature((4,-1,-1,-1,-1))])
152
Traceback (most recent call last):
153
...
154
NotImplementedError: We can only handle one pair of simple poles
155
156
"""
157
158
if len(self.odd_sing_list) - len(self.sim_pole_list) > 0:
159
raise ValueError("The Stratum does not have spin components")
160
elif len(self.sim_pole_list) > 0:
161
t = len(self.sim_pole_list)
162
if t > 2:
163
raise NotImplementedError("We can only handle " +
164
"one pair of simple poles")
165
if not t % 2 == 0:
166
raise ValueError("The Stratum does not have spin components")
167
168
M1 = self.smooth_LG.residue_matrix_from_RT
169
if self.res_cond != []:
170
M2 = M1.stack(self.matrix_from_res_conditions(self.res_cond))
171
else:
172
M2 = M1
173
a = 0
174
b = M2.rank()
175
c = M1.rank()
176
177
for q in range(0, t - 1):
178
for q1 in range(q + 1, t):
179
if self.sim_pole_list[q][0] != self.sim_pole_list[q1][0]:
180
res = [self.sim_pole_list[q], self.sim_pole_list[q1]]
181
T = self.matrix_from_res_conditions(res)
182
M0 = M2.stack(T)
183
a = M0.rank()
184
if a > b:
185
continue
186
else:
187
self.hor_edge.append([self.sim_pole_list[q],
188
self.sim_pole_list[q1]])
189
sim_pole_list_partitons = SetPartitions(t, [2 for i in
190
range(0, int(t / 2))]
191
).list()
192
rc = []
193
for part in sim_pole_list_partitons:
194
pair_res_cond_temp = []
195
for q in part:
196
w = list(q)
197
pair_res_cond_temp.append([self.sim_pole_list[i - 1]
198
for i in w])
199
200
M0 = M2.stack(self.matrix_from_res_conditions(
201
pair_res_cond_temp))
202
a = M0.rank()
203
if a > b:
204
continue
205
else:
206
self.pair_res_cond = pair_res_cond_temp
207
rc = pair_res_cond_temp + deepcopy(self._res_cond)
208
break
209
210
if self.pair_res_cond is None:
211
raise ValueError("The Stratum does not have spin components")
212
if t == 1 and a == 1:
213
self._res_cond = []
214
elif not self.is_empty():
215
new_rc = []
216
for j, q in enumerate(rc):
217
rk = M1.stack(self.matrix_from_res_conditions([q])).rank()
218
if rk > c:
219
M1 = M1.stack(self.matrix_from_res_conditions([q]))
220
c += 1
221
if q in self.pair_res_cond:
222
self.eff_pair_res.append(j)
223
new_rc.append(q)
224
new_rc.reverse()
225
self._res_cond = new_rc
226
return
227
228
def remove_pair_res(self, psis=None):
229
r"""
230
This method will return the psi polynomial of the stratum in the
231
tautological ring of ambient stratum (without a residue condition
232
on the pair of simple poles. The formula follows Sauvaget-19 and we pick
233
the leg of a simple pole for the elimination of psi class.
234
235
Only the BICs, which have some odd enhancement (so odd even are half
236
half), or still preserve the residue condition of the pair of
237
simple poles, are left. Hence, one can
238
still recursively compute the spin classes.
239
240
INPUT:
241
242
- psis (list): a list of dicts indicating the psi polynomial
243
244
EXAMPLES::
245
246
247
sage: from admcycles.diffstrata.spinstratum import SpinStratum
248
sage: from admcycles.diffstrata.sig import Signature
249
sage: from admcycles.admcycles import psiclass
250
sage: X = SpinStratum([Signature((4,-2,-1,-1))],res_cond=[[(0,2),(0,3)]])
251
sage: cl1 = X.remove_pair_res().to_prodtautclass_spin().pushforward()
252
sage: (psiclass(1,1,4)**2*cl1).evaluate()
253
5/24
254
255
"""
256
if not self.eff_pair_res == []:
257
# print("removing pair res")
258
res_cond = deepcopy(self._res_cond)
259
lost_rc = res_cond.pop()
260
leg = lost_rc[0]
261
new_stratum = SpinStratum(self._sig_list, res_cond,
262
pair_res_cond=self.pair_res_cond)
263
new_AG = new_stratum.additive_generator(((), 0), psis)
264
elgt = new_AG.as_taut() * (new_stratum.res_stratum_class(lost_rc) +
265
new_stratum.xi_with_leg() -
266
new_stratum.xi_with_leg(leg))
267
temp_psi_list = []
268
for a in elgt._psi_list:
269
if all(kappa % 2 != 0
270
for e, kappa in a[1]._G.LG.prongs_list()):
271
temp_psi_list.append(a)
272
psi_list = [(c, AG_with_spin(new_stratum,
273
AG._enh_profile, AG._leg_dict))
274
for c, AG in temp_psi_list]
275
elgt_odd_enh = ELGT_with_spin(new_stratum, psi_list)
276
# print(str(elgt_odd_enh))
277
result = elgt_odd_enh
278
return result
279
else:
280
raise NotImplementedError
281
282
def smooth_con_stratum_prodtautclass(self):
283
r"""
284
To compute the spin stratum class of a connected stratum. The output will be a
285
product tautological class. By making use of standised signature, we will do
286
the core computation in the function smooth_stratum_standard().
287
288
EXAMPLES::
289
290
sage: from admcycles.diffstrata.spinstratum import SpinStratum
291
sage: from admcycles.diffstrata.sig import Signature
292
sage: X=SpinStratum([Signature((4,0,-2))])
293
sage: X.smooth_con_stratum_prodtautclass().pushforward().basis_vector()
294
(-63/2, 21/2, -20, -63/2, -37/2, 38, 61/2, 61/2, 147/2, 29, 57/2, 29, -41, -60, 21, -63/2, -42, 61/2, -41, -139/2, 147/2, 63/2, -147/2, 21/2, 0, -21/2, -7, -21/2, -19/2, 19/2, 21/2, -21/2, 1, -21/2, 0, 0, 21/2, 21/2, -7, 1, -1, -1, 0, 7)
295
296
"""
297
res_cond = self._res_cond
298
sig = self._sig_list[0].sig
299
g = self._sig_list[0].g
300
LG = self.smooth_LG.LG
301
stgraph0 = LG.stgraph
302
303
if all(x == 0 for x in sig): # it is just g=1 and the stratum class is just fundclass of M_1,n
304
t = deepcopy(smooth_stratum_standard(sig))
305
if all(w == 0 for w in t[3]):
306
result = admcycles.admcycles.prodtautclass(stgraph0, terms=[])
307
else:
308
ind = admcycles.admcycles.generating_indices(t[0], t[1], t[2])
309
gen = admcycles.admcycles.tautgens(t[0], t[1], t[2])
310
result = sum([t[3][i] * gen[ind[i]]
311
for i in range(0, len(t[3]))]).toprodtautclass()
312
return result
313
314
L = standardise_sig(sig, res_cond) # We use the standardised signature
315
simplified = L[0]
316
num_of_reg = L[1]
317
legs = {x2: x1 for x1, x2 in L[2].items()}
318
new_rc = L[3]
319
320
# The stratum classes of signature with 0s are essentially just the
321
# forgetful pullback of stratum class of signature without 0s.
322
# We use the function smooth_stratum_standard to do the pullbacks solving.
323
324
t = deepcopy(smooth_stratum_standard(simplified, tuple(new_rc)))
325
if all(w == 0 for w in t[3]):
326
cl = admcycles.admcycles.fundclass(g, len(simplified))
327
else:
328
ind = admcycles.admcycles.generating_indices(t[0], t[1], t[2])
329
gen = admcycles.admcycles.tautgens(t[0], t[1], t[2])
330
cl = sum([t[3][i] * gen[ind[i]] for i in range(0, len(t[3]))])
331
if num_of_reg != 0:
332
cl = cl.forgetful_pullback([len(simplified) + i
333
for i in range(1, L[1] + 1)])
334
result = cl.rename_legs(legs, inplace=False).toprodtautclass()
335
# print(str(legs)+ "\n"+ str(cl) + "\n" + str(result))
336
return result
337
338
#######################################################################
339
#######################################################################
340
# Define the spin version additive generators #########################
341
#########################################
342
# * the spin taut class of those enhanced level graphs with some even
343
# enhancement are just considered as zero in this algorithm, because
344
# the pushforward of them to \bar M_g,n will be zero.
345
#######################################################################
346
347
348
class AG_with_spin (AdditiveGenerator):
349
350
def __init__(self, X, enh_profile, leg_dict=None):
351
assert isinstance(X, SpinStratum)
352
super().__init__(X, enh_profile, leg_dict)
353
self.X = X
354
355
def __repr__(self):
356
return "AG_with_spin(X=%r,enh_profile=%r,leg_dict=%r)"\
357
% (self._X, self._enh_profile, self._leg_dict)
358
359
def __str__(self):
360
str = ""
361
if self._leg_dict is not None:
362
for l in self._leg_dict:
363
str += "Spin Psi class %r with exponent %r on level %r * "\
364
% (l, self._leg_dict[l], self._level_dict[l])
365
str += "Graph %r" % (self._enh_profile,)
366
return str
367
368
def to_prodtautclass_spin(self, rearrange_markings=True):
369
r"""
370
This function is to compute the product tautological class of an
371
additive generator with spin. It is basically the same as
372
toprodtautclass, but just includes more case divisions.
373
374
EXAMPLES::
375
376
sage: from admcycles.diffstrata.spinstratum import SpinStratum,AG_with_spin
377
sage: from admcycles.diffstrata.sig import Signature
378
sage: X = SpinStratum([Signature((4,-2))])
379
sage: AG_with_spin(X, ((1,),0)).to_prodtautclass_spin()
380
Outer graph : [1, 0] [[3, 4], [1, 2, 5, 6]] [(3, 5), (4, 6)]
381
Vertex 0 :
382
Graph : [1] [[1, 2]] []
383
Polynomial : -1/2
384
Vertex 1 :
385
Graph : [0] [[1, 2, 3, 4]] []
386
Polynomial : psi_4
387
<BLANKLINE>
388
<BLANKLINE>
389
Vertex 0 :
390
Graph : [1] [[1, 2]] []
391
Polynomial : -1/2
392
Vertex 1 :
393
Graph : [0, 0] [[2, 3, 11], [1, 4, 12]] [(11, 12)]
394
Polynomial : 3
395
<BLANKLINE>
396
<BLANKLINE>
397
Vertex 0 :
398
Graph : [1] [[1, 2]] []
399
Polynomial : -1/2
400
Vertex 1 :
401
Graph : [0, 0] [[3, 4, 11], [1, 2, 12]] [(11, 12)]
402
Polynomial : -3
403
404
"""
405
LG = self._G.LG
406
stgraph = LG.stgraph
407
408
# If the level graph has any edge with positive even enhancement,
409
# half of the prong matchings give an even spin curve and half of
410
# them give an odd spin curve
411
if any(kappa % 2 == 0 for e, kappa in LG.prongs_list()):
412
return admcycles.admcycles.prodtautclass(stgraph, terms=[])
413
414
# Check whether the prodtautclass will be zero
415
# If any level has extra freedom of scaling, the class will be zero on
416
# moduli space of curves.
417
if any(self.level(l).zeroStratumClass()
418
for l in range(self.codim + 1)):
419
if rearrange_markings:
420
stgraph = self._G.relabel(self._G.standard_markings(), tidyup=False).LG.stgraph
421
return admcycles.admcycles.prodtautclass(stgraph, terms=[])
422
423
# To handle the case that the stratum is connected
424
if len(LG.genera) == 1:
425
adm_psis = admcycles.admcycles.decstratum(stgraph,
426
psi=self.leg_dict)
427
adm_psis_taut = admcycles.admcycles.tautclass([adm_psis])
428
429
# To handle the case that the genus is 0 and there are no
430
# simple poles.
431
if LG.genera[0] == 0 and self.X.sim_pole_list == []:
432
stratum_class = AdditiveGenerator(self.X, ((), 0)).as_taut()
433
stratum_class = stratum_class.to_prodtautclass().pushforward()
434
protaut = [adm_psis_taut * stratum_class]
435
result = admcycles.admcycles.prodtautclass(stgraph,
436
protaut=protaut)
437
return result
438
439
# To handle the case that there are only two poles which are a pair
440
# of simple poles.
441
if (self.X.res_cond == [] or
442
len(self.X.eff_pair_res) < len(self.X.pair_res_cond)):
443
stratum_class = self.X.smooth_con_stratum_prodtautclass()
444
stratum_class = stratum_class.pushforward()
445
protaut = [stratum_class * adm_psis_taut]
446
result = admcycles.admcycles.prodtautclass(stgraph,
447
protaut=protaut)
448
return result
449
450
# To resolve residue condition of a pair of simple poles if the
451
# residue condition is extra.
452
elif (self.X.eff_pair_res != [] and
453
len(self.X.eff_pair_res) == len(self.X.pair_res_cond)):
454
Q = self.X.remove_pair_res()
455
total = Q.to_prodtautclass_spin().pushforward()
456
protaut = [adm_psis_taut * total]
457
result = admcycles.admcycles.prodtautclass(stgraph,
458
protaut=protaut)
459
return result
460
461
# Now it processes to break down the level graph into level
462
# stratum and resolve the residue conditions (non simple poles).
463
# Then it computes the result by level clutching.
464
# The level graph that can come to this stage will have only odd
465
# enhancements or the stratum is not connected, i.e. no vertical edges.
466
# In the both cases, the spin class depends on the spin class on
467
# each level.
468
469
alpha = []
470
vertices = []
471
for l in range(self.codim + 1):
472
psis = self.psis_on_level(l)
473
level = self.level(l)
474
level = addspin(level) # level stratum with spin structure
475
476
if (level.eff_pair_res != [] and
477
len(level.eff_pair_res) == len(level.pair_res_cond)):
478
# To remove residue condition on a pair of simple poles on
479
# a disconnected stratum.
480
ptc = level.remove_pair_res(psis).to_prodtautclass_spin()
481
else:
482
T = level.remove_res_cond(psis)
483
ptc = ELGT_addspin(T).to_prodtautclass_spin()
484
# print(str(ptc0))
485
# print(str(ptc1))
486
alpha.append(ptc)
487
vertices.append(LG.verticesonlevel(LG.internal_level_number(l)))
488
489
if rearrange_markings: # if we want the labels of stgraph correspond to the markings
490
prodtautst = self._G.relabel(self._G.standard_markings(), tidyup=False).LG.stgraph
491
prod = admcycles.admcycles.prodtautclass(prodtautst)
492
493
else:
494
prod = admcycles.admcycles.prodtautclass(stgraph)
495
496
# To clutch the level stratum classes together
497
for l, ptc in enumerate(alpha):
498
prod = prod.factor_pullback(vertices[l], ptc)
499
500
return self.stack_factor * prod
501
502
503
def standardise_sig(sig, res_cond=[]):
504
r"""
505
To standardise the arrangement of entries in the signature, such that it go from low to high and all the 0s
506
are placed at the right end. This is just to reduce the required memory and time to compute the stratum classes.
507
It returns
508
509
1. the simplified sig (0s removed);
510
2. number of 0s;
511
3. the leg_dict of rearrangement;
512
4. new res_cond
513
514
INPUT:
515
516
- sig (tuple): a tuple of integers
517
- res_cond (list, default=[]): the list of res_cond
518
519
EXAMPLES::
520
521
sage: from admcycles.diffstrata.spinstratum import standardise_sig
522
sage: standardise_sig((2,4,-1,0,-1), res_cond=[[(0,2),(0,4)]])
523
[(-1, -1, 2, 4), 1, {1: 3, 2: 4, 3: 1, 4: 5, 5: 2}, [((0, 0), (0, 1))]]
524
525
"""
526
res = deepcopy(res_cond)
527
new_rc = []
528
l = list(set(sig))
529
g = Signature(sig).g
530
l.sort()
531
t = list(sig)
532
freq = {x: t.count(x) for x in l}
533
counter = deepcopy(freq)
534
standard_sig_without_regular = []
535
536
for x in l:
537
if x != 0:
538
standard_sig_without_regular += freq[x] * [x]
539
m = len(standard_sig_without_regular)
540
if g == 0 and m < 3:
541
standard_sig_without_regular += (3 - m) * [0]
542
543
leg_dict = {}
544
for i in range(0, len(sig)):
545
if sig[i] == 0:
546
p = sum([f for j, f in freq.items() if j != 0])
547
leg_dict[i + 1] = p + freq[0] - counter[0] + 1
548
counter[0] -= 1
549
if sig[i] != 0:
550
p = sum([f for j, f in freq.items() if (j != 0 and j < sig[i])])
551
leg_dict[i + 1] = p + freq[sig[i]] - counter[sig[i]] + 1
552
counter[sig[i]] -= 1
553
output = []
554
output.append(tuple(standard_sig_without_regular))
555
if 0 in l:
556
if g == 0 and m < 3:
557
output.append(freq[0] - 3 + m)
558
else:
559
output.append(freq[0])
560
else:
561
output.append(0)
562
output.append(leg_dict)
563
564
for r in res:
565
w = []
566
for q in r:
567
a = (0, leg_dict[q[1] + 1] - 1)
568
w.append(a)
569
new_rc.append(tuple(w))
570
output.append(new_rc)
571
return output
572
573
574
@cached_function
575
def smooth_stratum_standard(sig, res_cond=()):
576
r"""
577
This function is to compute the spin stratum class of a standardised
578
signature i.e. it is ordered and has no 0s (or all are 0s), also the
579
stratum has only one component. The results are cached.
580
581
EXAMPLES::
582
583
sage: from admcycles.diffstrata.spinstratum import smooth_stratum_standard
584
sage: smooth_stratum_standard((-1,-1,4))
585
[2,
586
3,
587
2,
588
(0, 0, 4, 0, -11/2, -12, 0, -7/2, 0, 1/2, 35/2, 4, 0, -12, 0, 0, 0, -2, 19/2, 0, 7/2, 0, 0, 0, 2, 0, -7/2, 0, 2, -2, 5/2, -13/2, 9/2, 8, 0, 0, 0, 17/2, -11, 2, -2, -9/2, 0, 2)]
589
590
591
"""
592
assert (sum([x for x in sig]) == -2 and len(sig) == 3) or all(j == 0 for j in sig) or all(j != 0 for j in sig)
593
594
if res_cond != ():
595
rc = []
596
for q in res_cond:
597
res_single_cond = [t for t in q]
598
rc.append(res_single_cond)
599
X = SpinStratum([Signature(sig)], rc)
600
else:
601
X = SpinStratum([Signature(sig)])
602
603
# LG = X.smooth_LG.LG
604
# stgraph = LG.stgraph
605
g = X._sig_list[0].g
606
n = X._sig_list[0].n
607
r = 0
608
if any(x < 0 for x in sig):
609
r = len(res_cond) + 1
610
k = g + r - 1
611
# The codim we are dealing with for computation of stratum class.
612
613
# length=len(admcycles.admcycles.generating_indices(g,n,k))
614
check_basic = is_base_case(sig, g, n, k)
615
616
# To check whether the stratum class is the base cases
617
if check_basic != []:
618
return check_basic
619
620
# Solve the pullback equations
621
ListM = Pullback_Matrices(g, n, r)
622
prodtaut_list = bdry_pullback_classify(X)
623
tensorTaut_list = []
624
for i, u in enumerate(prodtaut_list):
625
if u != []:
626
t = sum(u)
627
v = vector(t.totensorTautbasis(k, vecout=True))
628
tensorTaut_list.append(v)
629
else:
630
length = len(ListM[i][0])
631
v = vector(QQ, length)
632
tensorTaut_list.append(v)
633
634
sol_inTautbasis = SolveMultLin(ListM, tensorTaut_list)
635
return [g, n, k, sol_inTautbasis]
636
637
# To give the stratum classes with spin for the easy cases.
638
639
640
def is_base_case(sig, g, n, k):
641
if g == 0:
642
if all(q % 2 == 0 for q in sig):
643
return [g, n, k, vector([QQ(1)])]
644
645
# case of (0,-1,-1)
646
if n == 3:
647
return [g, n, k, vector([-QQ(1)])]
648
649
# case of (2k,-2k,-1,-1)
650
if n == 4:
651
result = admcycles.admcycles.psiclass(1, 0, 4).basis_vector()
652
return [g, n, k, result]
653
654
if g == 1:
655
if all(t == 0 for t in sig):
656
return [g, n, k, vector([-QQ(1)])]
657
658
if g == 2 and n == 1:
659
return [g, n, k, vector([QQ(1) / 2, -QQ(7) / 2, QQ(1) / 2])]
660
661
return []
662
663
664
def bdry_pullback_classify(stratum):
665
X = stratum
666
sig = X._sig_list[0].sig
667
g = X._sig_list[0].g
668
n = X._sig_list[0].n
669
# r = 0
670
# if any(m < 0 for m in sig):
671
# r = len(X.res_cond) + 1
672
# k = g + r - 1
673
Agraphs = admcycles.admcycles.list_strata(g, n, 1)
674
if g != 0 and n >= 3:
675
Agraphs = [G for i, G in enumerate(Agraphs) if i != 0]
676
else:
677
G = Agraphs[0].copy()
678
G.tidy_up()
679
Agraphs[0] = G
680
681
bddiv_codim1_LG_prodtautclass_list = [[] for G in Agraphs]
682
bic_stgraph = [bic.relabel(bic.standard_markings(), tidyup=False) for bic in X.bics]
683
684
for j, G in enumerate(Agraphs):
685
sig0 = [sig[i - 1] for i in G._legs[0] if i < n + 1]
686
node_sing = 2 * G._genera[0] - 2 - sum(sig0)
687
688
if node_sing == -1:
689
sig0.append(-1)
690
sig1 = [sig[i - 1] for i in G._legs[1] if i < n + 1] + [-1]
691
new_sig_list = [sig0, sig1]
692
splt_st0 = GeneralisedStratum([Signature(tuple(new_sig_list[0]))])
693
splt_st1 = GeneralisedStratum([Signature(tuple(new_sig_list[1]))])
694
elgt0 = splt_st0.additive_generator(((), 0)).as_taut()
695
elgt1 = splt_st1.additive_generator(((), 0)).as_taut()
696
st0_spin = ELGT_addspin(elgt0).to_prodtautclass_spin()
697
st1_spin = ELGT_addspin(elgt1).to_prodtautclass_spin()
698
prodtaut = admcycles.admcycles.prodtautclass(G)
699
700
# Note that for a horizontal one edge graph, the splitting of the
701
# two vertices will give rise to extra symplectic basis and thus
702
# if sum of spins are odd (even), then the horizontal graph
703
# has spin even(odd).
704
clutch = prodtaut.factor_pullback([0], st0_spin)
705
clutch = clutch.factor_pullback([1], -st1_spin)
706
bddiv_codim1_LG_prodtautclass_list[j].append(clutch)
707
708
else:
709
if len(G._genera) == 1:
710
sig_new = ([sig[i - 1] for i in G._legs[0] if i < n + 1] +
711
[-1, -1])
712
Y = SpinStratum([Signature(tuple(sig_new))],
713
res_cond=[[(0, n), (0, n + 1)]])
714
elgt = Y.additive_generator(((), 0)).as_taut()
715
prodtaut = admcycles.admcycles.prodtautclass(G)
716
717
stratum_class = ELGT_addspin(elgt).to_prodtautclass_spin()
718
Intersect = prodtaut.factor_pullback([0], stratum_class)
719
# print(str(Intersect))
720
bddiv_codim1_LG_prodtautclass_list[j].append(Intersect)
721
for num, bic in enumerate(bic_stgraph):
722
stgraph = bic.LG.stgraph
723
a = G._edges[0][0]
724
b = G._edges[0][1]
725
726
Astructure_list = admcycles.admcycles.Astructures(stgraph, G)
727
728
if Astructure_list != []:
729
# print(str(Astructure_list))
730
AG = AG_addspin(X.additive_generator(((num,), 0)))
731
prodtaut_temp = AG.to_prodtautclass_spin()
732
# print(str(prodtaut_temp))
733
ell = bic.ell
734
for w in Astructure_list:
735
G_no_edge = StableGraph(G._genera, G._legs, [])
736
image_half_edges = [(w[1][e[0]], w[1][e[1]])
737
for e in G._edges]
738
st_edges_new = [e for e in stgraph._edges if
739
(e not in image_half_edges) and
740
((e[1], e[0]) not in image_half_edges)]
741
st_new = StableGraph(stgraph._genera,
742
stgraph._legs, st_edges_new)
743
prodtaut = admcycles.admcycles.prodtautclass(st_new, prodtaut_temp.terms)
744
# print(str(G_no_edge)+"\n"+str(st_new))
745
E_temp = prodtaut.partial_pushforward(G_no_edge,
746
w[0], w[1])
747
E = admcycles.admcycles.prodtautclass(G, E_temp.terms)
748
# print(str(E_temp)+"\n" +str(E))
749
kappa_e = bic.LG.prong((w[1][a], w[1][b]))
750
Intersect = QQ(ell / kappa_e) * E
751
bddiv_codim1_LG_prodtautclass_list[j].append(Intersect)
752
753
return bddiv_codim1_LG_prodtautclass_list
754
755
756
# To generate a list of matrices of the clutching pullback maps.
757
@cached_function
758
def Pullback_Matrices(g, n, r):
759
k = g + r - 1
760
L1 = admcycles.admcycles.tautgens(g, n, k)
761
Listgen1 = admcycles.admcycles.generating_indices(g, n, k)
762
763
graph_list = admcycles.admcycles.list_strata(g, n, 1)
764
if g != 0 and n >= 3:
765
graph_list = [G for i, G in enumerate(graph_list) if i != 0]
766
767
M_list = []
768
for G in graph_list:
769
M0 = []
770
if len(G._genera) == 1:
771
G = G.copy()
772
G.tidy_up()
773
for i in Listgen1:
774
T = G.boundary_pullback(L1[i])
775
M0 = M0 + [T.totensorTautbasis(k, vecout=True)]
776
M = matrix(M0)
777
M_list.append(M)
778
return M_list
779
780
781
#######################################################################
782
#######################################################################
783
# Define the spin version ELGTautClass ################################
784
#######################################################################
785
786
787
class ELGT_with_spin (ELGTautClass):
788
789
def __init__(self, X, psi_list):
790
assert isinstance(X, SpinStratum)
791
super().__init__(X, psi_list)
792
self.X = X
793
794
def __repr__(self):
795
return "ELGT_SpinClass(X=%r,psi_list=%r)"\
796
% (self._X, self._psi_list)
797
798
def __str__(self):
799
str = "ELGT_SpinClass on %s\n" % self._X
800
for coeff, psi in self._psi_list:
801
str += "%s * %s + \n" % (coeff, psi)
802
return str
803
804
def to_prodtautclass_spin(self):
805
G = self.X.smooth_LG
806
# print(str(G))
807
stgraph = G.LG.stgraph
808
total = admcycles.admcycles.prodtautclass(stgraph, terms=[])
809
for c, AG in self._psi_list:
810
legdictlen = len(AG._G.dmp)
811
ptc = AG.to_prodtautclass_spin()
812
vertex_map = {}
813
for v, _ in enumerate(G.LG.genera):
814
mp_on_stratum = G.dmp[G.LG.legs[v][0]]
815
l_AG = AG._G.dmp_inv[mp_on_stratum]
816
LG = AG._G.LG
817
v_AG = LG.vertex(l_AG)
818
UG_v = LG.UG_vertex(v_AG)
819
T = LG.UG_without_infinity()
820
T = T.connected_component_containing_vertex(UG_v)
821
for w, g, kind in T:
822
if kind != 'LG':
823
continue
824
vertex_map[w] = v
825
leg_map = {i: i for i in range(1, legdictlen + 1)}
826
# print(str(stgraph)+"\n"+ str(leg_map)+ "\n"+ str(vertex_map)+ "\n" + str(self)+
827
# "\n" + str(ptc))
828
pf = ptc.partial_pushforward(stgraph, vertex_map, leg_map)
829
# print(str(pf) + "\n" + str(ptc)+ "\n" + str(c))
830
total += c * pf
831
return total
832
833
834
#############################################################################
835
# Some useful functions #####################################################
836
#############################################################################
837
838
839
# turn a ELGTautClass to spin version
840
841
def ELGT_addspin(elgt):
842
X = SpinStratum(elgt._X._sig_list, elgt._X._res_cond)
843
psi_list = [(c, AG_addspin(AG)) for c, AG in elgt._psi_list]
844
return ELGT_with_spin(X, psi_list)
845
846
847
# turn a AdditiveGenerator to spin version
848
849
def AG_addspin(ag):
850
assert isinstance(ag, AdditiveGenerator)
851
X = addspin(ag._X)
852
return AG_with_spin(X, ag._enh_profile, ag._leg_dict)
853
854
855
# turn a stratum to spin version
856
857
def addspin(stratum):
858
assert isinstance(stratum, GeneralisedStratum)
859
return SpinStratum(stratum._sig_list, stratum._res_cond)
860
861
862
def save_spin_stratum():
863
cachedict = dict(smooth_stratum_standard.cache)
864
sage.misc.persist.save(cachedict, 'spin_stratum_cache')
865
866
867
def load_spin_stratum():
868
cachedict = sage.misc.persist.load('spin_stratum_cache.sobj')
869
for a, b in cachedict.items():
870
smooth_stratum_standard.set_cache(b, *a[0])
871
872