unlisted
ubuntu2004r"""1Code for computations involving Witten's r-spin class.2Authors: Felix Janda (main author), Aaron Pixton (code improvements), Johannes Schmitt (integration into admcycles)3"""456from collections.abc import Iterable7import itertools8import numbers910from admcycles.admcycles import tautclass11from admcycles.DR import X, MODULI_ST, MODULI_RT, MODULI_CT, MODULI_SM, single_stratum, autom_count, interpolate, num_strata, convert_vector_to_monomial_basis1213from admcycles import TautologicalRing1415# from sage.combinat.subset import Subsets16from sage.arith.all import factorial, lcm17from sage.functions.other import ceil18from sage.rings.all import PolynomialRing, QQ, ZZ19from sage.modules.free_module_element import vector20# from sage.rings.polynomial.polynomial_ring import polygen21from sage.rings.polynomial.multi_polynomial_element import MPolynomial22from sage.rings.integer import Integer23from sage.calculus.var import var24from sage.misc.functional import symbolic_sum25from sage.symbolic.ring import SR26from copy import copy27from sage.misc.cachefunc import cached_function2829# Computation of Witten's rspin class for large r303132def rspin_leg_factor(d, a):33if a < 0:34a = X + a35return Pmpolynomial(d).subs(a=a)363738def rspin_edge_factor(w1, m, d1, d2):39R = PolynomialRing(QQ, 1, 'X')40X = R.gens()[0]41d = d1 + d2 + 142S = 043for i in range(d + 1):44S += R(rspin_leg_factor(i, (w1 + i) % (m - 1))(X=m) *45rspin_leg_factor(d - i, (m - 2 - i - w1) % (m - 1))(X=m) * X**i)46S /= X + 147assert S.denominator() == 148S = S.numerator()49# print(-S[d1])50return -S[d1]515253def rspin_coeff_setup(num, g, r, n=0, dvector=(), moduli_type=MODULI_ST):54markings = tuple(range(1, n + 1))55G = single_stratum(num, g, r, markings, moduli_type)56nr = G.M.nrows()57nc = G.M.ncols()58edge_list = []59exp_list = []60scalar_factor = 1 / autom_count(num, g, r, markings, moduli_type)61given_weights = [1 - G.M[i + 1, 0][0] for i in range(nr - 1)]62for i in range(1, nr):63for j in range(1, G.M[i, 0].degree() + 1):64scalar_factor /= factorial(G.M[i, 0][j])65scalar_factor *= (-1)**G.M[i, 0][j]66scalar_factor *= (rspin_leg_factor(j, 0))**(G.M[i, 0][j])67given_weights[i - 1] -= j * G.M[i, 0][j]68for j in range(1, nc):69ilist = [i for i in range(1, nr) if G.M[i, j] != 0]70if G.M[0, j] == 0:71if len(ilist) == 1:72i1 = ilist[0]73i2 = ilist[0]74exp1 = G.M[i1, j][1]75exp2 = G.M[i1, j][2]76else:77i1 = ilist[0]78i2 = ilist[1]79exp1 = G.M[i1, j][1]80exp2 = G.M[i2, j][1]81edge_list.append([i1 - 1, i2 - 1])82exp_list.append(exp1)83exp_list.append(exp2)84else:85exp1 = G.M[ilist[0], j][1]86scalar_factor *= rspin_leg_factor(exp1, dvector[G.M[0, j][0] - 1])87given_weights[ilist[0] - 1] += dvector[G.M[0, j][0] - 1] - exp188return edge_list, exp_list, given_weights, scalar_factor899091def rspin_coeff(num, g, r, n=0, dvector=(), r_coeff=None, step=1,92m0given=-1, deggiven=-1, moduli_type=MODULI_ST):93markings = tuple(range(1, n + 1))94G = single_stratum(num, g, r, markings, moduli_type)95nr = G.M.nrows()96nc = G.M.ncols()97edge_list, exp_list, given_weights, scalar_factor = rspin_coeff_setup(98num, g, r, n, dvector, moduli_type)99if m0given == -1:100m0 = (ceil(sum([abs(i.subs(X=0))101for i in dvector]) / 2) + g * 1 + 1) * step102else:103m0 = m0given104h0 = nc - nr - n + 1105if deggiven == -1:106deg = 2 * sum(exp_list) + 2 * len(edge_list)107else:108deg = deggiven109if r_coeff is None:110mrange = list(range(m0 + step, m0 + step * deg + step + 1, step))111else:112mrange = [r_coeff] # just evaluate at a single value m = r_coeff113mvalues = []114for m in mrange:115given_weights_m = [ZZ(i.subs(X=m)) for i in given_weights]116total = 0117for weight_data in itertools.product(118*[list(range(m - 1)) for i in range(len(edge_list))]):119vertex_weights = copy(given_weights_m)120for i in range(len(edge_list)):121vertex_weights[edge_list[i][0]] += weight_data[i]122vertex_weights[edge_list[i][1]] -= weight_data[i] + \1232 + exp_list[2 * i] + exp_list[2 * i + 1]124if len([i for i in vertex_weights if i % (m - 1) != 0]) > 0:125continue126term = 1127for i in range(len(edge_list)):128term *= rspin_edge_factor(weight_data[i],129m, exp_list[2 * i], exp_list[2 * i + 1])130total += term131if r_coeff is None:132mvalues.append(total * ZZ(m - 1)**(-h0))133else:134# print(m-1)135# undo the rescaling by r**degree136mvalues.append(ZZ(-1)**(r - g) * total * ZZ(m - 1)137** (-r + g - h0) * ZZ(m)**(-r))138# print(mrange,mvalues, scalar_factor, r, h0)139mpoly = ZZ(-1)**(r - g) * (interpolate(mrange, mvalues).subs(x=X)140* scalar_factor).simplify_rational()141if r_coeff is not None:142mpoly = QQ(mpoly.subs(X=r_coeff))143return mpoly144145146def rspin_compute(g, r, n=0, dvector=(), r_coeff=None,147step=1, m0=-1, deg=-1, moduli_type=MODULI_ST):148answer = []149markings = tuple(range(1, n + 1))150for i in range(num_strata(g, r, markings, moduli_type)):151answer.append(rspin_coeff(i, g, r, n, dvector,152r_coeff, step, m0, deg, moduli_type))153return vector(answer)154155156def rspin_degree_test(g, r, n=0, dvector=(), step=1,157m0=-1, deg=-1, moduli_type=MODULI_ST):158answer = []159markings = tuple(range(1, n + 1))160for i in range(num_strata(g, r, markings, moduli_type)):161answer.append(rspin_coeff(i, g, r, n, dvector,162step, m0, deg, moduli_type).degree(X))163return answer164165166def rspin_constant(g, r, n=0, dvector=(), step=1, m0=-1671, deg=-1, moduli_type=MODULI_ST):168answer = []169markings = tuple(range(1, n + 1))170for i in range(num_strata(g, r, markings, moduli_type)):171answer.append(rspin_coeff(i, g, r, n, dvector,172step, m0, deg, moduli_type).subs(X=0))173return vector(answer)174175176def Wittenrspin(g, Avector, r_coeff=None, d=None, rpoly=False, moduli='st'):177r"""178Returns the polynomial limit of Witten's r-spin class in genus g with input Avector,179as discussed in the appendix of [Pandharipande-Pixton-Zvonkine '16].180181More precisely, Avector is expected to be a vector of linear polynomials in QQ[r],182with leading coefficients being rational numbers in the interval [0,1] and constant183coefficients being integers. Elements of Avector should sum to C * r + 2g-2 for some184nonnegative integer C. Then Wittenrspin returns the tautological class obtained as185the limit of186187r^(g-1+C) W_{g,n}^r(Avector)188189for r >> 0 sufficiently large and divisible, evaluated at r=0.190191INPUT:192193- ``g`` -- integer; underlying genus194195- ``Avector`` -- tuple ; a tuple of either integers, elements of a polynomial196ring QQ[r] in some variable r or tuples (C_i, D_i) which are interpreted as197polynomials C_i * r + D_i. For the entries with C_i = 1 we assume D_i <=-2.198199- ``r_coeff`` -- integer or None (default: `None`); if a particular integer200r_coeff = r is specified, the function will return the (unscaled) class W_{g,n}^r(Avector),201not taking a limit for large r.202203- ``d`` -- integer; desired degree in tautological ring; will be set to g-1+C by204default205206- ``rpoly`` -- bool (default: `False`); if True, return the limit of207r^(g-1+C) W_{g,n}^r(Avector) without evaluating at r=0, as a tautclass with208coefficients being polynomials in r209210EXAMPLES:211212We start by verifying the conjecture from the appendix of [Pandharipande-Pixton-Zvonkine '16]213for g = 2 and mu = (2)::214215sage: from admcycles import Wittenrspin, Strataclass216sage: H1 = Wittenrspin(2, (2,))217sage: H2 = Strataclass(2, 1, (2,))218sage: (H1-H2).is_zero()219True220221We can also verify a new conjecture for classes of strata of meromorphic differentials for222g = 1 and mu = (3,-1,-2). The argument (1/2,-1) stands for an insertion 1/2 * r -1::223224sage: H1 = Wittenrspin(1, (3, (1/2,-1), (1/2,-2)))225sage: H2 = Strataclass(1, 1, (3,-1,-2))226sage: (H1+H2).is_zero()227True228229As a variant of this, we also verify that insertions r-b stand for poles of order b with230vanishing residues::231232sage: R.<r> = PolynomialRing(QQ,1)233sage: H1 = Wittenrspin(1, (5,r-2, r-3))234sage: H2 = Strataclass(1, 1, (5,-2,-3), res_cond=(2,))235sage: (H1+H2).is_zero()236True237238We can also compute the (scaled) Witten's class without substituting r=0::239240sage: Wittenrspin(1,(2,r-2),rpoly=True)241Graph : [1] [[1, 2]] []242Polynomial : (1/12*r^2 - 5/24*r + 1/12)*(kappa_1)_0 + (-1/12*r^2 + 29/24*r - 37/12)*psi_1 + (-1/12*r^2 + 17/24*r - 13/12)*psi_2243<BLANKLINE>244Graph : [0] [[4, 5, 1, 2]] [(4, 5)]245Polynomial : -1/48*r + 1/24246<BLANKLINE>247Graph : [0, 1] [[1, 2, 4], [5]] [(4, 5)]248Polynomial : 1/12*r^2 - 17/24*r + 13/12249250Instead of calculating the asymptotic, polynomial behaviour of Witten's class,251we can also input a concrete value for r, using the option r_coeff. Below we252verify the CohFT property of Witten's class, first for a separating boundary253divisor::254255sage: from admcycles import StableGraph256sage: A = Wittenrspin(2,(1,1),r_coeff=4)257sage: gr = StableGraph([1,1],[[1,2,3],[4]],[(3,4)])258sage: pb = gr.boundary_pullback(A)259sage: vector(pb.totensorTautbasis(1)[1])260(0, 3/4, 0, 3/4, 3/4)261sage: B = Wittenrspin(1,(1,1,2),r_coeff=4)262sage: B.basis_vector()263(0, 1/4, 0, 1/4, 1/4)264sage: C = Wittenrspin(1,(0,),r_coeff=4)265sage: C.basis_vector()266(3)267268Then for a nonseparating boundary divisor::269270sage: B = Wittenrspin(2,(2,),r_coeff=4)271sage: gr = StableGraph([1],[[1,2,3]],[(2,3)])272sage: pb = gr.boundary_pullback(B)273sage: pb.totensorTautbasis(1)274(0, 1/2, 1/4, 1/4, -1/4)275sage: A1 = Wittenrspin(1,(2,1,1),r_coeff=4)276sage: A2 = Wittenrspin(1,(2,0,2),r_coeff=4)277sage: A3 = Wittenrspin(1,(2,2,0),r_coeff=4)278sage: L=[t.basis_vector() for t in [A1,A2,A3]]; L279[(0, 1/2, 0, 0, 1/4), (0, 0, 0, 1/4, -1/4), (0, 0, 1/4, 0, -1/4)]280sage: sum(L)281(0, 1/2, 1/4, 1/4, -1/4)282283284We can also check manually, that interpolating the above results for285large r precisely gives the output of the option rpoly=True::286287sage: from admcycles.double_ramification_cycle import interpolate288sage: H1 = Wittenrspin(1, (3, (1/2,-1), (1/2,-2)), rpoly=True)289sage: H1.basis_vector()290(0, -1/4*r^2 + 5/2*r - 3, 1/4*r^2 - r, 1/4*r^2 - r - 3, -1/4*r^2 + 1/2*r + 5)291sage: res=[]292sage: pts = list(range(6,11,2))293sage: for r in pts:294....: res.append(r**(1-1+1)*Wittenrspin(1, (3, 1/2*r-1, 1/2*r-2),r_coeff=r).basis_vector(1))295sage: v = vector([interpolate(pts, [a[i] for a in res],'r') for i in range(5)])296sage: v-H1.basis_vector()297(0, 0, 0, 0, 0)298"""299n = len(Avector)300301if r_coeff is None:302polyentries = [a for a in Avector if isinstance(a, MPolynomial)]303if len(polyentries) > 0:304R = polyentries[0].parent()305r = R.gens()[0]306else:307R = PolynomialRing(QQ, 1, 'r')308r = R.gens()[0]309310X = SR.var('X')311AvectorX = []312Cvector = []313Dvector = []314315# Extract entries of Avector into a unified format (AvectorX)316# Collect linear and constant coefficients of entries of AvectorX in317# Cvector, Dvector318for a in Avector:319if isinstance(a, numbers.Integral):320AvectorX.append(Integer(a))321Cvector.append(QQ(0))322Dvector.append(Integer(a))323elif isinstance(a, MPolynomial):324AvectorX.append(QQ(a[1]) * X + QQ(a[0]))325Cvector.append(QQ(a[1]))326Dvector.append(QQ(a[0]))327elif isinstance(a, Iterable):328AvectorX.append(QQ(a[0]) * X + QQ(a[1]))329Cvector.append(QQ(a[0]))330Dvector.append(QQ(a[1]))331else:332raise ValueError(333'Entries of Avector must be Integers, Polynomials or tuples (C_i, D_i)')334335# For C_i = 1, D_i = -1 we require a simple pole with vanishing residue336# => return zero class337if any((Cvector[i] == 1) and (Dvector[i] == -1) for i in range(n)):338return tautclass([])339340step = lcm(c.denom() for c in Cvector)341C = sum(Cvector)342assert C in ZZ343assert sum(Dvector) == 2 * g - 2344else:345AvectorX = [ZZ(a) for a in Avector]346step = 0347348if d is None:349if r_coeff is None:350d = ZZ(g - 1 + C)351else:352denom = ZZ((r_coeff - 2) * (g - 1) + sum(Avector))353if denom % r_coeff != 0:354# Witten's class vanishes unless this congruence is satisfied355return tautclass([])356else:357d = ZZ(denom / ZZ(r_coeff))358moddict = {'sm': MODULI_SM, 'rt': MODULI_RT,359'ct': MODULI_CT, 'st': MODULI_ST}360modu = moddict[moduli]361362rvect = rspin_compute(g, d, n, tuple(AvectorX), r_coeff,363step, m0=-1, deg=-1, moduli_type=modu)364if not rpoly:365rvect = rvect.subs(X=0)366rvect = convert_vector_to_monomial_basis(367rvect, g, d, tuple(range(1, n + 1)), modu)368369if rpoly:370rvect = vector((b.subs(X=r) for b in rvect))371372R = TautologicalRing(g, n, moduli=moduli)373return R.from_vector(rvect, d)374# return Tautv_to_tautclass(rvect, g, n, d, moduli=moduli)375376377@cached_function378def Pmpolynomial(m):379r"""380Returns the expression P_m(X,a) as defined in [Pandharipande-Pixton-Zvonkine '16, Section 4.5].381382TESTS::383384sage: from admcycles.witten import Pmpolynomial385sage: Pmpolynomial(0)3861387sage: Pmpolynomial(1)388-1/12*X^2 + 1/2*(X - 1)*a - 1/2*a^2 + 5/24*X - 1/12389sage: Pmpolynomial(2)3901/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/288391"""392(b, X, a) = var('b,X,a')393if m == 0:394return 1 + 0 * X395return (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)) *396symbolic_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()397398399