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
r"""
2
Code for computations involving Witten's r-spin class.
3
Authors: Felix Janda (main author), Aaron Pixton (code improvements), Johannes Schmitt (integration into admcycles)
4
"""
5
6
from __future__ import absolute_import, print_function
7
8
import itertools
9
10
from six.moves import range
11
12
from admcycles.admcycles import Tautv_to_tautclass, tautclass
13
from admcycles.DR import *
14
15
# from sage.combinat.subset import Subsets
16
from sage.arith.all import factorial, lcm
17
from sage.functions.other import floor, ceil
18
from sage.misc.misc_c import prod
19
from sage.rings.all import PolynomialRing, QQ, ZZ
20
from sage.modules.free_module_element import vector
21
# from sage.rings.polynomial.polynomial_ring import polygen
22
from sage.rings.polynomial.multi_polynomial_element import MPolynomial
23
from collections import Iterable
24
from sage.rings.integer import Integer
25
from sage.calculus.var import var
26
from sage.misc.functional import symbolic_sum
27
28
29
# Computation of Witten's rspin class for large r
30
31
def rspin_leg_factor(d,a):
32
if a < 0: a = X + a
33
return Pmpolynomial(d).subs(a=a)
34
35
def rspin_edge_factor(w1,m,d1,d2):
36
R=PolynomialRing(QQ,1,'X')
37
X = R.gens()[0]
38
d = d1+d2+1
39
S = 0
40
for i in range(d+1):
41
S += R(rspin_leg_factor(i, (w1 + i) % (m-1))(X=m)*rspin_leg_factor(d-i, (m-2-i-w1) % (m-1))(X=m)*X**i)
42
S /= X+1
43
assert(S.denominator() == 1)
44
S = S.numerator()
45
#print(-S[d1])
46
return -S[d1]
47
48
def rspin_coeff_setup(num,g,r,n=0,dvector=(),moduli_type=MODULI_ST):
49
markings = tuple(range(1,n+1))
50
G = single_stratum(num,g,r,markings,moduli_type)
51
nr = G.M.nrows()
52
nc = G.M.ncols()
53
edge_list = []
54
exp_list = []
55
scalar_factor = 1/autom_count(num,g,r,markings,moduli_type)
56
given_weights = [1 - G.M[i+1,0][0] for i in range(nr-1)]
57
for i in range(1,nr):
58
for j in range(1,G.M[i,0].degree()+1):
59
scalar_factor /= factorial(G.M[i,0][j])
60
scalar_factor *= (-1)**G.M[i,0][j]
61
scalar_factor *= (rspin_leg_factor(j, 0))**(G.M[i,0][j])
62
given_weights[i-1] -= j*G.M[i,0][j]
63
for j in range(1,nc):
64
ilist = [i for i in range(1,nr) if G.M[i,j] != 0]
65
if G.M[0,j] == 0:
66
if len(ilist) == 1:
67
i1 = ilist[0]
68
i2 = ilist[0]
69
exp1 = G.M[i1,j][1]
70
exp2 = G.M[i1,j][2]
71
else:
72
i1 = ilist[0]
73
i2 = ilist[1]
74
exp1 = G.M[i1,j][1]
75
exp2 = G.M[i2,j][1]
76
edge_list.append([i1-1,i2-1])
77
exp_list.append(exp1)
78
exp_list.append(exp2)
79
else:
80
exp1 = G.M[ilist[0],j][1]
81
scalar_factor *= rspin_leg_factor(exp1, dvector[G.M[0,j][0]-1])
82
given_weights[ilist[0] - 1] += dvector[G.M[0,j][0] - 1] - exp1
83
return edge_list,exp_list,given_weights,scalar_factor
84
85
def rspin_coeff(num,g,r,n=0,dvector=(),r_coeff=None,step=1,m0given=-1,deggiven=-1,moduli_type=MODULI_ST):
86
markings = tuple(range(1,n+1))
87
G = single_stratum(num,g,r,markings,moduli_type)
88
nr = G.M.nrows()
89
nc = G.M.ncols()
90
edge_list,exp_list,given_weights,scalar_factor = rspin_coeff_setup(num,g,r,n,dvector,moduli_type)
91
if m0given == -1:
92
m0 = (ceil(sum([abs(i.subs(X=0)) for i in dvector])/2) + g*1+1)*step
93
else:
94
m0 = m0given
95
h0 = nc - nr - n + 1
96
if deggiven == -1:
97
deg = 2*sum(exp_list) + 2*len(edge_list)
98
else:
99
deg = deggiven
100
if r_coeff is None:
101
mrange = list(range(m0 + step, m0 + step*deg + step + 1, step))
102
else:
103
mrange = [r_coeff+1] # just evaluate at a single value m = r_coeff
104
mvalues = []
105
for m in mrange:
106
given_weights_m = [ZZ(i.subs(X=m)) for i in given_weights]
107
total = 0
108
for weight_data in itertools.product(*[list(range(m-1)) for i in range(len(edge_list))]):
109
vertex_weights = copy(given_weights_m)
110
for i in range(len(edge_list)):
111
vertex_weights[edge_list[i][0]] += weight_data[i]
112
vertex_weights[edge_list[i][1]] -= weight_data[i]+2+exp_list[2*i]+exp_list[2*i+1]
113
if len([i for i in vertex_weights if i % (m-1) != 0]) > 0:
114
continue
115
term = 1
116
for i in range(len(edge_list)):
117
term *= rspin_edge_factor(weight_data[i], m, exp_list[2*i], exp_list[2*i+1])
118
total += term
119
if r_coeff is None:
120
mvalues.append(total*ZZ(m-1)**(-h0))
121
else:
122
#print(m-1)
123
mvalues.append(total*ZZ(m-1)**(-r-h0)) # undo the rescaling by r**degree
124
#print(mrange,mvalues, scalar_factor, r, h0)
125
mpoly = ZZ(-1)**(r-g)*(interpolate(mrange, mvalues)*scalar_factor).simplify_rational()
126
if r_coeff is not None:
127
mpoly = QQ(mpoly.subs(X=r_coeff))
128
return mpoly
129
130
def rspin_compute(g,r,n=0,dvector=(),r_coeff=None,step=1,m0=-1,deg=-1,moduli_type=MODULI_ST):
131
answer = []
132
markings = tuple(range(1,n+1))
133
for i in range(num_strata(g,r,markings,moduli_type)):
134
answer.append(rspin_coeff(i,g,r,n,dvector,r_coeff,step,m0,deg,moduli_type))
135
return vector(answer)
136
137
def rspin_degree_test(g,r,n=0,dvector=(),step=1,m0=-1,deg=-1,moduli_type=MODULI_ST):
138
answer = []
139
markings = tuple(range(1,n+1))
140
for i in range(num_strata(g,r,markings,moduli_type)):
141
answer.append(rspin_coeff(i,g,r,n,dvector,step,m0,deg,moduli_type).degree(X))
142
return answer
143
144
def rspin_constant(g,r,n=0,dvector=(),step=1,m0=-1,deg=-1,moduli_type=MODULI_ST):
145
answer = []
146
markings = tuple(range(1,n+1))
147
for i in range(num_strata(g,r,markings,moduli_type)):
148
answer.append(rspin_coeff(i,g,r,n,dvector,step,m0,deg,moduli_type).subs(X=0))
149
return vector(answer)
150
151
def Wittenrspin(g, Avector, r_coeff = None, d = None, rpoly = False):
152
r"""
153
Returns the polynomial limit of Witten's r-spin class in genus g with input Avector,
154
as discussed in the appendix of [Pandharipande-Pixton-Zvonkine '16].
155
156
More precisely, Avector is expected to be a vector of linear polynomials in QQ[r],
157
with leading coefficients being rational numbers in the interval [0,1] and constant
158
coefficients being integers. Elements of Avector should sum to C * r + 2g-2 for some
159
nonnegative integer C. Then Wittenrspin returns the tautological class obtained as
160
the limit of
161
162
r^(g-1+C) W_{g,n}^r(Avector)
163
164
for r >> 0 sufficiently large and divisible, evaluated at r=0.
165
166
INPUT::
167
168
- ``g`` -- integer; underlying genus
169
170
- ``Avector`` -- tuple ; a tuple of either integers, elements of a polynomial
171
ring QQ[r] in some variable r or tuples (C_i, D_i) which are interpreted as
172
polynomials C_i * r + D_i. For the entries with C_i = 1 we assume D_i <=-2.
173
174
- ``r_coeff`` -- integer or None (default: `None`); if a particular integer
175
r_coeff = r is specified, the function will return the (unscaled) class W_{g,n}^r(Avector),
176
not taking a limit for large r.
177
178
- ``d`` -- integer; desired degree in tautological ring; will be set to g-1+C by
179
default
180
181
- ``rpoly`` -- bool (default: `False`); if True, return the limit of
182
r^(g-1+C) W_{g,n}^r(Avector) without evaluating at r=0, as a tautclass with
183
coefficients being polynomials in r
184
185
EXAMPLES:
186
187
We start by verifying the conjecture from the appendix of [Pandharipande-Pixton-Zvonkine '16]
188
for g = 2 and mu = (2)::
189
190
sage: from admcycles import Wittenrspin, Strataclass
191
sage: H1 = Wittenrspin(2, (2,))
192
sage: H2 = Strataclass(2, 1, (2,))
193
sage: (H1-H2).is_zero()
194
True
195
196
We can also verify a new conjecture for classes of strata of meromorphic differentials for
197
g = 1 and mu = (3,-1,-2). The argument (1/2,-1) stands for an insertion 1/2 * r -1::
198
199
sage: H1 = Wittenrspin(1, (3, (1/2,-1), (1/2,-2)))
200
sage: H2 = Strataclass(1, 1, (3,-1,-2))
201
sage: (H1+H2).is_zero()
202
True
203
204
As a variant of this, we also verify that insertions r-b stand for poles of order b with
205
vanishing residues::
206
207
sage: R.<r> = PolynomialRing(QQ,1)
208
sage: H1 = Wittenrspin(1, (5,r-2, r-3))
209
sage: H2 = Strataclass(1, 1, (5,-2,-3), res_cond=(2,))
210
sage: (H1+H2).is_zero()
211
True
212
213
We can also compute the (scaled) Witten's class without substituting r=0::
214
215
sage: Wittenrspin(1,(2,r-2),rpoly=True)
216
Graph : [1] [[1, 2]] []
217
Polynomial : (1/12*r^2 - 5/24*r + 1/12)*(kappa_1^1 )_0
218
<BLANKLINE>
219
Graph : [1] [[1, 2]] []
220
Polynomial : (-1/12*r^2 + 29/24*r - 37/12)*psi_1^1
221
<BLANKLINE>
222
Graph : [1] [[1, 2]] []
223
Polynomial : (-1/12*r^2 + 17/24*r - 13/12)*psi_2^1
224
<BLANKLINE>
225
Graph : [0, 1] [[1, 2, 4], [5]] [(4, 5)]
226
Polynomial : (1/12*r^2 - 17/24*r + 13/12)*
227
<BLANKLINE>
228
Graph : [0] [[4, 5, 1, 2]] [(4, 5)]
229
Polynomial : (-1/48*r + 1/24)*
230
231
Instead of calculating the asymptotic, polynomial behaviour of Witten's class,
232
we can also input a concrete value for r, using the option r_coeff. Below we
233
verify the CohFT property of Witten's class::
234
235
sage: R.<r> = PolynomialRing(QQ,1)
236
sage: H1 = Wittenrspin(1, (5,r-2, r-3))
237
sage: H2 = Strataclass(1, 1, (5,-2,-3), res_cond=(2,))
238
sage: (H1+H2).is_zero()
239
True
240
"""
241
n = len(Avector)
242
243
if r_coeff is None:
244
polyentries = [a for a in Avector if isinstance(a,MPolynomial)]
245
if len(polyentries) > 0:
246
R = polyentries[0].parent()
247
r = R.gens()[0]
248
else:
249
R = PolynomialRing(QQ,1,'r')
250
r = R.gens()[0]
251
252
X = SR.var('X')
253
AvectorX = []
254
Cvector = []
255
Dvector = []
256
257
# Extract entries of Avector into a unified format (AvectorX)
258
# Collect linear and constant coefficients of entries of AvectorX in Cvector, Dvector
259
for a in Avector:
260
if isinstance(a,Integer):
261
AvectorX.append(a)
262
Cvector.append(QQ(0))
263
Dvector.append(a)
264
elif isinstance(a,MPolynomial):
265
AvectorX.append(a[1]*X+a[0])
266
Cvector.append(QQ(a[1]))
267
Dvector.append(a[0])
268
elif isinstance(a,Iterable):
269
AvectorX.append(a[0]*X+a[1])
270
Cvector.append(QQ(a[0]))
271
Dvector.append(a[1])
272
else:
273
raise ValueError('Entries of Avector must be Integers, Polynomials or tuples (C_i, D_i)')
274
275
# For C_i = 1, D_i = -1 we require a simple pole with vanishing residue => return zero class
276
if any((Cvector[i]==1) and (Dvector[i]==-1) for i in range(n)):
277
return tautclass([])
278
279
step = lcm(c.denom() for c in Cvector)
280
C = sum(Cvector)
281
assert C in ZZ
282
assert sum(Dvector) == 2*g-2
283
else:
284
AvectorX=Avector
285
step=0
286
287
if d is None:
288
if r_coeff is None:
289
d = ZZ(g-1+C)
290
else:
291
denom = ZZ((r_coeff-2)*(g-1) + sum(Avector))
292
if denom % r_coeff != 0:
293
return tautclass([]) # Witten's class vanishes unless this congruence is satisfied
294
else:
295
d = ZZ(denom/ZZ(r_coeff))
296
rvect = rspin_compute(g,d,n,tuple(AvectorX),r_coeff,step,m0=-1,deg=-1,moduli_type=MODULI_ST)
297
if not rpoly:
298
rvect = rvect.subs(X=0)
299
rvect = convert_vector_to_monomial_basis(rvect, g, d, tuple(range(1, n+1)), MODULI_ST)
300
301
if rpoly:
302
rvect = vector((b.subs(X=r) for b in rvect))
303
304
return Tautv_to_tautclass(rvect, g, n, d)
305
306
@cached_function
307
def Pmpolynomial(m):
308
r"""
309
Returns the expression P_m(X,a) as defined in [Pandharipande-Pixton-Zvonkine '16, Section 4.5].
310
311
TESTS::
312
313
sage: from admcycles.witten import Pmpolynomial
314
sage: Pmpolynomial(0)
315
1
316
sage: Pmpolynomial(1)
317
-1/12*X^2 + 1/2*(X - 1)*a - 1/2*a^2 + 5/24*X - 1/12
318
sage: Pmpolynomial(2)
319
1/288*X^4 - 1/12*(5*X - 1)*a^3 + 1/8*a^4 + 7/288*X^3 + 1/48*(20*X^2 - 5*X - 4)*a^2 - 29/384*X^2 - 1/48*(6*X^3 + X^2 - 9*X + 2)*a + 7/288*X + 1/288
320
"""
321
(b,X,a) = var('b,X,a')
322
if m==0:
323
return 1+0*X
324
return ( QQ(1)/2 * symbolic_sum((2*m*X-X-2*b)*Pmpolynomial(m-1).subs(a=b-1),b,1,a) - QQ(1)/(4*m*X*(X-1)) * symbolic_sum((X-1-b)*(2*m*X-b)*(2*m*X-X-2*b)*Pmpolynomial(m-1).subs(a=b-1),b,1,X-2) ).simplify_rational()
325
326
327