unlisted
ubuntu2004# -*- coding: utf-8 -*-1r"""2Double ramification cycle34The main function to use to compute DR cycles are the following:56- :func:`DR_compute`7- :func:`DR_sparse`: same as ``DR_compute`` except that it returns the answer as a8sparse vector9- :func:`DR_reduced`: same as ``DR_compute`` except that it only requires two10arguments (g and dvector) and simplifies the answer using the 3-spin11tautological relations.1213EXAMPLES::1415sage: from admcycles.DR import DR_compute, DR_sparse, DR_reduced1617To compute the genus 1 DR cycle with weight vector (2,-2)::1819sage: DR_compute(1,1,2,(2,-2))20(0, 2, 2, 0, -1/24)2122In the example above, the five classes described on R^1(Mbar_{1,2}) are:23kappa_1, psi_1, psi_2, delta_{12}, delta_{irr}. (Here by delta_{irr} we mean24the pushforward of 1 under the gluing map Mbar_{0,4} -> Mbar_{1,2}, so twice25the class of the physical locus.)2627Sparse version::2829sage: DR_sparse(1,1,2,(2,-2))30[[1, 2], [2, 2], [4, -1/24]]3132Reduced version::3334sage: DR_reduced(1,(2,-2))35(0, 0, 0, 4, 1/8)36"""3738from copy import copy39import itertools4041from sage.rings.integer_ring import ZZ42from sage.rings.rational_field import QQ43from sage.functions.other import ceil44from sage.modules.free_module_element import vector4546from .moduli import MODULI_ST47from .graph import R, num_strata, single_stratum, autom_count48from .utils import interpolate49from .relations import FZ_rels505152def find_nonsep_pairs(num, g, r, markings=(), moduli_type=MODULI_ST):53G = single_stratum(num, g, r, markings, moduli_type)54nr = G.M.nrows()55nc = G.M.ncols()56answer = []57for i1 in range(1, nr):58for i2 in range(1, i1):59found_edge = False60for j in range(1, nc):61if G.M[i1, j] != 0 and G.M[i2, j] != 0:62found_edge = True63break64if not found_edge:65continue66S = set([i1])67did_something = True68while did_something:69did_something = False70for i3 in tuple(S):71for j in range(1, nc):72if G.M[i3, j] != 0:73for i4 in range(1, nr):74if G.M[i4, j] != 0 and i4 not in S and (i3 != i1 or i4 != i2):75S.add(i4)76did_something = True77if i2 in S:78answer.append([i2, i1])79return answer8081# assumes r <= 4 for now828384def DR_coeff_is_known(num, g, r, markings=(), moduli_type=MODULI_ST):85return r <= 48687# old stuff88G = single_stratum(num, g, r, markings, moduli_type)89nr = G.M.nrows()90nc = G.M.ncols()91nonsep_pairs = find_nonsep_pairs(num, g, r, markings, moduli_type)92if len(nonsep_pairs) > 3:93return False94if len(nonsep_pairs) == 3:95i1 = nonsep_pairs[0][0]96i2 = nonsep_pairs[0][1]97i3 = nonsep_pairs[2][1]98jlist = []99for j in range(1, nc):100if len([1 for i in [i1, i2, i3] if G.M[i, j] != 0]) == 2:101jlist.append(j)102if len(jlist) > 3:103return False104for j in jlist:105for i in [i1, i2, i3]:106if G.M[i, j][1] != 0:107return False108109return True110111# this stuff is old, keeping it around for now just in case112for j in range(1, nc):113ilist = []114for i in range(1, nr):115if G.M[i, j] != 0:116ilist.append(i)117if len(ilist) == 1:118continue119ii1 = ilist[0]120ii2 = ilist[1]121jlist = []122for jj in range(1, nc):123if G.M[ii1, jj] != 0 and G.M[ii2, jj] != 0:124jlist.append(jj)125if len(jlist) == 1 or jlist[0] != j:126continue127count = len(jlist)128for ii in [ii1, ii2]:129for jj in jlist:130count += G.M[ii, jj][1]131if count > 3:132return False133return True134135# next function doesn't work on arbitrary graphs yet, see above function136137138def DR_coeff(num, g, r, n=0, dvector=(), moduli_type=MODULI_ST):139markings = tuple(range(1, n + 1))140G = single_stratum(num, g, r, markings, moduli_type)141nr = G.M.nrows()142nc = G.M.ncols()143answer = QQ((1, autom_count(num, g, r, markings, moduli_type)))144145def balance(ilist):146bal = [0 for i1 in ilist]147for j1 in range(1, nc):148if G.M[0, j1] != 0:149for i3 in range(1, nr):150if G.M[i3, j1] != 0:151S = set([i3])152did_something = True153while did_something:154did_something = False155for i4 in tuple(S):156for j2 in range(1, nc):157if G.M[i4, j2] != 0:158for i5 in range(1, nr):159if G.M[i5, j2] != 0 and i5 not in S and (i4 not in ilist or i5 not in ilist):160S.add(i5)161did_something = True162for kk, ikk in enumerate(ilist):163if ikk in S:164bal[kk] += dvector[G.M[0, j1][0] - 1]165return bal166167def poly4(a, b, c):168return (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))169170nonsep_pairs = find_nonsep_pairs(num, g, r, markings, moduli_type)171if len(nonsep_pairs) == 4:172cycle = [nonsep_pairs[0][0], nonsep_pairs[0]173[1], 0, 0]174for i in range(1, 4):175for j in range(2):176if nonsep_pairs[i][j] == cycle[0]:177cycle[3] = nonsep_pairs[i][1 - j]178if nonsep_pairs[i][j] == cycle[1]:179cycle[2] = nonsep_pairs[i][1 - j]180dbal = balance(cycle)181a = dbal[0]182b = dbal[0] + dbal[1]183c = dbal[0] + dbal[1] + dbal[2]184answer *= poly4(a, b, c)185elif len(nonsep_pairs) == 3:186i1 = nonsep_pairs[0][0]187i2 = nonsep_pairs[0][1]188i3 = nonsep_pairs[2][1]189cycle = [i1, i2, i3]190cycle4 = cycle + [cycle[0]]191dbal = balance(cycle)192dbal4 = dbal + [dbal[0]]193done = False194for k in range(3):195jlist = [j for j in range(1, nc) if (196G.M[cycle4[k], j] != 0 and G.M[cycle4[k + 1], j] != 0)]197if len(jlist) == 2:198answer *= -ZZ.one() / 6 * \199poly4(0, dbal4[k], -dbal4[k + 1])200done = True201elif len(jlist) == 1:202if G.M[cycle4[k], jlist[0]][1] + G.M[cycle4[k + 1], jlist[0]][1] > 0:203answer *= -ZZ.one() / 2 * \204poly4(0, dbal4[k], -dbal4[k + 1])205done = True206if not done:207answer *= (dbal[0]**6 + dbal[1]**6 + dbal[2]**6 -20810 * dbal[0]**2 * dbal[1]**2 * dbal[2]**2) / ZZ(30)209210for j in range(1, nc):211ilist = []212for i in range(1, nr):213if G.M[i, j] != 0:214ilist.append(i)215if len(ilist) == 1:216x = G.M[ilist[0], j][1]217answer /= ZZ(x).factorial()218answer *= dvector[G.M[0, j][0] -2191]**(ZZ(2) * x)220continue221if ilist in nonsep_pairs:222continue223ii1 = ilist[0]224ii2 = ilist[1]225jlist = []226for jj in range(1, nc):227if G.M[ii1, jj] != 0 and G.M[ii2, jj] != 0:228jlist.append(jj)229if len(jlist) == 1:230x1 = G.M[ii1, j][1]231x2 = G.M[ii2, j][1]232answer *= -1233answer /= ZZ(x1).factoial()234answer /= ZZ(x2).factorial()235answer /= x1 + x2 + 1236answer *= balance([ii1, ii2])[0]**(ZZ(2) *237(x1 + x2 + 1))238continue239elif len(jlist) == 2:240if jlist[0] != j:241continue242xvec = [G.M[ii1, jlist[0]][1], G.M[ii1, jlist[1]][1],243G.M[ii2, jlist[0]][1], G.M[ii2, jlist[1]][1]]244x = sum(xvec)245if x == 0:246answer *= -ZZ.one() / 6247answer *= balance([ii1, ii2])[0]**4248elif x == 1:249answer *= -ZZ.one() / 30250answer *= balance([ii1, ii2])[0]**6251elif x == 2:252if xvec in [[2, 0, 0, 0], [0, 2, 0, 0], [0, 0, 2, 0], [0, 0, 0, 2]]:253answer *= -ZZ.one() / 168254elif xvec in [[1, 1, 0, 0], [0, 0, 1, 1], [1, 0, 0, 1], [0, 1, 1, 0]]:255answer *= -ZZ.one() / 280256elif xvec in [[1, 0, 1, 0], [0, 1, 0, 1]]:257answer *= -ZZ.one() / 84258answer *= balance([ii1, ii2])[0]**8259continue260elif len(jlist) == 3:261if jlist[0] != j:262continue263xvec = [G.M[ii1, jlist[0]][1], G.M[ii1, jlist[1]][1], G.M[ii1, jlist[2]]264[1], G.M[ii2, jlist[0]][1], G.M[ii2, jlist[1]][1], G.M[ii2, jlist[2]][1]]265x = sum(xvec)266if x == 0:267answer *= -ZZ.one() / 90268answer *= balance([ii1, ii2])[0]**6269elif x == 1:270answer *= -ZZ.one() / 840271answer *= balance([ii1, ii2])[0]**8272continue273elif len(jlist) == 4:274if jlist[0] != j:275continue276answer *= -ZZ.one() / 2520277answer *= balance([ii1, ii2])[0]**8278return answer279280281def veto_for_DR(num, g, r, markings=(), moduli_type=MODULI_ST):282G = single_stratum(num, g, r, markings, moduli_type)283nr = G.M.nrows()284nc = G.M.ncols()285marked_vertices = []286for i in range(1, nr):287if G.M[i, 0] != R(G.M[i, 0][0]):288return True289for j in range(1, nc):290if G.M[i, j][0] == 2:291return True292if G.M[0, j] != 0 and G.M[i, j] != 0:293marked_vertices.append(i)294for ii in range(1, nr):295S = set(marked_vertices)296S.add(ii)297did_something = True298while did_something:299did_something = False300for i in tuple(S):301if i == ii:302continue303for j in range(1, nc):304if G.M[i, j] != 0:305for i2 in range(1, nr):306if G.M[i2, j] != 0 and i2 not in S:307S.add(i2)308did_something = True309if len(S) < nr - 1:310return True311return False312313314def DR_uncomputed(g, r, markings, moduli_type=MODULI_ST):315# markings = tuple(range(1,n+1))316for i in range(num_strata(g, r, markings, moduli_type)):317if not veto_for_DR(i, g, r, markings, moduli_type) and not DR_coeff_is_known(i, g, r, markings, moduli_type):318print(i)319print(single_stratum(i, g, r, markings, moduli_type).M)320print("---------------------")321322323def reduce_with_rels(B, vec):324vec2 = copy(vec)325for row in B:326for i in range(len(row)):327if row[i] != 0:328if vec2[i] != 0:329vec2 -= QQ((vec2[i], row[i])) * row330break331return vec2332333334def DR_coeff_setup(num, g, r, n=0, dvector=(), kval=0, moduli_type=MODULI_ST):335markings = tuple(range(1, n + 1))336G = single_stratum(num, g, r, markings, moduli_type)337nr = G.M.nrows()338nc = G.M.ncols()339edge_list = []340exp_list = []341scalar_factor = ZZ.one() / \342autom_count(num, g, r, markings, moduli_type)343for i in range(1, nr):344for j in range(1, G.M[i, 0].degree() + 1):345scalar_factor /= ZZ(G.M[i, 0][j]).factorial()346scalar_factor /= ZZ(j).factorial()**G.M[i, 0][j]347scalar_factor *= (-1)**G.M[i, 0][j]348scalar_factor *= (kval**2)**(j * G.M[i, 0][j])349given_weights = [-kval * (2 * G.M[i + 1, 0][0] - 2 + sum([G.M[i + 1][j][0]350for j in range(1, nc)])) for i in range(nr - 1)]351for j in range(1, nc):352ilist = [i for i in range(1, nr)353if G.M[i, j] != 0]354if G.M[0, j] == 0:355if len(ilist) == 1:356i1 = ilist[0]357i2 = ilist[0]358exp1 = G.M[i1, j][1]359exp2 = G.M[i1, j][2]360else:361i1 = ilist[0]362i2 = ilist[1]363exp1 = G.M[i1, j][1]364exp2 = G.M[i2, j][1]365edge_list.append([i1 - 1, i2 - 1])366exp_list.append(exp1 + exp2 + 1)367scalar_factor /= - \368ZZ(exp1).factorial() * ZZ(exp2).factorial() * (exp1 + exp2 + 1)369else:370exp1 = G.M[ilist[0], j][1]371scalar_factor *= dvector[G.M[0, j][0] -3721]**(ZZ(2) * exp1) / ZZ(exp1).factorial()373given_weights[ilist[0] -3741] += dvector[G.M[0, j][0] - 1]375return edge_list, exp_list, given_weights, scalar_factor376377378def DR_coeff_new(num, g, r, n=0, dvector=(), kval=0, moduli_type=MODULI_ST):379markings = tuple(range(1, n + 1))380G = single_stratum(num, g, r, markings, moduli_type)381nr = G.M.nrows()382nc = G.M.ncols()383edge_list, exp_list, given_weights, scalar_factor = DR_coeff_setup(384num, g, r, n, dvector, kval, moduli_type)385m0 = ceil(sum([abs(i) for i in dvector]) / ZZ(2)) + g * abs(kval)386h0 = nc - nr - n + 1387deg = 2 * sum(exp_list)388mrange = range(m0 + 1, m0 + deg + 2)389mvalues = []390for m in mrange:391total = 0392for weight_data in itertools.product(*[list(range(m)) for i in range(len(edge_list))]):393vertex_weights = copy(given_weights)394for i in range(len(edge_list)):395vertex_weights[edge_list[i][0]] += weight_data[i]396vertex_weights[edge_list[i][1]] -= weight_data[i]397if any(i % m for i in vertex_weights):398continue399term = 1400for i in range(len(edge_list)):401term *= weight_data[i]**(ZZ(2) * exp_list[i])402total += term403mvalues.append(QQ((total, m**h0)))404mpoly = interpolate(mrange, mvalues)405return mpoly.subs(x=0) * scalar_factor406407408def DR_coeff_setup_m(m, num, g, r, n=0, dvector=(), kval=0, moduli_type=MODULI_ST):409markings = tuple(range(1, n + 1))410G = single_stratum(num, g, r, markings, moduli_type)411nr = G.M.nrows()412nc = G.M.ncols()413edge_list = []414exp_list = []415scalar_factor = ZZ.one() / \416autom_count(num, g, r, markings, moduli_type)417for i in range(1, nr):418for j in range(1, G.M[i, 0].degree() + 1):419scalar_factor /= ZZ(G.M[i, 0][j]).factorial()420scalar_factor /= ZZ(j).factorial()**G.M[i, 0][j]421scalar_factor *= (-1)**G.M[i, 0][j]422scalar_factor *= (kval**2 - kval * m + m**2 /423ZZ(6))**(j * G.M[i, 0][j])424given_weights = [-kval * (ZZ(2) * G.M[i + 1, 0][0] - ZZ(2) + sum(425[G.M[i + 1][j][0] for j in range(1, nc)])) for i in range(nr - 1)]426for j in range(1, nc):427ilist = [i for i in range(1, nr)428if G.M[i, j] != 0]429if G.M[0, j] == 0:430if len(ilist) == 1:431i1 = ilist[0]432i2 = ilist[0]433exp1 = G.M[i1, j][1]434exp2 = G.M[i1, j][2]435else:436i1 = ilist[0]437i2 = ilist[1]438exp1 = G.M[i1, j][1]439exp2 = G.M[i2, j][1]440edge_list.append([i1 - 1, i2 - 1])441exp_list.append(exp1 + exp2 + 1)442scalar_factor /= - \443ZZ(exp1).factorial() * ZZ(exp2).factorial() * (exp1 + exp2 + 1)444else:445exp1 = G.M[ilist[0], j][1]446dval = dvector[G.M[0, j][0] - 1]447if dval < 0:448dval = dval + m449scalar_factor *= (dval**2 - dval * m + m**2 /450ZZ(6))**(exp1) / ZZ(exp1).factorial()451given_weights[ilist[0] -4521] += dvector[G.M[0, j][0] - 1]453return edge_list, exp_list, given_weights, scalar_factor454455456def DR_coeff_m(m, num, g, r, n=0, dvector=(), kval=0, moduli_type=MODULI_ST):457markings = tuple(range(1, n + 1))458G = single_stratum(num, g, r, markings, moduli_type)459nr = G.M.nrows()460nc = G.M.ncols()461edge_list, exp_list, given_weights, scalar_factor = DR_coeff_setup_m(462m, num, g, r, n, dvector, kval, moduli_type)463h0 = nc - nr - n + 1464total = ZZ.zero()465for weight_data in itertools.product(*[list(range(m)) for i in range(len(edge_list))]):466vertex_weights = copy(given_weights)467for i in range(len(edge_list)):468vertex_weights[edge_list[i][0]] += weight_data[i]469vertex_weights[edge_list[i][1]] -= weight_data[i]470if any(i % m for i in vertex_weights):471continue472term = 1473for i in range(len(edge_list)):474dval = weight_data[i]475term *= (dval**2 - dval * m + m**2 / ZZ(6))**exp_list[i]476total += term477total /= m**h0478total *= scalar_factor479return total480481482def DR_compute(g, r, n=0, dvector=(), kval=0, moduli_type=MODULI_ST):483r"""484INPUT:485486g : integer487the genus488r : integer489cohomological degree (set to g for the actual DR cycle, should give490tautological relations for r > g)491n : integer492number of marked points493dvector : integer vector494weights to place on the marked points, should have sum zero in the case495of the DR cycle496kval : integer (0 by default)497if set to something else than 0 then the result is the twisted DR498cycle by copies of the log canonical bundle (e.g. 1 for differentials)499moduli_type : a moduli type500MODULI_ST by default (moduli of stable curves), can be set to moduli501spaces containing less of the boundary (e.g. MODULI_CT for compact type)502to compute there instead503"""504answer = []505markings = tuple(range(1, n + 1))506for i in range(num_strata(g, r, markings, moduli_type)):507answer.append(DR_coeff_new(i, g, r, n, dvector, kval, moduli_type))508return vector(answer) / ZZ(2) ** r509510511def DR_compute_m(m, g, r, n=0, dvector=(), kval=0, moduli_type=MODULI_ST):512answer = []513markings = tuple(range(1, n + 1))514for i in range(num_strata(g, r, markings, moduli_type)):515answer.append(DR_coeff_m(m, i, g, r, n, dvector, kval, moduli_type))516return vector(answer)517518519def DR_sparse(g, r, n=0, dvector=(), kval=0, moduli_type=MODULI_ST):520"""521Same as :func:`DR_compute` except that it returns the answer as a522sparse vector.523524EXAMPLES::525526sage: from admcycles.DR import DR_sparse527sage: DR_sparse(1,1,2,(2,-2))528[[1, 2], [2, 2], [4, -1/24]]529"""530vec = DR_compute(g, r, n, dvector, kval, moduli_type)531return [[i, veci] for i, veci in enumerate(vec) if veci != 0]532533534def DR_psi_check(g, n, dvector, which_psi):535from .algebra import psi_multiple536from .evaluation import socle_evaluation537vec = DR_sparse(g, g, n, dvector, 0)538r = g539while r < 3 * g - 3 + n:540vec = psi_multiple(vec, which_psi, g, r, n)541r += 1542total = ZZ.zero()543for a, b in vec:544total += b * socle_evaluation(a, g, tuple(range(1, n + 1)))545return total / 2**g546547548def DR_reduced(g, dvector=()):549"""550Same as :func:`DR_compute` except that it only requires two arguments551(g and dvector) and simplifies the answer using the 3-spin552tautological relations.553554EXAMPLES::555556sage: from admcycles.DR import DR_reduced557sage: DR_reduced(1,(2,-2))558(0, 0, 0, 4, 1/8)559"""560g = ZZ(g)561n = len(dvector)562r = g563kval = ZZ(sum(dvector) / (2 * g - 2 + n))564vec = DR_compute(g, r, n, dvector, kval)565vec = vector(QQ, (QQ(a) for a in vec))566rels = FZ_rels(g, r, tuple(range(1, n + 1)))567return reduce_with_rels(rels, vec)568569570