unlisted
ubuntu2004from collections import Counter12# pylint does not know sage3from sage.structure.sage_object import SageObject # pylint: disable=import-error4from sage.misc.cachefunc import cached_method # pylint: disable=import-error56import admcycles.diffstrata.additivegenerator7import admcycles.admcycles8910class ELGTautClass (SageObject):11"""12A Tautological class of a stratum X, i.e. a formal sum of of psi classes on13EmbeddedLevelGraphs.1415This is encoded by a list of summands.1617Each summand corresponds to an AdditiveGenerator with coefficient.1819Thus an ELGTautClass is a list with entries tuples (coefficient, AdditiveGenerator).2021These can be added, multiplied and reduced (simplified).2223INPUT :2425* X : GeneralisedStratum that we are on26* psi_list : list of tuples (coefficient, AdditiveGenerator) as27described above.28* reduce=True : call self.reduce() on initialisation29"""3031def __init__(self, X, psi_list, reduce=True):32self._psi_list = psi_list33self._X = X34if reduce:35self.reduce()3637@classmethod38def from_hash_list(cls, X, hash_list):39# This does not reduce!40return cls(X, [(c, X.additive_generator_from_hash(h))41for c, h in hash_list], reduce=False)4243@property44def psi_list(self):45return self._psi_list4647def __repr__(self):48return "ELGTautClass(X=%r,psi_list=%r)"\49% (self._X, self._psi_list)5051def __str__(self):52str = "Tautological class on %s\n" % self._X53for coeff, psi in self._psi_list:54str += "%s * %s + \n" % (coeff, psi)55return str5657def __eq__(self, other):58if isinstance(59other,60admcycles.diffstrata.additivegenerator.AdditiveGenerator):61return self == other.as_taut()62try:63return self._psi_list == other._psi_list64except AttributeError:65return False6667def __add__(self, other):68# for sum, we need to know how to add '0':69if other == 0:70return self71try:72if not self._X == other._X:73return NotImplemented74new_psis = self._psi_list + other._psi_list75return ELGTautClass(self._X, new_psis)76except AttributeError:77return NotImplemented7879def __iadd__(self, other):80return self.__add__(other)8182def __radd__(self, other):83return self.__add__(other)8485def __neg__(self):86return (-1) * self8788def __sub__(self, other):89return self + (-1) * other9091def __mul__(self, other):92if 0 == other:93return 094elif self._X.ONE == other:95return self96# convert AdditiveGenerators to Tautclasses:97if isinstance(98other,99admcycles.diffstrata.additivegenerator.AdditiveGenerator):100return self * other.as_taut()101try:102# check if other is a tautological class103other._psi_list104except AttributeError:105# attempt scalar multiplication:106new_psis = [(coeff * other, psi) for coeff, psi in self._psi_list]107return ELGTautClass(self._X, new_psis, reduce=False)108if not self._X == other._X:109return NotImplemented110else:111return self._X.intersection(self, other)112113def __rmul__(self, other):114return self.__mul__(other)115116def __pow__(self, exponent):117if exponent == 0:118return self._X.ONE119# TODO: quick check for going over top degree?120prod = self121for _ in range(1, exponent):122prod = self * prod123return prod124125@cached_method126def is_equidimensional(self):127"""128Determine if all summands of self have the same degree.129130Note that the empty empty tautological class (ZERO) gives True.131132Returns:133bool: True if all AdditiveGenerators in self.psi_list are of same degree,134False otherwise.135"""136if self.psi_list:137first_deg = self.psi_list[0][1].degree138return all(AG.degree == first_deg for _c, AG in self.psi_list)139return True140141def reduce(self):142"""143Reduce self.psi_list by combining summands with the same AdditiveGenerator144and removing those with coefficient 0 or that die for dimension reasons.145"""146# we use the hash of the AdditiveGenerators to group:147hash_dict = Counter()148for c, AG in self._psi_list:149hash_dict[AG] += c150self._psi_list = [(c, AG) for AG, c in hash_dict.items()151if c != 0 and AG.dim_check()]152153# To evaluate, we go through the AdditiveGenerators and154# take the (weighted) sum of the AdditiveGenerators.155def evaluate(self, quiet=True, warnings_only=False,156admcycles_output=False):157"""158Evaluation of self, i.e. cap with fundamental class of X.159160This is the sum of the evaluation of the AdditiveGenerators in psi_list161(weighted with their coefficients).162163Each AdditiveGenerator is (essentially) the product of its levels,164each level is (essentially) evaluated by admcycles.165166Args:167quiet (bool, optional): No output. Defaults to True.168warnings_only (bool, optional): Only warnings output. Defaults to False.169admcycles_output (bool, optional): admcycles debugging output. Defaults to False.170171Returns:172QQ: integral of self on X173174EXAMPLES::175176sage: from admcycles.diffstrata import *177sage: X=GeneralisedStratum([Signature((0,0))])178sage: assert (X.xi^2).evaluate() == 0179180sage: X=GeneralisedStratum([Signature((1,1,1,1,-6))])181sage: assert set([(X.cnb(((i,),0),((i,),0))).evaluate() for i in range(len(X.bics))]) == {-2, -1}182"""183if warnings_only:184quiet = True185DS_list = []186for c, AG in self.psi_list:187value = AG.evaluate(188quiet=quiet,189warnings_only=warnings_only,190admcycles_output=admcycles_output)191DS_list.append(c * value)192if not quiet:193print("----------------------------------------------------")194print("In summary: We sum")195for i, summand in enumerate(DS_list):196print("Contribution %r from AdditiveGenerator" % summand)197print(self.psi_list[i][1])198print("(With coefficient %r)" % self.psi_list[i][0])199print("To obtain a total of %r" % sum(DS_list))200print("----------------------------------------------------")201return sum(DS_list)202203def extract(self, i):204"""205Return the i-th component of self.206207Args:208i (int): index of self._psi_list209210Returns:211ELGTautClass: coefficient * AdditiveGenerator at position i of self.212"""213return ELGTautClass(self._X, [self._psi_list[i]], reduce=False)214215@cached_method216def degree(self, d):217"""218The degree d part of self.219220Args:221d (int): degree222223Returns:224ELGTautClass: degree d part of self225"""226new_psis = []227for c, AG in self.psi_list:228if AG.degree == d:229new_psis.append((c, AG))230return ELGTautClass(self._X, new_psis, reduce=False)231232@cached_method233def list_by_degree(self):234"""235A list of length X.dim with the degree d part as item d236237Returns:238list: list of ELGTautClasses with entry i of degree i.239"""240deg_psi_list = [[] for _ in range(self._X.dim() + 1)]241for c, AG in self.psi_list:242deg_psi_list[AG.degree].append((c, AG))243return [ELGTautClass(self._X, piece, reduce=False)244for piece in deg_psi_list]245246def is_pure_psi(self):247"""248Check if self is ZERO or a psi-product on the stratum.249250Returns:251boolean: True if self has at most one summand and that is of the form252AdditiveGenerator(((), 0), psis).253254EXAMPLES::255256sage: from admcycles.diffstrata import *257sage: X=Stratum((2,))258sage: X.ZERO.is_pure_psi()259True260sage: X.ONE.is_pure_psi()261True262sage: X.psi(1).is_pure_psi()263True264sage: X.xi.is_pure_psi()265False266"""267if not self.psi_list:268return True269return len(270self.psi_list) == 1 and self.psi_list[0][1].enh_profile == ((), 0)271272def to_prodtautclass(self):273"""274Transforms self into an admcycles prodtautclass on the stable graph of the smooth275graph of self._X.276277Note that this is essentially the pushforward to M_g,n, i.e. we resolve residues278and multiply with the correct Strataclasses along the way.279280Returns:281prodtautclass: admcycles prodtautclass corresponding to self pushed forward282to the stable graph with one vertex.283284EXAMPLES::285286sage: from admcycles.diffstrata import *287sage: X=Stratum((2,))288sage: X.ONE.to_prodtautclass()289Outer graph : [2] [[1]] []290Vertex 0 :291Graph : [2] [[1]] []292Polynomial : -7/24*(kappa_1)_0 + 79/24*psi_1293<BLANKLINE>294<BLANKLINE>295Vertex 0 :296Graph : [1] [[1, 2, 3]] [(2, 3)]297Polynomial : -1/48298<BLANKLINE>299<BLANKLINE>300Vertex 0 :301Graph : [1, 1] [[2], [1, 3]] [(2, 3)]302Polynomial : -19/24303sage: (X.xi^X.dim()).evaluate() == (X.xi^X.dim()).to_prodtautclass().pushforward().evaluate()304True305"""306G = self._X.smooth_LG307stgraph = G.LG.stgraph308total = admcycles.admcycles.prodtautclass(stgraph, terms=[])309for c, AG in self.psi_list:310ptc = AG.to_prodtautclass()311# sort vertices by connected component:312vertex_map = {}313# note that every vertex of G has at least one leg (that is a314# marked point of _X):315for v, _ in enumerate(G.LG.genera):316mp_on_stratum = G.dmp[G.LG.legs[v][0]]317# find this marked point on AG:318l_AG = AG._G.dmp_inv[mp_on_stratum]319# get the corresponding vertex320LG = AG._G.LG321v_AG = LG.vertex(l_AG)322# we use the underlying graph:323UG_v = LG.UG_vertex(v_AG)324for w, g, kind in LG.UG_without_infinity().connected_component_containing_vertex(325UG_v):326if kind != 'LG':327continue328vertex_map[w] = v329# map legs of AG._G to smooth_LG330# CAREFUL: This goes in the OTHER direction!331leg_map = {G.dmp_inv[mp]: ldeg for ldeg, mp in AG._G.dmp.items()}332pf = ptc.partial_pushforward(stgraph, vertex_map, leg_map)333total += c * pf334return total335336337