Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
181 views
unlisted
ubuntu2004
1
from collections import Counter
2
3
# pylint does not know sage
4
from sage.structure.sage_object import SageObject # pylint: disable=import-error
5
from sage.misc.cachefunc import cached_method # pylint: disable=import-error
6
7
import admcycles.diffstrata.additivegenerator
8
import admcycles.admcycles
9
10
11
class ELGTautClass (SageObject):
12
"""
13
A Tautological class of a stratum X, i.e. a formal sum of of psi classes on
14
EmbeddedLevelGraphs.
15
16
This is encoded by a list of summands.
17
18
Each summand corresponds to an AdditiveGenerator with coefficient.
19
20
Thus an ELGTautClass is a list with entries tuples (coefficient, AdditiveGenerator).
21
22
These can be added, multiplied and reduced (simplified).
23
24
INPUT :
25
26
* X : GeneralisedStratum that we are on
27
* psi_list : list of tuples (coefficient, AdditiveGenerator) as
28
described above.
29
* reduce=True : call self.reduce() on initialisation
30
"""
31
32
def __init__(self, X, psi_list, reduce=True):
33
self._psi_list = psi_list
34
self._X = X
35
if reduce:
36
self.reduce()
37
38
@classmethod
39
def from_hash_list(cls, X, hash_list):
40
# This does not reduce!
41
return cls(X, [(c, X.additive_generator_from_hash(h))
42
for c, h in hash_list], reduce=False)
43
44
@property
45
def psi_list(self):
46
return self._psi_list
47
48
def __repr__(self):
49
return "ELGTautClass(X=%r,psi_list=%r)"\
50
% (self._X, self._psi_list)
51
52
def __str__(self):
53
str = "Tautological class on %s\n" % self._X
54
for coeff, psi in self._psi_list:
55
str += "%s * %s + \n" % (coeff, psi)
56
return str
57
58
def __eq__(self, other):
59
if isinstance(
60
other,
61
admcycles.diffstrata.additivegenerator.AdditiveGenerator):
62
return self == other.as_taut()
63
try:
64
return self._psi_list == other._psi_list
65
except AttributeError:
66
return False
67
68
def __add__(self, other):
69
# for sum, we need to know how to add '0':
70
if other == 0:
71
return self
72
try:
73
if not self._X == other._X:
74
return NotImplemented
75
new_psis = self._psi_list + other._psi_list
76
return ELGTautClass(self._X, new_psis)
77
except AttributeError:
78
return NotImplemented
79
80
def __iadd__(self, other):
81
return self.__add__(other)
82
83
def __radd__(self, other):
84
return self.__add__(other)
85
86
def __neg__(self):
87
return (-1) * self
88
89
def __sub__(self, other):
90
return self + (-1) * other
91
92
def __mul__(self, other):
93
if 0 == other:
94
return 0
95
elif self._X.ONE == other:
96
return self
97
# convert AdditiveGenerators to Tautclasses:
98
if isinstance(
99
other,
100
admcycles.diffstrata.additivegenerator.AdditiveGenerator):
101
return self * other.as_taut()
102
try:
103
# check if other is a tautological class
104
other._psi_list
105
except AttributeError:
106
# attempt scalar multiplication:
107
new_psis = [(coeff * other, psi) for coeff, psi in self._psi_list]
108
return ELGTautClass(self._X, new_psis, reduce=False)
109
if not self._X == other._X:
110
return NotImplemented
111
else:
112
return self._X.intersection(self, other)
113
114
def __rmul__(self, other):
115
return self.__mul__(other)
116
117
def __pow__(self, exponent):
118
if exponent == 0:
119
return self._X.ONE
120
# TODO: quick check for going over top degree?
121
prod = self
122
for _ in range(1, exponent):
123
prod = self * prod
124
return prod
125
126
@cached_method
127
def is_equidimensional(self):
128
"""
129
Determine if all summands of self have the same degree.
130
131
Note that the empty empty tautological class (ZERO) gives True.
132
133
Returns:
134
bool: True if all AdditiveGenerators in self.psi_list are of same degree,
135
False otherwise.
136
"""
137
if self.psi_list:
138
first_deg = self.psi_list[0][1].degree
139
return all(AG.degree == first_deg for _c, AG in self.psi_list)
140
return True
141
142
def reduce(self):
143
"""
144
Reduce self.psi_list by combining summands with the same AdditiveGenerator
145
and removing those with coefficient 0 or that die for dimension reasons.
146
"""
147
# we use the hash of the AdditiveGenerators to group:
148
hash_dict = Counter()
149
for c, AG in self._psi_list:
150
hash_dict[AG] += c
151
self._psi_list = [(c, AG) for AG, c in hash_dict.items()
152
if c != 0 and AG.dim_check()]
153
154
# To evaluate, we go through the AdditiveGenerators and
155
# take the (weighted) sum of the AdditiveGenerators.
156
def evaluate(self, quiet=True, warnings_only=False,
157
admcycles_output=False):
158
"""
159
Evaluation of self, i.e. cap with fundamental class of X.
160
161
This is the sum of the evaluation of the AdditiveGenerators in psi_list
162
(weighted with their coefficients).
163
164
Each AdditiveGenerator is (essentially) the product of its levels,
165
each level is (essentially) evaluated by admcycles.
166
167
Args:
168
quiet (bool, optional): No output. Defaults to True.
169
warnings_only (bool, optional): Only warnings output. Defaults to False.
170
admcycles_output (bool, optional): admcycles debugging output. Defaults to False.
171
172
Returns:
173
QQ: integral of self on X
174
175
EXAMPLES::
176
177
sage: from admcycles.diffstrata import *
178
sage: X=GeneralisedStratum([Signature((0,0))])
179
sage: assert (X.xi^2).evaluate() == 0
180
181
sage: X=GeneralisedStratum([Signature((1,1,1,1,-6))])
182
sage: assert set([(X.cnb(((i,),0),((i,),0))).evaluate() for i in range(len(X.bics))]) == {-2, -1}
183
"""
184
if warnings_only:
185
quiet = True
186
DS_list = []
187
for c, AG in self.psi_list:
188
value = AG.evaluate(
189
quiet=quiet,
190
warnings_only=warnings_only,
191
admcycles_output=admcycles_output)
192
DS_list.append(c * value)
193
if not quiet:
194
print("----------------------------------------------------")
195
print("In summary: We sum")
196
for i, summand in enumerate(DS_list):
197
print("Contribution %r from AdditiveGenerator" % summand)
198
print(self.psi_list[i][1])
199
print("(With coefficient %r)" % self.psi_list[i][0])
200
print("To obtain a total of %r" % sum(DS_list))
201
print("----------------------------------------------------")
202
return sum(DS_list)
203
204
def extract(self, i):
205
"""
206
Return the i-th component of self.
207
208
Args:
209
i (int): index of self._psi_list
210
211
Returns:
212
ELGTautClass: coefficient * AdditiveGenerator at position i of self.
213
"""
214
return ELGTautClass(self._X, [self._psi_list[i]], reduce=False)
215
216
@cached_method
217
def degree(self, d):
218
"""
219
The degree d part of self.
220
221
Args:
222
d (int): degree
223
224
Returns:
225
ELGTautClass: degree d part of self
226
"""
227
new_psis = []
228
for c, AG in self.psi_list:
229
if AG.degree == d:
230
new_psis.append((c, AG))
231
return ELGTautClass(self._X, new_psis, reduce=False)
232
233
@cached_method
234
def list_by_degree(self):
235
"""
236
A list of length X.dim with the degree d part as item d
237
238
Returns:
239
list: list of ELGTautClasses with entry i of degree i.
240
"""
241
deg_psi_list = [[] for _ in range(self._X.dim() + 1)]
242
for c, AG in self.psi_list:
243
deg_psi_list[AG.degree].append((c, AG))
244
return [ELGTautClass(self._X, piece, reduce=False)
245
for piece in deg_psi_list]
246
247
def is_pure_psi(self):
248
"""
249
Check if self is ZERO or a psi-product on the stratum.
250
251
Returns:
252
boolean: True if self has at most one summand and that is of the form
253
AdditiveGenerator(((), 0), psis).
254
255
EXAMPLES::
256
257
sage: from admcycles.diffstrata import *
258
sage: X=Stratum((2,))
259
sage: X.ZERO.is_pure_psi()
260
True
261
sage: X.ONE.is_pure_psi()
262
True
263
sage: X.psi(1).is_pure_psi()
264
True
265
sage: X.xi.is_pure_psi()
266
False
267
"""
268
if not self.psi_list:
269
return True
270
return len(
271
self.psi_list) == 1 and self.psi_list[0][1].enh_profile == ((), 0)
272
273
def to_prodtautclass(self):
274
"""
275
Transforms self into an admcycles prodtautclass on the stable graph of the smooth
276
graph of self._X.
277
278
Note that this is essentially the pushforward to M_g,n, i.e. we resolve residues
279
and multiply with the correct Strataclasses along the way.
280
281
Returns:
282
prodtautclass: admcycles prodtautclass corresponding to self pushed forward
283
to the stable graph with one vertex.
284
285
EXAMPLES::
286
287
sage: from admcycles.diffstrata import *
288
sage: X=Stratum((2,))
289
sage: X.ONE.to_prodtautclass()
290
Outer graph : [2] [[1]] []
291
Vertex 0 :
292
Graph : [2] [[1]] []
293
Polynomial : -7/24*(kappa_1)_0 + 79/24*psi_1
294
<BLANKLINE>
295
<BLANKLINE>
296
Vertex 0 :
297
Graph : [1] [[1, 2, 3]] [(2, 3)]
298
Polynomial : -1/48
299
<BLANKLINE>
300
<BLANKLINE>
301
Vertex 0 :
302
Graph : [1, 1] [[2], [1, 3]] [(2, 3)]
303
Polynomial : -19/24
304
sage: (X.xi^X.dim()).evaluate() == (X.xi^X.dim()).to_prodtautclass().pushforward().evaluate()
305
True
306
"""
307
G = self._X.smooth_LG
308
stgraph = G.LG.stgraph
309
total = admcycles.admcycles.prodtautclass(stgraph, terms=[])
310
for c, AG in self.psi_list:
311
ptc = AG.to_prodtautclass()
312
# sort vertices by connected component:
313
vertex_map = {}
314
# note that every vertex of G has at least one leg (that is a
315
# marked point of _X):
316
for v, _ in enumerate(G.LG.genera):
317
mp_on_stratum = G.dmp[G.LG.legs[v][0]]
318
# find this marked point on AG:
319
l_AG = AG._G.dmp_inv[mp_on_stratum]
320
# get the corresponding vertex
321
LG = AG._G.LG
322
v_AG = LG.vertex(l_AG)
323
# we use the underlying graph:
324
UG_v = LG.UG_vertex(v_AG)
325
for w, g, kind in LG.UG_without_infinity().connected_component_containing_vertex(
326
UG_v):
327
if kind != 'LG':
328
continue
329
vertex_map[w] = v
330
# map legs of AG._G to smooth_LG
331
# CAREFUL: This goes in the OTHER direction!
332
leg_map = {G.dmp_inv[mp]: ldeg for ldeg, mp in AG._G.dmp.items()}
333
pf = ptc.partial_pushforward(stgraph, vertex_map, leg_map)
334
total += c * pf
335
return total
336
337