Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
181 views
unlisted
ubuntu2004
a

��c^��@s�dZddlZddlmZddlmZddlmZddlm	Z	m
Z
ddlmZm
Z
ddlmZdd	lmZdd
lmZddlmZddlmZdd
lmZmZmZddlmZddlmZddl m!Z!ddl"m#Z#ddl$m%Z%ddl&m'Z'ddl(m)Z)ddl*m+Z+ddl,m-Z-ddl.m/Z/ddl0m1Z1m2Z2m3Z3m4Z4m5Z5ddl6m7Z7ddl8m9Z9ddl:m;Z;e��<�Z=Gdd�de�Z>Gdd �d ee!�Z?Gd!d"�d"e�Z@dS)#zL
Tautological subring of the cohomology ring of the moduli space of curves.
�N)�
cached_method)�prod)�UniqueRepresentation)�op_EQ�op_NE)�
ModuleElement�parent)�coercion_model)�Functor)�ConstructionFunctor)�Algebras)�Rings)�	factorial�	bernoulli�multinomial)�
Partitions)�IntegerVectors)�Algebra��ZZ)�QQ��vector)�PolynomialRing)�	TermOrder��WeightedIntegerVectors)�matrix�)�_moduli_to_str�_str_to_moduli�	MODULI_TL�
get_moduli�socle_degree)�
decstratum)�StableGraph)�identify_classc@sxeZdZdZdWdd�Zdd�Zdd�Zd	d
�Zdd�Zd
d�Z	dd�Z
dd�Zdd�Zdd�Z
dd�Zdd�Zdd�ZdXdd�ZdYd d!�ZdZd"d#�Zd[d$d%�Zd&d'�Zd(d)�Zd*d+�Zd,d-�Zd.d/�Zd\d0d1�Zd2d3�Zd4d5�Zd6d7�Zd]d8d9�Zd:d;�Zd^d<d=�Z d_d>d?�Z!d`d@dA�Z"dBdC�Z#dadDdE�Z$dbdFdG�Z%dHdI�Z&dcdJdK�Z'dddLdM�Z(dedNdO�Z)dfdQdR�Z*dSdT�Z+dUdV�Z,dS)g�TautologicalClassaB
    An element of a tautological ring.

    Internally, it is represented by a list ``terms`` of objects of type
    :class:`admcycles.admcycles.decstratum`. Such element should never
    be constructed directly by calling :class:`TautologicalClass`. Instead
    use the parent class :class:`TautologicalRing` or dedicated functions
    as in the examples below

    EXAMPLES::

        sage: from admcycles import *
        sage: R = TautologicalRing(3,1)
        sage: gamma = StableGraph([1,2],[[1,2],[3]],[(2,3)])
        sage: ds1 = R(gamma, kappa=[[1],[]])
        sage: ds1
        Graph :      [1, 2] [[1, 2], [3]] [(2, 3)]
        Polynomial : (kappa_1)_0
        sage: ds2 = R(gamma, kappa=[[],[1]])
        sage: ds2
        Graph :      [1, 2] [[1, 2], [3]] [(2, 3)]
        Polynomial : (kappa_1)_1
        sage: t = ds1 + ds2
        sage: (t - R(gamma) * R.kappa(1)).is_zero()
        True

    Constructing a tautological class from dedicated functions::

        sage: psiclass(1, 2, 1)  # psi_1 on M_{2,1}
        Graph :      [2] [[1]] []
        Polynomial : psi_1
    TcCsXt�||�t|ttf�r�i|_|D]�}t|t�s6t�|j�	�rHt
d��|j�|j�rXq$|rp|�
|j�|��|j|jvr�|j|jj|j7_|j|js�|j|j=q$||j|j<q$n�t|t��rP|�rHi|_|��D]h\}}||jkr�t
�|�|j��rq�|�
|j�|��|r�||jv�r:|j||7<q�||j|<q�n||_nt�dS)a
        INPUT:

        - parent : a class:`TautologicalRing`
        - terms : a list of class:`~admcycles.admcycles.decstratum`
        - clean: boolean (default ``True``)
          whether to apply ``dimension_filter`` and ``consolidate`` on the input
        zDmutable stable graph in a decstratum when building TautologicalClassN)r�__init__�
isinstance�tuple�list�_termsr$�	TypeError�gamma�
is_mutable�
ValueError�vanishes�_moduli�dimension_filter�consolidate�poly�dict�items)�selfr�terms�clean�tr.�term�r=�?/home/user/Introduction lectures/admcycles/tautological_ring.pyr(NsD	


zTautologicalClass.__init__cCs$|��}|�|dd�|j��D��S)NcSsi|]\}}||���qSr=��copy��.0�gr;r=r=r>�
<dictcomp>��z*TautologicalClass.copy.<locals>.<dictcomp>)r�
element_classr,r7�r8�Pr=r=r>r@szTautologicalClass.copycCs4t|j�D]$}|j|��|j|s
|j|=q
dS�N)r+r,r3�r8rCr=r=r>r3�s
z"TautologicalClass.dimension_filtercs(�js
dSd��fdd�t�j�D��S)N�0z

c3s|]}t�j|�VqdSrI)�reprr,�rBrC�r8r=r>�	<genexpr>�rEz+TautologicalClass._repr_.<locals>.<genexpr>)r,�join�sortedrNr=rNr>�_repr_�szTautologicalClass._repr_cs>ddlm}m}�js|d�St�fdd�t�j�D�|��S)uU
        Return unicode art for the tautological class.

        EXAMPLES::

            sage: from admcycles import *
            sage: D = DR_cycle(1,(2,-2))
            sage: unicode_art(D)
            Graph :
            <BLANKLINE>
            ╭──╮
            │1 │
            ╰┬┬╯
             12
            <BLANKLINE>
            Polynomial : 2*ψ₁ + 2*ψ₂
            Graph :
             ╭╮
             45
            ╭┴┴╮
            │0 │
            ╰┬┬╯
             12
            <BLANKLINE>
            Polynomial : -1/24
        r)�unicode_art�
UnicodeArtrKc3s|]}�j|��VqdSrI)r,�
_unicode_art_rMrNr=r>rO�rEz2TautologicalClass._unicode_art_.<locals>.<genexpr>)�sage.typeset.unicode_artrSrTr,rrQ)r8rSrTr=rNr>rU�szTautologicalClass._unicode_art_cCs|jSrI)r,rNr=r=r>�is_empty�szTautologicalClass.is_emptycCs|����S)a,
        Return whether this element is nilpotent.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(1, 1)
            sage: R.one().is_nilpotent()
            False
            sage: R.psi(1).is_nilpotent()
            True
        )�constant_coefficient�is_nilpotentrNr=r=r>rY�s
zTautologicalClass.is_nilpotentcCs|����S)a
        Return whether this element is a unit.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(1, 1)
            sage: R.one().is_unit()
            True
            sage: R.psi(1).is_unit()
            False
        )rX�is_unitrNr=r=r>rZ�s
zTautologicalClass.is_unitcCs8zddlm}Wnty.ddlm}Yn0||�S)aH
        Return the inverse of this element.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(0, 5)
            sage: (R.one() + R.psi(1)).inverse_of_unit()
            Graph :      [0] [[1, 2, 3, 4, 5]] []
            Polynomial : 1 - psi_1 + psi_1^2
        r)�inverse_of_unitr)Zsage.rings.polynomial.miscr[�ImportError�misc)r8r[r=r=r>r[�s
z!TautologicalClass.inverse_of_unitcCsB|��}i}|j��D]\}}|�|�}|r|||<q|�||�S)aL
        Return the homogeneous component of degree ``d``.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(1, 2)
            sage: f = (1 - 2 * R.psi(1) + 3 * R.psi(2)**2)**2
            sage: f.degree_part(0)
            Graph :      [1] [[1, 2]] []
            Polynomial : 1
            sage: f.degree_part(1)
            Graph :      [1] [[1, 2]] []
            Polynomial : -4*psi_1
            sage: f.degree_part(2)
            Graph :      [1] [[1, 2]] []
            Polynomial : 6*psi_2^2 + 4*psi_1^2
        )rr,r7�degree_partrF)r8�drH�	new_termsrCr<r=r=r>r^�s

zTautologicalClass.degree_partcCs|��S)a�
        Return the coefficient in degree zero as an element in the base ring.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(2, 1)
            sage: (1/3 - R.psi(1) + R.kappa(2)).constant_coefficient()
            1/3

            sage: A.<x, y> = QQ[]
            sage: R = TautologicalRing(2, 1, base_ring=A)
            sage: f = (1 - x * R.psi(1)) * (1/3 + y * R.kappa(2))
            sage: f.constant_coefficient()
            1/3

        TESTS::

            sage: from admcycles import TautologicalRing, StableGraph
            sage: R = TautologicalRing(3,2)
            sage: Gamma = StableGraph([3],[[2,1]],[]); Gamma
            [3] [[2, 1]] []
            sage: a = R.one() + R(Gamma); a
            Graph :      [3] [[1, 2]] []
            Polynomial : 1
            <BLANKLINE>
            Graph :      [3] [[2, 1]] []
            Polynomial : 1
            sage: a.constant_coefficient()
            2
        )�
fund_evaluaterNr=r=r>rXs z&TautologicalClass.constant_coefficientcCsB|��}i}|j��D]\}}|�|�}|r|||<q|�||�S)a~
        Return the class where we drop components of degree above ``dmax``.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(1, 2)
            sage: f = (1 - 2 * R.psi(1) + 3 * R.psi(2)**2)**2
            sage: f.degree_cap(1)
            Graph :      [1] [[1, 2]] []
            Polynomial : 1 - 4*psi_1
        )rr,r7�
degree_caprF)r8�dmaxrHr`rCr<r=r=r>rb#s


zTautologicalClass.degree_capc
Os�i}|j��D]�\}}|jdd�}|jj}|jj}d}|t|�kr�||j|i|��||<||sx|�|�|�|�q6|d7}q6|r|||<q|�	�}	|	�
|	|�S)a�
        Perform substitution on coefficients.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: A.<a1, a2> = PolynomialRing(QQ,2)
            sage: R = TautologicalRing(2, 2, base_ring=A)
            sage: t = a1 * R.psi(1) + a2 * R.psi(2)
            sage: t
            Graph :      [2] [[1, 2]] []
            Polynomial : a1*psi_1 + a2*psi_2
            sage: t.subs({a2: 1 - a1})
            Graph :      [2] [[1, 2]] []
            Polynomial : a1*psi_1 + (-a1 + 1)*psi_2
            sage: t
            Graph :      [2] [[1, 2]] []
            Polynomial : a1*psi_1 + a2*psi_2

            sage: t = (a1 - a2) * R.psi(1) + (a1 + a2) * R.psi(2)
            sage: t.subs({a1: a2})
            Graph :      [2] [[1, 2]] []
            Polynomial : 2*a2*psi_2
            sage: t.subs({a1: 0, a2: 0})
            0
            sage: t
            Graph :      [2] [[1, 2]] []
            Polynomial : (a1 - a2)*psi_1 + (a1 + a2)*psi_2
        F��mutablerr)r,r7r@r5�coeff�monom�len�subs�poprrF)
r8�args�kwdsr`rCr<�coeffsrg�irHr=r=r>ri8s 


zTautologicalClass.subsNcCsb|��r|S|��}|dur.|�|�|�|�S|��}|��D]}||�|�|�|�7}q>|SdS)aa
        Return representation of self as a tautclass formed by a linear combination of
        the preferred tautological basis.

        If ``r`` is given, only take degree r part.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(0, 4)
            sage: t = 7 + R.psi(1) + 3 * R.psi(2) - R.psi(3)
            sage: t.FZsimplify()
            Graph :      [0] [[1, 2, 3, 4]] []
            Polynomial : 7 + 3*(kappa_1)_0

            sage: t.FZsimplify(r=0)
            Graph :      [0] [[1, 2, 3, 4]] []
            Polynomial : 7
        N)rWr�from_basis_vector�basis_vector�zero�degree_list)r8�r�R�resultr=r=r>�
FZsimplifyjszTautologicalClass.FZsimplifycCs�|��}|dus|durjddlm}|dd�|dur@||jksR|durj||jkrjtd�|||j|j���ddlm}||�	�dd�|j
��D��S)	Nr��deprecation�mzNthe arguments 'g' and 'n' of TautologicalClass.toprodtautclass are deprecated.z.invalid (g,n) (got ({},{}) instead of ({},{})))�
prodtautclasscSsg|]}|��g�qSr=r?�rBr;r=r=r>�
<listcomp>�rEz5TautologicalClass.toprodtautclass.<locals>.<listcomp>)r�
supersededrx�_g�_nr0�format�	admcyclesrz�
trivial_graphr,�values)r8rC�nrHrxrzr=r=r>�toprodtautclass�s
$z!TautologicalClass.toprodtautclassc	Cs�|��}|dus |dus |dur6ddlm}|dd�|durF|g}n|��}|��r^|��S|��}|D]}||�|�|�|�7}qj|j	|_	|S)a�
        Simplifies self by combining terms with same tautological generator, returns self.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(2, 1)
            sage: t = R.psi(1) + 11*R.psi(1)
            sage: t
            Graph :      [2] [[1]] []
            Polynomial : 12*psi_1
            sage: t.simplify()
            Graph :      [2] [[1]] []
            Polynomial : 12*psi_1
        NrrwryzFthe arguments 'g, n, r'  of TautologicalClass.simplify are deprecated.)
rr}rxrrrWr@rq�from_vectorrr,)	r8rCr�rsrHrx�Drur_r=r=r>�simplify�s
zTautologicalClass.simplifycCs�|��}|dus|durjddlm}|dd�|dur@||jksR|durj||jkrjtd�|||j|j���|��rz|��S|dur�|�	|�
|�|�S|��|S)zj
        Return a simplified version of self by combining terms with same tautological generator.
        NrrwryzIthe arguments 'g' and 'n' of TautologicalClass.simplified are deprecated.z,invalid g,n (got ({},{}) instead of ({},{})))rr}rxr~rr0r�rWr@r�rr�)r8rCr�rsrHrxr=r=r>�
simplified�s
$zTautologicalClass.simplifiedcCs8|��r|��S|��}|j|dd�|j��D�dd�S)NcSsi|]\}}||�qSr=r=�rBrCr<r=r=r>rD�rEz-TautologicalClass.__neg__.<locals>.<dictcomp>F�r:)rWr@rrFr,r7rGr=r=r>�__neg__�szTautologicalClass.__neg__cCs�|��r|��S|��r |��S|��}dd�|j��D�}|j��D]<\}}||vrz||j|j7_||s�||=qF|||<qF|j||dd�S)a8
        TESTS::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(1, 1)
            sage: R(1) + 1
            Graph :      [1] [[1]] []
            Polynomial : 2
            sage: 1 + R(1)
            Graph :      [1] [[1]] []
            Polynomial : 2
        cSsi|]\}}||���qSr=r?rAr=r=r>rD�rEz+TautologicalClass._add_.<locals>.<dictcomp>Fr�)rWr@rr,r7r5rF)r8�otherrHr`rCr<r=r=r>�_add_�s

zTautologicalClass._add_csd|��}t��|��usJ����r,|��S���r<|��S�fdd�|j��D�}|j||dd�S)a�
        Scalar multiplication by ``other``.

        TESTS::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(2, 2)
            sage: -3/5 * R.psi(1)
            Graph :      [2] [[1, 2]] []
            Polynomial : -3/5*psi_1
            sage: 0 * R.psi(1) == 1 * R.zero() == R.zero()
            True
            sage: assert (0 * R.psi(1)).parent() is R
            sage: assert (1 * R.zero()).parent() is R
        csi|]\}}|�|�qSr=r=r��r�r=r>rDrEz,TautologicalClass._lmul_.<locals>.<dictcomp>Fr�)	r�	base_ring�is_zerorq�is_oner@r,r7rF)r8r�rHr`r=r�r>�_lmul_�szTautologicalClass._lmul_c
CsN|��s|��r|����S|��}|jr�|jd|jkr�dd�t|j�D�}dd�t|j�D�}|j|dd�|j|dd�j|dd�Si}|j��D]�}|j��D]v}|�	||�j�
�D]^\}}	|�|j�r�q�|	j
|jd�|	jjdd�|	r�||v�r||j|	j7_q�|	||<q�q�q�t|�D]}||�s&||=�q&|j||dd	�S)
a�
        TESTS::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(2, 2)
            sage: R.psi(1) * R.psi(2)
            Graph :      [2] [[1, 2]] []
            Polynomial : psi_1*psi_2

            sage: R.generators(1)[3] * R.generators(2)[23]
            Graph :      [0, 1, 1] [[1, 2, 4], [5, 6], [7]] [(4, 5), (6, 7)]
            Polynomial : (kappa_1)_2

            sage: (R.zero() * R.one()) == (R.one() * R.zero()) == R.zero()
            True
            sage: R.one() * R.one() == R.one()
            True

        Multiplication with non-standard markings::

            sage: from admcycles import StableGraph
            sage: R2 = TautologicalRing(1, [4, 7])
            sage: g2 = StableGraph([0], [[1,2,4,7]], [(1,2)])
            sage: R2(g2) * (1 + R2.psi(4)) * (1 + R2.psi(7))
            Graph :      [0] [[1, 2, 4, 7]] [(1, 2)]
            Polynomial : 1 + psi_7 + psi_4
        �����cSsi|]\}}||d�qS�rr=�rBrn�jr=r=r>rD,rEz+TautologicalClass._mul_.<locals>.<dictcomp>cSsi|]\}}|d|�qSr�r=r�r=r=r>rD-rEF��inplace��moduli)�forcer�)rWrrq�	_markingsr�	enumerate�rename_legsr,r��multiplyr7r1r2r3r5r4r+rF)
r8r�rHZ
dic_to_stdZdic_from_stdr`�t1�t2rCr<r=r=r>�_mul_s.&


zTautologicalClass._mul_cCsz|�d�rtd��|��}|��}|��}|dkr6|S||}|}td|d�D]$}||}|tdt|�f�|7}qP|S)a�
        Return the exponential of this tautological class.

        The exponential is defined as ``exp(x) = 1 + x + 1/2*x^2 + ... ``.
        It is well defined as long as ``x`` has no degree zero term.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(1, 2)
            sage: R.psi(1).exp()
            Graph :      [1] [[1, 2]] []
            Polynomial : 1 + psi_1 + 1/2*psi_1^2

        TESTS::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(0, 3)
            sage: R.zero().exp() == R.one()
            True
        rznon-zero degree zero term�r)rr0r�oner#�rangerr)r8rH�frcru�y�kr=r=r>�expBs
zTautologicalClass.expcs�|��}�durt|j��dkr*td��|jt��kr\t|j|j|���d�}||��	�St
�fdd�|j��D�|���
��S)a�
        Computes integral against the fundamental class of the corresponding moduli space,
        e.g. the degree of the zero-cycle part of the tautological class (for moduli='st').

        For non-standard moduli, this computes the socle-evaluation, which is given by
        the integral against

            * lambda_g for moduli='ct'
            * lambda_g * lambda_{g-1} for moduli='rt' (and moduli='sm' for n=0)

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(1, 1)
            sage: R.kappa(1).evaluate()
            1/24

        Some non-standard moduli::

            sage: from admcycles import *
            sage: R = TautologicalRing(2,1,moduli='ct')
            sage: R.kappa(2).evaluate()
            7/5760
            sage: (kappaclass(2,2,1)*lambdaclass(2,2,1)).evaluate()
            7/5760
            sage: R.kappa(2).evaluate(moduli='st') # wrong degree
            0

        TESTS::

            sage: R = TautologicalRing(2,1,moduli='ct')
            sage: Rst = TautologicalRing(2,1)
            sage: L = lambdaclass(2,2,1)
            sage: all((Rst(t)*L).evaluate() == t.evaluate() for t in R.generators(2))
            True
            sage: R = TautologicalRing(2,2,moduli='rt')
            sage: Rst = TautologicalRing(2,2)
            sage: LL = Rst.lambdaclass(2)*Rst.lambdaclass(1)
            sage: all((Rst(t)*LL).evaluate() == t.evaluate() for t in R.generators(2))
            True
        N�tlz=no well-defined socle evaluation for space of treelike curves)r�r�c3s|]}|j�d�VqdS)r�N��evaluater{r�r=r>rO�rEz-TautologicalClass.evaluate.<locals>.<genexpr>)rrr2r0r"�TautologicalRingr~r�r�r��sumr,r�rq�r8r�rHrtr=r�r>r�gs*
zTautologicalClass.evaluatecCs|�d�dS)a�
        Computes degree zero part of the class as multiple of the fundamental class.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(2, 1)
            sage: t = R.psi(1)
            sage: s = t.forgetful_pushforward([1])
            sage: s
            Graph :      [2] [[]] []
            Polynomial : 2
            sage: s.fund_evaluate()
            2
        rrrNr=r=r>ra�szTautologicalClass.fund_evaluatecs^|tkr|tkrtd��t����}|�����t|�}t��fdd�|D��}||tkkS)a�
        Implementation of comparisons (``==`` and ``!=``).

        TESTS::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(2,2)
            sage: R.zero() == R.zero()
            True
            sage: R.zero() == R.psi(1)
            False
            sage: R.psi(1) == R.zero()
            False
            sage: R.psi(1) == R.psi(1)
            True
            sage: R.psi(1) == R.psi(2)
            False

            sage: R.zero() != R.zero()
            False
            sage: R.zero() != R.psi(1)
            True
            sage: R.psi(1) != R.zero()
            True
            sage: R.psi(1) != R.psi(1)
            False
            sage: R.psi(1) != R.psi(2)
            True
            sage: R = TautologicalRing(1,1)
            sage: A = R(1) + R.psi(1) - R.kappa(1)
            sage: A == R.zero()
            False
            sage: A == R.one()
            True
        Zincomparablec3s6|].}��|���|�kp,��|���|�kVqdSrI)rrp�rBr_�r�r8r=r>rO�rEz.TautologicalClass._richcmp_.<locals>.<genexpr>)rrr-�setrr�updaterQ�all)r8r��opr��equalr=r�r>�	_richcmp_�s$zTautologicalClass._richcmp_cst�fdd����D��S)z�
        TESTS::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(1, 1)
            sage: bool(R.psi(1))
            True
            sage: bool(R.zero())
            False
        c3s*|]"}��|���p ��|���VqdSrI)rr�rpr�rNr=r>rO�rEz-TautologicalClass.__bool__.<locals>.<genexpr>)r�rrrNr=rNr>�__bool__�szTautologicalClass.__bool__cCsn|��}t||j�}||jkr8td�t|t|j���||jkrdt|j|j||�	�d�}||�S|SdS)a�
        Return whether this class is a known tautological relation (using
        Pixton's implementation of the generalized Faber-Zagier relations).

        If optional argument `moduli` is given, it checks the vanishing on
        an open subset of the current moduli of stable curves.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(3, 0)
            sage: diff = R.kappa(1) - 12 * R.lambdaclass(1)
            sage: diff.is_zero()
            False

            sage: S = TautologicalRing(3, 0, moduli='sm')
            sage: S(diff).is_zero()
            True
            sage: diff.is_zero(moduli='sm')
            True
            sage: S(diff).is_zero(moduli='st')
            Traceback (most recent call last):
            ...
            ValueError: moduli='st' is larger than the moduli 'sm' of the parent
        �8moduli={!r} is larger than the moduli {!r} of the parent�r�N)
rr"r2r0r�rr�r~r�r�r�r=r=r>r��s
�

zTautologicalClass.is_zerocCsttdd�|j��D���S)aK
        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(2, 3)
            sage: R.psi(1).degree_list()
            [1]
            sage: (1 + R.psi(1)**2).degree_list()
            [0, 2]
            sage: ((1 + R.psi(1))**2).degree_list()
            [0, 1, 2]
        css&|]}|��D]\}}}|VqqdSrI)�gnr_list)rBr;rCr�r_r=r=r>rOrEz0TautologicalClass.degree_list.<locals>.<genexpr>)rQr�r,r�rNr=r=r>rrs
zTautologicalClass.degree_listcs�|��}�dur|St�tj�r:t��}|dkr�td��nPt�ttf�r�tt���}t	��t	t
|jd|j|d��kr�td��|���|dkr�|St
|j|j||j|��d��t��fdd�|j��D�����S)	a�
        Return the pullback of this tautological class under a forgetful map
        `\bar M_{g, n'} --> \bar M_{g,n}`

        INPUT:

        forget : list of integers
          legs that are forgotten by the map forgetful map

        EXAMPLES::

            sage: from admcycles import TautologicalRing

            sage: R = TautologicalRing(1, 2)
            sage: b = R.psi(2).forgetful_pullback([3])
            sage: b
            Graph :      [1] [[1, 2, 3]] []
            Polynomial : psi_2
            <BLANKLINE>
            Graph :      [1, 0] [[1, 4], [2, 3, 5]] [(4, 5)]
            Polynomial : -1
            sage: b.parent()
            TautologicalRing(g=1, n=3, moduli='st') over Rational Field
        Nrzforget can only be non-negativerz=can only forget the highest markings, got forget={} inside {}�r�r�c3s |]}���|����VqdSrI)rFZforgetful_pullback_list)rB�c�rt�forgetr=r>rOIrEz7TautologicalClass.forgetful_pullback.<locals>.<genexpr>)rr)�numbers�Integralrr0r*r+rhr�r�rr�r�r~r2r�r�r,r�rq)r8r�rHr�r=r�r>�forgetful_pullback s
&z$TautologicalClass.forgetful_pullbackcsF|��}�dur|��St�tj�rdt��}|dks>||jkrFtd��|dkrR|S|j|d��n�t�t	t
f�r�tt���}t���t
t��d�D]*}�|�|dkr�td��|���q��D]}||jvr�td�|���q�ntd��|dkr�|S�fdd	�|jD�}t|j||j|��d
�}|�|�fdd	�|j��D��S)a�
        Return the pushforward of a given tautological class under a forgetful
        map `\bar M_{g,n} \to \bar M_{g,n'}`.

        INPUT:

        forget : list of integers
          legs that are forgotten by the map forgetful map

        EXAMPLES::

            sage: from admcycles import TautologicalRing

            sage: R = TautologicalRing(1, 3)
            sage: s1 = R.psi(3)^2
            sage: s1.forgetful_pushforward([3])
            Graph :      [1] [[1, 2]] []
            Polynomial : (kappa_1)_0
            sage: s1.forgetful_pushforward([2,3])
            Graph :      [1] [[1]] []
            Polynomial : 1

            sage: R = TautologicalRing(1, [2, 5, 6])
            sage: s1 = R.psi(5)^2
            sage: s1.forgetful_pushforward([5])
            Graph :      [1] [[2, 6]] []
            Polynomial : (kappa_1)_0
            sage: s1.forgetful_pushforward([5]).parent()
            TautologicalRing(g=1, n=(2, 6), moduli='st') over Rational Field

            sage: R = TautologicalRing(2, 2)
            sage: gens1 = R.generators(1)
            sage: t = gens1[1] + 2*gens1[3]
            sage: t.forgetful_pushforward([2])
            Graph :      [2] [[1]] []
            Polynomial : 3
            sage: t.forgetful_pushforward([2]).parent()
            TautologicalRing(g=2, n=1, moduli='st') over Rational Field

        TESTS::

            sage: from admcycles import TautologicalRing
            sage: TautologicalRing(1, [2, 4]).psi(4).forgetful_pushforward([5])
            Traceback (most recent call last):
            ...
            ValueError: invalid marking 5 in forget
            sage: TautologicalRing(1, [2, 4]).psi(4).forgetful_pushforward(2)
            Traceback (most recent call last):
            ...
            ValueError: unstable pair (g,n) = (1, 0)
            sage: TautologicalRing(1, [2, 4]).psi(4).forgetful_pushforward(3)
            Traceback (most recent call last):
            ...
            ValueError: forget must be between 0 and the number of markings
        Nrz3forget must be between 0 and the number of markingsrzmultiple marking {} in forgetzinvalid marking {} in forgetzinvalid input forgetcsg|]}|�vr|�qSr=r=�rBrn�r�r=r>r|�rEz;TautologicalClass.forgetful_pushforward.<locals>.<listcomp>r�csg|]}|����qSr=)�forgetful_pushforward�rBr<r�r=r>r|�rE)rr@r)r�r�rrr0r�r*r+rhrQr�r�r-r�r~r2r�rFr,r�)r8r�rHr�rn�new_markingsrtr=r�r>r�Ks28
z'TautologicalClass.forgetful_pushforwardcs�|durddlm}|dd�|��}�fdd�|jD�}tt|����}||jkr�|rjtd�|j|���t	|j
||j|��d	�}n|}|r�t
|j���}	|j��|	D]}
|
j�d
d�|
|j|
j<q�|S|�fdd
�|j��D��SdS)a�
        Rename the legs according to the dictionary ``dic``.

        EXAMPLES::

            sage: from admcycles import TautologicalRing

            sage: R = TautologicalRing(1, 2)
            sage: cl = R.psi(1) * R.psi(2)
            sage: cl.rename_legs({2:5}, inplace=False)
            Graph :      [1] [[1, 5]] []
            Polynomial : psi_1*psi_5
            sage: parent(cl.rename_legs({2:5}, inplace=False))
            TautologicalRing(g=1, n=(1, 5), moduli='st') over Rational Field

        Note that inplace operation is forbidden if the relabellings of the markings
        is not a bijection::

            sage: _ = cl.rename_legs({2:5}, inplace=True)
            Traceback (most recent call last):
            ...
            ValueError: invalid inplace operation: change of markings (1, 2) -> (1, 5)

        TESTS::

            sage: from admcycles import *
            sage: G1 = StableGraph([1,1],[[1,2,4],[3,5]],[(4,5)])
            sage: A = G1.to_tautological_class()
            sage: _ = A.rename_legs({1:3,3:1})
            sage: list(A._terms)
            [[1, 1] [[2, 3, 4], [1, 5]] [(4, 5)]]
            sage: for g, t in A._terms.items():
            ....:     assert g == t.gamma
        NrrwryzYthe rename keyword in TautologicalClass.rename_legs is deprecated. Please set rename=Nonecsi|]}|��||��qSr=)�getr���dicr=r>rD�rEz1TautologicalClass.rename_legs.<locals>.<dictcomp>z6invalid inplace operation: change of markings {} -> {}r�Tr�csg|]}|j�dd��qS)Fr�)r�r{r�r=r>r|�rEz1TautologicalClass.rename_legs.<locals>.<listcomp>)r}rxrr�r*rQr�r0r�r�r~r2r�r+r,�clearr�r.)r8r��renamer�rxrHZ	clean_dicr�rt�lr;r=r�r>r��s*#

�
zTautologicalClass.rename_legscCs|j|��dd�S)a�
        Applies the permutation ``g`` to the markings.

        INPUT:

         g: permutation in the symmetric group `S_n`

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(1, 2)
            sage: G = SymmetricGroup(2)
            sage: g = G('(1,2)')
            sage: R.psi(1).permutation_action(g) == R.psi(2)
            True
        Fr�)r�r6rJr=r=r>�permutation_action�sz$TautologicalClass.permutation_actioncCsV|js
dS|��j}|dur0ddlm}||�}|��D]}|�|�|kr8dSq8dS)a}
        Return whether this tautological class is symmetric under the action of G.

        INPUT:

        G: (optional) subgroup of symmetric group
        if no argument is given take Sn

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(1, 2)
            sage: (R.psi(1) * R.psi(2)).is_symmetric()
            True
            sage: R.psi(1).is_symmetric()  # psi(1) = psi(2) in H^*(M_{1,2})
            True

            sage: R = TautologicalRing(2, 2)
            sage: R.psi(1).is_symmetric()
            False

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(2, 3)
            sage: G = PermutationGroup([(1,2)])
            sage: a = R.psi(1) + R.psi(2)
            sage: a.is_symmetric(G)
            True
            sage: a.is_symmetric()
            False
        TNr��SymmetricGroupF)r,rr�$sage.groups.perm_gps.permgroup_namedr��gensr�)r8�GZnum_legr�rCr=r=r>�is_symmetric�s
zTautologicalClass.is_symmetriccsX���}|��}�js|S|dur8ddlm}||j�}|��fdd�|D��|��S)a�
        Return the symmetrization of self under the action of ``G``.

        INPUT:

        G: (optional) subgroup of symmetric group
        if no argument is given take Sn

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(2, 2)
            sage: R.psi(1).symmetrize()
            Graph :      [2] [[1, 2]] []
            Polynomial : 1/2*psi_1 + 1/2*psi_2
        Nrr�c3s|]}��|�VqdSrI)r�rMrNr=r>rO7rEz/TautologicalClass.symmetrize.<locals>.<genexpr>)rrqr,r�r�rr��cardinality)r8r�rH�resr�r=rNr>�
symmetrizes
zTautologicalClass.symmetrizecCs@|��}|jr"|jd|jkr"|S|jdd�t|j�D�dd�S)a�
        Return an isomorphic tautological class where standard markings `{1, 2, ..., n}`
        are used.

        EXAMPLES::

            sage: from admcycles import StableGraph, TautologicalRing
            sage: R = TautologicalRing(4, [4,7])
            sage: g = StableGraph([3], [[4,7,1,2]], [(1,2)])
            sage: a = R(g, psi={4:1}) + R(g, psi={7:2}) + R(g, kappa=[[0,0,1]])
            sage: a.standard_markings()
            Graph :      [3] [[1, 2, 4, 7]] [(4, 7)]
            Polynomial : psi_1 + psi_2^2 + (kappa_3)_0
            sage: a.standard_markings().parent()
            TautologicalRing(g=4, n=2, moduli='st') over Rational Field
        r�cSsi|]\}}||d�qSr�r=r�r=r=r>rDMrEz7TautologicalClass.standard_markings.<locals>.<dictcomp>Fr�)rr�rr�r�rGr=r=r>�standard_markings9sz#TautologicalClass.standard_markingscs�|��}|���|��r8�dur(td��tt�����S�durd|���t��dkr\td���d�ddl	m
�t���fdd�|j�
�D��S)	a�
        EXAMPLES:

        We consider the special case of the moduli space ``g=1`` and ``n=2``::

            sage: from admcycles import TautologicalRing, StableGraph
            sage: R = TautologicalRing(1, 2)

        Degree zero::

            sage: R(2/3).vector()
            (2/3)
            sage: R.from_vector([2/3], 0)
            Graph :      [1] [[1, 2]] []
            Polynomial : 2/3

        Degree one examples::

            sage: kappa1 = R.kappa(1)
            sage: kappa1.vector()
            (1, 0, 0, 0, 0)
            sage: R.from_vector([1,0,0,0,0], 1)
            Graph :      [1] [[1, 2]] []
            Polynomial : (kappa_1)_0

            sage: psi1 = R.psi(1)
            sage: psi1.vector()
            (0, 1, 0, 0, 0)
            sage: gamma2 = StableGraph([0], [[1,2,3,4]], [(3,4)])
            sage: R(gamma2).vector()
            (0, 0, 0, 0, 1)

        Some degree two examples::

            sage: kappa2 = R.kappa(2)
            sage: kappa2.vector()
            (1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
            sage: (kappa1*psi1).vector()
            (0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
            sage: R(gamma2, kappa=[[1],[]]).vector()
            (0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0)
            sage: gamma3 = StableGraph([0,0], [[1,2,3],[4,5,6]], [(3,4),(5,6)])
            sage: R(gamma3).vector()
            (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0)

        Empty class::

            sage: R(0).vector(1)
            (0, 0, 0, 0, 0)

        Non-standard markings::

            sage: R = TautologicalRing(1, [4, 7])
            sage: R.psi(4).vector()
            (0, 1, 0, 0, 0)
            sage: R.from_vector((0, 1, 0, 0, 0), 1)
            Graph :      [1] [[4, 7]] []
            Polynomial : psi_4

            sage: TautologicalRing(1, 1).zero().vector()
            Traceback (most recent call last):
            ...
            ValueError: specify degree to obtain vector of empty class

        TESTS:

        Non-standard moduli::

            sage: R = TautologicalRing(2,2,moduli='ct')
            sage: all(u.vector() == vector(QQ,23,{i:1}) for i,u in enumerate(R.generators(2)))
            True
        Nz.specify degree to obtain vector of empty classr�5for non-homogeneous term, set r to the desired degreer)�converttoTautvectc3s$|]}�|�j�j��j�VqdSrI)r~rr2r��rHr�rsr=r>rO�rEz+TautologicalClass.vector.<locals>.<genexpr>)r�rrWr0rr�num_gensrrrhr�r�r�r,r��r8rsr=r�r>rOsIzTautologicalClass.vectorcCs�|��}|��}t||j�}||jkr@td�t|t|j���|durl|��}t|�dkrdtd��|d}||jkr�|��}t	|j
|j||��d�}||��
|�S|��}ddlm}||�|�|j
|j|t|j�S)ak
        Return a vector expressing the class in the basis of the tautological ring.

        The corresponding basis is obtained from
        :meth:~`TautologicalRing.basis`.

        EXAMPLES::

            sage: from admcycles import TautologicalRing, StableGraph
            sage: R = TautologicalRing(2, 1)
            sage: gamma = StableGraph([1],[[1,2,3]],[(2,3)])
            sage: b = R(gamma)
            sage: b.basis_vector()
            (10, -10, -14)

            sage: R.from_basis_vector((10, -10, -14), 1) == b
            True

            sage: Rct = TautologicalRing(2, 1, moduli='ct')
            sage: Rct(b).basis_vector(1)
            (0, 0)
            sage: Rct(b).basis_vector(1,moduli='tl')
            Traceback (most recent call last):
            ...
            ValueError: moduli='tl' is larger than the moduli 'ct' of the parent

            sage: R = TautologicalRing(2, 2)
            sage: c = R.psi(1)**2
            sage: for moduli in ('st', 'tl', 'ct', 'rt'):
            ....:     Rmod = TautologicalRing(2, 2, moduli=moduli)
            ....:     print(Rmod(c).basis_vector())
            (0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0)
            (0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0)
            (5/6, -1/6, 1/3, 0, 0)
            (1)
            sage: Rsm = TautologicalRing(2, 2, moduli='sm')
            sage: Rsm(c).basis_vector(2)
            ()

        Compatibility test with non-standard markings::

            sage: R1 = TautologicalRing(1, 2)
            sage: g1 = StableGraph([0], [[1,2,3,4]], [(3,4)])
            sage: a1 = R1(g1, psi={1:1}) + R1(g1, psi={2:2}) + R1(g1, kappa=[[0,0,1]])

            sage: R2 = TautologicalRing(1, [4,7])
            sage: g2 = StableGraph([0], [[4,7,1,2]], [(1,2)])
            sage: a2 = R2(g2, psi={4:1}) + R2(g2, psi={7:2}) + R2(g2, kappa=[[0,0,1]])

            sage: a1.basis_vector(0) == a2.basis_vector(0)
            True
            sage: a1.basis_vector(1) == a2.basis_vector(1)
            True
            sage: a1.basis_vector(2) == a2.basis_vector(2)
            True
            sage: a1.basis_vector(3) == a2.basis_vector(3)
            True
        r�Nrr�rr�)�Tautvecttobasis)r�rr"r2r0r�rrrrhr�r~r�r�rpr�r�rr)r8rsr�rHrtr�r=r=r>rp�s&;
�
zTautologicalClass.basis_vectorcCsTddlm}|dd�|��}|dur0||jksB|durJ||jkrJtd��|�|�S)Nrrwryz4toTautvect is deprecated. Please use vector instead.�invalid g,n)r}rxrr~rr0r)r8rCr�rsrxrHr=r=r>�
toTautvect�s
$zTautologicalClass.toTautvect�stcCs�ddlm}|dd�|��}|dur0||jksB|durJ||jkrJtd��t||jkrxt|j|j||�	�d�}||�}|�
|�S)Nrrwryz;toTautbasis is deprecated. Please use basis_vector instead.r�r�)r}rxrr~rr0r r2r�r�rp)r8rCr�rsr�rxrHr=r=r>�toTautbasiss
$zTautologicalClass.toTautbasiscs4ddlm}|dd�|����fdd�|��D�S)Nrrwryz7gnr_list is deprecated. Please use degree_list instead.csg|]}�j�j|f�qSr=)r~rr��rHr=r>r|rEz.TautologicalClass.gnr_list.<locals>.<listcomp>)r}rxrrr)r8rxr=r�r>r�s
zTautologicalClass.gnr_listcOs2ddlm}|dd�|j|i|��}|j|_|S)Nrrwryz2coeff_subs is deprecated. Please use subs instead.)r}rxrir,)r8rkrlrxrur=r=r>�
coeff_subss

zTautologicalClass.coeff_subs)T)N)NN)NNN)NNN)N)N)N)N)NT)N)N)N)NN)NNN)NNNr�)-�__name__�
__module__�__qualname__�__doc__r(r@r3rRrUrWrYrZr[r^rXrbrirvr�r�r�r�r�r�r�r�r�rar�r�r�rrr�r�r�r�r�r�r�rrpr�r�r�r�r=r=r=r>r',sT!
1 "2
!

"
7%
50
%
+
Z
<
*

X
V

r'cs�eZdZdZeZe�fdd��Zdd�Zdd�Z	e
dd	��Zd
d�Zdd
�Z
dd�Zdd�Zdd�Zdd�Zdd�Ze
dd��Zdd�Zdd�Zdd�Zd d!�Zd"d#�ZeZd$d%�Zd&d'�Zdcd)d*�Zd+d,�ZeZd-d.�ZeZ d/d0�Z!d1d2�Z"d3d4�Z#ddd6d7�Z$d8d9�Z%d:d;�Z&d<d=�Z'ded>d?�Z(dfd@dA�Z)dgdBdC�Z*dhdEdF�Z+dGdH�Z,didIdJ�Z-djdLdM�Z.dNdO�Z/e
dkdPdQ��Z0dldRdS�Z1dTdU�Z2dmdVdW�Z3dndXdY�Z4dZd[�Z5dod^d_�Z6e7Z7dpdadb�Z8�Z9S)qr�a�	
    The tautological subgring of the (even degree) cohomology of the moduli space of curves.

    EXAMPLES::

        sage: from admcycles import TautologicalRing
        sage: TautologicalRing(2)
        TautologicalRing(g=2, n=0, moduli='st') over Rational Field
        sage: TautologicalRing(1, 2)
        TautologicalRing(g=1, n=2, moduli='st') over Rational Field
        sage: TautologicalRing(1, 1, 'tl')
        TautologicalRing(g=1, n=1, moduli='tl') over Rational Field

        sage: from admcycles.moduli import MODULI_ST
        sage: TautologicalRing(0, 5, MODULI_ST, QQ)
        TautologicalRing(g=0, n=5, moduli='st') over Rational Field

    Since tautological rings are commutative algebras one can use them as basic building
    blocks of further algebraic constructors in SageMath::

        sage: R = TautologicalRing(2, 0)

        sage: Rxy.<x,y> = PolynomialRing(R, ['x', 'y'])
        sage: Rxy
        Multivariate Polynomial Ring in x, y over TautologicalRing(g=2, n=0, moduli='st') over Rational Field
        sage: R.kappa(1) * x + R.kappa(2) * y  # TODO: horrible display
        (Graph :      [2] [[]] []
        Polynomial : (kappa_1)_0)*x + (Graph :      [2] [[]] []
        Polynomial : (kappa_2)_0)*y

        sage: V = FreeModule(R, 2)
        sage: V
        Ambient free module of rank 2 over TautologicalRing(g=2, n=0, moduli='st') over Rational Field
        sage: V((R.kappa(1), R.kappa(2)))  # TODO: horrible display
        (Graph :      [2] [[]] []
        Polynomial : (kappa_1)_0, Graph :      [2] [[]] []
        Polynomial : (kappa_2)_0)

    TESTS::

        sage: from admcycles import TautologicalRing
        sage: TestSuite(TautologicalRing(1, 1)).run()
        sage: TestSuite(TautologicalRing(1, [3])).run()
        sage: TestSuite(TautologicalRing(1, 1, 'tl', QQ['x,y'])).run()

        sage: cm = get_coercion_model()
        sage: cm.get_action(QQ, TautologicalRing(2,0))
        Left scalar multiplication by Rational Field on TautologicalRing(g=2, n=0, moduli='st') over Rational Field

    Test for https://gitlab.com/modulispaces/admcycles/-/issues/70::

        sage: from admcycles import TautologicalRing
        sage: W = TautologicalRing(1,1)
        sage: V = PolynomialRing(W, 'x,y')
        sage: V.one()
        Graph :      [1] [[1]] []
        Polynomial : 1

    Test for https://gitlab.com/modulispaces/admcycles/-/issues/79::

        sage: from admcycles import TautologicalRing
        sage: TautologicalRing(2, 0).gens()
        (...)
    csZ|�dd�}|�dd�}|�dd�}|�dd�}|rJtd�t|������t|�dkrn|durftd��|d}t|�d	kr�|dur�td
��|d}t|�dkr�|dur�td��|d	}t|�d
kr�|dur�td��|d}t|�d
kr�td�|���t|�}t|tj	��r|dk�rtd��t
|�}|du�r<t
��}d�n�t|ttf��r�dd�t
|�D��t��}�����r��ddk�r�td��t�fdd�t|d�D���r�td��t���nDt|tj	��r�|dk�r�td��t
|�}ttd|d���ntd��|du�rt}n|tv�r(td�|���||fdv�rFtd�||���t��||�||�S)NrCr�r�r�zunknown arguments {}rzgenus g specified twicerr�z)number of marked points n specified twice�zmoduli specified twice�zbase_ring specified twicez+too many arguments for TautologicalRing: {}z g must be a non-negative integerr=cSsg|]}t|��qSr=rr�r=r=r>r|�rEz2TautologicalRing.__classcall__.<locals>.<listcomp>z"markings must be positive integersc3s"|]}�|�|dkVqdS�rNr=r���markingsr=r>rO�rEz1TautologicalRing.__classcall__.<locals>.<genexpr>zrepeated markingz n must be a non-negative integerz
invalid inputz*base_ring (={}) must be a commutative ring))rr)rr)rr�)rrzunstable pair (g,n) = ({}, {}))rjr0r�r+�keysrhr"r)r�r�rrqr*rQ�sort�anyr�r-r�_CommutativeRings�super�
__classcall__)�clsrkrlrCr�r�r���	__class__r�r>r�bsj
 



zTautologicalRing.__classcall__cCs<tj||t|�����d�||_||_t|�|_||_	dS)z�
        INPUT:

        g : integer
          genus
        markings : tuple of distinct positive integers
          list of markings
        moduli : integer
          code for the moduli
        base_ring : ring
          base ring
        )�categoryN)
rr(r�Commutative�FiniteDimensionalr~r�rhrr2)r8rCr�r�r�r=r=r>r(�s


zTautologicalRing.__init__cCsdS)NTr=rNr=r=r>�is_commutative�szTautologicalRing.is_commutativecCs4|jr|jd|jkr|St|j|j|j|��d�S)a�
        Return an equivalent moduli space with the standard markings `{1, 2, \ldots, n}`.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(2, [4, 7])
            sage: R
            TautologicalRing(g=2, n=(4, 7), moduli='st') over Rational Field
            sage: R.standard_markings()
            TautologicalRing(g=2, n=2, moduli='st') over Rational Field
        r�r�)r�rr�r~r2r�rNr=r=r>r��sz"TautologicalRing.standard_markingscCs|��dko|����S)a{
        Return ``False`` unless the tautological ring is supported in degree 0 and the base ring
        is an integral domain.

        This uses that any element of positive order is torsion.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: TautologicalRing(1,3,moduli='ct').socle_degree()
            2
            sage: TautologicalRing(1,3,moduli='ct').is_integral_domain()
            False
            sage: TautologicalRing(0,3).is_integral_domain()
            True
            sage: TautologicalRing(0,3,base_ring=ZZ.quotient(8)).is_integral_domain()
            False
        r)r#r��is_integral_domainrNr=r=r>r��sz#TautologicalRing.is_integral_domaincCs|��dko|����S)aQ
        Return ``False`` unless the tautological ring is supported in degree 0 and the base ring
        is a field.

        This uses that any element of positive order is torsion.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: TautologicalRing(1,3,moduli='ct').socle_degree()
            2
            sage: TautologicalRing(1,3,moduli='ct').is_field()
            False
            sage: TautologicalRing(0,3).is_field()
            True
            sage: TautologicalRing(0,3,base_ring=ZZ.quotient(5)).is_field()
            True
        r)r#r��is_fieldrNr=r=r>r��szTautologicalRing.is_fieldcCs|��dko|����S)a-
        Return ``False`` unless the tautological ring is supported in degree 0 and the base ring
        is a prime field.

        This uses that any element of positive order is torsion.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: TautologicalRing(1,3,moduli='ct', base_ring=GF(5)).is_prime_field()
            False
            sage: TautologicalRing(0,3,base_ring=QQ).is_prime_field()
            True
            sage: TautologicalRing(0,3,base_ring=GF(5)).is_prime_field()
            True
        r)r#r��is_prime_fieldrNr=r=r>r��szTautologicalRing.is_prime_fieldcCsHt|t�rD|j|jkrD|j|jkrD|j|jkrD|���|���rDdSdS)a�
        TESTS::

            sage: from admcycles import TautologicalRing
            sage: TautologicalRing(1, 1, base_ring=QQ['x','y']).has_coerce_map_from(TautologicalRing(1, 1, base_ring=QQ))
            True

            sage: M11st = TautologicalRing(1, 1, moduli='st')
            sage: M11ct = TautologicalRing(1, 1, moduli='ct')
            sage: M11st.has_coerce_map_from(M11ct)
            False
            sage: M11ct.has_coerce_map_from(M11st)
            True
        TN)r)r�r~r�r2r��has_coerce_map_from�r8r�r=r=r>�_coerce_map_from_	s

�
�
��z"TautologicalRing._coerce_map_from_cCst|j|j|j�|��fS)a�
        Return a functorial construction (when applied to a base ring).

        This function is mostly intended to make the tautological ring behave nicely
        with Sage ecosystem.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(1, 1)
            sage: QQ['x'].gen() * R.psi(1) # indirect doctest
            Graph :      [1] [[1]] []
            Polynomial : x*psi_1

            sage: A = TautologicalRing(3, 2, moduli='st', base_ring=QQ['x'])
            sage: B = TautologicalRing(3, 2, moduli='tl', base_ring=QQ)
            sage: A.psi(1) + B.psi(2) # indirect doctest
            Graph :      [3] [[1, 2]] []
            Polynomial : psi_1 + psi_2
            sage: (A.psi(1) + B.psi(2)).parent()
            TautologicalRing(g=3, n=2, moduli='tl') over Univariate Polynomial Ring in x over Rational Field
        )�TautologicalRingFunctorr~r�r2r�rNr=r=r>�constructionszTautologicalRing.constructioncCsZ|jttd|jd��kr8d�|j|jt|j|���Sd�|j|jt|j|���SdS)Nrz1TautologicalRing(g={}, n={}, moduli={!r}) over {})	r�r*r�rr�r~rr2r�rNr=r=r>rR8szTautologicalRing._repr_cCst|j|j|j�S)a�
        Return the socle degree of this tautological ring.

        The socle degree is the maximal degree whose corresponding graded
        component is non-empty. It is currently not implemented for the
        moduli of tree like curves.

        The formulas were computed in:

        - [GrVa01]_, [GrVa05]_ and [FaPa05]_ for compact type
        - [Lo95]_, [Fa99]_ and [FaPa00]_ for rational tail
        - [Lo95]_, [Io02]_ for smooth curves

        EXAMPLES::

            sage: from admcycles import TautologicalRing

        We display below the socle degree for various `(g, n)` for the moduli of
        smooth curves, rational tails, compact types and stable curves::

            sage: for (g,n) in [(0,3), (0,4), (0, 5), (1,1), (1,2), (2,0), (2, 1)]:
            ....:     dims = []
            ....:     for moduli in ['sm', 'rt', 'ct', 'st']:
            ....:         R = TautologicalRing(g, n, moduli=moduli)
            ....:         dims.append(R.socle_degree())
            ....:     print("g={} n={}: {}".format(g, n, " ".join(map(str, dims))))
            g=0 n=3: 0 0 0 0
            g=0 n=4: 0 1 1 1
            g=0 n=5: 0 2 2 2
            g=1 n=1: 0 0 0 1
            g=1 n=2: 0 1 1 2
            g=2 n=0: 0 0 1 3
            g=2 n=1: 1 1 2 4

        The socle degree is overestimated for tree-like::

            sage: R = TautologicalRing(2, moduli='tl')
            sage: R.socle_degree()
            3
            sage: R.kappa(3).is_zero() # generator for \bar M_2
            True
        )r#r~rr2rNr=r=r>r#>s+zTautologicalRing.socle_degreecCst|jgt|j�ggdd�S)z�
        Return the stable graph corresponding to the full stratum.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: TautologicalRing(2, [1, 3, 4]).trivial_graph()
            [2] [[1, 3, 4]] []
        Frd)r%r~r+r�rNr=r=r>r�kszTautologicalRing.trivial_graphcCs|��SrI)r�rNr=r=r>�_an_element_xszTautologicalRing._an_element_csz�fdd������D�}|dd�|dd�}|������jr\|����jd���jdkrv|���d��|S)aJ
        Return some elements in this tautological ring.

        This is mostly used for sage test system.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: _  = TautologicalRing(0, [4, 5, 7]).some_elements()
            sage: _ = TautologicalRing(2, [4, 7]).some_elements()
        csg|]}�|��qSr=r=�rB�srNr=r>r|�rEz2TautologicalRing.some_elements.<locals>.<listcomp>Nr������rr)r��
some_elements�append�irreducible_boundary_divisorr��psir~�kappa)r8Z	base_elts�eltsr=rNr>r{s
zTautologicalRing.some_elementscCs|�|g�S)z�
        Return the zero element.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: TautologicalRing(0, 4).zero()
            0
        )rFrNr=r=r>rq�s
zTautologicalRing.zerocCsv|��}|D]d}|�|�}|j��D]J\}}||jvr`|j|j|j7_|j|sn|j|=q$|��|j|<q$q|S)a(
        Return the sum of ``classes``.

        EXAMPLES::

            sage: from admcycles import TautologicalRing, psiclass
            sage: R = TautologicalRing(1, 2)
            sage: R.sum([R(1),R(1),R(0),R(1)])
            Graph :      [1] [[1, 2]] []
            Polynomial : 3
            sage: R.sum([psiclass(1,1,2),psiclass(2,1,2)]) == psiclass(1,1,2) + psiclass(2,1,2)
            True
            sage: R.sum([1,1])
            Graph :      [1] [[1, 2]] []
            Polynomial : 2

        Your elements must be coercible into the tautological ring::

            sage: R.sum(['a', 'b', 'c'])
            Traceback (most recent call last):
            ...
            TypeError: no canonical coercion from <... 'str'> to TautologicalRing(g=1, n=2, moduli='st') over Rational Field
        )rq�coercer,r7r5r@)r8�classesrur;rCr<r=r=r>r��s



zTautologicalRing.sumcCs||���S)a�
        Return the fundamental class as a cohomology class.

        The fundamental class is the unit of the ring.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(2, 1)
            sage: R.fundamental_class()
            Graph :      [2] [[1]] []
            Polynomial : 1

            sage: R.fundamental_class() ** 2 == R.fundamental_class()
            True
        �r�rNr=r=r>�fundamental_class�sz"TautologicalRing.fundamental_classcCs||��|did�S)a�
        Return the class `\psi_i` on `\bar M_{g,n}`.

        Alternatively, you could use the function :func:`~admcycles.admcycles.psiclass`.

        INPUT:

        i : integer
            the leg number associated to the psi class

        EXAMPLES::

            sage: from admcycles import TautologicalRing, StableGraph

            sage: R = TautologicalRing(2, 3)
            sage: R.psi(2)
            Graph :      [2] [[1, 2, 3]] []
            Polynomial : psi_2
            sage: R.psi(3)
            Graph :      [2] [[1, 2, 3]] []
            Polynomial : psi_3

            sage: R = TautologicalRing(3, 2)
            sage: R.psi(1)
            Graph :      [3] [[1, 2]] []
            Polynomial : psi_1

        TESTS::

            sage: from admcycles import TautologicalRing

            sage: R = TautologicalRing(3, 2)
            sage: R.psi(3)
            Traceback (most recent call last):
            ...
            ValueError: unknown marking 3 for psi
        r�rr)r8rnr=r=r>r�s&zTautologicalRing.psicCsZ|dkr$d|jd|j|��S|dkr4|��S||��dg|ddggd�SdS)a�
        Return the (Arbarello-Cornalba) kappa-class `\kappa_a` on `\bar M_{g,n}` defined by

          `\kappa_a= \pi_*(\psi_{n+1}^{a+1})`

        where `pi` is the morphism `\bar M_{g,n+1} \to \bar M_{g,n}`.

        INPUT:

        a : integer
            the degree a of the kappa class

        EXAMPLES::

            sage: from admcycles import TautologicalRing

            sage: R = TautologicalRing(3, 1)
            sage: R.kappa(2)
            Graph :      [3] [[1]] []
            Polynomial : (kappa_2)_0

            sage: R = TautologicalRing(3, 2)
            sage: R.kappa(1)
            Graph :      [3] [[1, 2]] []
            Polynomial : (kappa_1)_0
        rr�r)rN)r~rr�rqr�)r8�ar=r=r>rs
zTautologicalRing.kappaTcCs�|j}|j}||ks|dkr$|��S|dkr�|r�|dkr@|��S|dkr~ttd|d��}tdd|jd�}|j|dd��	|�Sttd|d��}t|d|jd�}|j|dd��	|�S|�
||j�S)a�
        Return the tautological class `\lambda_d` on `\bar M_{g,n}`.

        The `\lambda_d` class is defined as the d-th Chern class

          `\lambda_d = c_d(E)`

        of the Hodge bundle `E`. The result is represented as a sum of stable
        graphs with kappa and psi classes.

        INPUT:

        d : integer
          the degree
        pull : boolean (optional, default to ``True``)
          whether the class is computed as pullback from `\bar M_{g}`

        EXAMPLES::

            sage: from admcycles import TautologicalRing

            sage: R = TautologicalRing(2, 0)
            sage: R.lambdaclass(1)
            Graph :      [2] [[]] []
            Polynomial : 1/12*(kappa_1)_0
            <BLANKLINE>
            Graph :      [1] [[2, 3]] [(2, 3)]
            Polynomial : 1/24
            <BLANKLINE>
            Graph :      [1, 1] [[2], [3]] [(2, 3)]
            Polynomial : 1/24

            sage: R = TautologicalRing(1, 1)
            sage: R.lambdaclass(1)
            Graph :      [1] [[1]] []
            Polynomial : 1/12*(kappa_1)_0 - 1/12*psi_1
            <BLANKLINE>
            Graph :      [0] [[3, 4, 1]] [(3, 4)]
            Polynomial : 1/24

        TESTS::

            sage: from admcycles import lambdaclass
            sage: inputs = [(0,0,4), (1,1,3), (1,2,1), (2,2,1), (3,2,1), (-1,2,1), (2,3,2)]
            sage: for d,g,n in inputs:
            ....:     R = TautologicalRing(g, n)
            ....:     assert (R.lambdaclass(d) - R.lambdaclass(d,pull=False)).is_zero()

        Check for https://gitlab.com/modulispaces/admcycles/issues/58::

            sage: R = TautologicalRing(4,0)
            sage: L = R.lambdaclass(4)
            sage: L == R.double_ramification_cycle(())
            True
            sage: L.basis_vector()
            (-229/4, 28/3, 55/24, -13/12, 1/24, 0, 0, 0, 0, 0, 0, 0, -1/2, 1/6, 1, -1/3, -1/3, 2/3, 2/3, -4/3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4/3, 1/3, -5/3, 20/3, 16/9, -4/3, 2/9, 1/6, 0, 0, 0, 0, 0, 0, 0, 0, 0)
        rrr�r�F)�pull)r~rrqrr+r�r�r2�lambdaclassr��chern_character_to_class�hodge_chern_character)r8r_rrCr�Znewmarksrtr=r=r>r$s:zTautologicalRing.lambdaclasscCsl|jdkr|��S|j}|jr(|jdnd}t|dgt|j�|d|dgg|d|dfg�}||�S)a<
        Return the pushforward of the fundamental class under the irreducible
        boundary gluing map `\bar M_{g-1,n+2} \to \bar M_{g,n}`.

        EXAMPLES::

            sage: from admcycles import *

            sage: R = TautologicalRing(2, 5)
            sage: R.irreducible_boundary_divisor()
            Graph :      [1] [[1, 2, 3, 4, 5, 6, 7]] [(6, 7)]
            Polynomial : 1

            sage: R = TautologicalRing(3, 0)
            sage: R.irreducible_boundary_divisor()
            Graph :      [2] [[1, 2]] [(1, 2)]
            Polynomial : 1
        rr�rr�)r~rqr�r%r+)r8rC�lastr�r=r=r>r
qs
8z-TautologicalRing.irreducible_boundary_divisorcCsr|j}|jr|jdnd}t|||gt|�|dgtt|j�t|��|dgg|d|dfg�}||�S)a�
        Return the pushforward of the fundamental class under the boundary
        gluing map `\bar M_{h,A} \times \bar M_{g-h,{1,...,n} \ A} \to  \bar M_{g,n}`.

        INPUT:

        h : integer
          genus of the first vertex
        A : list
          list of markings on the first vertex

        EXAMPLES::

            sage: from admcycles import *

            sage: R = TautologicalRing(2, 5)
            sage: R.separable_boundary_divisor(1, (1,3,4))
            Graph :      [1, 1] [[1, 3, 4, 6], [2, 5, 7]] [(6, 7)]
            Polynomial : 1

            sage: R = TautologicalRing(3, 3)
            sage: R.separable_boundary_divisor(1, (2,))
            Graph :      [1, 2] [[2, 4], [1, 3, 5]] [(4, 5)]
            Polynomial : 1

            sage: R = TautologicalRing(2, [4, 6])
            sage: R.separable_boundary_divisor(1, (6,))
            Graph :      [1, 1] [[6, 7], [4, 8]] [(7, 8)]
            Polynomial : 1
        r�rr�)r~r�r%r+rQr�)r8�h�ArCrr�r=r=r>�separable_boundary_divisor�s&���z+TautologicalRing.separable_boundary_divisorcsh�jr"�jdt�j�kr"td���j}�j�|dkr>���Sddlm}||�d�}|�d�}t	����dkr||��
�S�ddks��dkr����Sddlm�t���fdd	�t
��D��}t���fd
d	�t
��D�������t��fdd	�t
d�d�D��}|td�d�||d�7}|t��fd
d	�|D��7}|��t�d�t�d�|S)a�
        Return the chern character `ch_d(E)` of the Hodge bundle `E` on `\bar
        M_{g,n}`.

        This function implements the formula from [Mu83]_.

        INPUT:

        d : integer
            The degree of the Chern character.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(1, 2)
            sage: R.hodge_chern_character(1)
            Graph :      [1] [[1, 2]] []
            Polynomial : 1/12*(kappa_1)_0 - 1/12*psi_1 - 1/12*psi_2
            <BLANKLINE>
            Graph :      [0] [[1, 2, 3, 4]] [(3, 4)]
            Polynomial : 1/24
            <BLANKLINE>
            Graph :      [0, 1] [[1, 2, 3], [4]] [(3, 4)]
            Polynomial : 1/12
        r�z<hodge_chern_character unsupported with non-standard markingsrr)�list_stratar�)�psiclc3sB|]:}d|��dd�|��dd��d|VqdS�r�rr�Nr=r��r_r�rr=r>rO�rEz9TautologicalRing.hodge_chern_character.<locals>.<genexpr>c3sB|]:}d|��dd�|��dd��d|VqdSrr=r�rr=r>rO�rEc3s|]}��|��VqdSrIrr�)r_r8r=r>rO�rE�r5c3s*|]"}td|��f��|�d�VqdS)rr N)r�automorphism_number)rB�ind)�psipsisum_twovertr8r=r>rO�rE)r�rh�NotImplementedErrorr~rrqr�rrjrrrr�r�rrr3rr)r8r_rCr�bdriesZirrbdryZpsipsisum_onevertZcontribr=)r_r�rr#r8r>r�s,
,z&TautologicalRing.hodge_chern_charactercs~t�t�r(�fdd�td|d�D��n�fdd�td|d�D��t�fdd�t|�D��}|dkrr||��S|j|d�S)	a
        Return the Chern class associated to the Chern character.

        INPUT:

        t : integer
          degree of the output Chern class
        char : tautological class or a function
          Chern character, either represented as a (mixed-degree) tautological class or as
          a function m -> ch_m

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(1, 1)
            sage: R.chern_character_to_class(1, R.hodge_chern_character)
            Graph :      [1] [[1]] []
            Polynomial : 1/12*(kappa_1)_0 - 1/12*psi_1
            <BLANKLINE>
            Graph :      [0] [[3, 4, 1]] [(3, 4)]
            Polynomial : 1/24

        Note that the output of generalized_hodge_chern takes the form of a chern character::

            sage: from admcycles import *
            sage: from admcycles.GRRcomp import *
            sage: g=2;n=2;l=0;d=[1,-1];a=[[1,[1],-1]]
            sage: chern_char_to_class(1,generalized_hodge_chern(l,d,a,1,g,n)) # known bug
            Graph :      [2] [[1, 2]] []
            Polynomial : 1/12*(kappa_1^1 )_0
            <BLANKLINE>
            Graph :      [2] [[1, 2]] []
            Polynomial : (-13/12)*psi_1^1
            <BLANKLINE>
            Graph :      [2] [[1, 2]] []
            Polynomial : (-1/12)*psi_2^1
            <BLANKLINE>
            Graph :      [0, 2] [[1, 2, 4], [5]] [(4, 5)]
            Polynomial : 1/12*
            <BLANKLINE>
            Graph :      [1, 1] [[4], [1, 2, 5]] [(4, 5)]
            Polynomial : 1/12*
            <BLANKLINE>
            Graph :      [1, 1] [[2, 4], [1, 5]] [(4, 5)]
            Polynomial : 13/12*
            <BLANKLINE>
            Graph :      [1] [[4, 5, 1, 2]] [(4, 5)]
            Polynomial : 1/24*
        cs0g|](}d|dt|d��j|d��qS)r�r�rs)rr�r��charr=r>r|)rEz=TautologicalRing.chern_character_to_class.<locals>.<listcomp>rcs,g|]$}d|dt|d��|��qS)r�r)rrr'r=r>r|+rEc3s<|]4}t|���tt|��t�fdd�|D��VqdS)c3s|]}�|dVqdSr�r=)rBr���argr=r>rO-rEzFTautologicalRing.chern_character_to_class.<locals>.<genexpr>.<genexpr>N)r�to_exprrhrrr)r=r>rO-rEz<TautologicalRing.chern_character_to_class.<locals>.<genexpr>rr&)r)r'r�r�rrr�)r8r;r(r�r=)r*r(r>r�s2
z)TautologicalRing.chern_character_to_classcCsl|��|jks|��|jkr<td�|��|��|j|j���tt|����}||j	krhtd�||j	���dS)Nz;invalid stable graph (has g={}, n={} instead of g={}, n={})z4invalid stable graph (has markings={} instead of {}))
rCr~r�rr0r�r*rQ�
list_markingsr�)r8Zstgr�r=r=r>�_check_stable_graph2s�
z$TautologicalRing._check_stable_graphNcsLt�t�rN���}|j|jks*|j|jkr2td��|�|dd��j��D��S�sZ|�	�St�t
�r�|�����|j
�r�|�	�S|dur�t|t�s�td��|D]2�t��fdd�t����D��s�td�����q�t�|||d	�}|�||g�St�ttf��r:�D]&}t|t��std
��|�|j��q|�|��Std�����dS)a�
        TESTS::

            sage: from admcycles import TautologicalRing, StableGraph
            sage: R = TautologicalRing(2,1)
            sage: R(2/3)
            Graph :      [2] [[1]] []
            Polynomial : 2/3

        From a stable graph with decorations::

            sage: g = StableGraph([1], [[1,2,3]], [(2,3)])
            sage: R(g)
            Graph :      [1] [[1, 2, 3]] [(2, 3)]
            Polynomial : 1

        A stable graph outside of the open set defines by the moduli gives zero::

            sage: Rct = TautologicalRing(2, 1, moduli='ct')
            sage: Rct(g)
            0

        From a sequence of decorated strata::

            sage: from admcycles.admcycles import decstratum
            sage: gg = StableGraph([2], [[1]], [])
            sage: arg = [decstratum(g), decstratum(gg, kappa=[[1]])]
            sage: R(arg)
            Graph :      [2] [[1]] []
            Polynomial : (kappa_1)_0
            <BLANKLINE>
            Graph :      [1] [[1, 2, 3]] [(2, 3)]
            Polynomial : 1

        Empty argument::

            sage: from admcycles import TautologicalRing
            sage: TautologicalRing(1, 1)([])
            0
        zincompatible moduli spacescSsi|]\}}||���qSr=r?r�r=r=r>rDgrEz:TautologicalRing._element_constructor_.<locals>.<dictcomp>Nzpsi must be a dictionaryc3s|]}��j|vVqdSrI)�_legs)rB�v�r*rnr=r>rOrrEz9TautologicalRing._element_constructor_.<locals>.<genexpr>zunknown marking {} for psi)rrr5zmust be a sequence of decstratazunknown argument of type arg={})r)r'rr~rr0rFr,r7rqr%r-r1r2r6r�r��	num_vertsr�r$r*r+r-r.r$)r8r*rrr5rH�decr<r=r0r>�_element_constructor_:s4)



 z&TautologicalRing._element_constructor_c
	CsPddlm}ddlm}|j|j|ttd|jd��t	|j
dd�d�}g}t|�t|�krbtd��t
|�}|����}zt�||�}Wn(ty�t|j|j|j
|d�}Yn0tt|��D]6}	||	s�q�|||	�}
|
j||	9_|�|
�q�|j�r@|jd	|jk�r@d
d�t|j�D�}|��}|�||�j|dd
�S|�||�SdS)ax
        Return the tautological class associated to the vector ``v`` and degree ``r`` on this tautological ring.

        See also :meth:`~TautlogicalRing.generators` and :meth:`~TautologicalRing.from_basis_vector`.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(1, 2)
            sage: R.from_vector((0, -1, 0, 1, 0), 1)
            Graph :      [1] [[1, 2]] []
            Polynomial : -psi_1
            <BLANKLINE>
            Graph :      [0, 1] [[1, 2, 4], [5]] [(4, 5)]
            Polynomial : 1

            sage: R = TautologicalRing(1, [4, 7])
            sage: R.from_vector((0, -1, 0, 1, 0), 1)
            Graph :      [1] [[4, 7]] []
            Polynomial : -psi_4
            <BLANKLINE>
            Graph :      [0, 1] [[1, 4, 7], [5]] [(1, 5)]
            Polynomial : 1

        This also works with non-standard moduli::

            sage: R = TautologicalRing(2,0,moduli='ct')
            sage: R.generators(1)
            [Graph :      [2] [[]] []
             Polynomial : (kappa_1)_0,
             Graph :      [1, 1] [[2], [3]] [(2, 3)]
             Polynomial : 1]
            sage: R.from_vector((1,2),1)
            Graph :      [2] [[]] []
            Polynomial : (kappa_1)_0
            <BLANKLINE>
            Graph :      [1, 1] [[2], [3]] [(2, 3)]
            Polynomial : 2

        TESTS::

            sage: R = TautologicalRing(2,0)
            sage: x = polygen(ZZ)
            sage: v = vector((x,0,0,0,0,0,0,0))
            sage: R.from_vector(v, 2).parent().base_ring()
            Univariate Polynomial Ring in x over Rational Field
        r��Graphtodecstratum��DRT��DRpy�Zmoduli_typezinput v has wrong lengthr�r�cSsi|]\}}|d|�qSr�r=r�r=r=r>rD�rEz0TautologicalRing.from_vector.<locals>.<dictcomp>Fr�N)r�r5�r7�
all_stratar~r*r�rr"r2rhr0rrr�r	�
common_parentr-r�r5r	r�r�r�rFr�)
r8r/rsr5r7�strata�stratmod�BR�TRrn�	currstratr�rtr=r=r>r��s20�zTautologicalRing.from_vectorcCsT|dks||��krdS|jtkr*td��ddlm}t||j|j|t	|jd��S)a�
        INPUT:

        r : integer
          the degree

        EXAMPLES::

            sage: from admcycles import *
            sage: R = TautologicalRing(2, 1)
            sage: [R.dimension(r) for r in range(5)]
            [1, 3, 5, 3, 1]
            sage: R = TautologicalRing(2, 1, moduli='ct')
            sage: [R.dimension(r) for r in range(5)]
            [1, 2, 1, 0, 0]
        rz;dimension not implemented for the moduli of treelike curvesr��generating_indicesr�)
r#r2r!r$r�rDrhr~rr)r8rsrDr=r=r>�	dimension�s
zTautologicalRing.dimensioncCsT|jtkrtd��ddlm}m}ddlm}||j|j	|t
|jd�}|j|j|tt
d|j	d��t|jdd�d�}g}t|�t|�kr�d	d
lm}	|	d�t|�t|��t�t
t|��D]:}
||
s�q�||||
�}|j||
9_|�|�q�|j�rD|jd|j	k�rDd
d�t|j�D�}|��}
|
�|
|�j|dd�S|�||�SdS)a�
        Return the tautological class of degree ``r`` corresponding to the
        vector ``v`` expressing the coefficients in the basis.

        See also :meth:`~TautologicalRing.basis` and :meth:`~TautologicalRing.from_vector`.

        INPUT:

        v : vector
        r : degree

        EXAMPLES::

            sage: from admcycles import TautologicalRing

            sage: R = TautologicalRing(2, 1)
            sage: R.from_basis_vector((1,0,-2), 1)
            Graph :      [2] [[1]] []
            Polynomial : (kappa_1)_0
            <BLANKLINE>
            Graph :      [1, 1] [[3], [1, 4]] [(3, 4)]
            Polynomial : -2

            sage: R = TautologicalRing(2, [3])
            sage: R.from_basis_vector((1,0,-2), 1)
            Graph :      [2] [[3]] []
            Polynomial : (kappa_1)_0
            <BLANKLINE>
            Graph :      [1, 1] [[1], [3, 4]] [(1, 4)]
            Polynomial : -2

        TESTS::

            sage: for mo in ['st', 'ct', 'rt', 'sm']:
            ....:     R = TautologicalRing(2,2,moduli=mo)
            ....:     for a in R.generators(2):
            ....:         assert a == R.from_basis_vector(a.basis_vector(),2)
        z<from_basis not implemented for the moduli of treelike curvesr)r5rDr6r�Tr8r:r)�warnz1vector v has wrong length {}; should have been {}r�cSsi|]\}}|d|�qSr�r=r�r=r=r>rD#	rEz6TautologicalRing.from_basis_vector.<locals>.<dictcomp>Fr�N)r2r!r$r�r5rDr;r7r~rrr<r*r�r"rh�warningsrFr��DeprecationWarningr5r	r�r�r�rFr�)r8r/rsr5rDr7Zgenindr>r?rFrnrBr�rtr=r=r>ro�s.'
�z"TautologicalRing.from_basis_vectorcCs:|dur6g}t|��d�D]}|�|�|��q|St|�}|dksR||��krVgSddlm}ddlm}g}|j	r�|j	d|j
kr�||j|ttd|j
d��t
|jdd�d	�D]}|�|�|||�g��q�nb||j|ttd|j
d��t
|jdd�d	�D]4}|�|�|||��d
d�t|j	�D��g���q|S)aZ

        INPUT:

        r : integer (optional)
            the degree. If provided, only returns the generators of a given degree.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(2,0)
            sage: R.generators(1)
            [Graph :      [2] [[]] []
             Polynomial : (kappa_1)_0,
             Graph :      [1, 1] [[2], [3]] [(2, 3)]
             Polynomial : 1,
             Graph :      [1] [[2, 3]] [(2, 3)]
             Polynomial : 1]
            sage: R = TautologicalRing(2,0,moduli='ct')
            sage: R.generators(1)
            [Graph :      [2] [[]] []
             Polynomial : (kappa_1)_0,
             Graph :      [1, 1] [[2], [3]] [(2, 3)]
             Polynomial : 1]
            sage: R = TautologicalRing(2,0,moduli='sm')
            sage: R.generators(1)
            []

        TESTS::

            sage: R = TautologicalRing(1, 2)
            sage: for v in R.generators(0): print(v.vector())
            (1)
            sage: for v in R.generators(1): print(v.vector())
            (1, 0, 0, 0, 0)
            (0, 1, 0, 0, 0)
            (0, 0, 1, 0, 0)
            (0, 0, 0, 1, 0)
            (0, 0, 0, 0, 1)
            sage: for v in R.generators(2): print(v.vector())
            (1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
            (0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
            (0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
            (0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
            (0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
            (0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0)
            (0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0)
            (0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0)
            (0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0)
            (0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0)
            (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0)
            (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0)
            (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0)
            (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0)
            (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)

        Test compatibility with nonstandard markings::

            sage: R = TautologicalRing(1,[2,4])
            sage: R.generators(1)
            [Graph :      [1] [[2, 4]] []
             Polynomial : (kappa_1)_0, Graph :      [1] [[2, 4]] []
             Polynomial : psi_2, Graph :      [1] [[2, 4]] []
             Polynomial : psi_4, Graph :      [0, 1] [[1, 2, 4], [5]] [(1, 5)]
             Polynomial : 1, Graph :      [0] [[1, 2, 4, 5]] [(1, 5)]
             Polynomial : 1]
        Nrr)r<r4r�Tr8r:cSsi|]\}}|d|�qSr�r=r�r=r=r>rD	rEz/TautologicalRing.generators.<locals>.<dictcomp>)r�r#�extend�
generatorsrr7r<r�r5r�rr~r*r"r2r	rFr�r�)r8rsrur<r5r��stratumr=r=r>rJ*	s"C002zTautologicalRing.generatorscCst|�|��S)aR
        Return a tuple whose entries are module generators for this tautological ring.

        INPUT:

        r: integer (optional)
        If provided, return generators in the given degree only.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(2,0)
            sage: R.gens(1)
            (Graph :      [2] [[]] []
             Polynomial : (kappa_1)_0,
             Graph :      [1, 1] [[2], [3]] [(2, 3)]
             Polynomial : 1,
             Graph :      [1] [[2, 3]] [(2, 3)]
             Polynomial : 1)
        )r*rJr�r=r=r>r��	szTautologicalRing.gensc	s�|dur*t�fdd�t���d�D��St|�}|dksF|���krNt��Sz
�j}Wn*ty�dg���d}�_Yn0ddlm}||dur�t|�j	|t
td�jd��t�j
dd�d	��||<||S)
aV
        Return the number of generators.

        INPUT:

        r: integer (optional)
        If provided, return the number of generators of the given degree.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(2, 0)
            sage: R.ngens()
            29
            sage: R.ngens(2)
            8

        TESTS::

            sage: R = TautologicalRing(2,0,moduli='ct')
            sage: R.socle_degree()
            1
            sage: R.ngens()
            3
            sage: len(R.generators(1))
            2
        Nc3s|]}��|�VqdSrI)�ngens)rBrsrNr=r>rO�	rEz)TautologicalRing.ngens.<locals>.<genexpr>rr��
num_strataTr8r:)r�r�r#rrq�_ngens�AttributeErrorr7rNr~r*rr"r2)r8rsrLrNr=rNr>rL�	s"
4zTautologicalRing.ngensrcCs^|dks||�|�krtd��|durPd}||�|�krP||�|�8}|d7}q*|�|�|S)aW
        Return the ``i``-th generator.

        INPUT:

        i : integer (default ``0``)
        The number of the generator.

        r : integer (optional)
        If provided, return the ``i``-th generator in degree ``r``.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(2, 0)
            sage: R.gen(23)
            Graph :      [0, 1] [[3, 4, 5], [6]] [(3, 4), (5, 6)]
            Polynomial : (kappa_1)_1
            sage: [R.ngens(i) for i in range(4)]
            [1, 3, 8, 17]
            sage: R.gen(11,3)
            Graph :      [0, 1] [[3, 4, 5], [6]] [(3, 4), (5, 6)]
            Polynomial : (kappa_1)_1
            sage: R.gen(2, 3)
            Graph :      [2] [[]] []
            Polynomial : (kappa_1^3)_0

        TESTS::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(2, 0)
            sage: R.generators() == [R.gen(i) for i in range(R.ngens())]
            True
            sage: R.generators(3) == [R.gen(i, 3) for i in range(R.ngens(3))]
            True
        rzundefined generatorNr)rLr0rJ)r8rnrsr=r=r>�gen�	s%
zTautologicalRing.gencCs@|�|�}tt|��D]$}tdt|�dt||��qdS)a

        Lists all tautological classes of degree r in the tautological ring.

        INPUT:

        r : integer
        The degree r of of the classes.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(2,0)
            sage: R.list_generators(2)
            [0] : Graph :      [2] [[]] []
            Polynomial : (kappa_2)_0
            [1] : Graph :      [2] [[]] []
            Polynomial : (kappa_1^2)_0
            [2] : Graph :      [1, 1] [[2], [3]] [(2, 3)]
            Polynomial : (kappa_1)_0
            [3] : Graph :      [1, 1] [[2], [3]] [(2, 3)]
            Polynomial : psi_2
            [4] : Graph :      [1] [[2, 3]] [(2, 3)]
            Polynomial : (kappa_1)_0
            [5] : Graph :      [1] [[2, 3]] [(2, 3)]
            Polynomial : psi_2
            [6] : Graph :      [0, 1] [[3, 4, 5], [6]] [(3, 4), (5, 6)]
            Polynomial : 1
            [7] : Graph :      [0] [[3, 4, 5, 6]] [(3, 4), (5, 6)]
            Polynomial : 1
        �[z] : N)rJr�rh�printrL)r8rs�Lrnr=r=r>�list_generators�	s
z TautologicalRing.list_generatorscs�|dur6g}t|��d�D]}|�|�|��q|S|jtkrHtd��ddlm}|�	|���fdd�||j
|j|t|jd�D�S)a
        Return a basis.

        INPUT:

        d : ``None`` (default) or integer
          if ``None`` return a full basis if ``d`` is provided, return a basis
          of the homogeneous component of degree ``d``

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(2, 0)
            sage: R.basis(2)
            [Graph :      [2] [[]] []
             Polynomial : (kappa_2)_0,
             Graph :      [2] [[]] []
             Polynomial : (kappa_1^2)_0]
            sage: R2 = TautologicalRing(2, 1, moduli='ct')
            sage: R2.basis(1)
            [Graph :      [2] [[1]] []
             Polynomial : (kappa_1)_0,
             Graph :      [2] [[1]] []
             Polynomial : psi_1]
        Nrz)basis not implemented for treelike curvesrCcsg|]}�|�qSr=r=r��r�r=r>r|B
rEz*TautologicalRing.basis.<locals>.<listcomp>r�)
r�r#rI�basisr2r!r$r�rDrJr~rr)r8r_rurDr=rVr>rW
s

zTautologicalRing.basisFc	cs�|��}t|d|j�D]�}dd�t|j|dd��D�}t|d�D]l}g}t|���D]2\}}	|	shqZ|�dg|dt	|��|	||<qZ||||gd�}
|r�|||
fn|
VqFqdS)a�
        Iterator over the polynomials in kappa and psi classes of degree ``d``.

        For combout=True it returns a generator of triples
        (kappalist, psidict, kppoly) of

          * a list kappalist of exponents of kappa_i,
          * a dictionary psidict sending i to the exponent of psi_i,
          * the associated TautologicalClass for this monomial.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: for a in TautologicalRing(0, 4).kappa_psi_polynomials(1): print(a)
            Graph :      [0] [[1, 2, 3, 4]] []
            Polynomial : (kappa_1)_0
            Graph :      [0] [[1, 2, 3, 4]] []
            Polynomial : psi_1
            Graph :      [0] [[1, 2, 3, 4]] []
            Polynomial : psi_2
            Graph :      [0] [[1, 2, 3, 4]] []
            Polynomial : psi_3
            Graph :      [0] [[1, 2, 3, 4]] []
            Polynomial : psi_4

            sage: for a in TautologicalRing(1, [2,7]).kappa_psi_polynomials(1): print(a)
            Graph :      [1] [[2, 7]] []
            Polynomial : (kappa_1)_0
            Graph :      [1] [[2, 7]] []
            Polynomial : psi_2
            Graph :      [1] [[2, 7]] []
            Polynomial : psi_7

        Here is the option where the combinatorial data is output separately::

            sage: for a in TautologicalRing(1, [2,7]).kappa_psi_polynomials(1, True): print(a)
            ([1], {}, Graph :      [1] [[2, 7]] []
            Polynomial : (kappa_1)_0)
            ([], {2: 1}, Graph :      [1] [[2, 7]] []
            Polynomial : psi_2)
            ([], {7: 1}, Graph :      [1] [[2, 7]] []
            Polynomial : psi_7)
        rcSsi|]\}}|r||�qSr=r=)rBrnrr=r=r>rDr
rEz:TautologicalRing.kappa_psi_polynomials.<locals>.<dictcomp>Nr)rr)
r�rr�zipr�rr�r+rIrh)r8r_ZcomboutZtriv�VrZkVrrnr�clr=r=r>�kappa_psi_polynomialsD
s,
z&TautologicalRing.kappa_psi_polynomialscCs.ddlm}||j|ttd|jd��|j�S)ao
        INPUT:

        r : integer
          degree

        TESTS::

            sage: from admcycles import *
            sage: R = TautologicalRing(1,2)
            sage: R.num_gens(1)
            5
            sage: len(R.generators(1))
            5
            sage: R = TautologicalRing(1,2,moduli='ct')
            sage: R.num_gens(1)
            4
        rrM)r7rNr~r*r�rr2)r8rsrNr=r=r>r�}
szTautologicalRing.num_genscs�|��|}|dur2|�|���fdd�|D�}n|rB|�|�}n
|�|�}|durr|�|���fdd�|D��n|r�|�|��n
|�|��tt�fdd�|D��}|��|S)a�
        Computes the matrix of the intersection pairing of generators in
        degree d (rows) against generators of opposite degree (columns).

        INPUT:

        d : integer
        degree of the classes associated to rows

        basis : bool (default: False)
        compute pairing of basis elements in the two degrees

        ind_d, ind_dcomp: tuple (optional)
        lists of indices of generators in degrees d and its complementary degree

        NOTE:

        The matrix is returned as an immutable object, to allow caching.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(1,2)
            sage: R.pairing_matrix(1)
            [  1/8  1/12  1/12  1/24     1]
            [ 1/12  1/24  1/24     0     1]
            [ 1/12  1/24  1/24     0     1]
            [ 1/24     0     0 -1/24     1]
            [    1     1     1     1     0]
            sage: R.pairing_matrix(1, basis=True)
            [ 1/8 1/12]
            [1/12 1/24]
            sage: R.pairing_matrix(1, ind_d=(0,1), ind_dcomp=(1,2,3))
            [1/12 1/12 1/24]
            [1/24 1/24    0]
        Ncsg|]}�|�qSr=r=r�)�allgensr=r>r|�
rEz3TautologicalRing.pairing_matrix.<locals>.<listcomp>csg|]}�|�qSr=r=r�)�	allcogensr=r>r|�
rEcsg|]��fdd��D��qS)csg|]}�|���qSr=r�)rB�b�rr=r>r|�
rEz>TautologicalRing.pairing_matrix.<locals>.<listcomp>.<listcomp>r=�rB)�cogensr_r>r|�
rE)r#rJrWrr�
set_immutable)r8r_rWZind_dZ	ind_dcomp�dcompr��Mr=)r]r\rar>�pairing_matrix�
s &



zTautologicalRing.pairing_matrixc	

CsHddlm}	t|�|jkr"td��|	|j|ddddddd|j|��d�S)a�
        Return the k-twisted double ramification cycle in genus g and codimension d
        for the partition Avector of k*(2g-2+n). For more information see the documentation
        of :func:`~admcycles.double_ramification_cycle.DR_cycle`.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(2,2)
            sage: DR = R.double_ramification_cycle([1,3], k=1, d=1)
            sage: (DR * R.psi(1)^3).evaluate()
            -11/1920
        r)�DR_cyclez$length of argument Avector must be nNFT)	r_r��rpoly�tautoutrW�chiodo_coeff�r_coeffr�r�)�double_ramification_cyclerfrhrr0r~r2r�)
r8ZAvectorr_r�rgrhrWrirjrfr=r=r>rk�
sz*TautologicalRing.double_ramification_cyclecCs$ddlm}|||j|j|jd��S)a�
        Return the class Theta_{g,n} from [Norbury - A new cohomology class on the moduli space of curves].
        For more information see the documentation of :func:`~admcycles.double_ramification_cycle.ThetaClass`.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(1, 1)
            sage: R.theta_class()
            Graph :      [1] [[1]] []
            Polynomial : 11/6*(kappa_1)_0 + 1/6*psi_1
            <BLANKLINE>
            Graph :      [0] [[3, 4, 1]] [(3, 4)]
            Polynomial : 1/24
            sage: R = TautologicalRing(2, 1, moduli='ct')
            sage: R.theta_class()
            0
        r)�
ThetaClassr�)rkrlr~rr2)r8rlr=r=r>�theta_class�
szTautologicalRing.theta_classcCs8|d||jkrtd��ddlm}|||j||��S)a
        Return the cycle class of the hyperelliptic locus of genus g curves with n marked
        fixed points and m pairs of conjugate points in `\bar M_{g,n+2m}`.

        For more information see the documentation of :func:`~admcycles.admcycles.Hyperell`.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(2,1)
            sage: H = R.hyperelliptic_cycle(1,0)
            sage: H.forgetful_pushforward([1]).fund_evaluate()
            6
        r��<the number n+2m must equal the total number of marked pointsr)�Hyperell)rr0r�ror~)r8r��mror=r=r>�hyperelliptic_cycle�
sz$TautologicalRing.hyperelliptic_cyclecCs8|d||jkrtd��ddlm}|||j||��S)a-
        Return the cycle class of the bielliptic locus of genus g curves with n marked
        fixed points and m pairs of conjugate points in `\bar M_{g,n+2m}`.

        For more information see the documentation of :func:`~admcycles.admcycles.Biell`.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(1,2)
            sage: B = R.bielliptic_cycle(0,1)
            sage: B.degree_list()
            [1]
            sage: B.forgetful_pushforward([2]).fund_evaluate()
            3
        r�rnr)�Biell)rr0r�rrr~)r8r�rprrr=r=r>�bielliptic_cycle
sz!TautologicalRing.bielliptic_cyclecCs<t|�|jkrtd��ddlm}|||||||j|j��S)a�
        Computes the Chern class c_deg of the derived pushforward of a line bundle
        \O(D) on the universal curve C_{g,n} over the space Mbar_{g,n} of stable curves, for

          D = l \tilde{K} + sum_{i=1}^n d_i \sigma_i  +  \sum_{h,S} a_{h,S} C_{h,S}

        where the numbers l, d_i and a_{h,S} are integers, \tilde{K} is the relative canonical
        class of the morphism C_{g,n} -> Mbar_{g,n}, \sigma_i is the image of the ith section
        and C_{h,S} is the boundary divisor of C_{g,n} where the moving point lies on a genus h
        component with markings given by the set S.

        For more information see the documentation of :func:`~admcycles.GRRcomp.generalized_lambda`.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(2,1)
            sage: l = 1; d = [0]; a = []
            sage: t = R.generalized_lambda(1,l,d,a)
            sage: t.basis_vector()
            (1/2, -1/2, -1/2)
        zBthe number of entries of d equal the total number of marked pointsr)�generalized_lambda)rhrr0�GRRcomprtr~)r8�degr�r_rrtr=r=r>rt#sz#TautologicalRing.generalized_lambdar=rc
Cs>t|�|jkrtd��ddlm}|||j||||||d��S)ad
        Return the fundamental class of the closure of the stratum of ``k``-differentials
        with vanishing and pole orders ``mu``.

        For more information see the documentation of :func:`~admcycles.stratarecursion.Strataclass`.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: R = TautologicalRing(1,2)
            sage: H1 = R.differential_stratum(1,(1, -1))
            sage: H1.is_zero()
            True
            sage: H5 = R.differential_stratum(1,(5, -5))
            sage: H5.forgetful_pushforward([2]).fund_evaluate()
            24
        zGthe length of partition mu must equal the total number of marked pointsr)�Strataclass)�virt�res_cond�xi_power�method)rhrr0�stratarecursionrwr~)r8r��murxryrzr{rwr=r=r>�differential_stratum?sz%TautologicalRing.differential_stratum�prescs|std�S|dkr$|dur$td�S|dur@���}|dur@d}|durLd}|dvrXd}g�g�|D]H}t|���dks�td	�St|���}|d
ksd��|���|�qdtd��}t��}ttd||d
����	�}	��
����fdd�tt���D�}
dd�|
D�}���fdd�|D��}g}
td�d�D]��t
���}�fdd�|D�}�fdd�|D��t�fdd�|D�����	�}|D]0�t��fdd�tt���D��}|
�|��q��q4��|
�|��}g}t|�D]�}||d}d}|D]B}|�|	|�d
k�s�|�|	|����r�d}|�|��q8�q�|�r܈�|��|	|�}|�	�}�q�|�r�|D]"}�j|	|dd
����|��qh��t|��}|dk�r������}||fS|dk�r�|���	�fS|dk�r������}��fdd�}|||fSdS)a_
        Computes a presentation of the tautological ring as a quotient of
        a polynomial ring by an ideal, where the generators of the polynomial
        ring are sent to the given generators.

        The function returns the above ideal in a polynomial ring together with
        a ring homomorphism. Assumes FZ relations are sufficient unless otherwise
        instructed.

        INPUT:

        - generators (list) --- optional, if provided we will use the given
          elements as ring generators (it is the user's responsibility to check
          they generate as an algebra)
        - elimimate_generators (bool) --- optional, decides whether to keep around unnecessary ring generators.
        - output (str) --- optional. Can be::
            'pres', in which case returns a surjecion from a polynomial ring to self, and an ideal of that ring (the kernel)
            'lists', in which case it returns generators for the ideal, then lists of generators for R and a correspondng list of generators for TR.
            'fun', in which case it returns an ideal, a rung hom from free polynomial ring to self, and a function from self to free polynomial ring (a lift). This does not allow the user to choose their own basis.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: TR = TautologicalRing(2,0)
            sage: I, f = TR.presentation()
            sage: R = I.ring()
            sage: I == R.ideal([2*R.0*R.1 + R.1^2, 40*R.0^3 - 43*R.1^3, R.1^4])
            True
            sage: R.ngens()
            2
            sage: I, f = TR.presentation(eliminate_generators = False)
            sage: R = I.ring()
            sage: R.ngens()
            5

        By specifying generators lambda_1 and delta_1, we can check a result by
        Faber.::

            sage: gens = (TR.lambdaclass(1), 1/2*TR.sepbdiv(1,()))
            sage: I, f = TR.presentation(gens)
            sage: R = I.ring()
            sage: x0, x1 = R.gens()
            sage: I == R.ideal([x1^2 + x0*x1, 5*x0^3 - x0^2 * x1])
            True
            sage: r = f(x1^2 + x0*x1)
            sage: r.is_zero()
            True

        Going to the locus of smooth curves, we check Theorem 1.1 in [CanLars]_ by Canning-Larson (we correct the exponent of kap1 in the last term).::

            sage: TR = TautologicalRing(7, 0, moduli = 'sm')
            sage: gens = (TR.kappa(1), TR.kappa(2))
            sage: I, f = TR.presentation(gens)
            sage: R = I.ring(); R
            Multivariate Polynomial Ring in x0, x1 over Rational Field
            sage: kap1, kap2 = R.gens()
            sage: idgens = [2423*kap1^2*kap2 - 52632*kap2^2, 1152000*kap2^2 - 2423*kap1^4, 16000*kap1^3*kap2 - 731*kap1^5]
            sage: I == R.ideal(idgens)
            True

        Theorem 1.2 in [CanLars]_ (we correct the signs in the first term).::

            sage: TR = TautologicalRing(8, 0, moduli = 'sm')
            sage: gens = (TR.kappa(1), TR.kappa(2))
            sage: I, f = TR.presentation(gens)
            sage: R = I.ring(); R
            Multivariate Polynomial Ring in x0, x1 over Rational Field
            sage: kap1, kap2 = R.gens()
            sage: idgens = [714894336*kap2^2 - 55211328*kap1^2*kap2 + 1058587*kap1^4, 62208000*kap1*kap2^2 - 95287*kap1^5, 144000*kap1^3*kap2 - 5617*kap1^5]
            sage: I == R.ideal(idgens)
            True

        Theorem 1.3 in [CanLars]_ (corrected the last coefficient of the third generator).::

            sage: TR = TautologicalRing(9, 0, moduli = 'sm')
            sage: gens = (TR.kappa(1), TR.kappa(2), TR.kappa(3))
            sage: I, f = TR.presentation(gens)
            sage: R = I.ring(); R
            Multivariate Polynomial Ring in x0, x1, x2 over Rational Field
            sage: kap1, kap2, kap3 = R.gens()
            sage: idgens = [5195*kap1^4 + 3644694*kap1*kap3 + 749412*kap2^2 - 265788*kap1^2*kap2, 33859814400*kap2*kap3 - 95311440*kap1^3*kap2 + 2288539*kap1^5, 19151377*kap1^5 + 16929907200*kap1*kap2^2 - 1142345520*kap1^3*kap2,  1422489600*kap3^2 - 983*kap1^6, 1185408000*kap2^3 - 47543*kap1^6]
            sage: I == R.ideal(idgens)
            True

        z3Need to insert a step checking FZ conjecture here. �funNz�if you want an inverse, please let me choose my own generators for now. Otherwise I would have to do a bit of linear algebra... TF)�listsr�rz7So far needs to assume the generators are homogeneous. r�wdeglex�x)�ordercsg|]}t�|d���qSr�rr�)�gen_degree_list�maxdegr=r>r|�rEz1TautologicalRing.presentation.<locals>.<listcomp>cSsg|]}|D]}|�qqSr=r=)rBr^rr=r=r>r|�rEcsg|]}�t|�di��qSr��r*)rBr�rtr=r>r|�rEcs,g|]$�t��fdd�tt���D���qS)csg|]}�|�|�qSr=r=r�)r��wr=r>r|�rEz<TautologicalRing.presentation.<locals>.<listcomp>.<listcomp>)rr�rhr`rV)r�r>r|�rEcsg|]}�t|�di��qSr�r�)rBr�r�r=r>r|�rEcsg|]}|����qSr=)rp)rBrp)rvr=r>r|�rEcsg|]}�|�|�qSr=r=r�)�poly_monomials_to_tryrsr=r>r|�rE�	degrevlexrr�csjd}d�td����D]@}|�|��|t���fdd�tt���D��7}�t��7�q||�d�dS)Nrrcs$g|]}�|���|��qSr=rVr�)rt�gennum�vdr=r>r|�rEz<TautologicalRing.presentation.<locals>.f.<locals>.<listcomp>)r�r#rpr�rh)ZtauteltZpolyeltr_)rtr8)r�r�r>r��s
&z(TautologicalRing.presentation.<locals>.f)r$rWrhrr�maxr	rrrr�r#r��idealrr�kernelr��groebner_basis�coefficient�is_constant�elimination_ideal�
remove_varrjr+�hom)r8rJZ	assume_FZZeliminate_generators�outputrQrp�TrLr��LLZLL2Z	deg_ideal�
ideal_gensZexponent_tuples_to_tryZtaut_monomials_to_try�relsrH�genlistZelimlistrnZelimr��I�IIZRhomr=)rtrvr�r�r�r�rsr8r>�presentationXs�V


 (




zTautologicalRing.presentation)T)NNN)N)N)N)rN)N)F)FNN)NNFTFFN)rr)rr)Fr=rr)NTNr):r�r�r�r�r'�Element�staticmethodr�r(r�rr�r�r�r�rrrRr#r�rrrqr�rr�rrrr
�irrbdivr�sepbdivrrr-r3r�rErorJr�rLrQrUrWr[r�rerkrmrqrsrtr~r&r��
__classcell__r=r=r�r>r�snA>
-
'("
M&=<
GJB
Z

.
/%
'
9<



r�c@sJeZdZdZdZdd�Zdd�Zdd�Zd	d
�Zdd�Z	d
d�Z
ejZdS)raM
    Construction functor for tautological ring.

    This class is the way to implement the "promotion of base ring" (see below in the examples).

    EXAMPLES::

        sage: from admcycles.tautological_ring import TautologicalRing, TautologicalRingFunctor
        sage: F = TautologicalRingFunctor(1, (1,), 'st')
        sage: F(QQ)
        TautologicalRing(g=1, n=1, moduli='st') over Rational Field

        sage: x = polygen(QQ, 'x')
        sage: (x**2 + 2) * TautologicalRing(1, 1).generators(1)[0]
        Graph :      [1] [[1]] []
        Polynomial : (x^2 + 2)*(kappa_1)_0
    �
cCs$t�|tt�||_||_||_dSrI)r
r(r�rCr�r�)r8rCr�r�r=r=r>r(sz TautologicalRingFunctor.__init__cCsd�|j|jt|j�S)Nz0TautologicalRingFunctor(g={}, n={}, moduli={!r}))r�rCr�rr�rNr=r=r>rRs�zTautologicalRingFunctor._repr_cCst|j|j|j|d�S)Nr�)r�rCr�r�)r8rtr=r=r>�_apply_functorsz&TautologicalRingFunctor._apply_functorcCs@t|t�r<|j|jkr<|j|jkr<t|j|jt|j|j��SdS)zE
        Return the merge of two tautological ring functors.
        N)r)rrCr��minr�rr=r=r>�merges

�
�zTautologicalRingFunctor.mergecCs.t|t�o,|j|jko,|j|jko,|j|jkSrI)r)rrCr�r�rr=r=r>�__eq__&s

�
�
�zTautologicalRingFunctor.__eq__cCs
||kSrIr=rr=r=r>�__ne__,szTautologicalRingFunctor.__ne__N)
r�r�r�r��rankr(rRr�r�r�r�r�__hash__r=r=r=r>r�s	r)Ar�r��sage.misc.cachefuncr�sage.misc.misc_cr�$sage.structure.unique_representationr�sage.structure.richcmprr�sage.structure.elementrr�sage.structure.allr	�sage.categories.functorr
�sage.categories.pushoutr�sage.categories.algebrasr�sage.categories.ringsr
�sage.arith.allrrr�sage.combinat.allr�sage.combinat.integer_vectorr�sage.rings.ringr�sage.rings.integer_ringr�sage.rings.rational_fieldr� sage.modules.free_module_elementr�1sage.rings.polynomial.polynomial_ring_constructorr� sage.rings.polynomial.term_orderr�%sage.combinat.integer_vector_weightedr�sage.matrix.constructorrr�rr r!r"r#r�r$�stable_graphr%Zidentify_classesr&r�r�r'r�rr=r=r=r>�<module>sh
{k