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

��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#�ZdZd$d%�Zd&d'�Zd(d)�Zd*d+�Zd,d-�Zd.d/�ZdXd0d1�Zd2d3�Zd4d5�Zd6d7�ZdXd8d9�Zd:d;�ZdXd<d=�Z dXd>d?�Z!d[d@dA�Z"dBdC�Z#dXdDdE�Z$dXdFdG�Z%dHdI�Z&dXdJdK�Z'dYdLdM�Z(dZdNdO�Z)d\dQdR�Z*dSdT�Z+dUdV�Z,dS)]�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
    TcCsRt�||�t|ttf�r_i|_|D]J}t|t�st�|j�	�r$t
d��|j�|j�r,q|r8|�
|j�|��|j|jvrV|j|jj|j7_|j|jsU|j|j=q||j|j<qdSt|t�r�|r�i|_|��D]2\}}||jkrxt
�|�|j�rqm|�
|j�|��|r�||jvr�|j||7<qm||j|<qmdS||_dSt�)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(NsL	


��



��

zTautologicalClass.__init__cCs$|��}|�|dd�|j��D��S)NcS�i|]	\}}||���qSr=��copy��.0�gr;r=r=r>�
<dictcomp>��z*TautologicalClass.copy.<locals>.<dictcomp>)r�
element_classr,r7�r8�Pr=r=r>rAszTautologicalClass.copycCs4t|j�D]}|j|��|j|s|j|=qdS�N)r+r,r3�r8rDr=r=r>r3�s
��z"TautologicalClass.dimension_filtercs(�jsdSd��fdd�t�j�D��S)N�0z

c3s�|]
}t�j|�VqdSrJ)�reprr,�rCrD�r8r=r>�	<genexpr>���z+TautologicalClass._repr_.<locals>.<genexpr>)r,�join�sortedrOr=rOr>�_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�
UnicodeArtrLc3s�|]
}�j|��VqdSrJ)r,�
_unicode_art_rNrOr=r>rP�rQz2TautologicalClass._unicode_art_.<locals>.<genexpr>)�sage.typeset.unicode_artrUrVr,rrS)r8rUrVr=rOr>rW�s zTautologicalClass._unicode_art_cCs|jSrJ)r,rOr=r=r>�is_empty��zTautologicalClass.is_emptycC�|����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_nilpotentrOr=r=r>r]��
zTautologicalClass.is_nilpotentcCr[)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
        )r\�is_unitrOr=r=r>r_�r^zTautologicalClass.is_unitcCs<zddlm}W||�Styddlm}Y||�Sw)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_unitcC�B|��}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_partrG)r8�drI�	new_termsrDr<r=r=r>rd�s
�zTautologicalClass.degree_partcC�|��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_evaluaterOr=r=r>r\s z&TautologicalClass.constant_coefficientcCrc)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_caprG)r8�dmaxrIrfrDr<r=r=r>ri#s

�zTautologicalClass.degree_capc
Os�i}|j��D]E\}}|jdd�}|jj}|jj}d}|t|�krF||j|i|��||<||s<|�|�|�|�n|d7}|t|�ks!|rL|||<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,r7rAr5�coeff�monom�len�subs�poprrG)
r8�args�kwdsrfrDr<�coeffsrn�irIr=r=r>rp8s$
��zTautologicalClass.subsNcCs^|��r|S|��}|dur|�|�|�|�S|��}|��D]
}||�|�|�|�7}q|S)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)rYr�from_basis_vector�basis_vector�zero�degree_list)r8�r�R�resultr=r=r>�
FZsimplifyjszTautologicalClass.FZsimplifycCs�|��}|dus|dur5ddlm}|dd�|dur ||jks)|dur5||jkr5td�|||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@�rCr;r=r=r>�
<listcomp>��z5TautologicalClass.toprodtautclass.<locals>.<listcomp>)r�
supersededr�_g�_nr0�format�	admcyclesr��
trivial_graphr,�values)r8rD�nrIrr�r=r=r>�toprodtautclass�s
$z!TautologicalClass.toprodtautclassc	Cs�|��}|dus|dus|durddlm}|dd�|dur#|g}n|��}|��r/|��S|��}|D]
}||�|�|�|�7}q5|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
        Nrr~r�zFthe arguments 'g, n, r'  of TautologicalClass.simplify are deprecated.)
rr�rryrYrArx�from_vectorrr,)	r8rDr�rzrIr�Dr|rer=r=r>�simplify�s
zTautologicalClass.simplifycCs�|��}|dus|dur5ddlm}|dd�|dur ||jks)|dur5||jkr5td�|||j|j���|��r=|��S|durJ|�	|�
|�|�S|��|S)zj
        Return a simplified version of self by combining terms with same tautological generator.
        Nrr~r�zIthe arguments 'g' and 'n' of TautologicalClass.simplified are deprecated.z,invalid g,n (got ({},{}) instead of ({},{})))rr�rr�r�r0r�rYrAr�rr�)r8rDr�rzrIrr=r=r>�
simplified�s
$zTautologicalClass.simplifiedcCs8|��r|��S|��}|j|dd�|j��D�dd�S)NcSsi|]\}}||�qSr=r=�rCrDr<r=r=r>rE��z-TautologicalClass.__neg__.<locals>.<dictcomp>F�r:)rYrArrGr,r7rHr=r=r>�__neg__�s zTautologicalClass.__neg__cCs�|��r|��S|��r|��S|��}dd�|j��D�}|j��D]\}}||vr=||j|j7_||s<||=q#|||<q#|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
        cSr?r=r@rBr=r=r>rE�rFz+TautologicalClass._add_.<locals>.<dictcomp>Fr�)rYrArr,r7r5rG)r8�otherrIrfrDr<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>rErFz,TautologicalClass._lmul_.<locals>.<dictcomp>Fr�)	r�	base_ring�is_zerorx�is_onerAr,r7rG)r8r�rIrfr=r�r>�_lmul_�szTautologicalClass._lmul_c
CsH|��s|��r|����S|��}|jrD|jd|jkrDdd�t|j�D�}dd�t|j�D�}|j|dd�|j|dd�j|dd�Si}|j��D]B}|j��D]:}|�	||�j�
�D].\}}	|�|j�rhq]|	j
|jd�|	jjdd�|	r�||vr�||j|	j7_q]|	||<q]qRqKt|�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
        �����cS�i|]	\}}||d�qS�rr=�rCru�jr=r=r>rE,rFz+TautologicalClass._mul_.<locals>.<dictcomp>cS�i|]	\}}|d|�qSr�r=r�r=r=r>rE-rFF��inplace��moduli)�forcer�)rYrrx�	_markingsr��	enumerate�rename_legsr,r��multiplyr7r1r2r3r5r4r+rG)
r8r�rIZ
dic_to_stdZdic_from_stdrf�t1�t2rDr<r=r=r>�_mul_s6&����zTautologicalClass._mul_cCsz|�d�r	td��|��}|��}|��}|dkr|S||}|}td|d�D]}||}|tdt|�f�|7}q(|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)r8rI�frjr|�y�kr=r=r>�expBs
zTautologicalClass.expcs�|��}�dur
t|j��dkrtd��|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>rP���z-TautologicalClass.evaluate.<locals>.<genexpr>)rrr2r0r"�TautologicalRingr�r�r�r��sumr,r�rx�r8r�rIr{r=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
        rrrOr=r=r>rh�szTautologicalClass.fund_evaluatecs^|tkr|tkrtd��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incomparablec3s8�|]}��|���|�kp��|���|�kVqdSrJ)rrw�rCre�r�r8r=r>rP�s�6z.TautologicalClass._richcmp_.<locals>.<genexpr>)rrr-�setry�updaterS�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��|���VqdSrJ)rr�rwr�rOr=r>rP���*z-TautologicalClass.__bool__.<locals>.<genexpr>)r�ryrOr=rOr>�__bool__�szTautologicalClass.__bool__cCsj|��}t||j�}||jkrtd�t|t|j���||jkr2t|j|j||�	�d�}||�S|S)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�)
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]\}}}|VqqdSrJ)�gnr_list)rCr;rDr�rer=r=r>rPs�&z0TautologicalClass.degree_list.<locals>.<genexpr>)rSr�r,r�rOr=r=r>rys
zTautologicalClass.degree_listcs�|��}�dur
|St�tj�rt��}|dkrtd��n(t�ttf�rEtt���}t	��t	t
|jd|j|d��krEtd��|���|dkrK|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"�|]}���|����VqdSrJ)rGZforgetful_pullback_list)rC�c�r{�forgetr=r>rPIs� z7TautologicalClass.forgetful_pullback.<locals>.<genexpr>)rr)�numbers�Integralrr0r*r+ror�r�r�r�r�r�r2r�r�r,r�rx)r8r�rIr�r=r�r>�forgetful_pullback s �&$z$TautologicalClass.forgetful_pullbackcsF|��}�dur|��St�tj�r2t��}|dks||jkr#td��|dkr)|S|j|d��nEt�t	t
f�rstt���}t���t
t��d�D]}�|�|dkr`td��|���qK�D]}||jvrqtd�|���qcntd��|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=�rCru�r�r=r>r��r�z;TautologicalClass.forgetful_pushforward.<locals>.<listcomp>r�c�g|]}|����qSr=)�forgetful_pushforward�rCr<r�r=r>r��r�)rrAr)r�r�rr�r0r�r*r+rorSr�r�r-r�r�r2r�rGr,r�)r8r�rIr�ru�new_markingsr{r=r�r>r�Ks88�
�� z'TautologicalClass.forgetful_pushforwardcs�|durddlm}|dd�|��}�fdd�|jD�}tt|����}||jkrB|r5td�|j|���t	|j
||j|��d	�}n|}|rft
|j���}	|j��|	D]}
|
j�d
d�|
|j|
j<qT|S|�fdd
�|j��D��S)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
        Nrr~r�zYthe rename keyword in TautologicalClass.rename_legs is deprecated. Please set rename=Nonecsi|]	}|��||��qSr=)�getr���dicr=r>rE�rFz1TautologicalClass.rename_legs.<locals>.<dictcomp>z6invalid inplace operation: change of markings {} -> {}r�Tr�csg|]	}|j�dd��qS)Fr�)r�r�r�r=r>r��rFz1TautologicalClass.rename_legs.<locals>.<listcomp>)r�rrr�r*rSr�r0r�r�r�r2r�r+r,�clearr�r.)r8r��renamer�rrIZ	clean_dicr�r{�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�r6rKr=r=r>�permutation_action�sz$TautologicalClass.permutation_actioncCsV|jsdS|��j}|durddlm}||�}|��D]}|�|�|kr(dSqdS)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�rDr=r=r>�is_symmetric�s
�zTautologicalClass.is_symmetriccsX���}|��}�js
|S|durddlm}||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�c3��|]}��|�VqdSrJ)r�rNrOr=r>rP7��z/TautologicalClass.symmetrize.<locals>.<genexpr>)rrxr,r�r�r�r��cardinality)r8r�rI�resr�r=rOr>�
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�cSr�r�r=r�r=r=r>rEMrFz7TautologicalClass.standard_markings.<locals>.<dictcomp>Fr�)rr�r�r�r�rHr=r=r>�standard_markings9sz#TautologicalClass.standard_markingscs�|��}|���|��r�durtd��tt�����S�dur2|���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�VqdSrJ)r�r�r2r��rIr�rzr=r>rP�s�$z+TautologicalClass.vector.<locals>.<genexpr>)r�rrYr0rr�num_gensryror�r�r�r,r��r8rzr=r�r>rOsI zTautologicalClass.vectorcCs�|��}|��}t||j�}||jkr td�t|t|j���|dur6|��}t|�dkr2td��|d}||jkrR|��}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�rryror�r�r�r�rwr�r�rr�)r8rzr�rIr{r�r=r=r>rw�s&;
�
 zTautologicalClass.basis_vectorcCsTddlm}|dd�|��}|dur||jks!|dur%||jkr%td��|�|�S)Nrr~r�z4toTautvect is deprecated. Please use vector instead.�invalid g,n)r�rrr�r�r0r)r8rDr�rzrrIr=r=r>�
toTautvect�s
$
zTautologicalClass.toTautvect�stcCs�ddlm}|dd�|��}|dur||jks!|dur%||jkr%td��t||jkr<t|j|j||�	�d�}||�}|�
|�S)Nrr~r�z;toTautbasis is deprecated. Please use basis_vector instead.r�r�)r�rrr�r�r0r r2r�r�rw)r8rDr�rzr�rrIr=r=r>�toTautbasiss
$
zTautologicalClass.toTautbasiscs4ddlm}|dd�|����fdd�|��D�S)Nrr~r�z7gnr_list is deprecated. Please use degree_list instead.csg|]	}�j�j|f�qSr=)r�r�r��rIr=r>r�rFz.TautologicalClass.gnr_list.<locals>.<listcomp>)r�rrry)r8rr=r�r>r�s
zTautologicalClass.gnr_listcOs2ddlm}|dd�|j|i|��}|j|_|S)Nrr~r�z2coeff_subs is deprecated. Please use subs instead.)r�rrpr,)r8rrrsrr|r=r=r>�
coeff_subss

zTautologicalClass.coeff_subs�TrJ)NN�NNN�NT)NNNr�)-�__name__�
__module__�__qualname__�__doc__r(rAr3rTrWrYr]r_r`rdr\rirpr}r�r�r�r�r�r�r�r�r�rhr�r�r�ryr�r�r�r�r�r�r�rrwr�r�r�r�r=r=r=r>r',sV
!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(ded@dA�Z)dedBdC�Z*dfdEdF�Z+dGdH�Z,dedIdJ�Z-dgdLdM�Z.dNdO�Z/e
dhdPdQ��Z0didRdS�Z1dTdU�Z2djdVdW�Z3djdXdY�Z4dZd[�Z5dkd^d_�Z6e7Z7dldadb�Z8�Z9S)mr�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()
        (...)
    csF|�dd�}|�dd�}|�dd�}|�dd�}|r%td�t|������t|�dkr7|dur3td��|d}t|�d	krI|durEtd
��|d}t|�dkr[|durWtd��|d	}t|�d
krm|duritd��|d}t|�d
krztd�|���t|�}t|tj	�r�|dkr�td��t
|�}|dur�t
��}d�n\t|ttf�r�dd�t
|�D��t��}����r��ddkr�td��t�fdd�t|d�D��r�td��t���n t|tj	�r�|dkr�td��t
|�}ttd|d���ntd��|dur�t}n|tv�r
td�|���||fdv�rtd�||���t��||�||�S)NrDr�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���z2TautologicalRing.__classcall__.<locals>.<listcomp>z"markings must be positive integersc3s$�|]
}�|�|dkVqdS�rNr=r���markingsr=r>rP�s�"z1TautologicalRing.__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) = ({}, {}))rqr0r�r+�keysror"r)r�r�rrxr*rS�sort�anyr�r-r�_CommutativeRings�super�
__classcall__)�clsrrrsrDr�r�r���	__class__rr>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�ror�r2)r8rDrr�r�r=r=r>r(�s



zTautologicalRing.__init__cCsdSr�r=rOr=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�r�r�r�r2r�rOr=r=r>r��sz"TautologicalRing.standard_markingscC�|��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_domainrOr=r=r>r��z#TautologicalRing.is_integral_domaincCr)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_fieldrOr=r=r>r�rzTautologicalRing.is_fieldcCr)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_fieldrOr=r=r>r�szTautologicalRing.is_prime_fieldcCsXt|t�r"|j|jkr$|j|jkr&|j|jkr(|���|���r*dSdSdSdSdSdS)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�rOr=r=r>�constructionszTautologicalRing.constructioncCsV|jttd|jd��krd�|j|jt|j|���Sd�|j|jt|j|���S)Nrz1TautologicalRing(g={}, n={}, moduli={!r}) over {})	r�r*r�r�r�r�rr2r�rOr=r=r>rT8szTautologicalRing._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�r�r2rOr=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]] []
        Frk)r%r�r+r�rOr=r=r>r�kszTautologicalRing.trivial_graphcCrgrJ)r�rOr=r=r>�_an_element_xrZzTautologicalRing._an_element_csz�fdd������D�}|dd�|dd�}|������jr.|����jd���jdkr;|���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=�rC�srOr=r>r��r�z2TautologicalRing.some_elements.<locals>.<listcomp>Nr������rr)r��
some_elements�append�irreducible_boundary_divisorr��psir��kappa)r8Z	base_elts�eltsr=rOr>r{s
zTautologicalRing.some_elementscCs|�|g�S)z�
        Return the zero element.

        EXAMPLES::

            sage: from admcycles import TautologicalRing
            sage: TautologicalRing(0, 4).zero()
            0
        )rGrOr=r=r>rx�s
zTautologicalRing.zerocCsv|��}|D]2}|�|�}|j��D]%\}}||jvr0|j|j|j7_|j|s/|j|=q|��|j|<qq|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
        )rx�coercer,r7r5rA)r8�classesr|r;rDr<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�rOr=r=r>�fundamental_class�sz"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�r"r')r8rur=r=r>r"�s&zTautologicalRing.psicCsV|dkrd|jd|j|��S|dkr|��S||��dg|ddggd�S)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#)r�r�r�rxr�)r8�ar=r=r>r#s
"zTautologicalRing.kappaTcCs�|j}|j}||ks|dkr|��S|dkrZ|rZ|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�r�rxr(r+r�r�r2�lambdaclassr��chern_character_to_class�hodge_chern_character)r8rer+rDr�Znewmarksr{r=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�rxr�r%r+)r8rD�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+rSr�)r8�h�ArDr/r�r=r=r>�separable_boundary_divisor�s&���z+TautologicalRing.separable_boundary_divisorcsh�jr�jdt�j�krtd���j}�j�|dkr���Sddlm}||�d�}|�d�}t	����dkr>|��
�S�ddksH�dkrL���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�)�psiclc3sD�|]}d|��dd�|��dd��d|VqdS�r�rr�Nr=r��rer�r4r=r>rP���Bz9TautologicalRing.hodge_chern_character.<locals>.<genexpr>c3sD�|]}d|��dd�|��dd��d|VqdSr5r=r�r6r=r>rP�r7c3s�|]
}��|��VqdSrJr)r�)rer8r=r>rP�rQ�r5c3s,�|]}td|��f��|�d�VqdS)rr8N)r�automorphism_number)rC�ind)�psipsisum_twovertr8r=r>rP�r�)r�ro�NotImplementedErrorr�r�rxr�r3rqrr(r4r�r�r#rr3rr)r8rerDr3�bdriesZirrbdryZpsipsisum_onevertZcontribr=)rer�r4r;r8r>r.�s,
,z&TautologicalRing.hodge_chern_charactercs~t�t�r�fdd�td|d�D��n�fdd�td|d�D��t�fdd�t|�D��}|dkr9||��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�rz)rr�r��charr=r>r�)s0z=TautologicalRing.chern_character_to_class.<locals>.<listcomp>rcs,g|]}d|dt|d��|��qS)r�r)rrr?r=r>r�+�,c3s>�|]}t|���tt|��t�fdd�|D��VqdS)c3s�|]	}�|dVqdSrr=)rCr���argr=r>rP-r�zFTautologicalRing.chern_character_to_class.<locals>.<genexpr>.<genexpr>N)r�to_exprrorrrBr=r>rP-s�<z<TautologicalRing.chern_character_to_class.<locals>.<genexpr>rr>)r)r'r�r�rr(r�)r8r;r@r�r=)rCr@r>r-�s
2z)TautologicalRing.chern_character_to_classcCsl|��|jks|��|jkrtd�|��|��|j|j���tt|����}||j	kr4td�||j	���dS)Nz;invalid stable graph (has g={}, n={} instead of g={}, n={})z4invalid stable graph (has markings={} instead of {}))
rDr�r�r�r0r�r*rS�
list_markingsr�)r8Zstgrr=r=r>�_check_stable_graph2s�
�z$TautologicalRing._check_stable_graphNcsBt�t�r'���}|j|jks|j|jkrtd��|�|dd��j��D��S�s-|�	�St�t
�ry|�����|j
�rA|�	�S|durjt|t�sNtd��|D]�t��fdd�t����D��sitd�����qPt�|||d	�}|�||g�St�ttf�r��D]}t|t�s�td
��|�|j�q�|�|��Std�����)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 spacescSr?r=r@r�r=r=r>rEgrFz:TautologicalRing._element_constructor_.<locals>.<dictcomp>Nzpsi must be a dictionaryc3s�|]
}��j|vVqdSrJ)�_legs)rC�v�rCrur=r>rPrrQz9TautologicalRing._element_constructor_.<locals>.<genexpr>zunknown marking {} for psi)r#r"r5zmust be a sequence of decstratazunknown argument of type arg={})r)r'rr�r�r0rGr,r7rxr%rFr1r2r6rr��	num_vertsr�r$r*r+r-r.r<)r8rCr#r"r5rI�decr<r=rIr>�_element_constructor_:s6
)


 �
z&TautologicalRing._element_constructor_c
	CsHddlm}ddlm}|j|j|ttd|jd��t	|j
dd�d�}g}t|�t|�kr1td��t
|�}|����}zt�||�}WntyWt|j|j|j
|d�}Ynwtt|��D]}	||	seq^|||	�}
|
j||	9_|�|
�q^|jr�|jd	|jkr�d
d�t|j�D�}|��}|�||�j|dd
�S|�||�S)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�cSr�r�r=r�r=r=r>rE�rFz0TautologicalRing.from_vector.<locals>.<dictcomp>Fr�)r�rN�rP�
all_stratar�r*r�r�r"r2ror0rrr�r	�
common_parentr-r�r5r r�r�r�rGr�)
r8rHrzrNrP�strata�stratmod�BR�TRru�	currstratr�r{r=r=r>r��s40��zTautologicalRing.from_vectorcCsT|dks
||��krdS|jtkrtd��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�r]ror�r�r)r8rzr]r=r=r>�	dimension�s
zTautologicalRing.dimensioncCsL|jtkr	td��ddlm}m}ddlm}||j|j	|t
|jd�}|j|j|tt
d|j	d��t|jdd�d�}g}t|�t|�krXd	d
lm}	|	d�t|�t|��t�t
t|��D]}
||
seq^||||
�}|j||
9_|�|�q^|jr�|jd|j	kr�d
d�t|j�D�}|��}
|
�|
|�j|dd�S|�||�S)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)rNr]rOr�TrQrSr)�warnz1vector v has wrong length {}; should have been {}r�cSr�r�r=r�r=r=r>rE#	rFz6TautologicalRing.from_basis_vector.<locals>.<dictcomp>Fr�)r2r!r<r�rNr]rTrPr�r�rrUr*r�r"ro�warningsr_r��DeprecationWarningr5r r�r�r�rGr�)r8rHrzrNr]rPZgenindrWrXr_rur[r�r{r=r=r>rv�s.
'�z"TautologicalRing.from_basis_vectorcCs:|durg}t|��d�D]
}|�|�|��q|St|�}|dks)||��kr+gSddlm}ddlm}g}|j	rk|j	d|j
krk||j|ttd|j
d��t
|jdd�d	�D]}|�|�|||�g��qZ|S||j|ttd|j
d��t
|jdd�d	�D]}|�|�|||��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)rUrMr�TrQrScSr�r�r=r�r=r=r>rE	rFz/TautologicalRing.generators.<locals>.<dictcomp>)r�r#�extend�
generatorsrrPrUr�rNr�r�r�r*r"r2r rGr�r�)r8rzr|rUrNr��stratumr=r=r>rc*	s$C00�0zTautologicalRing.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*rcr�r=r=r>r��	szTautologicalRing.gensc	s�|durt�fdd�t���d�D��St|�}|dks#|���kr't��Sz�j}WntyAdg���d}�_Ynwddlm}||durht|�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
        Nc3r�rJ)�ngens)rCrzrOr=r>rP�	r�z)TautologicalRing.ngens.<locals>.<genexpr>rr��
num_strataTrQrS)r�r�r#rrx�_ngens�AttributeErrorrPrgr�r*r�r"r2)r8rzrergr=rOr>re�	s"
�4zTautologicalRing.ngensrcCsj|dks||�|�krtd��|dur.d}||�|�kr.||�|�8}|d7}||�|�ks|�|�|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)rer0rc)r8rurzr=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)rcr�ro�printrM)r8rz�Lrur=r=r>�list_generators�	s
"�z TautologicalRing.list_generatorscs�|durg}t|��d�D]
}|�|�|��q|S|jtkr$td��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 curvesr\c�g|]}�|�qSr=r=r��r�r=r>r�B
r�z*TautologicalRing.basis.<locals>.<listcomp>r�)
r�r#rb�basisr2r!r<r�r]rcr�r�r)r8rer|r]r=rpr>rq
s

(zTautologicalRing.basisFc	cs��|��}t|d|j�D]N}dd�t|j|dd��D�}t|d�D]6}g}t|���D]\}}	|	s5q.|�dg|dt	|��|	||<q.||||gd�}
|rW|||
fn|
Vq$q
dS)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=)rCrur*r=r=r>rEr
rFz:TautologicalRing.kappa_psi_polynomials.<locals>.<dictcomp>Nr)r"r#)
r�rr��zipr�rr�rDrbro)r8reZcomboutZtriv�Vr"ZkVr#rur*�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
        rrf)rPrgr�r*r�r�r2)r8rzrgr=r=r>r�}
s"zTautologicalRing.num_genscs�|��|}|dur|�|���fdd�|D�}n
|r!|�|�}n|�|�}|dur9|�|���fdd�|D��n
|rA|�|��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]
        Ncror=r=r�)�allgensr=r>r��
r�z3TautologicalRing.pairing_matrix.<locals>.<listcomp>cror=r=r�)�	allcogensr=r>r��
r�csg|]��fdd��D��qS)csg|]}�|���qSr=r�)rC�b�r*r=r>r��
r�z>TautologicalRing.pairing_matrix.<locals>.<listcomp>.<listcomp>r=�rC)�cogensryr>r��
�)r#rcrqrr�
set_immutable)r8rerqZind_dZ	ind_dcomp�dcompr��Mr=)rwrvr{r>�pairing_matrix�
s &



zTautologicalRing.pairing_matrixc	

CsHddlm}	t|�|jkrtd��|	|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)	rer��rpoly�tautoutrq�chiodo_coeff�r_coeffr�r�)�double_ramification_cycler�ror�r0r�r2r�)
r8ZAvectorrer�r�r�rqr�r�r�r=r=r>r��
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�)r�r�r�r�r2)r8r�r=r=r>�theta_class�
szTautologicalRing.theta_classcC�8|d||jkr
td��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)r�r0r�r�r�)r8r��mr�r=r=r>�hyperelliptic_cycle�
sz$TautologicalRing.hyperelliptic_cyclecCr�)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�r�r)�Biell)r�r0r�r�r�)r8r�r�r�r=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)ror�r0�GRRcompr�r�)r8�degr�rer*r�r=r=r>r�#sz#TautologicalRing.generalized_lambdar=r+c
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)ror�r0�stratarecursionr�r�)r8r��mur�r�r�r�r�r=r=r>�differential_stratum?sz%TautologicalRing.differential_stratum�prescs�|std�S|dkr|durtd�S|dur ���}|dur d}|dur&d}|dvr,d}g�g�|D]$}t|���dksBtd	�St|���}|d
ksV��|���|�q2td��}t��}ttd||d
����	�}	��
����fdd�tt���D�}
dd�|
D�}���fdd�|D��}g}
td�d�D]B�t
���}�fdd�|D�}�fdd�|D��t�fdd�|D�����	�}|D]�t��fdd�tt���D��}|
�|�q�q���|
�|��}g}t|�D]=}||d}d}|D]}|�|	|�d
k�s|�|	|����rd}|�|�nq�|�r)��|��|	|�}|�	�}q�|�rA|D]}�j|	|dd
����|��q/��t|��}|dk�rW�����}||fS|dk�rc|���	�fS|dk�rz�����}��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��r|z1TautologicalRing.presentation.<locals>.<listcomp>cSsg|]	}|D]}|�qqSr=r=)rCrxr*r=r=r>r��rFc�g|]
}�t|�di��qSr��r*)rCr*�r{r=r>r���cs,g|]�t��fdd�tt���D���qS)csg|]
}�|�|�qSr=r=r�)r��wr=r>r��r�z<TautologicalRing.presentation.<locals>.<listcomp>.<listcomp>)rr�rorzrp)r�r>r��rAcr�r�r�)rCr�r�r=r>r��r�cr�r=)rw)rCr�)r�r=r>r��r�csg|]
}�|�|�qSr=r=r�)�poly_monomials_to_tryrzr=r>r��r��	degrevlexr�r�csjd}d�td����D] }|�|��|t���fdd�tt���D��7}�t��7�q||�d�dS)Nrrcs$g|]}�|���|��qSr=rpr�)r{�gennum�vdr=r>r��s$z<TautologicalRing.presentation.<locals>.f.<locals>.<listcomp>)r�r#rwr�ro)ZtauteltZpolyeltre)r{r8)r�r�r>r��s
&z(TautologicalRing.presentation.<locals>.f)r<rqrory�maxr rrrr�r#r��idealrr�kernelr��groebner_basis�coefficient�is_constant�elimination_ideal�
remove_varrqr+�hom)r8rcZ	assume_FZZeliminate_generators�outputrjr��Trer��LLZLL2Z	deg_ideal�
ideal_gensZexponent_tuples_to_tryZtaut_monomials_to_try�relsrI�genlistZelimlistruZelimr��I�IIZRhomr=)r{r�r�r�r�r�rzr8r>�presentationXs�V

�

 �(
��



�zTautologicalRing.presentationr�r�rJ)rN)F)FNN)NNFTFFNr)Fr=rr+)NTNr�):r�r�r�r�r'�Element�staticmethodr	r(rrr�rrrrrrTr#r�rrrxr�r(r�r"r#r,r!�irrbdivr2�sepbdivr.r-rFrLr�r^rvrcr�rerjrnrqrur�r�r�r�r�r�r�r�r&r��
__classcell__r=r=rr>r�spA>
-
'(
"M&=<
GJ
B
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�||_||_||_dSrJ)r
r(rrDrr�)r8rDrr�r=r=r>r(s
z TautologicalRingFunctor.__init__cCsd�|j|jt|j�S)Nz0TautologicalRingFunctor(g={}, n={}, moduli={!r}))r�rDrrr�rOr=r=r>rTs�zTautologicalRingFunctor._repr_cCst|j|j|j|d�S)Nr�)r�rDrr�)r8r{r=r=r>�_apply_functorsz&TautologicalRingFunctor._apply_functorcCsHt|t�r|j|jkr |j|jkr"t|j|jt|j|j��SdSdSdS)zE
        Return the merge of two tautological ring functors.
        N)r)rrDr�minr�rr=r=r>�merges

�
��zTautologicalRingFunctor.mergecCs.t|t�o|j|jko|j|jko|j|jkSrJ)r)rrDrr�rr=r=r>�__eq__&s

�
�
�zTautologicalRingFunctor.__eq__cCs
||kSrJr=rr=r=r>�__ne__,s
zTautologicalRingFunctor.__ne__N)
r�r�r�r��rankr(rTr�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&rrr'r�rr=r=r=r>�<module>sj
{k