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

��c6��@s8ddlZddlZddlmZddlmZddlmZddlm	Z	ddl
mZddlm
Z
ddlmZdd	lmZdd
lmZddlmZmZddlmZdd
lmZddlZddlZddlZddlZddlZddl Zddl!Zddl"Zddl#Zddl$m%Z%m&Z&m'Z'm(Z(Gdd�de�Z)Gdd�de)�Z*Gdd�de)�Z+dS)�N)�
partitions)�
SageObject)�matrix)�flatten)�
cached_method��QQ��	factorial)�WeightedIntegerVectors)�binomial)�pi�I)�prod)�deepcopy)�hash_AG�unite_embedded_graphs�adm_key�get_squished_levelc@s�eZdZdZd�dd�Zdd�Zdd�Zd	d
�Zdd�Zd�d
d�Z	dd�Z
dd�Zedd��Z
dd�Zdd�Zedd��Zedd��Zedd��Zedd ��Zed!d"��Zed#d$��Zed%d&��Zed'd(��Zed)d*��Zd+d,�Zed-d.��Zed/d0��Zed1d2��Zd3d4�Zed�d6d7��Zed8d9��Z d�d:d;�Z!ed�d<d=��Z"d�d>d?�Z#ed�d@dA��Z$ed�dBdC��Z%d�dDdE�Z&edFdG��Z'edHdI��Z(edJdK��Z)ed�dLdM��Z*edNdO��Z+edPdQ��Z,edRdS��Z-edTdU��Z.edVdW��Z/d�dYdZ�Z0d�d\d]�Z1ed�d_d`��Z2dadb�Z3dcdd�Z4ededf��Z5dgdh�Z6didj�Z7ed�dkdl��Z8d�dmdn�Z9dodp�Z:d�dqdr�Z;dsdt�Z<d�dudv�Z=d�dwdx�Z>edydz��Z?d�d{d|�Z@ed}d~��ZAedd���ZBed�d�d���ZCd�d�d��ZDd�d��ZEed�d�d���ZFed�d���ZGd�d�d��ZHed�d���ZIed�d���ZJed�d���ZKed�d���ZLd�d��ZMed�d�d���ZNd�d��ZOed�d�d���ZPed�d���ZQd�d�d��ZRd�d�d��ZSd�d�d��ZTd�d�d��ZUd�d�d��ZVd�d�d��ZWd�d��ZXd�d�d��ZYd�d�d��ZZd�d�d��Z[d�d�d��Z\�dd�d��Z]�dd�d��Z^d�d��Z_�dd�d��Z`�dd�d��Za�dd�d„Zb�dd�dĄZc�dd�dƄZdd�dȄZeid5d5d5fd�dʄZf�dd�d̄Zged�d΄�Zhed�dЄ�Zied�d҄�Zjed�dԄ�Zked�dք�Zl�dd�d؄Zm�d	d�dڄZnd�d܄ZodS(
�GeneralisedStratumaF
    A union of (meromorphic) strata with residue conditions.

    A GeneralisedStratum is uniquely identified by the following information:

    * sig_list : list of signatures [sig_1,...,sig_n], where sig_i is the Signature
      of the component i,
    * res_cond : list of residue conditions, i.e. [R_1,...,R_n] where each R_l is
      a list of tuples (i,j), corresponding to the j-th component of sig_i, that
      share a residue condition (i.e. the residues at these poles add up to 0).

    Note that the residue theorem for each component will be added automatically.
    NcCs�tdd�|D��sJ�t|�|_||_tdd�|D��|_dd�|D�|_dd�t|�D�|_t|j�|_	|durvg}||_
|��dS)Ncss|]}|jdkVqdS��N��k��.0�sig�r�K/home/user/Introduction lectures/admcycles/diffstrata/generalisedstratum.py�	<genexpr>��z.GeneralisedStratum.__init__.<locals>.<genexpr>cSsg|]
}|j�qSr��nrrrr�
<listcomp>�r z/GeneralisedStratum.__init__.<locals>.<listcomp>cSsg|]
}|j�qSr)�grrrrr#�r cSs$g|]\}}|jD]}||f�qqSr)Zpole_ind)r�ir�jrrrr#�s
�)�all�len�_h0�	_sig_list�sum�_n�_g�	enumerate�	_polelist�_p�	_res_cond�	init_more)�self�sig_list�res_condrrr�__init__�s
zGeneralisedStratum.__init__cCs:d|_d|_d|_d|_i|_d|_i|_d|_d|_dS�N)	�_bics�
_smooth_LG�_all_graphs�_lookup_list�_lookup�_DG�_AGs�_ONE�_ZERO�r3rrrr2�szGeneralisedStratum.init_morecCsd|j|jfS)Nz+GeneralisedStratum(sig_list=%r,res_cond=%r))r*r1rArrr�__repr__�s�zGeneralisedStratum.__repr__cCs|d}|jdkr|d7}n|d7}|jD]}|t|j�d7}q&|d7}|js\|tg�d7}|jD]}|t|�d7}qb|S)N�r�Product of Strata:
�	Stratum: �
�with residue conditions: )r)r*�reprrr1�r3�repr�resrrr�__str__�s



zGeneralisedStratum.__str__cCs�dd�}t|�td|j�td|���td�d}t|j�D].\}}t|�}td||||�f�||7}qDtd|�d	S)
a-
        Print facts about self.

        This calculates everything, so could take long(!)

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=GeneralisedStratum([Signature((1,1))])
            sage: X.info()
            Stratum: (1, 1)
            with residue conditions: []
            Genus: [2]
            Dimension: 4
            Boundary Graphs (without horizontal edges):
            Codimension 0: 1 graph
            Codimension 1: 4 graphs
            Codimension 2: 4 graphs
            Codimension 3: 1 graph
            Total graphs: 10

            sage: X=GeneralisedStratum([Signature((4,))])
            sage: X.info()
            Stratum: (4,)
            with residue conditions: []
            Genus: [3]
            Dimension: 5
            Boundary Graphs (without horizontal edges):
            Codimension 0: 1 graph
            Codimension 1: 8 graphs
            Codimension 2: 19 graphs
            Codimension 3: 16 graphs
            Codimension 4: 4 graphs
            Total graphs: 48
        cSs|dkrdSdSdS)Nr�graph�graphsrr!rrr�_graph_word�sz,GeneralisedStratum.info.<locals>._graph_wordz	Genus: %sz
Dimension: %sz+Boundary Graphs (without horizontal edges):rzCodimension %s: %s %szTotal graphs: %sN)�printr-�dimr.�
all_graphsr()r3rO�tot�crNr"rrr�info�s$
zGeneralisedStratum.infocCst||�}|�|�S)aB
        The AdditiveGenerator for the psi-polynomial given by leg_dict on enh_profile.

        For example, if psi_2 is the psi-class at leg 2 of enh_profile,
        the polynomial psi_2^3 would be encoded by the leg_dict {2 : 3}.

        This method should always be used instead of generating AdditiveGenerators
        directly, as the objects are cached here, i.e. the _same_ object is returned
        on every call.

        INPUT:

        enh_profile: tuple
        The enhanced profile

        leg_dict: dict (optional)
        A dictionary mapping legs of the underlying
        graph of enh_profile to positive integers, corresponding to
        the power of the psi class associated to this leg. Defaults to None.

        OUTPUT:

        The AdditiveGenerator

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=Stratum((2,))
            sage: a = X.additive_generator(((0,),0))
            sage: a is X.additive_generator(((0,),0))
            True
            sage: a is AdditiveGenerator(X,((0,),0))
            False
        )r�additive_generator_from_hash)r3�enh_profile�leg_dict�ag_hashrrr�additive_generator�s#
z%GeneralisedStratum.additive_generatorcCs,||jvr"tjjj�||�|j|<|j|Sr7)r>�	admcycles�
diffstrata�additivegenerator�AdditiveGeneratorZ	from_hash)r3rYrrrrVs


�
z/GeneralisedStratum.additive_generator_from_hashccs$|jD]}|�|�dkr|VqdS)z�
        Return an iterator over simple poles.

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X = GeneralisedStratum([Signature((1,1))])
            sage: list(X.simple_poles())
            []
        �����N)r/�stratum_point_order)r3�prrr�simple_poless
zGeneralisedStratum.simple_polescst�fdd����D��S)a�
        Checks if self fails to exist for residue reasons (simple pole with residue forced zero).

        Returns:
            bool: existence of simple pole with residue zero.

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X = Stratum((1,-1))
            sage: X.is_empty()
            True
            sage: X = Stratum((1,1))
            sage: X.is_empty()
            False
        c3s|]}�j�|�VqdSr7)�	smooth_LGZresidue_zero�rrarArrr6s�z.GeneralisedStratum.is_empty.<locals>.<genexpr>)�anyrbrArrAr�is_empty$s�zGeneralisedStratum.is_emptycCs
|jdkS)ac
        Return whether self is not connected.

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X = Stratum((1,1))
            sage: X.is_disconnected()
            False
            sage: X = GeneralisedStratum([Signature((5,1)),Signature((1,3))])
            sage: X.is_disconnected()
            True
        r)r)rArrr�is_disconnected9sz"GeneralisedStratum.is_disconnectedcCs|\}}|j|j|S)z�
        The pole order at the stratum point p.

        Args:
            p (tuple): Point (i,j) of self.

        Returns:
            int: Pole order of p.
        )r*r)r3rar%r&rrrr`Is
z&GeneralisedStratum.stratum_point_ordercCs$|��rgS|jdur|��S|jS)z�
        Initialise BIC list on first call.

        Note that _bics is a list of tuples of EmbeddedLevelGraphs
        (each tuple consists of one EmbeddedLevelGraph for every
        connected component).
        N)rfr8�gen_bicrArrr�bicsVs
	
zGeneralisedStratum.bicscCs|jSr7)r1rArrrr5eszGeneralisedStratum.res_condcCs|��rgS|jdu�rt�gg|_t|j�}|jdd�t|�D�g7_|}|�r|j�t��|jdD]x}|d}|j�	|��
�D]}|jd�|f|�q�t|�dkrn|d}|j�|��
�D]}|jd�||f�q�qnt
|jd�|jd<t|jd�}qP|j��|jS)aa
        The list of all (ordered) profiles.

        Note that starting with SAGE 9.0 profile numbering is no longer deterministic.

        Note that this generates all graphs and can take a long time for large strata!

        Returns:
            list: Nested list of tuples.

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=Stratum((2,))
            sage: assert len(X.lookup_list) == 3
            sage: X.lookup_list[0]
            [()]
            sage: X.lookup_list[1]
            [(0,), (1,)]
            sage: assert len(X.lookup_list[2]) == 1
        NcSsg|]
}|f�qSrr�rr%rrrr#�r z2GeneralisedStratum.lookup_list.<locals>.<listcomp>�����rr_r)rfr;�tupler(ri�range�append�set�DG�
top_to_bic�values�add�
bot_to_bic�list�pop)r3r"Znew_profiles�profile�firstr%�lastrrr�lookup_listis*

zGeneralisedStratum.lookup_listcCs,|��rJ�|jdur&tjj�|�|_|jSr7)rfr=r[r\�stratatautringZGenDegenerationGraphrArrrrp�s
�zGeneralisedStratum.DGcCs*|jdur$|�t�df�}|��|_|jS)Nr)r?rZrl�as_taut)r3�onerrr�ONE�s

zGeneralisedStratum.ONEcCs"|jdurtjj�|g�|_|jSr7)r@r[r\�elgtautclass�ELGTautClassrArrr�ZERO�s
zGeneralisedStratum.ZEROc	sv���rgS�jdurpg�_�jD]*}�j�ttj��fdd�|D����q"t�fdd�t	�j
j�D��spJ��jS)a�	
        Nested list of all EmbeddedLevelGraphs in self.

        This list is built on first call.

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=GeneralisedStratum([Signature((1,1))])
            sage: assert comp_list(X.all_graphs[0], [EmbeddedLevelGraph(X, LG=LevelGraph([2],[[1, 2]],[],{1: 1, 2: 1},[0],True),dmp={1: (0, 0), 2: (0, 1)},dlevels={0: 0})])
            sage: assert comp_list(X.all_graphs[1],             [EmbeddedLevelGraph(X, LG=LevelGraph([1, 0],[[1, 2], [3, 4, 5, 6]],[(1, 5), (2, 6)],{1: 0, 2: 0, 3: 1, 4: 1, 5: -2, 6: -2},[0, -1],True),dmp={3: (0, 0), 4: (0, 1)},dlevels={0: 0, -1: -1}),            EmbeddedLevelGraph(X, LG=LevelGraph([1, 1, 0],[[1], [2], [3, 4, 5, 6]],[(2, 5), (1, 6)],{1: 0, 2: 0, 3: 1, 4: 1, 5: -2, 6: -2},[0, 0, -1],True),dmp={3: (0, 0), 4: (0, 1)},dlevels={0: 0, -1: -1}),            EmbeddedLevelGraph(X, LG=LevelGraph([1, 1],[[1], [2, 3, 4]],[(1, 4)],{1: 0, 2: 1, 3: 1, 4: -2},[0, -1],True),dmp={2: (0, 0), 3: (0, 1)},dlevels={0: 0, -1: -1}),            EmbeddedLevelGraph(X, LG=LevelGraph([2, 0],[[1], [2, 3, 4]],[(1, 4)],{1: 2, 2: 1, 3: 1, 4: -4},[0, -1],True),dmp={2: (0, 0), 3: (0, 1)},dlevels={0: 0, -1: -1})])
            sage: assert comp_list(X.all_graphs[2],            [EmbeddedLevelGraph(X, LG=LevelGraph([1, 0, 0],[[1], [2, 3, 4], [5, 6, 7, 8]],[(1, 4), (3, 8), (2, 7)],{1: 0, 2: 0, 3: 0, 4: -2, 5: 1, 6: 1, 7: -2, 8: -2},[0, -1, -2],True),dmp={5: (0, 0), 6: (0, 1)},dlevels={0: 0, -2: -2, -1: -1}),            EmbeddedLevelGraph(X, LG=LevelGraph([1, 0, 0],[[1, 2], [3, 4, 5], [6, 7, 8]],[(1, 4), (2, 5), (3, 8)],{1: 0, 2: 0, 3: 2, 4: -2, 5: -2, 6: 1, 7: 1, 8: -4},[0, -1, -2],True),dmp={6: (0, 0), 7: (0, 1)},dlevels={0: 0, -2: -2, -1: -1}),            EmbeddedLevelGraph(X, LG=LevelGraph([1, 1, 0],[[1], [2, 3], [4, 5, 6]],[(1, 3), (2, 6)],{1: 0, 2: 2, 3: -2, 4: 1, 5: 1, 6: -4},[0, -1, -2],True),dmp={4: (0, 0), 5: (0, 1)},dlevels={0: 0, -2: -2, -1: -1}),            EmbeddedLevelGraph(X, LG=LevelGraph([1, 1, 0],[[1], [2], [3, 4, 5, 6]],[(2, 5), (1, 6)],{1: 0, 2: 0, 3: 1, 4: 1, 5: -2, 6: -2},[0, -1, -2],True),dmp={3: (0, 0), 4: (0, 1)},dlevels={0: 0, -2: -2, -1: -1})])
            sage: assert comp_list(X.all_graphs[2], [EmbeddedLevelGraph(X, LG=LevelGraph([1, 0, 0, 0],[[1], [2, 3, 4], [5, 6, 7], [8, 9, 10]],[(1, 4), (3, 7), (2, 6), (5, 10)],{1: 0, 2: 0, 3: 0, 4: -2, 5: 2, 6: -2, 7: -2, 8: 1, 9: 1, 10: -4},[0, -1, -2, -3],True),dmp={8: (0, 0), 9: (0, 1)},dlevels={0: 0, -2: -2, -1: -1, -3: -3})])
        Nc3s|]}��|�VqdSr7)�lookup�rr$rArrr�s�z0GeneralisedStratum.all_graphs.<locals>.<genexpr>c3s8|]0}�j�|���D]}|�j�|���vVqqdSr7)rprqrrrt)rrr&rArrr�s�)rfr:rzrnru�	itertools�chain�
from_iterabler'rmrpr")r3�lrrArrR�s

��
�zGeneralisedStratum.all_graphscst|jsng}t|j�D]J\�}tjj�|�}�fdd�td|jd�D�}|�	|||ddif�qt
t|��|_|jS)a�
        The smooth EmbeddedLevelGraph inside a LevelStratum.

        Note that the graph might be disconnected!

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=GeneralisedStratum([Signature((1,1))])
            sage: assert X.smooth_LG.is_isomorphic(EmbeddedLevelGraph(X,LG=LevelGraph([2],[[1, 2]],[],{1: 1, 2: 1},[0],True),dmp={1: (0, 0), 2: (0, 1)},dlevels={0: 0}))

            Note that we get a single disconnected graph if the stratum is
            disconnected.

            sage: X=GeneralisedStratum([Signature((0,)), Signature((0,))])
            sage: X.smooth_LG
            EmbeddedLevelGraph(LG=LevelGraph([1, 1],[[1], [2]],[],{1: 0, 2: 0},[0, 0],True),dmp={1: (0, 0), 2: (1, 0)},dlevels={0: 0})

        Returns:
            EmbeddedLevelGraph: The output of unite_embedded_graphs applied to
                the (embedded) smooth_LGs of each component of self.
        csi|]}|�|df�qS�rr�rr&�r%rr�
<dictcomp>�r z0GeneralisedStratum.smooth_LG.<locals>.<dictcomp>rr)r9r.r*r[r\�
levelgraphrcrmr"rnrrl)r3�
graph_listrr$�dmprr�rrc�szGeneralisedStratum.smooth_LGcCs|�|j�S)ai
        Calculate the matrix associated to the residue space,
        i.e. a matrix with a line for every residue condition and a column for every pole of self.

        The residue conditions consist ONLY of the ones coming from the GRC (in _res_cond)
        For inclusion of the residue theorem on each component, use smooth_LG.full_residue_matrix!
        )�matrix_from_res_conditionsr1rArrr�residue_matrix�s	z!GeneralisedStratum.residue_matrixcs2g}|D]�|�fdd�|jD�g7}qtt|�S)a!
        Calculate the matrix for a list of residue conditions, i.e.
        a matrix with one line for every residue condition and a column for each pole of self.

        Args:
            res_conds (list): list of residue conditions, i.e. a nested list R
                R = [R_1,R_2,...] where each R_i is a list of poles (stratum points)
                whose residues add up to zero.

        Returns:
            SAGE matrix: residue matrix (with QQ coefficients)

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=GeneralisedStratum([Signature((2,-2,-2)),Signature((2,-2,-2))])
            sage: X.matrix_from_res_conditions([[(0,1),(0,2),(1,2)],[(0,1),(1,1)],[(1,1),(1,2)]])
            [1 1 0 1]
            [1 0 1 0]
            [0 0 1 1]
        csg|]}t|�v��qSr)�intrd�Zres_crrr#r zAGeneralisedStratum.matrix_from_res_conditions.<locals>.<listcomp>)r/rr)r3Z	res_condsZres_vecrr�rr�sz-GeneralisedStratum.matrix_from_res_conditionscCs|����Sr7)r��rankrArrr�residue_matrix_rank"sz&GeneralisedStratum.residue_matrix_rankcCs4|��rdS|jj}tdd�|jD��|��dS)a�
        Return the dimension of the stratum, that is the sum of 2g_i + n_i - 1 - residue conditions -1 for projective.

        The residue conditions are given by the rank of the (full!) residue matrix.

        Empty strata return -1.

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=GeneralisedStratum([Signature((4,))])
            sage: all(B.top.dim() + B.bot.dim() == X.dim()-1 for B in X.bics)
            True
        r_cSs g|]}d|j|jd�qS)�r)r$r"rrrrr#:r z*GeneralisedStratum.dim.<locals>.<listcomp>r)rfrc�full_residue_matrixr+r*r�)r3�MrrrrQ&s��zGeneralisedStratum.dimcCs|��dS)za
        Unprojectivised dimension of self.

        Returns:
            int: dim() + 1
        r)rQrArrr�N=szGeneralisedStratum.Nc	s:g|_|��rdSg}t|j�D]�\�}�fdd�td|jd�D�}g}tjj�	|j
�D]2}|�d�d|�d�di}||||f}|�|�qZ|j
dkr�dD]$}|�|tjj�|�|d|if�q�|�|�q tj|�}	|	D]D}
tdd	�|
D��r�td
d	�|
D��r�t|
�}|��r�|j�|�q�tjj�|j�|_|jS)a�
        Generates all BICs (using bic) as EmbeddedLevelGraphs.

        Returns:
            list: self._bics i.e. a list of (possibly disconnected)
                EmbeddedLevelGraphs.
                (More precisely, each one is a tuple consisting of one
                EmbeddedLevelGraph for every connected component that has
                been fed to unite_embedded_graphs).
        Ncsi|]}|�|df�qSr�rr�r�rrr�br z.GeneralisedStratum.gen_bic.<locals>.<dictcomp>rrr_�rr_css|]}d|d��vVqdS)r�N�rrr�rrrr|r z-GeneralisedStratum.gen_bic.<locals>.<genexpr>css|]}d|d��vVqdS)r_r�Nr�r�rrrr}r )r8rfr.r*rmr"r[r\�bicZ
bic_alt_noisor�internal_level_numberrnr)r�rcr��productrer�is_legal�isom_rep)r3Zemb_bic_listr�mp_dictZemb_bic_list_curr$Z
level_dict�EGr�Z	prod_bicsZ
prod_graph�pgrr�rrhGs:

�
�
�zGeneralisedStratum.gen_bicFc
Cs�|d}|d}t|��t|��s&dS|j|�}|j|�}|sNtd||f��|}tt|��ddd�D]\}	}
|
|vrzqh|�|	�}qhdd�|�|�D�}zt|�}Wn"t	y�t
d||f��Yn0|r�|S|gt|�SdS)aG
        Provide explicit leg maps (as list of dictionaries: legs of LG to legs of LG), from
        the graph associated to enh_profile to the one associated to enh_deg_profile.

        If enh_deg_profile is not a degeneration (on the level of profiles), None is
        returned.

        Raises a RuntimeError if enh_profile is empty and a UserWarning if
        there are no degenerations in the appropriate profile.

        INPUT:

        enh_profile (enhanced profile): tuple (profile, index).

        enh_deg_profile (enhanced profile): tuple (profile, index).

        only_one (bool, optional): Give only one (the 'first') map (or None if none exist).
        Defaults to False.

        OUTPUT:

        list of dicts: List of the leg isomorphisms, None if not a degeneration,
        only one dict if only_one=True.
        rNz%r is not a graph in %r!r_css|]\}}|VqdSr7r)rZv_isoZl_isorrrr�r z7GeneralisedStratum.explicit_leg_maps.<locals>.<genexpr>z"Squish of %r not isomorphic to %r!)ro�issubset�lookup_graph�RuntimeErrorrur.�squish_vertical�isomorphisms�next�
StopIteration�UserWarning)
r3rWZenh_deg_profile�only_onerw�deg_profiler$�degenZdegen_squish�level�	bic_indexZisomsZ
first_isomrrr�explicit_leg_maps�s2

��z$GeneralisedStratum.explicit_leg_mapscCs�|d}|d}zt|�||��}Wnty:gYS0g}tt|�|���D]2}|�||f|�rR|�||f|�rR|�||f�qR|S)a'
        Find common degenerations of two graphs.

        INPUT:

        s_enh_profile (tuple): Enhanced profile, i.e. a tuple (p,k) consisting of

        * a sorted (!) profile p
        * an index in self.lookup(p)
          thus giving the information of an EmbeddedLevelGraph in self.

        o_enh_profile (tuple): Enhanced profile.

        OUTPUT:

        A list of enhanced profiles, i.e. entries of type [tuple profile, index]
        (that undegenerate to the two given graphs).

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=GeneralisedStratum([Signature((4,))])

        To retrieve the actual EmbeddedLevelGraphs, we must use lookup_graph.
        (Because of BIC renumbering between different SAGE versions we can't provide any concrete examples :/)

        Note that the number of components can also go down.

        Providing common graphs works::

            sage: X.common_degenerations(((2,),0),((2,),0))
            [((2,), 0)]

            Empty intersection gives empty list.

        r)rl�merge_profiles�	TypeErrorrmr(r��is_degenerationrn)r3�
s_enh_profile�
o_enh_profile�	s_profile�	o_profiler��return_listr%rrr�common_degenerations�s"&
���z'GeneralisedStratum.common_degenerationscCs|durd}|dks||jkr$|jS|dks6||jkr:|S|dksL||jkrR|jS|dksd||jkrh|Sg}|jD]L\}}|jD]<\}}|�|||�}	|	dks�|	|jkr�q�|�|||	�q�qr|�|�}
|
dkr�|jS|���r|���r|
���sJd|||
f��|
S)a 
        Excess intersection of two tautological classes in Chow of ambient_enh_profile.

        Raises a RuntimeError if any summand of any tautological class is not on
        a degeneration of ambient_enh_profile.

        INPUT:

        s_taut_class (ELGTautClass): tautological class

        o_taut_class (ELGTautClass): tautological class

        amb_enh_profile (tuple, optional): enhanced profile of ambient graph.
        Defaults to None.

        OUTPUT:

        ELGTautClass: Tautological class on common degenerations
        N�rrrrzGProduct of equidimensional classes is not equidimensional! %s * %s = %s)r�r~�psi_list�intersection_AGrn�ELGsum�is_equidimensional)r3Zs_taut_classZo_taut_class�amb_enh_profiler��s_coeff�	s_add_genZo_coeff�	o_add_genr�return_valuerrr�intersection%s:�
��zGeneralisedStratum.intersectioncs |durd}�j��j����|�s4td�|f�����|�sPtd�|f���j�j}|���tt�d�t�d��kr��jS�����}|s��jS��	��|���dkr܇�fdd�|D�}t
jj�
�t|��S�dks��jkr�S������fdd�|D�}��t|��SdS)	a�
        Excess intersection formula for two AdditiveGenerators in Chow of amb_enh_profile.

        Note that as AdditiveGenerators and enhanced profiles are hashable,
        this method can (and will) be cached (in contrast with intersection).

        Raises a RuntimeError if any of the AdditiveGenerators is not on
        a degeneration of ambient_enh_profile.

        INPUT:

        s_add_gen (AdditiveGenerator): first AG
        o_add_gen (AdditiveGenerator): second AG
        amb_enh_profile (tuple, optional): enhanced profile of ambient graph.
        Defaults to None.

        OUTPUT:

        A Tautological class on common degenerations
        Nr��%r is not a degeneration of %rrrcsHg|]@}��|�jD].\}}��|�jD]\}}||||f�q(qqSr)�	pull_backr�)r�LZ_csZs_pbZ_coZo_pb)r�r�rrr#�s�z6GeneralisedStratum.intersection_AG.<locals>.<listcomp>c
sBg|]:}������|���|�|����|������|��qSr)r�r��gen_pullback_taut�minimal_common_undegeneration)rr���NBr�r�r�r�r3rrr#�s �����)rWr�r�Z
psi_degreerQ�maxr(r�r��cnbr[r\rr�rur�)r3r�r�r�Zdeg_sum�
degenerationsrZsummandsrr�rr�VsD������
z"GeneralisedStratum.intersection_AGcCsh|durd}nt|d�|df}t|d�t|d�dksJ|�||�sZtd||f��|�|||�S)a�
        Normal bundle of enh_profile in ambient.

        Note that this is equivalent to cnb(enh_profile, enh_profile, ambient).

        Args:
            enh_profile (tuple): enhanced profile
            ambient (tuple, optional): enhanced profile. Defaults to None.

        Raises:
            ValueError: Raised if enh_profile is not a codim 1 degeneration of ambient

        Returns:
            ELGTautClass: Normal bundle N_{enh_profile, ambient}
        Nr�rrz&%r is not a codim 1 degeneration of %r)rlr(r��
ValueErrorr�)r3rW�ambientrrr�
normal_bundle�s�
���z GeneralisedStratum.normal_bundlec	Cs�|durd}nt|d�|df}|�||�s>td||f��|�||�sZtd||f��|�||�}||krrdS|�|||�s�Jd||f��g}|�|||�D]�}|\}}t||�}	|j||	j}
|j|	|dd�}|j|	d|dd�}||}
dt	|
�|�
|
||�}|dt	|
�|�
|�||	�||�8}|dk�rJ|jS|�
��s`Jd	|��|�|�q�|�svdS|d}|dd�D]}|�|||�}�q�|�
��s�Jd	|��|S)
a�
        Common Normal bundle of two graphs in an ambient graph.

        Note that for a trivial normal bundle (transversal intersection)
        we return 1 (int) and NOT self.ONE !!

        The reason is that the correct ONE would be the ambient graph and that
        is a pain to keep track of in intersection....

        Raises a RuntimeError if s_enh_profile or o_enh_profile do not
        degenerate from amb_enh_profile.

        INPUT:

        s_enh_profile (tuple): enhanced profile
        o_enh_profile (tuple): enhanced profile
        amb_enh_profile (tuple, optional): enhanced profile. Defaults to None.

        OUTPUT:

        ELGTautClass: Product of normal bundles appearing.
        1 if the intersection is transversal.
        Nr�rrr�znminimal common undegeneration is %r, ambient profile is %r, but there aren't codim one common undegenerations!T��quietz&Not all summands in %s of same degree!)rlr�r�r�� codim_one_common_undegenerationsrri�ell�xi_at_levelrr��calLr�r�rnr�)r3r�r�r�Zmin_comr��eprar%Zsquished_level�llZxi_topZxi_botZxis�summandZNBprod�nbrrrr��sl������

�
���

�zGeneralisedStratum.cnbc

Cs�|durd}|�||�s(td||f��|j}|�||�sJtd||f��|�||�}|s`|jS|�|||�}|dks�||jkr�dSg}|D]J}|dkr�|�|�|��q�|�|�|�	|||�
||��|�|�|��q�|�|�}	|	dkr�|	S|jSdS)at
        Generalised pullback of additive generator to o_enh_profile in amb_enh_profile.

        Args:
            add_gen (AdditiveGenerator): additive generator on a degeneration of amb_enh_profile.
            o_enh_profile (tuple): enhanced profile (degeneration of amb_enh_profile)
            amb_enh_profile (tuple, optional): enhanced profile. Defaults to None.

        Raises:
            RuntimeError: If one of the above is not actually a degeneration of amb_enh_profile.

        Returns:
            ELGTautClass: Tautological class on common degenerations of AdditiveGenerator profile and o_enh_profile.
        Nr�r�rr)r�r�rWr�r�r�rnr�r�r�r�r�)
r3�add_genr�r�r�r�r�r�r�r�rrr�gen_pullbacksN������

zGeneralisedStratum.gen_pullbackc	Cs6g}|jD] \}}|�||�|||��q
|�|�S)a�
        Generalised pullback of tautological class to o_enh_profile in amb_enh_profile.

        This simply returns the ELGSum of gen_pullback of all AdditiveGenerators.

        Args:
            taut_class (ELGTautClass): tautological class each summand on a degeneration of amb_enh_profile.
            o_enh_profile (tuple): enhanced profile (degeneration of amb_enh_profile)
            amb_enh_profile (tuple, optional): enhanced profile. Defaults to None.

        Raises:
            RuntimeError: If one of the above is not actually a degeneration of amb_enh_profile.

        Returns:
            ELGTautClass: Tautological class on common degenerations of AdditiveGenerator profile and o_enh_profile.
        )r�rnr�r�)r3Z
taut_classr�r�r�rT�AGrrrr�Cs�z$GeneralisedStratum.gen_pullback_tautc
Cs�g}|�|�D]�}|j|�}|jjr|�||�D]\}z4|j�||d||df�rf|�|�WqWq0ty�td||f��Yq00q0q|S)a�
        A list of enhanced profiles where the (explicit) edge 'edge' became long.

        We go through the codim one degenerations of enh_profile and check
        each graph, if edge became long (under any degeneration).

        However, we count each graph only once, even if there are several ways
        to undegenerate (see examples).

        Raises a RuntimeError if the leg is not a leg of the graph of enh_profile.

        INPUT:

        enh_profile (tuple): enhanced profile: (profile, index).

        edge (tuple): edge of the LevelGraph associated to enh_profile:
        (start leg, end leg).

        OUTPUT:

        The list of enhanced profiles.

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=Stratum((1,1))
            sage: V=[ep for ep in X.enhanced_profiles_of_length(1) if X.lookup_graph(*ep).level(0)._h0 == 2]
            sage: epV=V[0]
            sage: VLG=X.lookup_graph(*epV).LG
            sage: assert len(X.explicit_edge_becomes_long(epV, VLG.edges[1])) == 1
            sage: assert X.explicit_edge_becomes_long(epV, VLG.edges[1]) == X.explicit_edge_becomes_long(epV, VLG.edges[1])

        rrz$%r does not seem to be an edge of %r)	�codim_one_degenerationsr��LGZ
has_long_edger��is_longrn�KeyErrorr�)r3rW�edge�ep_listr�r$Zleg_maprrr�explicit_edge_becomes_long]s #
 

��z-GeneralisedStratum.explicit_edge_becomes_longcs(|j|�����fdd��jjD�}|S)a�
        Edges going from (relative) level start_level to (relative) level stop_level.

        Note that we assume here that edges respect the level structure, i.e.
        start on start_level and end on end_level!

        Args:
            enh_profile (tuple): enhanced profile
            start_level (int): relative level number (0...codim)
            stop_level (int): relative level number (0...codim)

        Returns:
            list: list of edges, i.e. tuples (start_point,end_point)

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=Stratum((2,))

            Compact type:

            sage: assert len([ep for ep in X.enhanced_profiles_of_length(1) if len(X.explicit_edges_between_levels(ep,0,1)) == 1]) == 1

            Banana:

            sage: assert len([ep for ep in X.enhanced_profiles_of_length(1) if len(X.explicit_edges_between_levels(ep,0,1)) == 2]) == 1

        csHg|]@}�j��j�|d���kr�j��j�|d���kr|�qS�rr)r��level_number�
levelofleg�r�e��G�start_level�
stop_levelrrr#�s �������zDGeneralisedStratum.explicit_edges_between_levels.<locals>.<listcomp>)r�r��edges)r3rWr�r�r�rr�r�explicit_edges_between_levels�s

�z0GeneralisedStratum.explicit_edges_between_levelsc	Csnt|d�}|s(dd�tt|j��D�Sg}|j�|d���D] }|�t|g|dd���q@|j�	|d���D] }|�t|dd�|g��qvtt|�d�D]j}|j�	||���D]P}||j�||d���vr�|�t|d|d�|g||dd���q�q�tt
|��}g}|D]@}tt|�|���D]&}|�||f|��r>|�||f��q>�q(|S)a�
        Degenerations of enh_profile with one more level.

        Raises a RuntimeError if we find a degeneration that doesn't squish
        back to the graph we started with.

        INPUT:

        enh_profile (enhanced profile): tuple (profile, index)

        OUTPUT:

        list of enhanced profiles.

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=GeneralisedStratum([Signature((4,))])
            sage: assert all(len(p) == 2 for p, _ in X.codim_one_degenerations(((2,),0)))

        Empty profile gives all bics:

            sage: assert X.codim_one_degenerations(((),0)) == [((0,), 0), ((1,), 0), ((2,), 0), ((3,), 0), ((4,), 0), ((5,), 0), ((6,), 0), ((7,), 0)]
        rcSsg|]}|fdf�qS�rr�r�brrrr#�r z>GeneralisedStratum.codim_one_degenerations.<locals>.<listcomp>Nr_r)
rurmr(rirprqrrrnrlrtror�r�)r3rWrwZdeg_listr�r%Zenh_listrarrrr��s*(�z*GeneralisedStratum.codim_one_degenerationscCsF|durd}g}|�|�D]&}|�||�r|�||�r|�|�q|S)aX
        Profiles that are 1-level degenerations of amb_enh_profile and include
        s_enh_profile and o_enh_profile.

        INPUT:

        s_enh_profile (tuple): enhanced profile
        o_enh_profile (tuple): enhanced profile
        amb_enh_profile (tuple): enhanced profile

        OUTPUT:

        list of enhanced profiles
        Nr�)r�r�rn)r3r�r�r�Zprofile_listr�rrrr��s���z3GeneralisedStratum.codim_one_common_undegenerationscCs�|d}|d}g}|D]}||vr|�|�qt|�}t|�|��dkrR|dfStt|�|���D]0}|�|||f�rd|�|||f�rd||fSqdtd|��dS)az
        The minimal dimension graph that is undegeneration of both s_enh_profile
        and o_enh_profile.

        Raises a RuntimeError if there are no common undgenerations in the intersection profile.

        INPUT:

        s_enh_profile (tuple): enhanced profile
        o_enh_profile (tuple): enhanced profile

        OUTPUT:

        tuple: enhanced profile
        rrz&No common undegeneration in profile %rN)rnrlr(r�rmr�r�)r3r�r�r�r�r�r�r%rrrr�s"��z0GeneralisedStratum.minimal_common_undegenerationcCs�|d}|d}t|�t|�ks$dSt|�|��t|�|��krLdkrpnn |�||�slJd||f��dSz$|j||dd�dur�WdSWdSWnty�YdS0dS)a�
        Check if s_enh_profile is a degeneration of o_enh_profile.

        Args:
            s_enh_profile (tuple): enhanced profile
            o_enh_profile (tuple): enhanced profile

        Returns:
            bool: True if the graph associated to s_enh_profile is a degeneration
                of the graph associated to o_enh_profile, False otherwise.

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=GeneralisedStratum([Signature((4,))])
            sage: assert X.is_degeneration(((7,),0),((7,),0))

            The empty tuple corresponds to the stratum:

            sage: assert X.is_degeneration(((2,),0),((),0))
        rFrzB%r and %r contain only one graph, but these are not degenerations!T�r�N)ror(r�r�r�)r3r�r�r�r�rrrr�<s*,����
z"GeneralisedStratum.is_degenerationcCs�|\}}t|�dkr(|dkr$td��dSt|�}|�|�t|�}g}tt|�|���D]}|�|||f�rX|�|�qXt|�dkr�td|||f��||dfS)aU
        Squish level l of the graph associated to enh_profile. Returns the enhanced profile
        associated to the squished graph.

        Args:
            enh_profile (tuple): enhanced profile
            l (int): level of graph associated to enhanced profile

        Raises:
            RuntimeError: Raised if a BIC is squished at a level other than 0.
            RuntimeError: Raised if the squished graph is not found in the squished profile.

        Returns:
            tuple: enhanced profile.

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=Stratum((2,))
            sage: assert all(X.squish(ep,0) == ((),0) for ep in X.enhanced_profiles_of_length(1))
            sage: assert all(X.squish((p,i),1-l) == ((p[l],),0) for p, i in X.enhanced_profiles_of_length(2) for l in range(2))
        rrz$BIC can only be squished at level 0!r�z:Cannot squish %r at level %r! No unique graph found in %r!)	r(r�rurvrlrmr�r�rn)r3rWr�rar%�new_p�enhancementsr&rrr�squishks*�
��zGeneralisedStratum.squishcCsp||j�|���vr@||j�|���vs<Jd||||f��dS||j�|���vshJd||||f��dSdS)z�
        Determine if (i,j) is a 3-level graph.

        INPUT:

        i (int): Index of BIC.

        j (int): Index of BIC.

        OUTPUT:

        bool: True if (i,j) is a 3-level graph, False otherwise.
        zJ%r is a bottom degeneration of %r, but %r is not a top degeneration of %r!TzJ%r is not a bottom degeneration of %r, but %r is a top degeneration of %r!FN)rprtrrrq)r3r%r&rrr�	lies_over�s
��
��zGeneralisedStratum.lies_overcsDt��fdd�tt��d�D��s2Jd�f��t��fdd�tt��d�D��sdJd�f��g}d}d}|t��k�r|t��k�r�|�|kr�|��|�|d7}|d7}qp���|�|�r�|��|�|d7}qp���|�|��r|��|�ndS|d7}qp|�|d�7}|�|d�7}t|�S)a�
        Merge profiles with respect to the ordering "lies_over".

        Args:
            p (iterable): sorted profile
            q (iterable): sorted profile

        Returns:
            tuple: sorted profile or None if no such sorted profile exists.

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=GeneralisedStratum([Signature((4,))])
            sage: X.merge_profiles((5,),(5,))
            (5,)
        c3s&|]}���|�|d�VqdSr�r�rj)rar3rrr�r z4GeneralisedStratum.merge_profiles.<locals>.<genexpr>rzProfile %r not sorted!c3s&|]}���|�|d�VqdSrr�rj)�qr3rrr�r rN)r'rmr(rnr�rl)r3rar��new_profile�next_pZnext_qr)rar�r3rr��s0&�&�


z!GeneralisedStratum.merge_profilesrcs:t��fdd�tt��d�D��r2����|SdSdS)ad
        Return the graph associated to an enhanced profile.

        Note that starting in SAGE 9.0 profile numbering will change between sessions!

        Args:
            bic_list (iterable): (sorted!) tuple/list of indices of bics.
            index (int, optional): Index in lookup list. Defaults to 0.

        Returns:
            EmbeddedLevelGraph: graph associated to the enhanced (sorted) profile
                (None if empty).

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=Stratum((2,))
            sage: X.lookup_graph(())
            EmbeddedLevelGraph(LG=LevelGraph([2],[[1]],[],{1: 2},[0],True),dmp={1: (0, 0)},dlevels={0: 0})

            Note that an enhanced profile needs to be unpacked with *:

            sage: X.lookup_graph(*X.enhanced_profiles_of_length(2)[0])  # 'unsafe' (edge ordering may change)  # doctest:+SKIP
            EmbeddedLevelGraph(LG=LevelGraph([1, 0, 0],[[1], [2, 3, 4], [5, 6, 7]],[(1, 4), (2, 6), (3, 7)],{1: 0, 2: 0, 3: 0, 4: -2, 5: 2, 6: -2, 7: -2},[0, -1, -2],True),dmp={5: (0, 0)},dlevels={0: 0, -1: -1, -2: -2})

        c3s&|]}���|�|d�VqdSrr�rj��bic_listr3rrr
s�z2GeneralisedStratum.lookup_graph.<locals>.<genexpr>rN)r'rmr(r�)r3r��indexrr�rr��s
�zGeneralisedStratum.lookup_graphTcs��std|f�tj��t|�}|sF�s>td�tj���jgSt|�dkrx�shtd�tj���j|dgSz&�j|}�s�td�tj��|WSt	�y�t
|�}|��}�j|�gg}gg}|D�](}��s�td|dd	�tj��zd�j�
|�|}	��s0td
t|	��tj��g}
|	D]"}|D]}|
�||g��q@�q8|
}Wq�t	�yzd�j�|�|}
��s�tdt|
��tj��g}|
D]"}|D]}|�||g��q��q�|}Wn$t	�y�gYYYS0Yq�0q؈�s<td�td
t|��jt|��jf�tj�����fdd�t�||�D�}��s�td|�t|�f�tddd	�tj��tjj�|�}|�j|<��s�tdt|��tj��|YS0dS)a�
        Take a profile (i.e. a list of indices of BIC) and return the corresponding
        EmbeddedLevelGraphs (i.e. the product of these BICs).

        Note that this will be a one element list "most of the time", but
        it can happen that this is not irreducible:

        This implementation is not dependent on the order (!) (we look in top and
        bottom degenerations and clutch...)

        However, for caching purposes, it makes sense to use only the sorted profiles...

        NOTE THAT IN PYTHON3 PROFILES ARE NO LONGER DETERMINISTIC!!!!!

        (they typically change with every python session...)

        Args:
            bic_list (iterable): list of indices of bics

        Returns:
            list: The list of EmbeddedLevelGraphs corresponding to the profile.

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=GeneralisedStratum([Signature((4,))])

            This is independent of the order.

            sage: p, _ = X.enhanced_profiles_of_length(2)[0]
            sage: assert any(X.lookup(p)[0].is_isomorphic(G) for G in X.lookup((p[1],p[0])))

            Note that the profile can be empty or reducible.

        z%Looking up enhanced profiles in %r...z)Empty profile, returning smooth_LG. Done.rz-BIC, profile irreducible by definition. Done.rzUsing cached lookup. Done.zLooking at BIC %r:� ��endz1Adding %r BICs from top component to top_lists...z4Adding %r BICs from bottom component to bot_lists...z&Done building top_lists and bot_lists.zgThis gives us %r profiles in %s and %r profiles in %s that we will now clutch pairwise and recursively.csZg|]R\}}�jj|�d�D]8}�jj|�d�D]"}tjj��||�j�j�j	��q0qqS)r�)
�topr��botr[r\r{�clutch�clutch_dictZemb_top�emb_bot)r�top_list�bot_listrr��Br�r3rrr#�s��z-GeneralisedStratum.lookup.<locals>.<listcomp>z6For profile %r in %s, we have thus obtained %r graphs.z%Sorting these by isomorphism class...z#Done. Found %r isomorphism classes.N)rP�sys�stdout�flushrlrcr(rir<r�rurvrpZtop_to_bic_invrnZbot_to_bic_invrrr�r�r[r\r�r�)r3r�r�Z
lookup_keyZcached_listr%Z	top_listsZ	bot_listsr&Ztop_bicsZ
new_top_listsr�r	Zbot_bicsZ
new_bot_listsr
r�Zrep_listrrrr�s�$







��
��
��

�
��


zGeneralisedStratum.lookup�belowcs�|j|��|dkr>|dkr,|r(�t�fS�S|r:dt�fSdS|�jkrt|dkrb|r^�t�fS�S|rpdt�fSdS|\}}||d}|j|��j�|��|dkr��fdd�tt�jj��D�}�j	��fdd	��j
��D�}	nJ|dks�J���fd
d�tt�jj��D�}�j��fdd	��j
��D�}	t
|����fdd��jjD�}
�j�||
�}t
t|j������fd
d	��j��D���fdd��jjD�}�fdd�|D�}
|j|fdf|dd�}|�s�J�dd	�|��D��t���fdd��D���sJ�|
�r&|
��}�j�|�|<�qt
t|
��}t
����}|�|��sNJ��||Bk�s`J�tjj��|�|	�}|�r�|t|�fS|S)a�
        Extract an EmbeddedLevelGraph from the subgraph of enh_profile that is either
        above or below level l.

        This is embedded into the top/bottom component of the bic at profile[l-1].
        In particular, this is a 'true' sub graph, i.e. the names of the vertices and
        legs are the same as in enh_profile.

        Note: For l==0 or l==number_of_levels we just return enh_profile.

        INPUT:

        l (int): (relative) level number.
        direction (str, optional): 'above' or 'below'. Defaults to 'below'.
        return_split_edges (bool, optional. Defaults to False): also return a tuple
        of the edges split across level l.

        OUTPUT:

        EmbeddedLevelGraph: Subgraph of top/bottom component of the bic at profile[l-1].

        If return_split_edges=True: Returns tuple (G,e) where

        * G is the EmbeddedLevelGraph
        * e is a tuple of edges of enh_profile that connect legs above level
          l with those below (i.e. those edges needed for clutching!)

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=Stratum((2,))
            sage: ep = X.enhanced_profiles_of_length(2)[0]
            sage: X.sub_graph_from_level(ep, 1, 'above')
            EmbeddedLevelGraph(LG=LevelGraph([1],[[1]],[],{1: 0},[0],True),dmp={1: (0, 0)},dlevels={0: 0})
            sage: X.sub_graph_from_level(ep, 1, 'below')  # 'unsafe' (edge order might change)  # doctest:+SKIP
            EmbeddedLevelGraph(LG=LevelGraph([0, 0],[[2, 3, 4], [5, 6, 7]],[(2, 6), (3, 7)],{2: 0, 3: 0, 4: -2, 5: 2, 6: -2, 7: -2},[-1, -2],True),dmp={5: (0, 0), 4: (0, 1)},dlevels={-1: -1, -2: -2})
            sage: X.bics[ep[0][0]].top
            LevelStratum(sig_list=[Signature((0,))],res_cond=[],leg_dict={1: (0, 0)})
            sage: X.bics[ep[0][1]].bot
            LevelStratum(sig_list=[Signature((2, -2, -2))],res_cond=[[(0, 1), (0, 2)]],leg_dict={3: (0, 0), 4: (0, 1), 5: (0, 2)})
        rrN�abovercs g|]}�j�|��kr|�qSr�r��
levelofvertex�r�v�r��
internal_lrrr#�s�z;GeneralisedStratum.sub_graph_from_level.<locals>.<listcomp>csi|]\}}|�kr||�qSrr�rrr�rrrr��s�z;GeneralisedStratum.sub_graph_from_level.<locals>.<dictcomp>cs g|]}�j�|��kr|�qSrrrrrrr#�s�csi|]\}}|�kr||�qSrrrrrrr��s�cs8g|]0}�j�|d��vr�j�|d��vr|�qSr�)r��vertexr�)r��
vertex_setrrr#�s�cs*i|]"\}}|�vr|�j�j|�qSr)rX�dmp_invr)rr��leg_setrrr�s�cs(g|] }|d�v|d�vkr|�qSr�rr��rrrr#s�cs(g|] }|d�vr|dn|d�qSr�rr�rrrr#s�Tr�cSsi|]\}}||�qSrrrrrrr�r c3s$|]}�j�|�|kVqdSr7�rX�r�leg)�G_to_Br��new_dmprrrr z:GeneralisedStratum.sub_graph_from_level.<locals>.<genexpr>)r�rlZnumber_of_levelsrir�r�rmr(�generar�dlevels�itemsrror��extractr�legsr�r�r'rvrX�keys�
isdisjointr[r\�embeddedlevelgraph�EmbeddedLevelGraph)r3rWr��	direction�return_split_edgesrw�_iZ
bic_number�new_verticesZnew_dlevels�	new_edges�new_LG�split_edgesZsplit_half_edgesZB_to_Gr!Zlegs_in_new_edges�
marked_pointsZ	sub_graphr)rr�r"r�rrr#rr�sub_graph_from_level�sv,






  
�
��
 �z'GeneralisedStratum.sub_graph_from_levelc
s�|j||ddd�\�}|j||ddd�\�}||ks8J�|}|\}}|j||d}|��}	|	d�jkrz|jks�nJ��|	d<|	d�jkr�|jks�nJ��|	d<��fdd	�|D�|	d
<|	S)a,
        Splits enh_profile self into top and bottom component at level l.

        (Note that the 'cut' occurs right above level l, i.e. to get the top level
        and the rest, l should be 1! (not 0))

        The top and bottom components are EmbeddedLevelGraphs, embedded into
        top and bottom of the corresponding BIC (obtained via sub_graph_from_level).

        The result is made so that it can be fed into clutch.

        Args:
            enh_profile (tuple): enhanced profile.
            l (int): (relative) level of enh_profile.

        Returns:
            dict: dictionary consising of
                * X:            GeneralisedStratum self.X
                * top:          EmbeddedLevelGraph: top component
                * bottom:       EmbeddedLevelGraph: bottom component
                * clutch_dict:  clutching dictionary mapping ex-half-edges on
                        top to their partners on bottom (both as points in the
                        respective strata via dmp!)
                * emb_dict_top: a dictionary embedding top into the stratum of self
                * emb_dict_bot: a dictionary embedding bot into the stratum of self
                * leg_dict:     a dictionary legs of enh_profile -> legs of top/bottom

        Note that clutch_dict, emb_top and emb_bot are dictionaries between
        points of strata, i.e. after applying dmp to the points!

        EXAMPLES::

            In particular, we can use this to "glue" the BICs of 10^top into (10,9,6) and
            obtain all components of the profile.

        rT�r-r.rrr�bottomcs&i|]}�j|d�j|d�qSr��r�r���	bot_graph�	top_graphrrr�ksz;GeneralisedStratum.split_graph_at_level.<locals>.<dictcomp>r)r5ri�split�Xrr)
r3rWr��se_top�se_botr3rar/r�clutching_inforr9r�split_graph_at_level/s&&�
�
""�
z'GeneralisedStratum.split_graph_at_levelc
sB|\}}t|�dks td|��|�||��|j|dj���d��|j|dj�|j|dfdf|dd�}dd�|��D��|j|dfdf|dd�}d	d�|��D�����fd
d�t	�j
�D�}��fdd�t	�j
�D�}���fdd�t	�j
�D�}i}	i}
i}�jjD�]}�j�
�j�|d��dk�r��j�
�j�|d��dk�rx��|d�|	���|d�<nF�j�
�j�|d��dk�s�J����|d�|���|d�<nd�j�
�j�|d��dk�s�J��j�
�j�|d��dk�sJ����|d�|
��|d�<�q|���||||	|
|d
�
S)a7
        Splits embedded 3-level graph into top, middle and bottom component, along with
        all the information required (by clutch) to reconstruct self.

        We return a dictionary so that the result can be fed into clutch (naming of
        optional arguments...)

        This is mainly a technical backend for doublesplit_graph_before_and_after_level.

        Note that in contrast to EmbeddedLevelGraph.split, we want to feed a length-2-profile
        so that we can really split into the top and bottom of the associated BICs (the only
        strata we can control!)

        This method is mainly intended for being fed into clutch.

        Raises a ValueError if self is not a 3-level graph.

        INPUT:

        enh_profile (tuple): enhanced profile.

        Returns:

        A dictionary consisting of

        * X:                  GeneralisedStratum self,
        * top:                LevelStratum top level of top BIC,
        * bottom:             LevelStratum bottom level of bottom BIC,
        * middle:             LevelStratum level -1 of enh_profile,
        * emb_dict_top:       dict: points of top stratum -> points of X,
        * emb_dict_bot:       dict: points of bottom stratum -> points of X,
        * emb_dict_mid:       dict: points of middle stratum -> points of X,
        * clutch_dict:        dict: points of top stratum -> points of middle stratum,
        * clutch_dict_lower:  dict: points of middle stratum -> points of bottom stratum,
        * clutch_dict_long:   dict: points of top stratum -> points of bottom stratum.
        r�zError: Not a 3-level graph! %rrrTr�cSsi|]\}}||�qSrrrrrrr��r z2GeneralisedStratum.doublesplit.<locals>.<dictcomp>cSsi|]\}}||�qSrrrrrrr��r cs:i|]2}�j��j�|��dkr�j�|�j|�qSr��r�r�r�rXr��rr�)r��G_to_toprrrr��s�cs6i|].}�j��j�|��dkr�j|�j|�qSr�rBrC)r��middlerrr��s�cs:i|]2}�j��j�|��dkr�j�|�j|�qS)r�rBrC)r��G_to_botr7rrr��s�)
r=rr7rE�emb_dict_top�emb_dict_mid�emb_dict_botr�clutch_dict_lower�clutch_dict_long)r(r�r�rirr�rr�r&�iterr�r�r�r�r��stratum_number)
r3rWrar%Ztop_to_GZbot_to_GrGrHrIrrJrKr�r)r�rFrDr7rErr�doublesplitpsp%
�����	�
�"�
�""���zGeneralisedStratum.doublesplitc	Cs�|\}}||d||f}t|�|��}|dksBJd||f��g}t|�D]}|�|||f�rN|�|�qNt|�dkr�td|||f��||dfS)a\
        Find the 3-level graph that has level l of enh_profile as its middle level.

        A RuntimeError is raised if no unique (or no) 3-level graph is found.

        INPUT:

        enh_profile (tuple): enhanced profile
        l (int): (relative) level number

        OUTPUT:

        tuple: enhanced profile of the 3-level graph.
        rrz/No 3-level graph for subprofile %r of %r found!z:No unique 3-level undegeneration in %r around level %r! %r)r(r�rmr�rnr�)	r3rWr�rw�_�three_level_profileZpossible_enhancementsr�r%rrr�three_level_profile_for_level�s"���z0GeneralisedStratum.three_level_profile_for_levelcs�|\}}|dks |t|�dkr,td|��|�||�}|j||ddd�\�}|j||dddd�\�}|�||�}|�|�}	�j|	dks�J��j|	d	ks�J�|	d
��|j|��d�ks�J��|	d<�|	d	<g}
g}|D]8}|j�	|j�
|d��|k�r|
�|�q�|�|�q�g}
g}|D]:}|j�	|j�
|d��|k�rR|
�|�n
|�|��q$t|�t|�k�svJ�|j
||dd�}dd
�|��D�����fdd
�|
D�|	d<���fdd
�|
D�|	d<��fdd
�|D�|	d<|	S)af

        Split the graph enh_profile directly above and below level l.

        This can be used for gluing an arbitrary degeneration of level l into enh_profile.

        The result is made so that it can be fed into clutch.

        To ensure compatibility with top/bot/middle_to_bic when gluing, we have
        to make sure that everything is embedded into the "correct" generalised strata.

        We denote the 3-level graph around level l by H.

        Then the top part will be embedded into the top of the top BIC of H,
        the bottom part will be embedded into the bot of the bottom BIC of H
        and the middle will be the middle level of H.

        For a 3-level graph is (almost) equivalent to doublesplit(), the only difference
        being that here we return the 0-level graph for each level.

        Args:
            enh_profile (tuple): enhanced profile.
            l (int): (relative) level of enh_profile.

        Raises:
            ValueError: Raised if l is 0 or lowest level.
            RuntimeError: Raised if we don't find a unique 3-level graph around l.

        Returns:
            dict: A dictionary consisting of:
                X:                  GeneralisedStratum self.X,
                top:                LevelStratum top level of top BIC of H,
                bottom:             LevelStratum bottom level of bottom BIC of H,
                middle:             LevelStratum middle level of H,
                emb_dict_top:       dict: points of top stratum -> points of X,
                emb_dict_bot:       dict: points of bottom stratum -> points of X,
                emb_dict_mid:       dict: points of middle stratum -> points of X,
                clutch_dict:        dict: points of top stratum -> points of middle stratum,
                clutch_dict_lower:  dict: points of middle stratum -> points of bottom stratum,
                clutch_dict_long:   dict: points of top stratum -> points of bottom stratum.

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=GeneralisedStratum([Signature((4,))])
            sage: assert all(clutch(**X.doublesplit_graph_before_and_after_level(ep,l)).is_isomorphic(X.lookup_graph(*ep)) for levels in range(3,X.dim()+1) for ep in X.enhanced_profiles_of_length(levels-1) for l in range(1,levels-1))
            sage: X=GeneralisedStratum([Signature((2,2,-2))])
            sage: assert all(clutch(**X.doublesplit_graph_before_and_after_level(ep,l)).is_isomorphic(X.lookup_graph(*ep)) for levels in range(3,X.dim()+2) for ep in X.enhanced_profiles_of_length(levels-1) for l in range(1,levels-1))  # long time
        rrz+Doublesplit must occur at 'inner' level! %rrTr6rrr7rEr�cSsi|]\}}||�qSrrrrrrr�|r zOGeneralisedStratum.doublesplit_graph_before_and_after_level.<locals>.<dictcomp>cs*i|]"}�j|d�j�|d�qSr�)r�rXr�)r��ep_to_mr;rrr�~srcs*i|]"}�j�|d�j|d�qSr�)rXr�r�)r�r:rRrrr��srJcs&i|]}�j|d�j|d�qSr�r8r�r9rrr��srK)r(r�r�r5rQrNr=r�r�r�r�rnror�r&)r3rWr�rar%r�r>r?Zt_l_enh_profiler@Ztop_to_lZ
top_to_botr�Zbot_to_lZ
bot_to_top�middle_leg_mapr)r�r:rRr;r�(doublesplit_graph_before_and_after_levelsZ1�
�

��
�
�
z;GeneralisedStratum.doublesplit_graph_before_and_after_levelcs�|\}}|dkrL|�|d���d��djj�s6J���dj�djfS|t|�kr�|�||���d��djj�s~J���dj�djfS|�||��|�||�}|j|��	d��dks�J�|j
||dd�}�fdd	�|��D�}�|�dfS)
a�
        Retrieve the splitting and embedding dictionaries for splitting at level l,
        as well as the level in 'standard form', i.e. as either:

        * a top of a BIC
        * a bot of a BIC
        * a middle of a 3-level graph

        This is essentially only a frontend for split_graph_at_level and
        doublesplit_graph_before_and_after_level and saves us the annoying
        case distinction.

        This is important, because when we glue we should *always* use the
        dmp's of the splitting dictionary, which can (and will) be different
        from leg_dict of the level!

        INPUT:

        enh_profile (tuple): enhanced profile
        l (int): (relative) level number

        OUTPUT:

        tuple: (splitting dict, leg_dict, level) where
        splitting dict is the splitting dictionary:

        * X:            GeneralisedStratum self.X
        * top:          EmbeddedLevelGraph: top component
        * bottom:       EmbeddedLevelGraph: bottom component
        * clutch_dict:  clutching dictionary mapping ex-half-edges on
          top to their partners on bottom (both as points in the
          respective strata via dmp!)
        * emb_dict_top: a dictionary embedding top into the stratum of self
        * emb_dict_bot: a dictionary embedding bot into the stratum of self

        leg_dict is the dmp at the current level (to be used instead
        of leg_dict of G.level(l)!!!)

        and level is the 'standardised' LevelStratum at l (as described above).

        Note that clutch_dict, emb_top and emb_bot are dictionaries between
        points of strata, i.e. after applying dmp to the points!
        rrrr7rETr�cs.i|]&\}}|�djvr|�dj|�qS)rErr��drrr��s�z>GeneralisedStratum.splitting_info_at_level.<locals>.<dictcomp>)rA�
is_isomorphicr=rcr�r(rTrQr�r�r�r&)r3rWr�rwrOrPrSZL_to_mrrUr�splitting_info_at_level�s&,��z*GeneralisedStratum.splitting_info_at_levelcCs�|std|�tj��|t|j�kr.t�Sg}t|j|�D]`\}}|svtd||dt|j|�f�tj��tt|j	|dd���D]}|�
||f�q�q@t|�S)aR
        A little helper for generating all enhanced profiles in self of a given length.

        Note that this generates the *entire* lookup_list first!
        For large strata this can take a long time!

        Args:
            l (int): length (codim) of profiles to be generated.

        Returns:
            tuple: tuple of enhanced profiles

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=Stratum((4,))
            sage: len(X.lookup_list[2])
            17
            sage: len(X.enhanced_profiles_of_length(2))
            19

        z,Generating enhanced profiles of length %r...z$Building all graphs in %r (%r/%r)...rTr�)rPr
rrr(rzrlr.rmr�rn)r3r�r�r�rTrar%rrr�enhanced_profiles_of_length�s
�
z.GeneralisedStratum.enhanced_profiles_of_lengthc
Cs,d}|durt|���}n|g}|D�]}t|j|�D]�\}}|j}d}	|shtd|dt|�ddd�t|���D]T}
|�|
�}|��d	kr�|r�td|dt|�ddd�td
|
d�d}|	|��7}	qt|	|��|k�r|r�td|dt|�ddd�td
|	d|��|d�d}q8|s8td�q8q$|S)a�
        Check if, for each non-horizontal level graph of codimension codim
        the dimensions of the levels add up to the dimension of the level graph
        (dim of stratum - codim).

        If codim is omitted, check the entire stratum.

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=GeneralisedStratum([Signature((1,1))])
            sage: X.check_dims()
            Codimension 0 Graph 0: Level sums ok!
            Codimension 1 Graph 0: Level sums ok!
            Codimension 1 Graph 1: Level sums ok!
            Codimension 1 Graph 2: Level sums ok!
            Codimension 1 Graph 3: Level sums ok!
            Codimension 2 Graph 0: Level sums ok!
            Codimension 2 Graph 1: Level sums ok!
            Codimension 2 Graph 2: Level sums ok!
            Codimension 2 Graph 3: Level sums ok!
            Codimension 3 Graph 0: Level sums ok!
            True

            sage: X=GeneralisedStratum([Signature((4,))])
            sage: X.check_dims(quiet=True)
            True

            sage: X=GeneralisedStratum([Signature((10,0,-10))])
            sage: X.check_dims()
            Codimension 0 Graph 0: Level sums ok!
            Codimension 1 Graph 0: Level sums ok!
            Codimension 1 Graph 1: Level sums ok!
            Codimension 1 Graph 2: Level sums ok!
            Codimension 1 Graph 3: Level sums ok!
            Codimension 1 Graph 4: Level sums ok!
            Codimension 1 Graph 5: Level sums ok!
            Codimension 1 Graph 6: Level sums ok!
            Codimension 1 Graph 7: Level sums ok!
            Codimension 1 Graph 8: Level sums ok!
            Codimension 1 Graph 9: Level sums ok!
            Codimension 1 Graph 10: Level sums ok!
            Codimension 1 Graph 11: Level sums ok!
            True

            sage: X=GeneralisedStratum([Signature((2,2,-2))])
            sage: X.check_dims(quiet=True)  # long time (3 seconds)
            True
        TNrZCodimension�Graph�:rrr_zError: Levelzis of dimension -1!Fz!Error: Level dimensions add up to�not�!zLevel sums ok!)	rmrQr.rRr�rPrH�numberoflevels�stratum_from_level)r3�codimr�r�ZcodimsrTr%Zemb_gr$Zdimsumr�r�rrr�
check_dims�s@2

���zGeneralisedStratum.check_dimscCs|�t�dg|di�}|��S)a(
        CURRENTLY ONLY ALLOWED FOR CONNECTED STRATA!!!!

        The psi class on the open stratum at leg.

        Args:
            leg (int): leg number (as index of signature, not point of stratum!!!)

        Returns:
            ELGTautClass: Tautological class associated to psi.
        rr�rZrlr|)r3r!�psirrrrcJ	szGeneralisedStratum.psicCs|�t|�|f���S)a.
        Tautological class from the graph with enhanced profile (profile, index).

        INPUT:

        profile (iterable): profile
        index (int, optional): Index of profile. Defaults to 0.

        OUTPUT:

        ELGTautClass: Tautological class consisting just of this one graph.
        rb)r3rwrrrr�taut_from_graphZ	s
z"GeneralisedStratum.taut_from_graphcCs4g}|D]}|dkrq|�|j�qtjj�||�S)aD
        Sum of tautological classes.

        This is generally faster than += (i.e. sum()), because reduce is only called
        once at the end and not at every step.

        Args:
            L (iterable): Iterable of ELGTautClasses on self.

        Returns:
            ELGTautClass: Sum over input classes.
        r)�extendr�r[r\rr�)r3r�Znew_psi_list�Trrrr�i	s
�zGeneralisedStratum.ELGsumcCsB|durd}|j}n
|j|�}|}t|�D]}|�|||�}q*|S)a$
        Calculate T^k with ambient amb.

        Args:
            T (ELGTautClass): Tautological class on self.
            k (int): positive integer.
            amb (tuple, optional): enhanced profile. Defaults to None.

        Returns:
            ELGTautClass: T^k in CH(amb).
        Nr�)r~rdrmr�)r3rfr�ambr~rrOrrr�pow~	s
zGeneralisedStratum.powcs����}�durd�|sZ�s(td���fdd��������fdd�t|d�D��S�j��}�s�td	t�j�d
d�tj�	��jD]@\}}	|	�
|�|�}
|
dks�|
�jkr��jS��||
��}q��s�td
�|S)a�
        (Formal) exp of a Tautological Class.

        This is done (by default) by calculating exp of every AdditiveGenerator
        (which is cached) and calculating the product of these.

        Alternatively, prod=False computes sums of powers of T.

        Args:
            T (ELGTautClass): Tautological Class on X.

        Returns:
            ELGTautClass: Tautological Class on X.
        Nr�zCalculating exp of %s...cs�std|�dS)NzCalculating power %r...r)rPr�r�rr�_status�	sz'GeneralisedStratum.exp.<locals>._statuscs2g|]*}�|�tdt|�f����|���qSr��rr
rhrj)rfrirgr3rrr#�	s�z*GeneralisedStratum.exp.<locals>.<listcomp>rz+Calculating exp as product of %r factors...rrr�Done!)
rQrPr�rmrdr(r�r
rr�expr�r�)r3rfrgr�r�stopr�r�rTr��fr)rfrirgr�r3rrl�	s8
��
��

zGeneralisedStratum.expcCs0|j|j}|�|fdf�}|j|dd�|jS)Nr�rg)rir�rZrlr~)r3r%r�r�rrr�exp_bic�	szGeneralisedStratum.exp_biccs:���}�durd�������fdd�t|d�D��S)z�
        (Formal) td^-1 contribution, i.e. (1-exp(-l*T))/T.

        Args:
            l (int): weight
            T (ELGTautClass): Tautological class on self.

        Returns:
            ELGTautClass: Tautological class on self.
        Nr�cs8g|]0}t��|tt|d�����|���qSr�rj)rr�rfrgr�r3rrr#�	s��z1GeneralisedStratum.td_contrib.<locals>.<listcomp>r)rQr�rm)r3r�rfrgr�rrqr�
td_contrib�	s
�zGeneralisedStratum.td_contribcCs4z|jWSty.|jdd�|_|jYS0dS)a�
        xi of self in terms of psi and BICs according to Sauvaget's formula.

        Note that we first find an "optimal" leg.

        Returns:
            ELGTautClass: psi class on smooth stratum + BIC contributions (all
                    with multiplicities...)

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=Stratum((2,))
            sage: print(X.xi)  # 'unsafe' (order of summands might change) # doctest:+SKIP
            Tautological class on Stratum: (2,)
            with residue conditions: []
            <BLANKLINE>
            3 * Psi class 1 with exponent 1 on level 0 * Graph ((), 0) +
            -1 * Graph ((0,), 0) +
            -1 * Graph ((1,), 0) +
            <BLANKLINE>
        Tr�N)�_xi�AttributeError�xi_with_legrArrr�xi�	s
zGeneralisedStratum.xicCs"|dkr|jS|j|�|d�S)z�
        Cached method for calculating powers of xi.

        Args:
            n (int): non-negative integer (exponent)

        Returns:
            ELGTautClass: xi^n
        rr)r~rv�xi_pow�r3r"rrrrw�	szGeneralisedStratum.xi_powc
s�|std��|dur*��|�\}}�n&|}�j|dj|d}��|����t��}|j|}|d��|�}�fdd��D�}	|t	j
j����fdd�t
|	�D���_|rˆj|fS�jSdS)a(
        xi class of self expressed using Sauvaget's relation (with optionally a choice of leg)

        INPUT:

        leg (tuple, optional): leg on self, i.e. tuple (i,j) for the j-th element
        of the signature of the i-th component. Defaults to None. In this case,
        an optimal leg is chosen.

        quiet (bool, optional): No output. Defaults to False.

        with_leg (bool, optional): Return choice of leg. Defaults to False.

        OUTPUT:

        ELGTautClass: xi in terms of psi and bics according to Sauvaget.
        (ELGTautClass, tuple): if with_leg=True, where tuple is the corresponding
        leg on the level i.e. (component, signature index) used.

        EXAMPLES:

        In the stratum (2,-2) the pole is chosen by default (there is no 'error term')::

            sage: from admcycles.diffstrata import *
            sage: X=Stratum((2,-2))
            sage: print(X.xi)
            Tautological class on Stratum: (2, -2)
            with residue conditions: []
            <BLANKLINE>
            -1 * Psi class 2 with exponent 1 on level 0 * Graph ((), 0) +
            <BLANKLINE>
            sage: print(X.xi_with_leg(leg=(0,1)))
            Tautological class on Stratum: (2, -2)
            with residue conditions: []
            <BLANKLINE>
            -1 * Psi class 2 with exponent 1 on level 0 * Graph ((), 0) +
            <BLANKLINE>

        We can specify the zero instead and pick up the extra divisor::

            sage: print(X.xi_with_leg(leg=(0,0)))  # 'unsafe' (order of summands might change) # doctest:+SKIP
            Tautological class on Stratum: (2, -2)
            with residue conditions: []
            <BLANKLINE>
            3 * Psi class 1 with exponent 1 on level 0 * Graph ((), 0) +
            -1 * Graph ((0,), 0) +
            <BLANKLINE>
        z4Applying Sauvaget's relation to express xi for %r...Nrrcsg|]}��|fdg��qSr�)rZr�rArrr#F
r z2GeneralisedStratum.xi_with_leg.<locals>.<listcomp>cs&g|]\}}�j�|j|f�qSr�rir�)rr%r��Zbot_bic_listr3rrr#H
r )rP�!_choose_leg_for_sauvaget_relationr*r�bics_with_leg_on_bottomr�rlrrcr[r\rr�r.rs)
r3r!r��with_legr�rr�Zinternal_legrvZadd_gensrrzrru
s*2��


�
zGeneralisedStratum.xi_with_legc
Cs�t|j�}d}tt|jj���dd�d�}|D]p}|�|�}|sz|j|dj	|d}|slt
d||f�||gfSt|�}||kr.|}|}|dd�}	q.|dks�Jd	|��|j|dj	|d}|s�t
d
|||t|j�f�|||	fS)a\
        Choose the best leg for Sauvaget's relation, i.e. the one that appears on bottom
        level for the fewest BICs.

        Returns:
            tuple: tuple (leg, order, bic_list) where:
                * leg (tuple), as a tuple (number of conn. comp., index of the signature tuple),
                * order (int) the order at leg, and
                * bic_list (list of int) is a list of indices of self.bics where leg
                    is on bottom level.

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=Stratum((2,-2))
            sage: X._choose_leg_for_sauvaget_relation()
            ((0, 1), -2, [])

            In the minimal stratum, we always find all BICS:

            sage: X=Stratum((2,))
            sage: X._choose_leg_for_sauvaget_relation()
            ((0, 0), 2, [0, 1])
        r_cSs|dS�Nrr)�xrrr�<lambda>n
r zFGeneralisedStratum._choose_leg_for_sauvaget_relation.<locals>.<lambda>��keyrrzGChoosing leg %r (of order %r) because it never appears on bottom level.NzNo best leg found for %r!zTChoosing leg %r (of order %r), because it only appears on bottom %r out of %r times.)r(ri�sortedrurcrr)r|r*rrP)
r3r�Z	best_caseZbest_legZleg_listr�r
�orderZ	on_bottomZ
best_bot_listrrrr{P
s@
�
�����z4GeneralisedStratum._choose_leg_for_sauvaget_relationcCsbg}t|j�D]N\}}|j|}|j|j�|�}|dvsJJd||f��|dkr|�|�q|S)a�
        A list of BICs where l is on bottom level.

        Args:
            l (tuple): leg on self (i.e. (i,j) for the j-th element of the signature
                of the i-th component)

        Returns:
            list: list of indices self.bics

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=GeneralisedStratum([Signature((2,))])
            sage: X.bics_with_leg_on_bottom((0,0))
            [0, 1]
        r�z)Leg %r of BIC %r is not on level 0 or -1!r_)r.rirr%r�r�rn)r3r�r
r%rr!Z	leg_levelrrrr|�
s


�z*GeneralisedStratum.bics_with_leg_on_bottomcCs�|dkr4|dksJ�|r.|jj|}|�|�S|jS|�||�\}}}dd�|��D�}	t|���t|j���ksvJ�|dur�|jd|d�\}
}n0||vr�t	d|||f��||}|j||d	�}
g}|
dkr�|j
S|
jD]�\}}
|
jdk�r0i}|
jD]"}|	|jj|}|
j|||<q�||j
||d
�f}n8|
jdk�r\|�|
||�\}}|||f}ntd|
��|�|�q�tjj�||�S)
a�
        Pullback of xi on level l to enh_profile.

        This corresponds to xi_Gamma^[i] in the paper.

        RuntimeError raised if classes produced by xi on the level have
        unexpected codimension. ValueError is raised if the leg provided is not
        found on the level.

        INPUT:

        l (int): level number (0,...,codim)
        enh_profile (tuple): enhanced profile
        leg (int, optional): leg (as a leg of enh_profile!!!), to be used
        in Sauvaget's relation. Defaults to None, i.e. optimal choice.

        OUTPUT:

        ELGTautClass: tautological class consisting of psi classes on
                enh_profile and graphs with oner more level.

        EXAMPLES:

        Compare multiplication with xi to xi_at_level (for top-degree)::

            sage: from admcycles.diffstrata import *
            sage: X=Stratum((2,-2,0))
            sage: assert all(X.xi_at_level(0, ((i,),0)) == X.xi*X.taut_from_graph((i,)) for i in range(len(X.bics)))
        r�rcSsi|]\}}||�qSrrrrrrr��
r z2GeneralisedStratum.xi_at_level.<locals>.<dictcomp>NT)r}r�z Leg %r is not on level %r of %r!r�rrz/Classes in xi should all be of codim 0 or 1! %s)rcr�rurvrXr&rorrrXr�r�r�r`rZ�glue_AG_at_levelr�rnr[r\rr�)r3r�rWr!r�Z	level_legrVrXr��inv_leg_dictZl_xiZ	taut_listrTr��new_leg_dictZAGlegZleg_on_GZ	next_taut�coeff�glued_AGrrrr��
sJ
�
�
�zGeneralisedStratum.xi_at_levelcs�|\�}|j\}}t��dkr<�dks*J�d��||f�fS�dkr\���fdd�|D�}nJ�t��kr����fdd�|D�}n&��|��\�����fdd�|D�}t��}t|d��||�d��}g}	t��|��dks�Jd|����|��\}
}}|j|u�rt	d	�|f��dk�rT|
d
j
|u�s<J�|
d
j
j|j�|
d
<n`�t��k�r�|
dj
|u�svJ�|
dj
j|j�|
d<n&|
d|u�s�J�|
dj|j�|
d<tj
jjfi|
��}
t��|��D] \}}|
�|��r�|	�|��q�t|	�dk�rtd
|||	f��|	d}��||f�}��|�}d}tt|��D]2}|t�j||j�t|j||j�9}�qH|tt|
j��tt|jj�t|jj��9}||fS)a�
        Glue an AdditiveGenerator into level l of enh_profile.

        Note that AG must be an AdditiveGenerator on the level obtained via
        self.splitting_info_at_level!

        Currently this is only implemented for graphs (and only really tested
        for BICs!!!)

        TODO: Test for AGs that are not BICs and psi classes.

        Args:
            AG (AdditiveGenerator): AdditiveGenerator on level
            enh_profile (tuple): enhanced profile of self.
            l (int): level number of enh_profile.

        Raises:
            RuntimeError: raised if the new profile is empty.

        Returns:
            tuple: A tuple consisting of the stackfactor (QQ) and the
                AdditiveGenerator of the glued graph.
        rrcs g|]}�j����|�qSr)rprq�rr��r�rwr3rrr#s���z7GeneralisedStratum.glue_AG_at_level.<locals>.<listcomp>cs$g|]}�j���d�|�qSr�)rprtr�r�rrr#s�
��cs g|]}�j���f�|�qSr)rpZ
middle_to_bicr�)�enhancementr3rPrrr#$s��Nz"Error: Glued into empty profile %rz`Warning! Additive Generator should live on level %r of %r! I hope you know what you're doing....rr7rEz)%r is not a unique degeneration of %r! %r)rWr(rZrQrurlr�rX�_XrPr=r�r[r\r{rr.rWrnr�rmrrir��
automorphisms�_G)r3r�rWr��_compZ	AGprofileZAGcompZnew_bicsra�	comp_listrVrXr�Zglued_graphr%�Hr�ZGAG�stack_factorr)r�r�rwr3rPrr��
st

���� ��
�
�
�z#GeneralisedStratum.glue_AG_at_levelcCs�g}|dus|dkrLt|j�D]*\}}|j|j}|�||�|f��qnh|�||�\}}}	t|	j�D]J\}}|	�|fdf�}
|�|
||�\}}t||j�}
|�|
|�	��qh|s�|j
S|�|�S)aT
        The error term of the normal bundle on level l of enh_profile * -ll
        (pulled back to enh_profile)

        Args:
            enh_profile (tuple, optional): enhanced profile. Defaults to None.
            l (int, optional): level. Defaults to 0.

        Returns:
            ELGTautClass: Tautological class on self
        Nr�r)r.rir�rnrdrXrZr�rr|r�r�)r3rWr��resultr%rr�rVrXr�ZBAG�sfr�r�rrrr�XszGeneralisedStratum.calLcCsh|��d}||jg}t|j�D]:\}}|j��d}|j}|�||||�|f��q"|�|�S)z�
        The first chern class of Omega^1(log) (Thm 1.1).

        OUTPUT:

        ELGTautClass: c_1(E) according to Thm 1.1.
        r)	rQrvr.rirr�rnrdr�)r3r�Zc1Er%r�Ntopr�rrr�c1_E~s	 zGeneralisedStratum.c1_EcCs�t|��d�}||dtd�|�d�g}t|j�D]�\}}|j��d}|�d|fdf�}|�d|fdf�}t|j�}|�|d||d||d|||d|||�q:|�	d�D]�}	|	\}
}|j|
d}|j|
d}
|j��d}|
j��d}t|j�}t|
j�}td�td�||||d|||d|||}|�||j
|	��q�|�|�S)z�
        A direct formula for the second Chern class.

        Returns:
            ELGTautClass: c_2 of the Tangent bundle of self.
        rr�r�rrQrwr.rirr�r�rnrYrdr�)r3r�Zc2Er%rr��xitop�xibotr�r�rarO�delta0�delta1ZNd0�Nd1�ld0�ld1�factorrrr�c2_E�s. 
$�


&�zGeneralisedStratum.c2_Ecsbt���d�}t||�tt|����|�g}td|d�D�]
}g}��|�D]�}|\}}�fdd�|D�}	dd�|	D�}
dd�|	D�}�j|��d|�|d�}d}
�j|�}t|�D]X}|
||||
|9}
��	|��
|
||||��||��||��|�|�}q���	|||�}|�
|
|�|��qV|�
��|��qBt|���|�S)z�
        A direct formula for powers of ch_1

        Args:
            n (int): exponent

        Returns:
            ELGTautClass: ch_1(T)^n
        rcsg|]}�j|�qSr�rir�rArrr#�r z.GeneralisedStratum.ch1_pow.<locals>.<listcomp>cSsg|]
}|j�qSr�r��rrrrrr#�r cSsg|]}|j��d�qSr��rrQr�rrrr#�r rro)rrQr
rwrmrYrlr�rdr�rrr�r�rn�degreer�)r3r"r�Zchpowr�r�r�rarO�delta�ld�Nd�exir��td_prodr%rrrAr�ch1_pow�s0$
��zGeneralisedStratum.ch1_powcCs*t|��d�}|td�|�d�g}t|j�D]b\}}|j��d}|�d|fdf�}|�d|fdf�}t|j�}|�|d||||�q2|�	d�D]~}	|	\}
}|j|
d}|j|
d}
|
j��d}t|j�}t|
j�}td�td�||||}|�||j
|	��q�|�|�S)z]
        A direct formula for ch_2.

        Returns:
            ELGTautClass: ch_2
        rr�rr�)r3r�Zch2Er%rr�r�r�r�r�rarOr�r�r�r�r�r�rrr�ch2_E�s$
 

 zGeneralisedStratum.ch2_Ecs�t���d�}|tt|����|�g}td|d�D�]:}g}��|�D�]}|\}}�fdd�|D�}	�j|dj��d}
d}|	D]}||9}q�|||
}
�j}t|�D]4}��	|��
|	|��||��||��|�|�}q�g}t||d�D]N}��	��
��d|�||�|�||�|�}|�td�tt|��|��q|�|
��|��qN|���|��q:��|�S)z�
        A formula for the Chern character.

        Args:
            d (int): cut-off degree

        Returns:
            ELGTautClass: sum of ch_0 to ch_d.
        rcsg|]}�j|j�qSrryr�rArrr#�r z/GeneralisedStratum.ch_E_alt.<locals>.<listcomp>r_r)rrQr
rwrmrYrirr~r�rrr�r�rhr�r�rnr�)r3rVr��ch_Er�r�r�rarOr�r��ld_prodr�r�r�r%�	inner_sumr&�prrrAr�ch_E_alt�s<

(����"zGeneralisedStratum.ch_E_altcCs\|js$t|j�dkr$d|jd}nd}|sJ|r:|d}n|}td|�|j|j||d�S)z�
        Calculate exp(xi) using that no powers higher than 2g appear for connected
        holomorphic strata.

        Args:
            quiet (bool, optional): No output. Defaults to True.

        Returns:
            ELGTautClass: exp(xi)
        rr�rNzStoping exp(xi) at degree %r)r�rm)r/r(r-rPrlrv)r3r�rmZstop_strrrr�exp_xi
s
zGeneralisedStratum.exp_xic	Cs�|j|�}|�|�}|js@t|j�dkr@|d|jdkr@|jS|dkr^|dksTJ�|�|�S|j|�}|�||�}t	|�D]}|�
|||�}q||S)a�
        Calculate powers of xi_at_level (using ambient enh_profile).

        Note that when working with xi_at_level on enh_profile, multiplication
        should always take place in CH(enh_profile), i.e. using intersection
        instead of ``*``. This is simplified for powers by this method.

        Moreover, by Sauvaget, xi^n = 0 for n >= 2g for connected holomorphic
        strata, so we check this before calculating.

        INPUT:

        level (int): level of enh_profile.
        enh_profile (tuple): enhanced profile of self.
        exponent (int): exponent

        OUTPUT:

        ELGTautClass: Pushforward of (xi_{enh_profile}^[l])^n to self.
        rr�rr�)r�r�r/r(r-r�rwrdr�rmr�)	r3r�rW�exponentr�r��powerrvrOrrr�xi_at_level_pow%s



z"GeneralisedStratum.xi_at_level_powcCs|j|��|d�S)z�
        exp(calL)

        Args:
            quiet (bool, optional): No output. Defaults to True.

        Returns:
            ELGTautClass: exp(calL)
        r�)rlr�)r3r�rrr�exp_LTszGeneralisedStratum.exp_LcCsbt|��d�}||��d|jg}td|�D�]"}g}|�|�D]�}|\}}|j|d}|j��d}	d||	|��d|j}
g}t|�D]F}|j||j}
|�	||�}|
|�
|
|�|||�|�}|�|�q�|�r:|d}|dd�D]}|�
|||�}q�|�d�}|d|7}|
|||j|�9}
|�|
�qF|�|�|��q2|�|�S)z�
        The twisted Chern character of self, see sec 9 of the paper.

        Returns:
            ELGTautClass: class of P_B
        rr_rN)rrQr�r~rmrYrirr�r�rrr�rnr�r�rdr�)r3r��PBr��innerrWrarOrr�r��	prod_listr%r�r��td_NBrrn�constrrr�P_BasB	
���
�zGeneralisedStratum.P_Bc
Cs�|std�|��}dd�t|�D�}|jg}|dur>|��}td|d�D]r}|s`td|�g}td|d�D]*}	|�d|	d|||	||	�qr|�td�t|�|�|��qL|S)a�
        Newton's identity to recursively translate the Chern character into the
        Chern polynomial.

        Args:
            ch (ELGTautClass): Chern character
            upto (int, optional): Calculate polynomial only up to this degree. Defaults to None (full polynomial).
            quiet (bool, optional): No output. Defaults to True.

        Returns:
            list: Chern polynomial as list of ELGTautClasses (indexed by degree)
        zStarting charToPol...cSsg|]\}}t|�|�qSrr	)rrrTrrrr#�r z0GeneralisedStratum.charToPol.<locals>.<listcomp>NrzCalculating c_%r...r_)	rP�list_by_degreer.r~rQrmrnrr�)
r3�ch�uptor��Cra�ErZekr%rrr�	charToPol�s
("zGeneralisedStratum.charToPolcCs�|j|d���}g}|��}t|�D]�}t|���}d||}|j}|��D]T\}	}
|tt	|	d�|
�tt	|
��9}|	dkr�||�
|
�9}qN|||	|
9}qN|�||�q$|�|�S)z�
        Top chern class from Chern polynomial.

        Args:
            quiet (bool, optional): No output. Defaults to True.

        Returns:
            ELGTautClass: c_top of the tangent bundle of self.
        r�r_r)
�	ch_E_fastr�rQrr+rrr~r&rr
r�rnr�)r3r�r��top_cr�rar�r�Zch_prodr%r"rrr�top_chern_class_alt�s
$z&GeneralisedStratum.top_chern_class_altcs���}g}t|d�D]�}|s:tdt��|��|f�g}��|�D]�}|\}}�fdd�|D�}	d}
|	D]}|
|9}
qng}t||dg|d�D]N}
�j|�}t|
�D] \}}��|��	|||�|�}q�|�
|
dd|�q�|�
|
��|��qH|�
��|��q��|�S)z�
        A direct formula for the top Chern class using only xi_at_level.

        Args:
            quiet (bool, optional): No output. Defaults to True.

        Returns:
            ELGTautClass: c_top of the Tangent bundle of self.
        r�)Going through %r profiles of length %r...csg|]}�j|j�qSrryr�rArrr#�r z=GeneralisedStratum.top_chern_class_direct.<locals>.<listcomp>r)rQrmrPr(rYrrdr.r�r�rnr�)r3r�r�r�r�r�r�rarOr�r�r�r��K�xi_prodr%rrrAr�top_chern_class_direct�s2
�

�z)GeneralisedStratum.top_chern_class_directcsp���}|\}}t|�}�fdd�|D�}�fdd�|D�}|�|d�d}	|D]}
|	|
9}	qR��d||dd�}td|d�D]0}��|��||||||dd�|�}q�|d|jdd�}
|s�td|
��j|����	|�}�fd	d�t|d�D�}|�st|�|d}|D]}||9}�q"|j
|}|�shtd
|j
�td|�td|�||
kS)
a
        Comparison of level-wise computation vs xi_at_level.

        Args:
            ep (tuple): enhanced profile
            quiet (bool, optional): no output. Defaults to False.

        Returns:
            bool: Should always be True.

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=Stratum((2,))
            sage: assert all(X.top_xi_at_level_comparison(ep, quiet=True) for l in range(len(X.lookup_list)) for ep in X.enhanced_profiles_of_length(l))
        csg|]}�j|j�qSrryr�rArrr#�r zAGeneralisedStratum.top_xi_at_level_comparison.<locals>.<listcomp>cs g|]}�j|j��d�qSr�)rirrQr�rArrr#�r rrTr�zProduct of xis at levels: %rc	s2g|]*}��|��dd��|����jdd��qS)rr�Tr�)r�r�rQ�evaluaterj)r�rrr#
s�
��zStack factor: %r�Product: %rzTotal product: %r)rQr(rnr�rmr�r�rPr�rZr�)r3r�r�r�rarOr�r�ZNvecr�r�r�r%Zxi_at_level_prodr��top_xi_at_levelrrZtot_prodr)r�r3r�top_xi_at_level_comparison�sF
$�


��
z-GeneralisedStratum.top_xi_at_level_comparisoncCs�|j|�}|�|�}|��}ddlm}||vrx|��}|sRtddd�tj�	�|�
dd|�}	|	jdd	�}
||<|
S|s�td
dd�tj�	�||SdS)a�
        Evaluate the top xi power on a level.

        Note that this is _not_ computed on self but on the GeneralisedStratum
        corresponding to level l of ep (the result is a number!).

        Moreover, all results are cached and the cache is synchronised with
        the ``XI_TOPS`` cache.

        The key for the cache is L.dict_key (where L is the LevelStratum).

        Args:
            ep (tuple): enhanced profile
            level (int): level number of ep
            quiet (bool, optional): No output. Defaults to True.

        Returns:
            QQ: integral of the top xi power against level l of ep.

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=Stratum((2,))
            sage: X.top_xi_at_level(((),0), 0)
            -1/640

        TESTS:

        Check that the cache can be deactivated and reactivated::

            sage: from admcycles.diffstrata import Stratum
            sage: import admcycles.diffstrata.cache
            sage: tmp = admcycles.diffstrata.cache.TOP_XIS
            sage: admcycles.diffstrata.cache.TOP_XIS = admcycles.diffstrata.cache.FakeCache()
            sage: X = Stratum((2,))
            sage: X.top_xi_at_level(((),0), 0) is X.top_xi_at_level(((),0), 0)
            False
            sage: admcycles.diffstrata.cache.TOP_XIS = tmp
            sage: X.top_xi_at_level(((),0), 0) is X.top_xi_at_level(((),0), 0)
            True
        r)�TOP_XISz(calc)rrrr�Tr�z(cache)N)r�r��dict_key�cacher�rQrPr
rrr�r�)r3r�r�r�r�r�r�r�r�Ztop_xi�answerrrrr�
s *



z"GeneralisedStratum.top_xi_at_levelc	s����}d}t|d�D�]�}|sDt�j|dd��}td||f�t��|��D�]N\}}|s�td|d||fdd�tj��|\}}	�fd	d
�|D�}
|r��j	|dj
��d}n|d}d}|
D]}
||
9}q̈�|�}|||j}|�stddd�tj��t|d�D]f}|�s>td|dd�tj��|�j
|||d�9}|dk�r|�sxtd
dd�tj���q��q|�s�td�tj��||7}qRqd||S)a�
        Calculate the (Orbifold) Euler characteristic of self by evaluating top xi
        powers on levels.

        This is (by far) the fastest way of computing Euler characteristics.

        Note that only combinatorial information about the degeneration graph
        of self is used (enhanced_profiles_of_length(L)) and top_xi_at_level
        the values of which are cached and synched with ``TOP_XIS`` cache.

        Args:
            quiet (bool, optional): No output. Defaults to True.

        Returns:
            QQ: (Orbifold) Euler characteristic of self.

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=Stratum((2,))
            sage: X.euler_char_immediate_evaluation()
            -1/40
        rrFr�r�z%r / %r, %r:rrcsg|]}�j|j�qSrryr�rArrr#x
r zFGeneralisedStratum.euler_char_immediate_evaluation.<locals>.<listcomp>zCalculating xi atzlevel %rz
Product 0.zDone.r_)rQrmr(rYrPr.r
rrrirrZr�r�)r3r�r��ecr��totalr%r�rarOr�Z	NGammaTopr�r�r�rrrAr�euler_char_immediate_evaluationU
sL








z2GeneralisedStratum.euler_char_immediate_evaluationcCs|��S)a�
        Calculate the (Orbifold) Euler characteristic of self by evaluating top xi
        powers on levels. See also euler_char_immediate_evaluation.

        Returns:
            QQ: (Orbifold) Euler characteristic of self.

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=Stratum((2,))
            sage: X.euler_characteristic()
            -1/40
        )r�rArrr�euler_characteristic�
sz'GeneralisedStratum.euler_characteristic�directcCs^|dkr|j|d�}n$|dkr,|j|d�}n|j||d�}|sFtd�d|��|jdd�S)a�
        Calculate the (Orbifold) Euler characteristic of self by computing the top
        Chern class and evaluating this.

        Note that this is significantly slower than using self.euler_characteristic!

        The optional keyword argument alg determines how the top Chern class
        is computed and can be either:
        * direct (default): using top_chern_class_direct
        * alt: using top_chern_class_alt
        * other: using top_chern_class

        Args:
            quiet (bool, optional): no output. Defaults to True.
            alg (str, optional): algorithm (see above). Defaults to 'direct'.

        Returns:
            QQ: (Orbifold) Euler characteristic of self.

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=Stratum((2,))
            sage: X.euler_char()
            -1/40
            sage: X.euler_char(alg='alt')
            -1/40
            sage: X.euler_char(alg='other')
            -1/40
        r�r��alt)r��algz
Evaluating...r_T)r�r��top_chern_classrPrQr�)r3r�r�Ztccrrr�
euler_char�
szGeneralisedStratum.euler_char�fastcCs|j|||||d�dS)aI
        Compute the top Chern class from the Chern polynomial via the Chern character.

        This uses chern_poly.

        INPUT:

        inside: bool (optional)
        Passed to chern_poly. Defaults to True.

        prod: bool (optional)
        Passed to chern_poly. Defaults to True.

        top: bool (optional)
        Passed to chern_poly. Defaults to False.

        quiet: bool (optional)
        Passed to chern_poly. Defaults to True.

        alg: str (optional)
        Passed to chern_poly. Defaults to 'fast'.

        OUTPUT:

        c_top(T) of self.
        )�insiderrr�r�r_)�
chern_poly)r3r�rrr�r�rrrr��
s
��z"GeneralisedStratum.top_chern_classcCsN|dkr|j|d�}n(|dkr,|j|d�}n|j||||d�}|j|||d�S)a�
        The Chern polynomial calculated from the Chern character.

        The optional keyword argument alg determines how the Chern character
        is computed and can be either:

        * fast (default): use ch_E_fast
        * bic_prod: use ch_E_prod
        * other: use ch_E

        INPUT:

        inside: bool (optional)
        Passed to ch_E. Defaults to True.

        prod: bool (optional)
        Passed to ch_E. Defaults to True.

        top: bool (optional)
        Passed to ch_E. Defaults to False.

        quiet: bool (optional)
        No output. Defaults to True.

        alg: str (optional)
        Algorithm used (see above). Defaults to 'fast'.

        upto: integer (optional)
        highest degree of polynomial to calculate. Defaults to None (i.e. dim so the whole polynomial).

        OUTPUT:

        The Chern polynomial as list of ELGTautClasses (indexed by degree)
        �bic_prodr�r�)r�rrr�)r�r�)�	ch_E_prodr�r�r�)r3r�rrr�r�r�r�rrrr��
s$zGeneralisedStratum.chern_polycs����d}g}t|�D�]�}|s<tdt��|��|f�g}��|�D�]x}|sftd|fdd�|\}}	�fdd�|D�}
dd�|
D�}d	d�|
D�}d}
|D]}|
|9}
q�g}t||dg|d�D�]�}|�s6td
|ddd�td||t|dd
��dd�tdt||t|dd
��|d��t||t|dd
��|d�}��d||d�}t	t
|��dd
�D�]\}}|�s td||fdd�td|||ddd�td||t||dd
��fdd�tdt|||d||t||dd
��|d��|t|||d||t||dd
��|d�9}��||d�}��||d��
|||�||�}��|||�}�q~|�||�q�|�|
��|��qJ|���|��q��|�S)a
        A direct formula for the n-th Chern class of the tangent bundle of self.

        Args:
            n (int): degree
            quiet (bool, optional): No output. Defaults to True.

        Returns:
            ELGTautClass: c_n(T) of self.
        rr�zProfile: %rrrcsg|]}�j|�qSrr�r�rArrr#0r z2GeneralisedStratum.chern_class.<locals>.<listcomp>cSsg|]
}|j�qSrr�r�rrrr#1r cSsg|]}|j��d�qSr�r�r�rrrr#2r zxi coefficient: k_0:rzN-L-sum:Nz	Binomial:zk_%r: %rz
r_Gamma,i:zL-i: %r, sum: %r)rQrmrPr(rYrr+rr�rur.r�rhr�r�rnr�)r3r"r�r�Zc_nr�r�r�rarOr�r�r�r�r�r�r�r�rr%rr�ZX_powrrAr�chern_classsl�
"("��0�����zGeneralisedStratum.chern_classcCs�t|��d�}||jg}t|j�D]�\}}|sFtdt|�|f�g}|D]`}|sXqN|j|dj��d}||krzqN||}	|j}
|D]}|
|�	|�9}
q�|�
|	|
�qN|�
|�|��q&|j|d�|�|�S)z�
        The product version of the Chern character formula.

        Args:
            quiet (bool, optional): No output. Defaults to True.

        Returns:
            ELGTautClass: Chern character of the tangent bundle.
        rr�r_r�)
rrQr~r.rzrPr(rirrprnr�r�)r3r�r�r�r�Zprofilesr�rar�r�r��Dirrrr�Qs*

�zGeneralisedStratum.ch_E_prodcsRt���d�}|�j|d�g}td|�D�]}|sPtdt��|��|f�g}��|�D]�}|\}}�fdd�|D�}	�j|dj��d}
||
kr�q^d}|	D]}||9}q�|||
}
�j	}t|�D]4}��
|��|	|��||��
||��|�|�}qΈ���d|�|�}��
|||�}|�|
|�q^|���|��q,��|�S)a	
        A more direct (and faster) formula for the Chern character (see sec 9 of the paper).

        Args:
            quiet (bool, optional): No output. Defaults to True.

        Returns:
            ELGTautClass: Chern character of the tangent bundle.
        rr�r�csg|]}�j|j�qSrryr�rArrr#�r z0GeneralisedStratum.ch_E_fast.<locals>.<listcomp>r_r)rrQr�rmrPr(rYrirr~r�rrr�r�rlr�rnr�)r3r�r�r�r�r�r�rarOr�r�r�r�r�r�r%r�r�rrArr�ps8
�
(�zGeneralisedStratum.ch_E_fastcCs|�|j|���dS)a
        The top Chern class of self.

        This is computed by calculating the Chern polynomial
        from the Chern character as P_B*exp(L) and taking the top-degree part.

        Returns:
            ELGTautClass: top Chern class of the tangent bundle.
        r_)r�r�r�rArrr�
top_chern_alt�s
z GeneralisedStratum.top_chern_altcs��std�t���d��g}t�j�D]$\}}|�|j��|fdf�f�q*tj	j
j�|dd�}|�rN�svtd������fdd�t�j�D�d	|g�}|}�s�td
dd�t
d�d�D]P}	�s�t|	dd�td�t|	d�||}|�jk�r�q|j�|j�qĈ�s,td
�td�����j�d��j|g�}
nD��s\td���j�j|�d�}
��s�td�|
�j�d�8}
��s�td�|
S)a�
        The calculation of (N*self.exp_xi() - self.ONE)*self.exp_L() split into
        pieces with more debugging outputs (calculation can take a LONG time!)

        Args:
            top (bool, optional): Do calculations on level. Defaults to False.
            quiet (bool, optional): No output. Defaults to True.

        Returns:
            ELGTautClass: First term of ch.
        zCalculating first term...rrF)�reducezCalculating exp_xi_L...c	s>g|]6\}}�|j�j��d|fdf�|fdf�d��qS)rr�)r�rlr�)rr%r�r�r�r3rrr#�s����z1GeneralisedStratum.first_term.<locals>.<listcomp>r_z+Calculating recursive exponential factors: rrrkzAdding exp_xi...r�zCalculating exp(xi+L)...zSubtracting exp_L...zDone calculating first term!)rPrrQr.rirnr�rZr[r\rr�r�rmr�Z	_psi_listrer�r�r~rlrvr�)r3rr�ZBICsr%rr�Zexp_xi_LryrrKrr�r�
first_term�sT����zGeneralisedStratum.first_termc
sFt���d�}�j||d�g}td|�D�]}g}|sDtd|���|�D�]؉�\}	}
�j|	d}|j��d}|s�d||����j	}
n>|s�tddd�d||�j
��d���|d	��j��}
g}t|�D]H}�j|	|j
}���|�}|��|����|���}|�|�q�|�r|d}|dd
�D]}��||��}�qF|�r�tt|	�d�D]$}��|��
���|�����}�qrn8��|��
����fdd�tt|	�d�D������}|�r��||
��}|�d�}|d|7}|�r|}
n|
||�j��9}
|�|
�qN|���|��q*��|�S)
a�
        The Chern character (according to sec. 9 of the paper)

        Args:
            inside (bool, optional): work with ambient. Defaults to True.
            prod (bool, optional): product instead of sum. Defaults to True.
            top (bool, optional): work on level. Defaults to False.
            quiet (bool, optional): no output. Defaults to True.

        Returns:
            ELGTautClass: Chern character of the tangent bundle of self.
        r)rr�z&Going through profiles of length %r...rr_zCalculating inner exp(xi): rrr�Nc3s|]}���|�VqdSr7)r�rC�rWr3rrrs
��z*GeneralisedStratum.ch_E.<locals>.<genexpr>)rrQr�rmrPrYrirr�r~rlr�rdr�r�rrr�rnr�r(r�r�r�)r3r�rrr�r�r�r�r�rarOrr�r�r�r%r�r�r�rnr�r�rr�rr��s���������
	�����
�zGeneralisedStratum.ch_Ecs d�jdd�}g}|r4td|�f�td|�t�j�D]�\}�|rVtd|��j��fdd�|D�}�fd	d�|D�}|rڈ�|g�}�j}	|	j}
|�|
��	�|
�	�kr�|�|
��	�|
�	�ks�J�|r>td
�q>�j
}|r�td|�|�||f�q>|���fdd�|D��7}|S)
z�
        The class of the stratum cut out by cond inside self.

        INPUT:

        cond (list): list of a residue condition, i.e. a list of poles of self.

        OUTPUT:

        Tautological class of Prop. 9.3
        r_Tr�z;Calculating the class of the stratum cut out by %r in %r...z-xi = %szChecking BIC %r:csg|]}�j|�qSr)rrd�rrrr#Ar z8GeneralisedStratum.res_stratum_class.<locals>.<listcomp>cs g|]}|�jvr�j|�qSrrr )rrrr#Bs�zDiscarding (because of top).zAppending with coefficient -%rcs$g|]\}}|��|fd��qSr�)rd)rr�r%rArrr#Ss�)
rurPr.rirr�rcr��stackr�r�rnr�)r3�cond�debugZst_classr�r%Zpoles_on_bicZcond_on_top�MTZtop_GZRTr�r)rr3rr�res_stratum_class)sD��
��
z$GeneralisedStratum.res_stratum_classc
Cs�t||�}ddlm}||vr�tjj||d�}	tj�|d|�}
|rF|rVtd|	|
f�|
|	}|rf|rvtd|���|��}||<|S||SdS)a�
        Evaluate the psi monomial on a (connected) stratum without residue conditions
        using admcycles.

        stgraph should be the one-vertex graph associated to the stratum sig.

        We use admcycles Strataclass to calculate the class of the stratum inside
        Mbar_{g,n} and multiply this with psis (in admcycles) and evaluate the product.

        The result is cached and synched with the ``ADM_EVALS`` cache.

        Args:
            stgraph (stgraph): admcycles stgraph
            psis (dict): psi polynomial on stgraph
            sig (tuple): signature tuple
            g (int): genus of sig
            quiet (bool, optional): No output. Defaults to False.
            admcycles_output (bool, optional): Print the admcycles classes. Defaults to False.

        Returns:
            QQ: integral of psis on stgraph.

        TESTS:

        Check that the cache can be deactivated and reactivated::

            sage: from admcycles import StableGraph
            sage: from admcycles.diffstrata import Stratum
            sage: import admcycles.diffstrata.cache
            sage: X = Stratum((1,1))
            sage: stg = StableGraph([2], [[1,2]], [])
            sage: psis = {1: 1, 2: 3}
            sage: sig = (1, 1)
            sage: g = 2
            sage: tmp = admcycles.diffstrata.cache.ADM_EVALS
            sage: admcycles.diffstrata.cache.ADM_EVALS = admcycles.diffstrata.cache.FakeCache()
            sage: X.adm_evaluate(stg, psis, sig, g, quiet=True) is X.adm_evaluate(stg, psis, sig, g, quiet=True)
            False
            sage: admcycles.diffstrata.cache.ADM_EVALS = tmp
            sage: X.adm_evaluate(stg, psis, sig, g, quiet=True) is X.adm_evaluate(stg, psis, sig, g, quiet=True)
            True
        r)�	ADM_EVALS�rczDS: %r
 Stratum_class: %rr�N)	rr�r�r[�
decstratum�stratarecursion�StrataclassrPr�)
r3�stgraph�psisrr$r��admcycles_outputr�r��DSZ
Stratum_classr�r�rrr�adm_evaluateWs-
zGeneralisedStratum.adm_evaluatecCs�|duri}|js"|�d|���Szt|j�}WntyFi}Yn0t|j�}|jj}|r�|�	�}|�
|�}|r�|�|�}n|}|��|jj
��dkrZq�qZt|j||�}|�d|�}	|��|��dkr�|	��|�|�}
n|r�J�|	��}
|
S)ao
        Remove residue conditions until the rank drops (or there are none left).

        We return the stratum with fewer residue conditions and, in
        case the rank dropped, with the product of the stratum class.

        Note that this does *not* ensure that all residue conditions are removed!

        Args:
            psis (dict, optional): Psi dictionary on self. Defaults to None.

        Returns:
            ELGTautClass: ELGTautClass on Stratum with less residue conditions
                (or self if there were none!)

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=GeneralisedStratum([Signature((1,1,-2,-2))], res_cond=[[(0,2)], [(0,3)]])
            sage: print(X.remove_res_cond())
            Tautological class on Stratum: Signature((1, 1, -2, -2))
            with residue conditions:
            dimension: 1
            leg dictionary: {}
            <BLANKLINE>
            1 * Psi class 3 with exponent 1 on level 0 * Graph ((), 0) +
            <BLANKLINE>
            sage: X.evaluate(quiet=True) == X.remove_res_cond().evaluate()
            True
        Nr�r)r5rZr|r�	_leg_dictrtr1rc�residue_matrix_from_RTrvr�r�r�r��LevelStratumr*rQr�)r3r�r��new_rc�RT_M�	lost_cond�new_M�full_M�new_stratum�new_AG�	new_classrrr�remove_res_cond�s2


z"GeneralisedStratum.remove_res_condcsdd�|jD�}|sdS|D]�}|j�d��|j�d��|j����|j���}t��t|�t|jj�kspJ����fdd�tt|jj��D�}t|jj�}t|jj�}g}t|jj	�}t
jj�
|||||�}	t
jj�||	t|j�t|j��}
|
��rdSqdS)a�
        Check if self splits, i.e. if a subset of vertices can be scaled
        independently (then the stratum class is ZERO).

        We do this by checking if BICs B, B' exist with:
            * no edges
            * the top vertices of B are the bottom vertices of B'
            * the bottom vertices of B' are the top vertices of B.

        Explicitly, we loop through all BICs with no edges, constructing for
        each one the BIC with the levels interchanged (as an EmbeddedLevelGraph)
        and check its legality.

        Returns:
            boolean: True if splitting exists, False otherwise.

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: GeneralisedStratum([Signature((0,)),Signature((0,))]).zeroStratumClass()
            True
            sage: GeneralisedStratum([Signature((2,))]).zeroStratumClass()
            False
            sage: GeneralisedStratum([Signature((4,-2,-2,-2)),Signature((4,-2,-2,-2))], res_cond=[[(0,2),(1,2)]]).zeroStratumClass()
            True
            sage: GeneralisedStratum([Signature((2, -2, -2)), Signature((1, 1, -2, -2))],[[(0, 2), (1, 2)], [(0, 1), (1, 3)]]).zeroStratumClass()
            False
        cSsg|]}|jjs|�qSr)r�r�r�rrrr#�r z7GeneralisedStratum.zeroStratumClass.<locals>.<listcomp>Frrcsg|]}|�vr�n��qSrrr�Zinternal_botZinternal_topZtop_verticesrrr#s�T)rir�r��verticesonlevelr(r$rmrr(�
poleordersr[r\r��
LevelGraphr+r,r�r%r�)r3Z
bics_no_edgesr�Zbot_verticesZ
new_levelsr0�new_legsr1�new_poleordersr2Znew_ELGrrr�zeroStratumClass�s2 �
��z#GeneralisedStratum.zeroStratumClasscCs�|j}|j}|j��|j��kr�|jdkrZ|r2|rVtd�td|�td�td�dS|��dkrjdS|j|j	||j
dj|��||d�S|jdkr�|r�|r�td�td|�td�|j
��s�|r�|r�td�dSt|j�}|j}|�r4|��}	|�|�}
|
�r|
�|�}n|}|��|j��dkr�qBq�td|j��zt|j�}Wnt�yhi}Yn0t|j
||�}
|�s�td	|
�|
��|��dk�s�J�|
�d
|�}|��|
�|	�}|j|d�}|S)a*
        Evaluate the psi monomial psis on self.

        Psis is a dictionary legs of self.smooth_LG -> exponents encoding a psi monomial.

        We translate residue conditions of self into intersections of simpler classes
        and feed the final pieces into admcycles for actual evaluation.

        Args:
            psis (dict, optional): Psi monomial (as legs of smooth_LG -> exponent). Defaults to {}.
            quiet (bool, optional): No output. Defaults to False.
            warnings_only (bool, optional): Only warnings. Defaults to False.
            admcycles_output (bool, optional): adm_eval output. Defaults to False.

        Raises:
            RuntimeError: raised if a required residue condition is not found.

        Returns:
            QQ: integral of psis against self.
        rz4----------------------------------------------------zLevel %r disconnected.z)No residue conditions: contribution is 0.r)r�r�z$Level is product: contribution is 0.z,No Conditions cause dimension to drop in %r!zRecursing into stratum %rr�r�)rcr�r�r�r�r)rPrQr�r�r*rr$�underlying_graph�is_connectedrr1rvr�r�r�r�rtr�rZr|r�r�)r3r�r�Z
warnings_onlyr�r�r�r�r�r�r�r�r�rrrr�rrrr�sp

�



��
zGeneralisedStratum.evaluatecs�fdd�}|dur�jSg}t�j�D]�\}��jj��}|�|�tj�||�}t|�}|dkrfq(|�	�d���fdd�|D�}	t
�fdd�|	D��}
|s�td|||f�td	|
�td
t�fdd�t
�jd�D���|�|
��|f��q(��|�S)
Ncsld}i}t�j�D]8}tt�j|j��D]}|d7}|||j||f<q*q|�jksZJ�|j||d�dS)Nrr)�shift)rmr)r(r*rrr,�rename_legs)ZELGr��numr�r%r&rArr�_renameusz5GeneralisedStratum.boundary_pullback.<locals>._renamercs8g|]0}|d�d�j|d�d�jf�qS)rr)r,�r�m)r�r3rrr#�s�z8GeneralisedStratum.boundary_pullback.<locals>.<listcomp>c3s(|] }t�j�t�j�|��VqdSr7)rr�r��prongr�r�rrr�r z7GeneralisedStratum.boundary_pullback.<locals>.<genexpr>zD
Found BIC %r with stable graph %r in preimage with %r degenerationsz%sum of involved edges (ell / prong): zAny level pushs to ZERO: c3s|]}��|���VqdSr7)r�r
rCr�rrr�sr)r�r.rir�r��copyr[�Astructuresr(r�r+rPrermr`rnrdr�)r3r�r�r�tautlistr�r�Zgraph_morphismsZnum_degenerationsZpre_er�r)rr�r3r�boundary_pullbackps8

���
�
z$GeneralisedStratum.boundary_pullbackcCsJ|jdkr|jS|jg}td|jd�D]}|�|�|��q(|�|�S)aK
        The Mumford class of self.

        Note that kappa_1_AC = kappa_1 + sum(psis)

        Returns:
            ELGTautClass: kappa_1_AC - sum(psis)

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=Stratum((1,1,1,-5))
            sage: X.kappa_1.evaluate()
            -3
        r)r)r��
kappa_1_ACrmr,rnrcr�)r3�kappar%rrr�kappa_1�s
zGeneralisedStratum.kappa_1cCs�|jdkr|jS|jg}t|j�D]B\}}|j|jjj�	�|jj}|dkr"|�
||�|f��q"|jsz|�
|j
�|�|�S)a�
        The Arbarello--Cornalba class of self.

        Note that this 'corresponds' to admcycles kappaclass.

        Returns:
            ELGTautClass: c_1(omega_log) + sum(l_Gamma * G_Gamma * bics) (-xi if hol)

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: X=Stratum((1,1,1,-5))
            sage: X.kappa_1_AC.evaluate()
            1
            sage: from admcycles import *
            sage: (kappaclass(1,0,4) * Strataclass(0, 1, [1,1,1,-5])).evaluate()
            1
            sage: from admcycles.diffstrata import *

            sage: X=Stratum((2,))
            sage: (X.kappa_1_AC.to_prodtautclass().pushforward() - kappaclass(1, 2, 1)*Strataclass(2, 1, [2])).is_zero()
            True
            sage: X=Stratum((1,1))
            sage: (X.kappa_1_AC.to_prodtautclass().pushforward() - kappaclass(1, 2, 2)*Strataclass(2, 1, [1,1])).is_zero()
            True
        rr)r)r�r�r.rir�rrcr�r�rnrdr/rvr�)r3rr�rr�rrrr�s
�zGeneralisedStratum.kappa_1_ACcs��jdkr�jS�jdj}t|��jg}|��fdd�t|�D��t�j�D]T\}�t�fdd��j	D��}t�j
j���}�j
||}|�|��|f��qR��|�S)Nrrcs"g|]\}}|��|d��qSr�r�)rr%rrArrr#�r z4GeneralisedStratum.kappa_1_dawei.<locals>.<listcomp>c3s|]}�j�|�VqdSr7�rr`rdr�rrr�r z3GeneralisedStratum.kappa_1_dawei.<locals>.<genexpr>)r)r�r*rr+rvrer.rirr��prongsrrr�rnrdr�)r3rrr�Zsum_mi�sum_kr�r)rr3r�
kappa_1_dawei�s
z GeneralisedStratum.kappa_1_daweics�|jdkr|jS|j|jg}t|j�D]^\}�t�jj�	��}�fdd��j
D�}�jtdd�|D��|}|�||�
|f��q(|�|�S)Nrcsg|]}�j�|��qSrrrdr�rrr#�r z8GeneralisedStratum.kappa_1_dawei_alt.<locals>.<listcomp>css*|]"}t||d�t|d�VqdS�r�rNrrrrrr�r z7GeneralisedStratum.kappa_1_dawei_alt.<locals>.<genexpr>)r)r��	kappa_EKZrvr.rir+r�rrrrr�rnrdr�)r3rr�rZmisr�rr�r�kappa_1_dawei_alt�s
z$GeneralisedStratum.kappa_1_dawei_altcCs<dd�|jD�}tdd�|D��r*td��tdd�|D��S)aT
        The EKZ kappa, i.e. sum m_i*(m_i+2)/(m_i+1) for self.

        Raises:
            ValueError: If self has a simple pole (div by 0)

        Returns:
            QQ: EKZ kappa factor of self.

        EXAMPLES::

            sage: from admcycles.diffstrata import *
            sage: Stratum((1,1)).kappa_EKZ
            3
        cSsg|]}|jD]}|�qqSr�r)rrrrrrr#r z0GeneralisedStratum.kappa_EKZ.<locals>.<listcomp>css|]}|dkVqdS)r_Nrrrrrrr z/GeneralisedStratum.kappa_EKZ.<locals>.<genexpr>z*Cannot compute EKZ kappa for simple poles!css*|]"}t||d�t|d�VqdSrrrrrrrr )r*rer�r+)r3�sigsrrrr �szGeneralisedStratum.kappa_EKZcCs$|dur|�|�}tdd�|D��S)a2
        By default the total horizontal boundary.
        In general: takes an iterator (e.g. _horizontal_pf_iter) and
        sums over the second item (the pushforward classes)

        Args:
            graphs (iterable, optional): an iterator yielding tuples of
                the form G, tautclass. By default _horizontal_pf_iter.
                Defaults to None.
            psis (dict, optional): psi dictionary for passing on.
                Defaults to None.

        Returns:
            tautclass: the sum over the second items of graphs.
        Ncss|]\}}|VqdSr7r)rrOZhpfrrrr&r z<GeneralisedStratum.horizontal_pushforward.<locals>.<genexpr>)�_horizontal_pf_iterr+)r3rNr�rrr�horizontal_pushforwards
z)GeneralisedStratum.horizontal_pushforwardc#s�|jdkrt�|jd}|jdj�|j�|rltj�|gtt	d�d��gg�}tj�
tjj||d�g�}nd}|dk�r2|jj
j��}|�d�t��ddg}|jr�d�fd�dfg}ttjj�|�g�}|�|�����g}	ntj�|dd|�g}	tjj||	d�}
td�td�}||||
��fVt|j�dk�r�tj�|�d�D�]P}t|���dk�rn�qR|��\}}
��fdd�|�d�D�dg}��fd	d�|�d�D�dg}t |�d|dk�sRt |�d|
dk�r�qRt!d
d�|D���sRt!d
d�|D���r�qRdt|�dfdt|�dfg}ttjj�|�tjj�|�g�}|�|���}
|
dk�rr�qR||
_"td�t|�#��}||||
��fV�qRdS)
a�
        Iterate over the components of the pushforward of the
        horizontal boundary.

        Args:
            psis (dict, optional): Psi dictionary. Defaults to None.

        Raises:
            StopIteration: If disconnected or done.

        Yields:
            tuple: G, tautclass where G is StableGraph and tautclass
                is the pushed forward class of the horizontal divisor
                corresponding to G.
        rrr�r_)�protautr�cs g|]}|�kr�|d�qSr�rrC�r"rrrr#er z:GeneralisedStratum._horizontal_pf_iter.<locals>.<listcomp>cs g|]}|�kr�|d�qSr�rrCr'rrr#fr css|]}|dkVqdS)rNr)r�arrrrir z9GeneralisedStratum._horizontal_pf_iter.<locals>.<genexpr>N)$r)r�r-r*rr,r[�StableGraphrurm�	tautclassr�rcr�r�r�degenerate_nonsepr/rr\�	Signaturer��to_prodtautclass�pushforwardr�r��
prodtautclassrr(�list_stratar$r(r+r'�gamma�automorphism_number)r3r�r$r�Z	psi_contrr��new_sig�rcr=r�ptcr��gl�grZsiglZsigrrr'rr$(sj

��

���  ,( 
��
z&GeneralisedStratum._horizontal_pf_itercs��jdks�jdkrt��jd}�j}ddttd|td|d|���d|d�t	�fdd�t
d|d�D����S)u�
        If self is a connected stratum of holomorphic differentials,
        calculate the Masur-Veech volume of self using the formula [CMSZ2020]_
        of Chen-Möller-Sauvaget-Zagier.
        Otherwise throws NotImplementedError.

        EXAMPLES:

            sage: from admcycles.diffstrata import Stratum
            sage: X = Stratum((0,))
            sage: X.masur_veech_volume()
            1/3*pi^2

            sage: X = Stratum((2,))
            sage: X.masur_veech_volume()
            1/120*pi^4

            sage: X = Stratum((1,1))
            sage: X.masur_veech_volume()
            1/135*pi^4

            sage: X = Stratum((4,))
            sage: X.masur_veech_volume()
            61/108864*pi^6

            sage: X = Stratum((-1,-1,0))
            sage: X.masur_veech_volume()
            Traceback (most recent call last):
            ...
            NotImplementedError

        rrrkr�r�c3s|]}��|�VqdSr7r�rCrArrr�r z8GeneralisedStratum.masur_veech_volume.<locals>.<genexpr>)r)r0�NotImplementedErrorr-r,rr
r
rwrrmr�)r3r$r"rrAr�masur_veech_volumeys!
*��z%GeneralisedStratum.masur_veech_volume)N)N)F)N)N)N)N)N)N)N)r)T)rF)T)NF)r)N)NTTN)N)NTF)T)NT)Nr)T)T)NT)T)T)F)T)T)Tr�)TTFTr�)TTFTr�N)T)T)T)FT)TTFT)F)FF)N)NT)NN)N)p�__name__�
__module__�__qualname__�__doc__r6r2rBrLrUrZrVrbrrfrgr`�propertyrir5rzrpr~r�rRrcr�r�r�rQr�rhr�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r5rArNrQrTrXrYrarcrdr�rhrlrprrrvrwrur{r|r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�rr
r�rrrrr!r r%r$r9rrrrrvsR
4
&




1



(
 




	_G
6
1L
J8�

3
0
6�
%
.
-

<
"
�A
!pC+
T


-



J
7F
j
&


!
#/
&


"
4
<
@
)�
�
,
7%3U.�
<F7�
`0

)


Qrcs eZdZdZ�fdd�Z�ZS)�Stratumzg
    A simpler frontend for a GeneralisedStratum with one component and
    no residue conditions.
    cst��tjj�|�g�dSr7)�superr6r[r\rr,)r3r��	__class__rrr6�s�zStratum.__init__)r:r;r<r=r6�
__classcell__rrrArr?�sr?csfeZdZdZd�fdd�	Zdd�Zdd�Zed	d
��Ze	dd��Z
e	d
d��Zdd�Zdd�Z
�ZS)r�a�
    A stratum that appears as a level of a levelgraph.

    This is a ``GeneralisedStratum`` together with a dictionary mapping the
    leg numbers of the (big) graph to the legs of the ``Generalisedstratum``.

    Note that if this is initialised from an EmbeddedLevelGraph, we also
    have the attribute leg_orbits, a nested list giving the orbits of
    the points under the automorphism group of the graph.

    * leg_dict : a (bijective!) dictionary mapping the leg numbers of a graph
        to the corresponding tuple (i,j), i.e. the point j on the component i.

    * res_cond : a (nested) list of residue conditions given by the r-GRC when
        extracting a level.

    Ncszt��||�|durZi|_tt|��D].}t||j�D]}||f|j||d<q:q(n||_dd�|j��D�|_dS)NrcSsi|]\}}||�qSrrrrrrr��r z)LevelStratum.__init__.<locals>.<dictcomp>)r@r6r�rmr(r"r&�
_inv_leg_dict)r3r4r5rXr%r&rArrr6�szLevelStratum.__init__cCsd|j|j|jfS)Nz1LevelStratum(sig_list=%r,res_cond=%r,leg_dict=%r))r*r1rXrArrrrB�s�zLevelStratum.__repr__cCs�d}|jdkr|d7}n|d7}|jD]}|t|�d7}q&|d7}|jD]}|t|�d7}qJ|d7}|dt|���d7}|d	t|j�d7}z|d
t|j�d7}Wnty�Yn0|S)NrCrrDrErFrGrzdimension: zleg dictionary: zleg orbits: )r)r*rHr1rQr�Z
leg_orbitsrtrIrrrrL�s"



zLevelStratum.__str__cs�i�g}ttt|j�dd�d��D]h\}}|\}}g}ttt|j�dd�d��D]*\}}|\}	}
|�|
�||f�||	f<qP|�t|��q"t|�}t�fdd�|jD��}tdd�|D��}||fS)	a
        The hash-key for the cache of top-xi-powers.

        More precisely, we sort each signature, sort this list and renumber
        the residue conditions accordingly. Finally, everything is made into a tuple.

        Returns:
            tuple: nested tuple.
        cSs
|djSr~r"rrrrr��r z'LevelStratum.dict_key.<locals>.<lambda>r�cSs|dSr~rrrrrr�r cs"g|]}t�fdd�|D���qS)csg|]}�|�qSrr)rr���rc_dictrrr#r z4LevelStratum.dict_key.<locals>.<listcomp>.<listcomp>)r�)rZcondsrErrr#s�z)LevelStratum.dict_key.<locals>.<listcomp>css|]}t|�VqdSr7)rl)rrTrrrr
r z(LevelStratum.dict_key.<locals>.<genexpr>)r.r�r*rrnrlr1)r3r�new_iZnew_signr%�signZcurr_sigZnew_j�sr&r(r4rrErr��s(��
�zLevelStratum.dict_keycCs|jSr7�r�rArrrrX
szLevelStratum.leg_dictcCs|jSr7�rDrArrrr�szLevelStratum.inv_leg_dictcCs
|j|S)z~
        Returns a tuple (i,j) for the point j on the component i that corresponds
        to the leg n of the graph.
        rJrxrrrrMszLevelStratum.stratum_numbercCs
|j|S)zm
        Returns the leg number (of the graph G) that corresponds to the psi class
        number n.
        rKrxrrr�
leg_numberszLevelStratum.leg_number)NN)r:r;r<r=r6rBrLrr�r>rXr�rMrLrCrrrArr��s



r�),r�r
Zsympy.utilities.iterablesr�sage.structure.sage_objectr�sage.matrix.constructorr�sage.misc.flattenr�sage.misc.cachefuncr�sage.rings.rational_fieldr�sage.functions.otherr
�%sage.combinat.integer_vector_weightedrr�sage.symbolic.constantsr
r�sage.misc.misc_crrrZadmcycles.admcyclesr[Zadmcycles.stratarecursionZadmcycles.diffstrata.levelgraphZadmcycles.diffstrata.bic�admcycles.diffstrata.sigZ#admcycles.diffstrata.stratatautringZ'admcycles.diffstrata.embeddedlevelgraphZ&admcycles.diffstrata.additivegeneratorZ!admcycles.diffstrata.elgtautclassZadmcycles.diffstrata.auxiliaryrrrrrr?r�rrrr�<module>sxVT