unlisted
ubuntu2004r"""1Generators of tautological ring23Each generator is called a stratum and is encoded in a class class:`Graph`. A4stratum is called *pure* if it has neither kappa nor psi marking (ie a stable5graph with no decoration).6"""7from copy import copy8import itertools910from sage.misc.cachefunc import cached_function11from sage.misc.lazy_attribute import lazy_attribute1213from sage.rings.all import PolynomialRing, ZZ14from sage.combinat.all import IntegerVectors, Partitions15from sage.functions.other import floor16from sage.matrix.constructor import matrix1718from .utils import aut19from .moduli import MODULI_SM, MODULI_RT, MODULI_CT, MODULI_ST, MODULI_SMALL, dim_form202122R = PolynomialRing(ZZ, 1, order='lex', names=('X',))23X = R.gen()242526class Graph:27r"""28A decorated graph representing a tautological class.2930The graph is encoded as a matrix where3132- each row after the first one corresponds to a vertex33- each column after the first one corresponds to an edge or leg34- the first column gives the genera of the vertices35- the first row gives the markings on the legs36- the other cells are37- 0 if the vertex and edge/leg are not incident38- 1 if the vertex and edge/leg are incident once39- 2 if the edge is a loop at the vertex40- entries with polynomials in X describe kappa/psi decorations:41- in the first column, each X^n term corresponds to a kappa_n at that42vertex43- in other locations, each X term corresponds to a psi at that half-edge44- at loops, 2 + aX + bX^2 corresponds to having psi^a on one side of the45loop and psi^b on the other4647EXAMPLES::4849sage: from admcycles.DR.graph import Graph, X50sage: gr = Graph(matrix(2, 3, [-1, 1, 2, 1, X + 1, 1]))51sage: gr52[ -1 1 2]53[ 1 X + 1 1]54sage: gr.num_vertices()55156sage: gr.num_edges()57258"""59def __init__(self, M=None, genus_list=None):60if M:61self.M = copy(M)62elif genus_list:63self.M = matrix(R, len(genus_list) + 1,641, [-1] + genus_list)65else:66self.M = matrix(R, 1, 1, -1)6768def __repr__(self):69return repr(self.M)7071@lazy_attribute72def degree_vec(self):73r"""74Return the tuple of degrees of vertices.7576EXAMPLES::7778sage: from admcycles.DR.graph import Graph, X79sage: gr = Graph(matrix(2, 3, [-1, 1, 2, 1, X + 1, 1]))80sage: gr.degree_vec81(2,)82"""83res = [0 for i in range(1, self.M.nrows())]84for i in range(1, self.M.nrows()):85for j in range(1, self.M.ncols()):86res[i - 1] += self.M[i, j][0]87return tuple(res)8889@lazy_attribute90def target_parity(self):91ans = 092for i in range(1, self.M.nrows()):93local_parity = 1 + self.M[i, 0][0]94for j in range(1, self.M.ncols()):95local_parity += self.M[i, j][1] + self.M[i, j][2]96for j in range(1, self.M[i, 0].degree() + 1):97local_parity += j * self.M[i, 0][j]98local_parity %= 299ans += (local_parity << (i - 1))100return ans101102def degree(self, i):103r"""104Return the degree of vertex ``i``.105"""106return self.degree_vec[i - 1]107108def num_vertices(self):109return self.M.nrows() - 1110111def num_edges(self):112return self.M.ncols() - 1113114def h1(self):115return self.M.ncols() - self.M.nrows() + 1116117def add_vertex(self, g):118self.M = self.M.stack(matrix(1, self.M.ncols()))119self.M[-1, 0] = g120121def add_edge(self, i1, i2, marking=0):122self.M = self.M.augment(matrix(self.M.nrows(), 1))123self.M[0, -1] = marking124if i1 > 0:125self.M[i1, -1] += 1126if i2 > 0:127self.M[i2, -1] += 1128129def del_vertex(self, i):130self.M = self.M[:i].stack(self.M[(i + 1):])131132def del_edge(self, i):133if i == self.num_edges():134self.M = self.M[0:, :i]135else:136self.M = self.M[0:, :i].augment(self.M[0:, (i + 1):])137138def split_vertex(self, i, row1, row2):139self.M = self.M.stack(matrix(2, self.M.ncols(), row1 + row2))140self.add_edge(self.M.nrows() - 2,141self.M.nrows() - 1)142self.del_vertex(i)143144def replace_vertex_with_graph(self, i, G):145nv = self.num_vertices()146ne = self.num_edges()147# i should have degree d, there should be no classes near i, and G should have genus equal to the genus of i148hedge_list_ones = []149hedge_list_others = []150unsym_cols = [-1]151mark_nr = -1152for k in range(1, G.M.ncols()):153if G.M[0, k] > 0:154if G.M[0, k] == 1:155mark_nr += 1156unsym_cols.append(G.M[0, k] + mark_nr)157else:158unsym_cols.append(0)159for k in range(1, self.M.ncols()):160for j in range(self.M[i, k][0]):161if self.M[0, k] == 1:162hedge_list_ones.append(k)163else:164hedge_list_others.append(k)165hedge_list = hedge_list_ones + hedge_list_others166self.del_vertex(i)167for j in range(G.num_edges() - len(hedge_list)):168self.add_edge(0, 0)169for j in range(G.num_vertices()):170self.add_vertex(G.M[j + 1, 0])171col = ne + 1172for k in range(1, G.M.ncols()):173if G.M[0, k] > 0:174mark = ZZ(unsym_cols[k])175for j in range(G.num_vertices()):176if self.M[nv + j, hedge_list[mark - 1]] == 0:177self.M[nv + j, hedge_list[mark - 1]] = G.M[j + 1, k]178elif G.M[j + 1, k] != 0:179a = self.M[nv + j, hedge_list[mark - 1]][1]180b = G.M[j + 1, k][1]181self.M[nv + j, hedge_list[mark - 1]] = 2 + \182max(a, b) * X + min(a, b) * X**2183else:184for j in range(G.num_vertices()):185self.M[nv + j, col] = G.M[j + 1, k]186col += 1187188def compute_invariant(self):189nr, nc = self.M.nrows(), self.M.ncols()190self.invariant = [[self.M[i, 0], [], [], [191[] for j in range(1, nr)]] for i in range(1, nr)]192for k in range(1, nc):193L = [i for i in range(1, nr)194if self.M[i, k] != 0]195if len(L) == 1:196if self.M[0, k] != 0:197self.invariant[L[0] -1981][2].append((self.M[0, k], self.M[L[0], k]))199else:200self.invariant[L[0] - 1][1].append(self.M[L[0], k])201else:202self.invariant[L[0] - 1][3][L[1] - 1].append((self.M[L[0], k],203self.M[L[1], k]))204self.invariant[L[1] - 1][3][L[0] - 1].append((self.M[L[1], k],205self.M[L[0], k]))206for i in range(1, nr):207self.invariant[i - 1][3] = [term for term in self.invariant[i - 1][3]208if len(term)]209for k in range(len(self.invariant[i - 1][3])):210self.invariant[i - 1][3][k].sort()211self.invariant[i -2121][3][k] = tuple(self.invariant[i - 1][3][k])213self.invariant[i - 1][3].sort()214self.invariant[i - 1][3] = tuple(self.invariant[i - 1][3])215self.invariant[i - 1][2].sort()216self.invariant[i - 1][2] = tuple(self.invariant[i - 1][2])217self.invariant[i - 1][1].sort()218self.invariant[i - 1][1] = tuple(self.invariant[i - 1][1])219self.invariant[i - 1] = tuple(self.invariant[i - 1])220vertex_invariants = [[i, self.invariant[i - 1]]221for i in range(1, nr)]222self.invariant.sort()223self.invariant = tuple(self.invariant)224vertex_invariants.sort(key=lambda x: x[1])225self.vertex_groupings = []226for i in range(nr - 1):227if i == 0 or vertex_invariants[i][1] != vertex_invariants[i - 1][1]:228self.vertex_groupings.append([])229self.vertex_groupings[-1].append(230vertex_invariants[i][0])231232def purify(self):233for i in range(self.M.nrows()):234for j in range(self.M.ncols()):235self.M[i, j] = R(self.M[i, j][0])236237def contract(self, i, vlist, elist):238# assumes graph is undecorated239if self.M[0, i] != 0:240print("ERROR: cannot contract a marking")241return242S = [row for row in range(2431, self.M.nrows()) if self.M[row, i] != 0]244if len(S) == 1:245self.M[S[0], 0] += 1246self.del_edge(i)247elist = elist[:(i - 1)] + elist[i:]248else:249self.del_edge(i)250elist = elist[:(i - 1)] + elist[i:]251self.add_vertex(0)252self.M[-1] += self.M[S[0]]253self.M[-1] += self.M[S[1]]254self.del_vertex(S[1])255self.del_vertex(S[0])256vlist = vlist[:(S[0] - 1)] + vlist[S[0]:(S[1] - 1)] + \257vlist[S[1]:] + [vlist[S[0] - 1] + vlist[S[1] - 1]]258return vlist, elist259260261def graph_isomorphic(G1, G2):262r"""263Return whether ``G1`` and ``G2`` are isomorphic.264"""265if G1.invariant != G2.invariant:266return False267268M1 = G1.M269M2 = G2.M270group1 = G1.vertex_groupings271group2 = G2.vertex_groupings272273nr = M1.nrows()274nc = M1.ncols()275for sigma_data in itertools.product(*(itertools.permutations(range(len(group))) for group in group1)):276sigma = [0] * (nr - 1)277for i in range(len(group1)):278for j in range(len(group1[i])):279sigma[group1[i][j] - 1] = group2[i][sigma_data[i][j]]280good = True281for i in range(1, nr):282ii = sigma[i - 1]283for j in range(1, i):284jj = sigma[j - 1]285L1 = []286for k in range(1, nc):287if M1[i, k] != 0 and M1[j, k] != 0:288L1.append([M1[i, k], M1[j, k]])289L1.sort()290L2 = []291for k in range(1, nc):292if M2[ii, k] != 0 and M2[jj, k] != 0:293L2.append([M2[ii, k], M2[jj, k]])294L2.sort()295if L1 != L2:296good = False297break298if good is False:299break300if good:301return True302return False303304305def graph_count_automorphisms(G, vertex_orbits=False):306return count_automorphisms(G.M, G.vertex_groupings, vertex_orbits)307308309def count_automorphisms(M, grouping, vertex_orbits=False):310"""311INPUT:312313M: adjacency matrix314grouping: list of lists315vertex_orbits: boolean316"""317nr, nc = M.nrows(), M.ncols()318count = ZZ.zero()319if vertex_orbits:320isom_list = []321for sigma_data in itertools.product(*(itertools.permutations(range(len(group))) for group in grouping)):322sigma = [0 for i in range(nr - 1)]323for i in range(len(grouping)):324for j in range(len(grouping[i])):325sigma[grouping[i][j] - 1] = grouping[i][sigma_data[i][j]]326good = True327for i in range(1, nr):328ii = sigma[i - 1]329for j in range(1, i):330jj = sigma[j - 1]331L1 = []332for k in range(1, nc):333if M[i, k] != 0 and M[j, k] != 0:334L1.append([M[i, k], M[j, k]])335L1.sort()336L2 = []337for k in range(1, nc):338if M[ii, k] != 0 and M[jj, k] != 0:339L2.append([M[ii, k], M[jj, k]])340L2.sort()341if L1 != L2:342good = False343break344if not good:345break346if good:347count += 1348if vertex_orbits:349isom_list.append(sigma)350351if vertex_orbits:352orbit_list = []353vertices_used = []354while len(vertices_used) < nr - 1:355i = next(ii for ii in range(1, nr) if ii not in vertices_used)356orbit = []357for sigma in isom_list:358if sigma[i - 1] not in orbit:359orbit.append(sigma[i - 1])360vertices_used.append(sigma[i - 1])361orbit.sort()362orbit_list.append(orbit)363return orbit_list364365for i in range(1, nr):366for k in range(1, nc):367if M[i, k][0] == 2 and M[i, k][1] == M[i, k][2]:368count *= 2369L = []370for k in range(1, nc):371if M[i, k] != 0:372if sum(1 for j in range(1, nr) if M[j, k] != 0) == 1:373L.append([M[0, k], M[i, k]])374count *= aut(L)375376for j in range(1, i):377L = []378for k in range(1, nc):379if M[i, k] != 0 and M[j, k] != 0:380L.append([M[i, k], M[j, k]])381count *= aut(L)382return count383384385def graph_list_isomorphisms(G1, G2, only_one=False):386r"""387Return the list of isomorphisms from ``G1`` to ``G2``.388"""389# Warning: does not count loops!390# If this is too slow, we can probably improve by caching a list of automorphisms and applying those to the first isom found.391if G1.invariant != G2.invariant:392return []393394M1 = G1.M395M2 = G2.M396group1 = G1.vertex_groupings397group2 = G2.vertex_groupings398399nr = M1.nrows()400nc = M2.ncols()401isom_list = []402for sigma_data in itertools.product(*(itertools.permutations(range(len(group))) for group in group1)):403sigma = [0] * (nr - 1)404for i in range(len(group1)):405for j in range(len(group1[i])):406sigma[group1[i][j] - 1] = group2[i][sigma_data[i][j]]407good = True408for i in range(1, nr):409ii = sigma[i - 1]410for j in range(1, i):411jj = sigma[j - 1]412L1 = []413for k in range(1, nc):414if M1[i, k] != 0 and M1[j, k] != 0:415L1.append([M1[i, k], M1[j, k]])416L1.sort()417L2 = []418for k in range(1, nc):419if M2[ii, k] != 0 and M2[jj, k] != 0:420L2.append([M2[ii, k], M2[jj, k]])421L2.sort()422if L1 != L2:423good = False424break425if not good:426break427if good:428cols1 = [[M1[i, j] for i in range(nr)] for j in range(1, nc)]429cols2 = [[M2[0, j]] + [M2[sigma[i - 1], j] for i in range(1, nr)]430for j in range(1, nc)]431edge_group1 = []432edge_group2 = []433used1 = []434for j in range(1, nc):435if j not in used1:436edge_group1.append([])437edge_group2.append([])438for k in range(1, nc):439if cols1[k - 1] == cols1[j - 1]:440edge_group1[-1].append(k)441used1.append(k)442if cols2[k - 1] == cols1[j - 1]:443edge_group2[-1].append(k)444for edge_sigma_data in itertools.product(*(itertools.permutations(range(len(edge_group))) for edge_group in edge_group1)):445edge_sigma = [0 for i in range(nc - 1)]446for i in range(len(edge_group1)):447for j in range(len(edge_group1[i])):448edge_sigma[edge_group1[i][j] -4491] = edge_group2[i][edge_sigma_data[i][j]]450isom_list.append([sigma, edge_sigma])451if only_one:452return isom_list453return isom_list454455456def degenerate(G_list, moduli_type=MODULI_ST):457r"""458Return the list of degenerations of the graphs in ``G_list`` up to459isomorphisms.460"""461mod_size = moduli_type + 1462if moduli_type == MODULI_SMALL:463mod_size = MODULI_SM + 1464G_list_new = [[] for i in range(mod_size)]465for which_type in range(mod_size):466for G in G_list[which_type]:467for i in range(1, G.num_vertices() + 1):468row = list(G.M[i])469m = row[0] + sum(row)470if m < 4:471continue472row1 = [0 for j in range(len(row))]473while [2 * x for x in row1] <= row:474if row1[0] == 1 and moduli_type <= MODULI_RT:475break476if row1[0] + sum(row1) >= 2 and row1[0] + sum(row1) <= m - 2:477row2 = [row[j] - row1[j] for j in range(len(row))]478G_copy = Graph(G.M)479G_copy.split_vertex(i, row1, row2)480new_type = which_type481if new_type == MODULI_SM:482new_type = MODULI_RT483if new_type == MODULI_RT and row1[0] > 0:484new_type = MODULI_CT485G_list_new[new_type].append(G_copy)486row1[-1] += 1487for j in range(1, len(row)):488if row1[-j] <= row[-j]:489break490row1[-j] = 0491row1[-j - 1] += 1492for i in range(mod_size):493G_list_new[i] = remove_isomorphic(G_list_new[i])494return G_list_new495496497def decorate(G_list, r, moduli_type=MODULI_ST):498r"""499Add decorations to the graphs in ``G_list``.500"""501mod_size = moduli_type + 1502if moduli_type == MODULI_SMALL:503mod_size = MODULI_SM + 1504G_list_new = [[] for i in range(mod_size)]505for which_type in range(mod_size):506for G in G_list[which_type]:507G_deco = [[] for i in range(mod_size)]508nr, nc = G.M.nrows(), G.M.ncols()509two_list = []510one_list = []511for i in range(1, nr):512for j in range(1, nc):513if G.M[i, j] == 2:514two_list.append([i, j])515elif G.M[i, j] == 1:516one_list.append([i, j])517a = nr - 1518b = len(two_list)519c = len(one_list)520dims = [[dim_form(G.M[i + 1, 0][0], G.degree(i + 1), mod_type)521for i in range(a)] for mod_type in range(mod_size)]522for vec in IntegerVectors(r, a + b + c):523new_type = which_type524if moduli_type > MODULI_SMALL:525test_dims = vec[:a]526for i in range(b):527test_dims[two_list[i][0] - 1] += vec[a + i]528for i in range(c):529test_dims[one_list[i][0] - 1] += vec[a + b + i]530for mod_type in range(which_type, mod_size):531for i in range(a):532if test_dims[i] > dims[mod_type][i]:533new_type = mod_type + 1534break535if new_type > moduli_type:536continue537S_list = []538for i in range(a):539S_list.append(Partitions(vec[i]))540for i in range(a, a + b):541S_list.append(542[[vec[i] - j, j] for j in range(floor(vec[i] / ZZ(2) + 1))])543S = itertools.product(*S_list)544for vec2 in S:545G_copy = Graph(G.M)546for i in range(a):547for j in vec2[i]:548G_copy.M[i + 1, 0] += X**j549for i in range(a, a + b):550G_copy.M[two_list[i - a][0], two_list[i - a]551[1]] += vec2[i][0] * X + vec2[i][1] * X**2552for i in range(c):553G_copy.M[one_list[i][0],554one_list[i][1]] += vec[i + a + b] * X555G_deco[new_type].append(G_copy)556for mod_type in range(mod_size):557G_list_new[mod_type] += remove_isomorphic(G_deco[mod_type])558return G_list_new559560561def remove_isomorphic(G_list):562r"""563Return a list of isomorphism class representatives of the graphs in ``G_list``.564"""565G_list_new = []566inv_dict = {}567count = 0568for G1 in G_list:569G1.compute_invariant()570if G1.invariant not in inv_dict:571inv_dict[G1.invariant] = []572good = True573for i in inv_dict[G1.invariant]:574if graph_isomorphic(G1, G_list_new[i]):575good = False576break577if good:578G_list_new.append(G1)579inv_dict[G1.invariant].append(count)580count += 1581return G_list_new582583584def num_strata(g, r, markings=(), moduli_type=MODULI_ST):585r"""586Return the number of strata in given genus, rank, markings and moduli type.587588EXAMPLES::589590sage: from admcycles.DR.moduli import MODULI_SM, MODULI_CT, MODULI_RT, MODULI_ST591sage: from admcycles.DR.graph import num_strata592sage: for r in range(4):593....: print('r={}: {} {} {} {}'.format(r,594....: num_strata(2, r, (1, 1), MODULI_SM),595....: num_strata(2, r, (1, 1), MODULI_RT),596....: num_strata(2, r, (1, 1), MODULI_CT),597....: num_strata(2, r, (1, 1), MODULI_ST)))598r=0: 1 1 1 1599r=1: 2 3 5 6600r=2: 0 7 16 28601r=3: 0 0 38 113602"""603return len(all_strata(g, r, markings, moduli_type))604605606def num_pure_strata(g, r, markings=(), moduli_type=MODULI_ST):607r"""608Return the number of pure strata in given genus, rank, markings and moduli type.609610EXAMPLES::611612sage: from admcycles.DR.moduli import MODULI_SM, MODULI_CT, MODULI_RT, MODULI_ST613sage: from admcycles.DR.graph import num_pure_strata614sage: for r in range(4):615....: print('r={}: {} {} {} {}'.format(r,616....: num_pure_strata(2, r, (1, 1), MODULI_SM),617....: num_pure_strata(2, r, (1, 1), MODULI_RT),618....: num_pure_strata(2, r, (1, 1), MODULI_CT),619....: num_pure_strata(2, r, (1, 1), MODULI_ST)))620r=0: 1 1 1 1621r=1: 0 1 3 4622r=2: 0 0 3 10623r=3: 0 0 2 19624"""625return len(all_pure_strata(g, r, markings, moduli_type))626627628def single_stratum(num, g, r, markings=(), moduli_type=MODULI_ST):629r"""630Return the ``num``-th stratum.631"""632return all_strata(g, r, markings, moduli_type)[num]633634635def single_pure_stratum(num, g, r, markings=(), moduli_type=MODULI_ST):636r"""637Return the ``num``-th pure stratum.638"""639return all_pure_strata(g, r, markings, moduli_type)[num]640641642@cached_function643def autom_count(num, g, r, markings=(), moduli_type=MODULI_ST):644return graph_count_automorphisms(single_stratum(num, g, r, markings, moduli_type))645646647@cached_function648def pure_strata_autom_count(num, g, r, markings=(), moduli_type=MODULI_ST):649return graph_count_automorphisms(single_pure_stratum(num, g, r, markings, moduli_type))650651652@cached_function653def automorphism_cosets(num, g, r, markings=(), moduli_type=MODULI_ST):654G = single_stratum(num, g, r, markings, moduli_type)655pureG = Graph(G.M)656pureG.purify()657pureG.compute_invariant()658pure_auts = graph_list_isomorphisms(pureG, pureG)659num_pure = len(pure_auts)660impure_auts = graph_list_isomorphisms(G, G)661num_impure = len(impure_auts)662chosen_auts = []663used_auts = []664v = G.num_vertices()665e = G.num_edges()666for i in range(num_pure):667if i not in used_auts:668chosen_auts.append(pure_auts[i])669for g in impure_auts:670sigma = [[pure_auts[i][0][g[0][k] - 1]671for k in range(v)], [pure_auts[i][1][g[1][k] - 1] for k in range(e)]]672for ii in range(num_pure):673if pure_auts[ii] == sigma:674used_auts.append(ii)675break676return [num_impure, chosen_auts]677678679@cached_function680def unpurify_map(g, r, markings=(), moduli_type=MODULI_ST):681r"""682Return a dictionary which maps pure strata to list of strata.683684EXAMPLES::685686sage: from admcycles.DR.graph import unpurify_map687sage: unpurify_map(2, 2)688{(0, 0): [0, 1], (1, 0): [2, 3], (1, 1): [4, 5], (2, 0): [6], (2, 1): [7]}689"""690unpurify = {}691pure_strata = [all_pure_strata(g, r0, markings, moduli_type)692for r0 in range(r + 1)]693impure_strata = all_strata(g, r, markings, moduli_type)694for i, strati in enumerate(impure_strata):695G = Graph(strati.M)696G.purify()697r0 = G.num_edges() - len(markings)698found = False699for j in range(len(pure_strata[r0])):700if G.M == pure_strata[r0][j].M:701G_key = (r0, j)702found = True703break704assert found, "failed purification"705if G_key not in unpurify:706unpurify[G_key] = []707unpurify[G_key].append(i)708return unpurify709710711@cached_function712def all_strata(g, r, markings=(), moduli_type=MODULI_ST):713r"""714Return the lists of strata with given genus, degree, markings and moduli.715716EXAMPLES::717718sage: from admcycles.DR.graph import all_strata719sage: all_strata(2, 1, (1, 1))720[[ -1 1 1]721[X + 2 1 1],722[ -1 1 1]723[ 2 X + 1 1],724[-1 1 1 0]725[ 0 1 1 1]726[ 2 0 0 1],727[-1 1 1 0]728[ 1 0 0 1]729[ 1 1 1 1],730[-1 1 1 0]731[ 1 0 1 1]732[ 1 1 0 1],733[-1 0 1 1]734[ 1 2 1 1]]735"""736mod_size = moduli_type + 1737if moduli_type == MODULI_SMALL:738mod_size = MODULI_SM + 1739big_list = [[] for i in range(mod_size)]740for loops in range(g + 1):741if loops == 1 and moduli_type <= MODULI_CT:742break743if loops > r:744break745for edges in range(r - loops + 1):746if edges == 1 and moduli_type <= MODULI_SM:747break748G = Graph()749G.add_vertex(g - loops)750for k in range(loops):751G.add_edge(1, 1)752for k in markings:753G.add_edge(1, 0, k)754GGG = [[] for i in range(mod_size)]755if loops == 0:756if edges == 0:757GGG[MODULI_SM] = [G]758else:759GGG[MODULI_RT] = [G]760else:761GGG[MODULI_ST] = [G]762for k in range(edges):763GGG = degenerate(GGG, moduli_type)764GGG = decorate(GGG, r - loops - edges, moduli_type)765for i in range(mod_size):766big_list[i] += GGG[i]767combined_list = []768for i in range(mod_size):769combined_list += big_list[i]770return combined_list771772773@cached_function774def all_pure_strata(g, r, markings=(), moduli_type=MODULI_ST):775r"""776Return the lists of pure strata with given genus, degree, markings and moduli.777778EXAMPLES::779780sage: from admcycles.DR.graph import all_pure_strata781sage: from admcycles.DR.moduli import MODULI_CT782sage: all_pure_strata(2, 0, (1, 1))783[[-1 1 1]784[ 2 1 1]]785sage: all_pure_strata(2, 1, (1, 1), MODULI_CT)786[[-1 1 1 0]787[ 0 1 1 1]788[ 2 0 0 1],789[-1 1 1 0]790[ 1 0 0 1]791[ 1 1 1 1],792[-1 1 1 0]793[ 1 0 1 1]794[ 1 1 0 1]]795"""796big_list = [[] for i in range(moduli_type + 1)]797for loops in range(g + 1):798if loops == 1 and moduli_type <= MODULI_CT:799break800if loops > r:801break802for edges in range(r - loops, r - loops + 1):803if edges >= 1 and moduli_type <= MODULI_SM:804break805G = Graph()806G.add_vertex(g - loops)807for k in range(loops):808G.add_edge(1, 1)809for k in markings:810G.add_edge(1, 0, k)811G.compute_invariant()812GGG = [[] for i in range(moduli_type + 1)]813if loops == 0:814if edges == 0:815GGG[MODULI_SM] = [G]816else:817GGG[MODULI_RT] = [G]818else:819GGG[MODULI_ST] = [G]820for k in range(edges):821GGG = degenerate(GGG, moduli_type)822for i in range(moduli_type + 1):823big_list[i] += GGG[i]824combined_list = []825for i in range(moduli_type + 1):826combined_list += big_list[i]827return combined_list828829830@cached_function831def strata_invariant_lookup(g, r, markings=(), moduli_type=MODULI_ST):832inv_dict = {}833L = all_strata(g, r, markings, moduli_type)834for i in range(len(L)):835if L[i].invariant not in inv_dict:836inv_dict[L[i].invariant] = []837inv_dict[L[i].invariant].append(i)838return inv_dict839840841@cached_function842def num_of_stratum(G, g, r, markings=(), moduli_type=MODULI_ST):843r"""844Return the index of the graph ``G`` in the list of all strata with given845genus, rank, markings and moduli type.846847This is the inverse of :func:`single_stratum`.848849EXAMPLES::850851sage: from admcycles.DR.graph import Graph, X, R, num_of_stratum852sage: G = Graph(matrix(R, 2, 3, [-1, 1, 2, X+1, 1, 1]))853sage: num_of_stratum(G, 1, 1, (1, 2))8540855"""856G.compute_invariant()857L = all_strata(g, r, markings, moduli_type)858LL = strata_invariant_lookup(g, r, markings, moduli_type)859x = LL[G.invariant]860861if len(x) == 1:862return x[0]863for i in x:864if graph_isomorphic(G, L[i]):865return i866867raise ValueError("wrong Graph with g={}, r={}, markings={}, moduli_type={}\n{}".format(868g, r, markings, moduli_type, G))869870871def list_strata(g, r, n=0, moduli_type=MODULI_ST):872"""873Displays the list of all strata.874875EXAMPLES::876877sage: from admcycles.DR.graph import list_strata878sage: list_strata(1,1,2)879generator 0880[ -1 1 2]881[X + 1 1 1]882------------------------------883generator 1884[ -1 1 2]885[ 1 X + 1 1]886------------------------------887generator 2888[ -1 1 2]889[ 1 1 X + 1]890------------------------------891generator 3892[-1 1 2 0]893[ 0 1 1 1]894[ 1 0 0 1]895------------------------------896generator 4897[-1 0 1 2]898[ 0 2 1 1]899------------------------------900"""901L = all_strata(g, r, tuple(range(1, n + 1)), moduli_type)902for i, Li in enumerate(L):903print("generator %s" % i)904print(Li.M)905print("-" * 30)906907908@cached_function909def contraction_table(g, r, markings=(), moduli_type=MODULI_ST):910contraction_dict = {}911pure_strata = [all_pure_strata(g, r0, markings, moduli_type)912for r0 in range(r + 1)]913for r0 in range(r + 1):914for ii in range(len(pure_strata[r0])):915G = pure_strata[r0][ii]916S = [j for j in range(1, G.M.ncols()) if G.M[0, j] == 0]917contractions = {}918for edge_subset in itertools.product(*[[0, 1] for i in range(r0)]):919key = tuple(i for i in range(920r0) if edge_subset[i] == 0)921A = [S[i] for i in key]922A.reverse()923vlist = [[i] for i in range(1, G.M.nrows())]924elist = list(range(1, G.M.ncols()))925Gcopy = Graph(G.M)926for i in A:927vlist, elist = Gcopy.contract(i, vlist, elist)928Gcopy.compute_invariant()929rnew = r0 - len(A)930contraction_result = []931for i in range(len(pure_strata[rnew])):932L = graph_list_isomorphisms(933pure_strata[rnew][i], Gcopy, True)934if L:935contraction_result.append((rnew, i))936contraction_result.append(L[0])937break938contraction_result.append((vlist, elist))939contractions[key] = contraction_result940941for edge_assignment in itertools.product(*[[0, 1, 2] for i in range(r0)]):942if sum(1 for i in edge_assignment if i == 1) > r - r0:943continue944key1 = tuple(i for i in range(945r0) if edge_assignment[i] == 0)946B = [S[i]947for i in range(r0) if edge_assignment[i] == 1]948key2 = tuple(i for i in range(949r0) if edge_assignment[i] == 2)950if key1 > key2:951continue952contract1 = contractions[key1]953contract2 = contractions[key2]954dict_key = [contract1[0], contract2[0]]955dict_entry = [contract1[1], contract2[1]]956if dict_key[0] > dict_key[1]:957dict_key.reverse()958dict_entry.reverse()959dict_entry = [(r0, ii), B, contract2[2],960contract1[2]] + dict_entry961else:962dict_entry = [(r0, ii), B, contract1[2],963contract2[2]] + dict_entry964dict_key = tuple(dict_key)965if dict_key not in contraction_dict:966contraction_dict[dict_key] = []967contraction_dict[dict_key].append(dict_entry)968if dict_key[0] == dict_key[1]:969contraction_dict[dict_key].append(970dict_entry[:2] + [dict_entry[3], dict_entry[2], dict_entry[5], dict_entry[4]])971return contraction_dict972973974