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

��c��@s&dZddlZddlmZddlmZddlmZddlm	Z	ddl
mZddlm
Z
dd	lmZdd
lmZddlmZddlmZdd
lmZddlmZddlmZddlmZddlmZddl m!Z!ddl"m#Z#ddl$m%Z%ddl&m'Z'm(Z(m)Z)m*Z*m+Z+Gdd�de�Z,dd�Z-ddd�Z.dS)z
Stable graphs
�N��defaultdict)�prod)�
SageObject)�Permutations)�Subsets)�ZZ)�
FreeModule)�floor)�Graph)�Integer)�	factorial)�SymmetricGroup)�	polygon2d)�text)�bezier_path)�line)�circle�)�deprecated_function_alias)�	MODULI_ST�	MODULI_TL�	MODULI_CT�	MODULI_RT�	MODULI_SMc@sheZdZdZgd�Zd�dd�Zdd�Zd	d
�Zdd�Zd�d
d�Z	dd�Z
dd�Zdd�Zdd�Z
dd�Zdd�Zdd�Zdd�Zdd �Zd�d!d"�Zd�d#d$�Zd�d%d&�Zd'd(�Zd�d*d+�Zd�d,d-�Zd.d/�Zd0d1�Zd2d3�Zd4d5�Zd6d7�Zd�d8d9�Zd�d:d;�Zd<d=�Z d�d>d?�Z!e Z"d@dA�Z#dBdC�Z$d�dDdE�Z%dFdG�Z&d�dHdI�Z'dJdK�Z(dLdM�Z)dNdO�Z*d�dPdQ�Z+dRdS�Z,dTdU�Z-dVdW�Z.dXdY�Z/dZd[�Z0d\d]�Z1d�d^d_�Z2d`da�Z3d�dbdc�Z4ddde�Z5dfdg�Z6d�dhdi�Z7iiifdjdk�Z8dldm�Z9d�dndo�Z:dpdq�Z;d�drds�Z<dtdu�Z=d�dvdw�Z>e?dxe>�Z@dydz�ZAd{d|�ZBd�d~d�ZCd�d��ZDd�d��ZEd�d��ZFd�d��ZGd�d�d��ZHd�d��ZId)S)��StableGrapha
    Stable graph.

    A stable graph is a graph (with allowed loops and multiple edges) that
    has "genus" and "marking" decorations at its vertices. It is represented
    as a list of genera of its vertices, a list of legs at each vertex and a
    list of pairs of legs forming edges.

    The markings (the legs that are not part of edges) are allowed to have
    repetitions.

    EXAMPLES:

    We create a stable graph with two vertices of genera 3,5 joined by an edge
    with a self-loop at the genus 3 vertex::

        sage: from admcycles import StableGraph
        sage: StableGraph([3,5], [[1,3,5],[2]], [(1,2),(3,5)])
        [3, 5] [[1, 3, 5], [2]] [(1, 2), (3, 5)]

    The markings can have repetitions::

         sage: StableGraph([1,0], [[1,2], [3,2]], [(1,3)])
         [1, 0] [[1, 2], [3, 2]] [(1, 3)]

    It is also possible to create graphs which are not necessarily stable::

        sage: StableGraph([1,0], [[1], [2,3]], [(1,2)])
        [1, 0] [[1], [2, 3]] [(1, 2)]

        sage: StableGraph([0], [[1]], [])
        [0] [[1]] []

    If the input is invalid a ``ValueError`` is raised::

        sage: StableGraph([0, 0], [[1], [2], [3]], [])
        Traceback (most recent call last):
        ...
        ValueError: genera and legs must have the same length

        sage: StableGraph([0, 'hello'], [[1], [2]], [(1,2)])
        Traceback (most recent call last):
        ...
        ValueError: genera must be a list of non-negative integers

        sage: StableGraph([0, 0], [[1,2], [3]], [(3,4)])
        Traceback (most recent call last):
        ...
        ValueError: the edge (3, 4) uses invalid legs

        sage: StableGraph([0, 0], [[2,3], [2]], [(2,3)])
        Traceback (most recent call last):
        ...
        ValueError: the edge (2, 3) uses invalid legs
    )�_genera�_legs�_edges�_maxleg�_mutable�_graph_cache�_canonical_label_cache�_hashFTcCsl|r�t|t�rt|t�rt|t�std��t|�t|�kr!td��tdd�|D��s.td��tt�}t|�D])\}}t|t�sCtd��|D]}	t|	tt	f�rR|	dkrVtd��||	d	7<qEq6|D]/}
t|
t
�rot|
�d
krvtd�|
���|�|
dd�d	ks�|�|
d	d�d	kr�td�|
���qb||_
||_||_td
d�|jD��|_t|�|_d|_d|_d|_dS)a
        INPUT:

        - ``genera`` -- (list) List of genera of the vertices of length m.

        - ``legs`` -- (list) List of length m, where ith entry is list of legs
          attached to vertex i.

        - ``edges`` -- (list) List of edges of the graph. Each edge is a 2-tuple of legs.

        - ``mutable`` - (boolean, default to ``False``) whether this stable graph
          should be mutable

        - ``check`` - (boolean, default to ``True``) whether some additional sanity
          checks are performed on input
        z!genera, legs, edges must be listsz)genera and legs must have the same lengthcss&�|]}t|ttf�o|dkVqdS)rN)�
isinstance�intr��.0�g�r)�:/home/user/Introduction lectures/admcycles/stable_graph.py�	<genexpr>us�$z'StableGraph.__init__.<locals>.<genexpr>z.genera must be a list of non-negative integerszlegs must be a list of listsrzlegs must be positive integersr�zinvalid edge {}zthe edge {} uses invalid legscS�g|]	}t|dg��qS�r��max�r'�jr)r)r*�
<listcomp>��z(StableGraph.__init__.<locals>.<listcomp>N)r$�list�	TypeError�len�
ValueError�allrr%�	enumerater�tuple�format�getrrrr0r�boolr r!r"r#)�self�genera�legs�edges�mutable�checkZmlegs�v�l�i�er)r)r*�__init__[sD
��
�(�

zStableGraph.__init__cCs
d|_dS)z+
        Set this graph immutable.
        FN�r �r?r)r)r*�
set_immutable�s
zStableGraph.set_immutablecCs|jS)z7
        Return whether this graph is mutable.
        rJrKr)r)r*�
is_mutable�szStableGraph.is_mutablecCsJ|jrtd��|jdur"tt|j�tdd�|jD��t|j�f�|_|jS)a�
        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: G1 = StableGraph([3,5], [[1,3,5],[2]], [(1,2),(3,5)])
            sage: G2 = StableGraph([3,5], [[1,3,5],[2]], [(1,2),(3,5)])
            sage: G3 = StableGraph([3,5], [[3,5,1],[2]], [(2,1),(3,5)])
            sage: G4 = StableGraph([2,2], [[1,3,5],[2]], [(1,2),(3,5)])
            sage: G5 = StableGraph([3,5], [[1,4,5],[2]], [(1,2),(4,5)])
            sage: hash(G1) == hash(G2)
            True
            sage: (hash(G1) == hash(G3) or hash(G1) == hash(G4) or
            ....:  hash(G1) == hash(G5) or hash(G3) == hash(G4) or
            ....:  hash(G3) == hash(G5) or hash(G4) == hash(G5))
            False
        z%mutable stable graph are not hashableNcs��|]}t|�VqdS�N)r;�r'�xr)r)r*r+���z'StableGraph.__hash__.<locals>.<genexpr>)r r6r#�hashr;rrrrKr)r)r*�__hash__�s

�zStableGraph.__hash__cCsr|��s|s|St�t�}|jdd�|_dd�|jD�|_dd�|jD�|_|j|_||_d|_d|_	d|_
|S)a�
        Return a copy of this graph.

        When it is asked for an immutable copy of an immutable graph the
        current graph is returned without copy.

        INPUT:

        - ``mutable`` - (boolean, default ``True``) whether the returned graph must
          be mutable

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: G = StableGraph([3,5], [[1,3,5],[2]], [(1,2),(3,5)])
            sage: H = G.copy()
            sage: H == G
            True
            sage: H.is_mutable()
            True
            sage: H is G
            False

            sage: G.copy(mutable=False) is G
            True
        NcS�g|]}|dd��qSrOr)rPr)r)r*r3��z$StableGraph.copy.<locals>.<listcomp>cSrUrOr)rPr)r)r*r3�rV)rMr�__new__rrrrr r!r"r#)r?rC�Gr)r)r*�copy�s
zStableGraph.copycCs&t|j�dt|j�dt|j�S)N� )�reprrrrrKr)r)r*�__repr__�s&zStableGraph.__repr__cCs�t|�t|�urtd��|��}|��}||krdS||kr dS|��}|��}||kr.dS||kr4dS|j|jkr<dS|j|jkrDdS|j|jkrLdS|j|jkrTdS|j|jkr\dS|j|jkrddSdS)a
        TESTS::

            sage: from admcycles import StableGraph
            sage: g0 = StableGraph([1], [[1, 2, 3]], [])
            sage: g1 = StableGraph([0], [[5, 6, 1, 2, 3]], [(5, 6)])
            sage: g2 = StableGraph([0, 1], [[1, 2, 5], [3, 6]], [(5, 6)])
            sage: g3 = StableGraph([0, 1], [[1, 3, 5], [2, 6]], [(5, 6)])
            sage: g4 = StableGraph([0, 1], [[2, 3, 5], [1, 6]], [(5, 6)])
            sage: sorted([g3, g1, g2, g4, g0]) == sorted([g2, g0, g4, g1, g3]) == [g0, g1, g2, g3, g4]
            True

            sage: g0 < g0
            False
            sage: g0 <= g0
            True

            sage: g1 > g0 and g1 >= g0 and g2 > g1 and g2 >= g1
            True
            sage: g1 < g0 or g1 <= g0 or g2 < g1 or g2 <= g1
            False
        zincomparable elementsTF)�typer6�	num_edges�	num_vertsrrr)r?�otherZsne�oneZsnvZonvr)r)r*�__lt__�s6zStableGraph.__lt__cCs8t|�t|�ur
dS|j|jko|j|jko|j|jkS)a'
        TESTS::

            sage: from admcycles import StableGraph
            sage: G1 = StableGraph([3,5], [[1,3,5],[2]], [(1,2),(3,5)])
            sage: G2 = StableGraph([3,5], [[1,3,5],[2]], [(1,2),(3,5)])
            sage: G3 = StableGraph([3,5], [[3,5,1],[2]], [(2,1),(3,5)])
            sage: G4 = StableGraph([2,2], [[1,3,5],[2]], [(1,2),(3,5)])
            sage: G5 = StableGraph([3,5], [[1,4,5],[2]], [(1,2),(4,5)])
            sage: G1 == G2
            True
            sage: G1 == G3 or G1 == G4 or G1 == G5
            False
        F)r]rrr�r?r`r)r)r*�__eq__s
�
�zStableGraph.__eq__cCs
||kSrOr)rcr)r)r*�__ne__1s
zStableGraph.__ne__cCs||kp||kSrOr)rcr)r)r*�__le__4�zStableGraph.__le__cCs||kSrOr)rcr)r)r*�__gt__7szStableGraph.__gt__cCs||kp||kSrOr)rcr)r)r*�__ge__:rgzStableGraph.__ge__c	s\|js|jdur|jSddlm}|t�}tt|j�dddd�}|jD]D\}}|�	|�}|�	|�}|�
||�rE|�|||�||�d�n|�
||d�||kr\|||f�||f�q#|||f�||f�q#|t��tt|j|j��D]\}\}}	t|�|��}	|ft|	�}
�|
�|�qut��}�fdd�|D�}||t|�|f}
|js�|
|_|
S)	NrrTF)�loops�
multiedges�weightedrc�g|]}�|�qSr)r)�r'�k�Zvertex_partitionr)r*r3]�z&StableGraph._graph.<locals>.<listcomp>)r r!�collectionsrr5rr7rr�vertex�has_edge�set_edge_label�
edge_label�add_edge�appendr:�zipr�sorted�
list_markingsr;�dict)r?rrArX�li�ljrGr2r(rF�invZvertex_data�	partition�outputr)rpr*�_graph=s2

zStableGraph._graphcCsT|js|jdur|jS|��\}}}}|j|dddd�\}}||f}|js(||_|S)NT�sage)r��edge_labels�certificate�	algorithm)r r"r��canonical_label)r?rX�_r��H�phir�r)r)r*�_canonical_labelds
�zStableGraph._canonical_labelc	sL�jstd�����\}}}}���\}}dd�|��D��|r"i}��fdd�t����D�}	|	t�j�ks9J���fdd�t����D�}
g}|j	dd�D]%\}}
}||}||
}d}||kri||}}d	}|�
||||
||f�qO|�����}t
����}|r�t|�nd
d}t|||t|���t��dtd
d�|D��ks�J�d
}g}|D]�\}}}}
}}||ks�J�|
|��fdd�t||d|d�D��|
|��fdd�t|d|d|d�D��|��fdd�t||d|d�D��|�r�t|||
f�|k�sJt|||
f�|f��|�rU|||k�s%J�||
|k�s.J�t|||
f�D]\}\}}�|d|||<�|d|d||<�q6n8|||k�s^J�||
|k�sgJ�t|||
f�D]\}\}}�|d|||<�|d|d||<�qo|d|7}q�|	�_|
�_|�_|�r�||fSdS)a�
        Set canonical label to this stable graph.

        The canonical labeling is such that two isomorphic graphs get relabeled
        the same way. If certificate is true return a pair `dicv`, `dicl` of
        mappings from the vertices and legs to the canonical ones.

        EXAMPLES::

            sage: from admcycles import StableGraph

            sage: S = StableGraph([1,2,3], [[1,4,5],[2,6,7],[3,8,9]], [(4,6),(5,8),(7,9)])

            sage: T = S.copy()
            sage: T.set_canonical_label()
            sage: T  # random
            [1, 2, 3] [[1, 4, 6], [2, 5, 8], [3, 7, 9]] [(4, 5), (6, 7), (8, 9)]
            sage: assert S.is_isomorphic(T)

        Check that isomorphic graphs get relabelled the same way::

            sage: S0 = StableGraph([3,5], [[1,3,5,4],[2]],  [(1,2),(3,5)], mutable=False)
            sage: S1 = S0.copy()
            sage: S2 = StableGraph([3,5], [[2,3,1,4],[5]],  [(2,5),(3,1)], mutable=True)
            sage: S3 = StableGraph([5,3], [[5],[2,3,4,1]],  [(2,5),(3,1)], mutable=True)
            sage: S1.set_canonical_label()
            sage: S2.set_canonical_label()
            sage: S3.set_canonical_label()
            sage: assert S1 == S2 == S3
            sage: assert S0.is_isomorphic(S1)

            sage: S0 = StableGraph([1,2,3], [[1,4,5],[2,6,7],[3,8,9]], [(4,6),(5,8),(7,9)], mutable=False)
            sage: S1 = S0.copy()
            sage: S2 = StableGraph([3,2,1], [[3,8,5],[2,6,7],[1,4,9]], [(8,6),(5,4),(7,9)], mutable=True)
            sage: S3 = StableGraph([2,1,3], [[7,2,5],[6,4,1],[3,9,8]], [(7,6),(5,8),(4,9)], mutable=True)
            sage: S1.set_canonical_label()
            sage: S2.set_canonical_label()
            sage: S3.set_canonical_label()
            sage: assert S1 == S2 == S3
            sage: assert S0.is_isomorphic(S1)

            sage: S0 = StableGraph([1,2,3], [[1,4,5],[2,6,7],[3,8,9]], [(4,6),(5,8),(7,9)], mutable=True)
            sage: S1 = StableGraph([3,2,1], [[3,8,5],[2,6,7],[1,4,9]], [(8,6),(5,4),(7,9)], mutable=True)
            sage: S2 = StableGraph([2,1,3], [[7,2,5],[6,4,1],[3,9,8]], [(7,6),(5,8),(4,9)], mutable=True)
            sage: for S in [S0, S1, S2]:
            ....:     T = S.copy()
            ....:     assert S == T
            ....:     vm, lm = T.set_canonical_label(certificate=True)
            ....:     S.relabel(vm, lm, inplace=True)
            ....:     S.tidy_up()
            ....:     assert S == T, (S, T)


        TESTS::

            sage: from admcycles import StableGraph
            sage: S0 = StableGraph([2], [[]], [], mutable=True)
            sage: S0.set_canonical_label()

            sage: S0 = StableGraph([0,1,0], [[1,2,4], [3], [5,6,7]], [(2,3), (4,5), (6,7)])
            sage: S1 = S0.copy()
            sage: S1.set_canonical_label()
            sage: assert S0.is_isomorphic(S1)
            sage: S2 = S1.copy()
            sage: S1.set_canonical_label()
            sage: assert S1 == S2

            sage: S0 = StableGraph([1,0], [[2,3], [5,7,9]], [(2,5), (7,9)], mutable=True)
            sage: S1 = StableGraph([1,0], [[1,3], [2,7,9]], [(1,2), (7,9)], mutable=True)
            sage: S0.set_canonical_label()
            sage: S1.set_canonical_label()
            sage: assert S0 == S1
        z,the graph must be mutable; use inplace=FalsecS�i|]\}}||�qSr)r)�r'rGr2r)r)r*�
<dictcomp>��z3StableGraph.set_canonical_label.<locals>.<dictcomp>csg|]	}�j�|�qSr)�r�r'rG��invdicvr?r)r*r3�r4z3StableGraph.set_canonical_label.<locals>.<listcomp>csg|]}t���|���qSr))rzr{r�r�r)r*r3��F��sortTrrr,css �|]\}}}}}}|VqdSrOr))r'r��multr)r)r*r+�s�z2StableGraph.set_canonical_label.<locals>.<genexpr>c3��|]}�|VqdSrOr)r���non_markingsr)r*r+�rRc3r�rOr)r�r�r)r*r+�rRc3s$�|]
}�|�|dfVqdS)rNr)r�r�r)r*r+���"N)r r8r�r��items�ranger_rzrrBrxr��num_legs�setr{r0r7�sum�extendr:rr)r?r�rXZvdatrA�partr��dicv�dicl�
new_genera�new_legsZ	can_edges�e0�e1r��f0�f1r�nl�marks�m0roZ	new_edgesrG�l1�l0r))r�r�r?r*�set_canonical_labelvsjJ
",0(.��zStableGraph.set_canonical_labelc
s�t|j�}dg|}dg|}tt|j|j��D]\}\}	}
|�||�}|	||<�fdd�|
D�||<q�fdd�|jD�}|rR|jsGtd��||_||_||_dSt	||||�S)a�
        INPUT:

        - ``vm`` -- (dictionary) a vertex map (from the label of this graph to
          the new labels). If a vertex number is missing it will remain untouched.

        - ``lm`` -- (dictionary) a leg map (idem). If a leg label is missing it
          will remain untouched.

        - ``inplace`` -- (boolean, default ``False``) whether to relabel the
          graph inplace

        - ``mutable`` -- (boolean, default ``False``) whether to make the
          resulting graph immutable. Ignored when ``inplace=True``

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: G = StableGraph([3,5], [[1,3,5,4],[2]],  [(1,2),(3,5)])
            sage: vm = {0:1, 1:0}
            sage: lm = {1:3, 2:5, 3:7, 4:9, 5:15}
            sage: G.relabel(vm, lm)
            [5, 3] [[5], [3, 7, 15, 9]] [(3, 5), (7, 15)]

            sage: G.relabel(vm, lm, mutable=False).is_mutable()
            False
            sage: G.relabel(vm, lm, mutable=True).is_mutable()
            True

            sage: H = G.copy()
            sage: H.relabel(vm, lm, inplace=True)
            sage: H == G.relabel(vm, lm)
            True
        Ncsg|]}��||��qSr)�r=rn��lmr)r*r3,rVz'StableGraph.relabel.<locals>.<listcomp>cs(g|]\}}��||���||�f�qSr)r�r�r�r)r*r3.s(�,the graph is not mutable; use a copy instead)
r7rr:ryrr=rr r8r)
r?�vmr��inplacerC�mr@rArGr(rFr2rBr)r�r*�relabels
$


zStableGraph.relabelcs�t|�t|�kr
t�|��\}}}}|��\}}}	}
||ks*tdd�t||
�D��r0|r.dSdS|��\}�|��\}}
||krF|rDdSdS|r�dd�|
��D����fdd�tt|j	��D�}i}|D]}|dd	�D]}|||<qlqd|��D]a\\}}}|�
||�}t|�|ks�J||||f����|}��|}||kr�|	||f}nd
d�|	||fD�}t|�|ks�J|||||f��t||�D]\\}}\}}|||<|||<q�qxd||ffSdS)
a
        Test whether this stable graph is isomorphic to ``other``.

        INPUT:

        - ``certificate`` - if set to ``True`` return also a vertex mapping and
                            a legs mapping

        EXAMPLES::

            sage: from admcycles import StableGraph

            sage: G1 = StableGraph([3,5], [[1,3,5,4],[2]],  [(1,2),(3,5)])
            sage: G2 = StableGraph([3,5], [[2,3,1,4],[5]],  [(2,5),(3,1)])
            sage: G3 = StableGraph([5,3], [[5],[2,3,4,1]],  [(2,5),(3,1)])
            sage: G1.is_isomorphic(G2) and G1.is_isomorphic(G3)
            True

        Graphs with distinct markings are not isomorphic::

            sage: G4 = StableGraph([3,5], [[1,3,5,4],[2]],  [(1,2),(3,4)])
            sage: G1.is_isomorphic(G4) or G2.is_isomorphic(G4) or G3.is_isomorphic(G4)
            False

        Graph with marking multiplicities::

            sage: H1 = StableGraph([0], [[1,1]], [])
            sage: H2 = StableGraph([0], [[1,1]], [])
            sage: H1.is_isomorphic(H2)
            True

            sage: H3 = StableGraph([0,0], [[1,2,4],[1,3]], [(2,3)])
            sage: H4 = StableGraph([0,0], [[1,2],[1,3,4]], [(2,3)])
            sage: H3.is_isomorphic(H4)
            True

        TESTS::

            sage: from admcycles import StableGraph

            sage: G = StableGraph([0, 0], [[5, 8, 4, 3], [9, 2, 1]], [(8, 9)])
            sage: H = StableGraph([0, 0], [[1, 2, 6], [3, 4, 5, 7]], [(6, 7)])
            sage: G.is_isomorphic(H)
            True
            sage: ans, (vm, lm) = G.is_isomorphic(H, certificate=True)
            sage: assert ans
            sage: G.relabel(vm, lm)
            [0, 0] [[6, 2, 1], [5, 7, 4, 3]] [(7, 6)]

            sage: G = StableGraph([0, 0], [[4, 8, 2], [3, 9, 1]], [(8, 9)])
            sage: H = StableGraph([0, 0], [[1, 3, 5], [2, 4, 6]], [(5, 6)])
            sage: G.is_isomorphic(H)
            True
            sage: ans, (vm, lm) = G.is_isomorphic(H, certificate=True)
            sage: assert ans
            sage: G.relabel(vm, lm)
            [0, 0] [[3, 5, 1], [4, 6, 2]] [(6, 5)]

            sage: G = StableGraph([0, 1, 1], [[1, 3, 5], [2, 4], [6]], [(1, 2), (3, 4), (5, 6)])
            sage: H = StableGraph([1, 0, 1], [[1], [2, 3, 5], [4, 6]], [(1, 2), (3, 4), (5, 6)])
            sage: _ = G.is_isomorphic(H, certificate=True)

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

            sage: g1 = StableGraph([0, 0], [[1, 3], [2,4]], [(1, 2), (3, 4)])
            sage: g2 = StableGraph([0, 0], [[1], [2]], [(1, 2)])
            sage: g1.is_isomorphic(g2)
            False

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

            sage: gr1 = StableGraph([0, 0, 0, 2, 1, 2], [[1, 2, 6], [3, 7, 8], [4, 5, 9], [10], [11], [12]], [(6, 7), (8, 9), (3, 10), (4, 11), (5, 12)])
            sage: gr2 = StableGraph([0, 0, 1, 1, 1, 2], [[1, 2, 5, 6, 7], [3, 4, 8], [9], [10], [11], [12]], [(7, 8), (3, 9), (4, 10), (5, 11), (6, 12)])
            sage: gr1.is_isomorphic(gr2)
            False
        css$�|]
\}}t|�t|�kVqdSrO�r7)r'�p�qr)r)r*r+�r�z,StableGraph.is_isomorphic.<locals>.<genexpr>)FNFcSr�r)r)r�r)r)r*r��r�z-StableGraph.is_isomorphic.<locals>.<dictcomp>csi|]	}|��|�qSr)r)r��Zophi_invZsphir)r*r��r4rNcS�g|]\}}||f�qSr)r))r'�a�br)r)r*r3�rVz-StableGraph.is_isomorphic.<locals>.<listcomp>T)r]r6r��anyryr�r�r�r7rrv)r?r`r�ZsGZsvdatZslegs�spartZoGZovdatZolegsZopartZsHZoHZophiZ
vertex_mapZlegs_maprFrGr2�slr��ii�jjZolr�r��c�dr)r�r*�
is_isomorphic9sBM 
�
�zStableGraph.is_isomorphiccOs&|��\}}}}|j||dd�|��S)a{
        Return the action of the automorphism group on the vertices.

        All arguments provided are forwarded to the method `automorphism_group`
        of Sage graphs.

        EXAMPLES::

            sage: from admcycles import StableGraph

            sage: G = StableGraph([0], [[1, 2, 3, 4, 5, 6]], [(3, 4), (5, 6)])
            sage: G.vertex_automorphism_group()
            Permutation Group with generators [()]

            sage: G = StableGraph([0, 0], [[1, 1, 2], [1, 1, 3]], [(2, 3)])
            sage: G.vertex_automorphism_group()
            Permutation Group with generators [(0,1)]
            sage: G = StableGraph([0, 0], [[4, 1, 2], [1, 3, 4]], [(3, 2)])
            sage: G.vertex_automorphism_group()
            Permutation Group with generators [(0,1)]

            sage: G = StableGraph([0, 0, 0], [[1,2], [3,4], [5,6]],
            ....:                 [(2,3),(4,5),(6,1)])
            sage: A = G.vertex_automorphism_group()
            sage: A
            Permutation Group with generators [(1,2), (0,1)]
            sage: A.cardinality()
            6

        Using extra arguments::

            sage: G.vertex_automorphism_group(algorithm='sage')
            Permutation Group with generators [(1,2), (0,1)]
            sage: G.vertex_automorphism_group(algorithm='bliss')   # optional - bliss
            Permutation Group with generators [(1,2), (0,1)]
        T)r�r�)r��automorphism_group)r?�args�kwdsrXr�r�r)r)r*�vertex_automorphism_group�s%���z%StableGraph.vertex_automorphism_groupNcs^|durtttt�j����}t�j�dkr|d�S��fdd�tt�j��D�}|||d�S)a�
        Given a leg automorphism ``g`` return its action on the vertices.

        Note that there is no check that the element ``g`` is a valid automorphism.

        INPUT:

        - ``g`` - a permutation acting on the legs

        - ``A`` - ambient permutation group for the result

        - ``check`` - (default ``True``) parameter forwarded to the constructor
          of the permutation group acting on vertices.

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: G = StableGraph([0, 0, 0, 0], [[1,8,9,16], [2,3,10,11], [4,5,12,13], [6,7,14,15]],
            ....:                 [(1,2),(3,4),(5,6),(7,8),(9,10),(11,12),(13,14),(15,16)])
            sage: Averts = G.vertex_automorphism_group()
            sage: Alegs = G.leg_automorphism_group()
            sage: g = Alegs('(1,14)(2,13)(3,4)(5,10)(6,9)(7,8)(11,12)(15,16)')
            sage: assert G.leg_automorphism_induce(g) == Averts('(0,3)(1,2)')
            sage: g = Alegs('(3,11)(4,12)(5,13)(6,14)')
            sage: assert G.leg_automorphism_induce(g) == Averts('')
            sage: g = Alegs('(1,11,13,15,9,3,5,7)(2,12,14,16,10,4,6,8)')
            sage: assert G.leg_automorphism_induce(g) == Averts('(0,1,2,3)')

        TESTS::

            sage: G = StableGraph([3], [[]], [])
            sage: S = SymmetricGroup([0])
            sage: G.leg_automorphism_induce(S(''))
            ()
        Nr�cs$g|]}����j|d���qSr.)rsr)r'�u�r(r?r)r*r3�$z7StableGraph.leg_automorphism_induce.<locals>.<listcomp>�rD)rr5r�r7r)r?r(�ArDr�r)r�r*�leg_automorphism_induce�s$z#StableGraph.leg_automorphism_inducecCstt|jg��}|durt|�}dd�t|�D�}|��\}}}}	|jdd�D]S\}
}}||
�}
||�}|
|kr?||
|f}ndd�|||
fD�}|
|krU||
|f}ndd�|||
fD�}t||�D]\\}}\}}||||<||||<qeq'|||d	�S)
aQ
        Provide a canonical lift of vertex automorphism to leg automorphism.

        Note that there is no check that ``g`` defines a valid automorphism of
        the vertices.

        INPUT:

        - ``g`` - permutation automorphism of the vertices

        - ``A`` - an optional permutation group in which the result belongs to

        - ``check`` -- (default ``True``) parameter forwarded to the constructor
          of the permutation group

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: G = StableGraph([0, 0, 0, 0], [[1,8,9,16], [2,3,10,11], [4,5,12,13], [6,7,14,15]],
            ....:                 [(1,2),(3,4),(5,6),(7,8),(9,10),(11,12),(13,14),(15,16)])
            sage: Averts = G.vertex_automorphism_group()
            sage: G.vertex_automorphism_lift(Averts(''))
            ()
            sage: G.vertex_automorphism_lift(Averts('(0,3)(1,2)'))
            (1,6)(2,5)(3,4)(7,8)(9,14)(10,13)(11,12)(15,16)
            sage: G.vertex_automorphism_lift(Averts('(0,1,2,3)'))
            (1,3,5,7)(2,4,6,8)(9,11,13,15)(10,12,14,16)
        NcSr�r)r)r�r)r)r*r�/r�z8StableGraph.vertex_automorphism_lift.<locals>.<dictcomp>Tr�cSr�r)r)�r'�s�tr)r)r*r3;rVz8StableGraph.vertex_automorphism_lift.<locals>.<listcomp>cSr�r)r)r�r)r)r*r3@rVr�)rzr�rrr:r�rBry)r?r(r�rDr�Zleg_posrXr��edge_to_legsr�r�rE�lab�gu�gv�start�end�lu�lvZgluZglvr)r)r*�vertex_automorphism_lifts&�z$StableGraph.vertex_automorphism_liftcOs@tt|jg��}|��\}}}}t|�}g}	|jdd�D]w\}
}}|dkr*|
|kr*q|
|kr5||
|f}
n|||
f}
|dkr{|
d\}}|
d\}}|	�|j||f||fg|dd��|dkr{|	�|jtd	d
�|
D��tdd
�|
D��g|dd��|
|kr�|
d\}}|	�|j||fg|dd��q|j	|	g|�Ri|��S)a
        Return the group of automorphisms of this stable graph stabilizing the vertices.

        All arguments are forwarded to the subgroup method of the Sage symmetric group.

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: G = StableGraph([0],[[1,2,3,4,5,6,7,8]], [(1,2),(3,4),(5,6),(7,8)])
            sage: G.leg_automorphism_group_vertex_stabilizer()
            Subgroup generated by [(1,2), (1,3)(2,4), (1,3,5,7)(2,4,6,8)] of (Symmetric group of order 8! as a permutation group)

            sage: G = StableGraph([1,1],[[1,2],[3,4]], [(1,3),(2,4)])
            sage: G.leg_automorphism_group_vertex_stabilizer()
            Subgroup generated by [(1,2)(3,4)] of (Symmetric group of order 4! as a permutation group)
        Tr�rr,rFr��css�|]\}}|VqdSrOr)�r'rQ�yr)r)r*r+mrRzGStableGraph.leg_automorphism_group_vertex_stabilizer.<locals>.<genexpr>css�|]\}}|VqdSrOr)r�r)r)r*r+nrR)
rzr�rr�rrBrx�
element_classr;�subgroup)r?r�r�rArXr�r�r��S�gensr�rEr�Z	multiedgeZlu0Zlv0Zlu1Zlv1r�r�r)r)r*�(leg_automorphism_group_vertex_stabilizerHs2"���z4StableGraph.leg_automorphism_group_vertex_stabilizercOs�tt|jg��}|��\}}}}t|�}g}	|����D]}
|	�|�|
|��q|�	���D]}
|	�|
�q.|j
|	g|�Ri|��S)a�
        Return the action of the automorphism group on the legs.

        The arguments provided to this function are forwarded to the
        constructor of the subgroup of the symmetric group.

        EXAMPLES::

            sage: from admcycles import StableGraph

        A triangle::

            sage: G = StableGraph([0, 0, 0], [[1,2], [3,4], [5,6]],
            ....:                 [(2,3),(4,5),(6,1)])
            sage: Alegs = G.leg_automorphism_group()
            sage: assert Alegs.cardinality() == G.automorphism_number()

        A vertex with four loops::

            sage: G = StableGraph([0], [[1,2,3,4,5,6,7,8]], [(1,2),(3,4),(5,6),(7,8)])
            sage: Alegs = G.leg_automorphism_group()
            sage: assert Alegs.cardinality() == G.automorphism_number()
            sage: a = Alegs.random_element()

        Using extra arguments::

            sage: G = StableGraph([0,0,0], [[6,1,7,8],[2,3,9,10],[4,5,11,12]],
            ....:       [(1,2), (3,4), (5,6), (7,8), (9,10), (11,12)])
            sage: G.leg_automorphism_group()
            Subgroup generated by [(11,12), (9,10), (7,8), (1,2)(3,6)(4,5)(7,9)(8,10), (1,6)(2,5)(3,4)(9,11)(10,12)] of (Symmetric group of order 12! as a permutation group)
            sage: G.leg_automorphism_group(canonicalize=False)
            Subgroup generated by [(1,6)(2,5)(3,4)(9,11)(10,12), (1,2)(3,6)(4,5)(7,9)(8,10), (11,12), (9,10), (7,8)] of (Symmetric group of order 12! as a permutation group)
        )rzr�rr�rr�r�rxr�r�r�)r?r�r�rArXr�r�r�r�r�r(r)r)r*�leg_automorphism_groupus"z"StableGraph.leg_automorphism_groupcCs||��\}}}}|����}|jdd�D]\}}}|t|�9}||kr)|d|9}q|��}t|�tt|��kr<td��|S)a�
        Return the size of the automorphism group (acting on legs).

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: G = StableGraph([0, 2], [[1, 2, 4], [3, 5]], [(4, 5)])
            sage: G.automorphism_number()
            1

            sage: G = StableGraph([0, 0, 0], [[1,2,7,8], [3,4,9,10], [5,6,11,12]],
            ....:                 [(2,3),(4,5),(6,1),(8,9),(10,11),(12,7)])
            sage: G.automorphism_number()
            48
            sage: G.leg_automorphism_group().cardinality()
            48

            sage: G = StableGraph([0, 0], [[1, 1, 2], [1, 1, 3]], [(2, 3)])
            sage: G.automorphism_number()
            Traceback (most recent call last):
            ...
            NotImplementedError: automorphism_number not valid for repeated marking

        TESTS::

            sage: from sage.combinat.permutation import Permutations
            sage: glist=[1,1,2,2]
            sage: for p in Permutations([0,1,2,3]):
            ....:     gr = StableGraph([0]+[glist[i] for i in p],[[1,2,3,4],[5],[6],[7],[8]],[(1,5),(2,6),(3,7),(4,8)])
            ....:     assert gr.automorphism_number() == 4, (gr, gr.automorphism_number())
        Fr�r,z2automorphism_number not valid for repeated marking)	r�r��cardinalityrBr
r{r7r��NotImplementedError)r?rXr��autrGr2r��markingsr)r)r*�automorphism_number�s �zStableGraph.automorphism_numbercCs&t|j�t|j�t|j�t��S)z�
        Return the genus of this stable graph

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: G = StableGraph([1,2],[[1,2], [3,4]], [(1,3),(2,4)])
            sage: G.g()
            4
        )r�rr7rrrarKr)r)r*r(�s&z
StableGraph.gcCst|���S)z�
        Return the number of legs of the stable graph.

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: G = StableGraph([1,2],[[1,2],[3,4,5,6]],[(1,3),(2,4)]);G.n()
            2
        )r7r{rKr)r)r*�n�s
z
StableGraph.ncCs*|dur|r
|jdd�S|jS|j|S)a�
        Return the list of genera of the stable graph.

        If an integer argument is given, return the genus at this vertex.

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: G = StableGraph([1,2],[[1,2],[3,4,5,6]],[(1,3),(2,4)])
            sage: G.genera()
            [1, 2]
            sage: G.genera(0), G.genera(1)
            (1, 2)
        Nr�)r?rGrYr)r)r*r@�s
zStableGraph.generacCs|r	|jdd�S|jS)a}
        Return the list of edges.

        By default, this returns a copy of the corresponding list,
        set copy=False to obtain the internal variable of the graph.

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: Gamma = StableGraph([1,1], [[1,2,3,4,5,6],[7,8,9]], [(2,3), (4,5), (6,7)])
            sage: L = Gamma.edges(); L
            [(2, 3), (4, 5), (6, 7)]
            sage: L.append((8,9)); L
            [(2, 3), (4, 5), (6, 7), (8, 9)]
            sage: Gamma # not changed by above operation
            [1, 1] [[1, 2, 3, 4, 5, 6], [7, 8, 9]] [(2, 3), (4, 5), (6, 7)]
        N�r)r?rYr)r)r*rBszStableGraph.edgescC�
t|j�S)a
        Return the number of vertices.

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: Gamma = StableGraph([1,1], [[1,2,3,4,5,6],[7,8,9]], [(2,3), (4,5), (6,7), (8,9)])
            sage: Gamma.num_verts()
            2
        )r7rrKr)r)r*r_�
zStableGraph.num_vertscCsB|dur|rdd�|jD�S|jS|r|j|dd�S|j|S)a�
        Return the list of legs at vertex v, or the whole list of
        legs if v is not specified.

        By default, this returns a copy of the corresponding list(s),
        set copy=False to obtain the internal variable of the graph.

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: Gamma = StableGraph([1,1], [[1,2,3,4,5,6],[7,8,9]], [(2,3), (4,5), (6,7)])
            sage: L = Gamma.legs(0); L
            [1, 2, 3, 4, 5, 6]
            sage: L.append(10); L
            [1, 2, 3, 4, 5, 6, 10]
            sage: Gamma # not changed by above operation
            [1, 1] [[1, 2, 3, 4, 5, 6], [7, 8, 9]] [(2, 3), (4, 5), (6, 7)]
            sage: Gamma.legs()
            [[1, 2, 3, 4, 5, 6], [7, 8, 9]]
        NcSrUrOr)�r'rFr)r)r*r38rVz$StableGraph.legs.<locals>.<listcomp>�r)r?rErYr)r)r*rA"s zStableGraph.legscCr�)a
        Return the number of edges.

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: Gamma = StableGraph([1,1], [[1,2,3,4,5,6],[7,8,9]], [(2,3), (4,5), (6,7), (8,9)])
            sage: Gamma.num_edges()
            4
        )r7rrKr)r)r*r^?r�zStableGraph.num_edgescCs0d}|jD]\}}||�|�|�|�k7}q|S)a2
        Return the number of loops (ie edges attached twice to a vertex).

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: Gamma = StableGraph([1,1], [[1,2,3,4,5,6],[7,8,9]], [(2,3), (4,5), (6,7), (8,9)])
            sage: Gamma.num_loops()
            3
        r)rrs)r?r��h0�h1r)r)r*�	num_loopsLszStableGraph.num_loopscCs*|durtdd�|jD��St|j|�S)ah
        Return the number of legs at vertex i, or the total number of legs
        if i is not specified.

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: Gamma = StableGraph([0,2], [[1,2,3,4],[6,8]], [(4,6)])
            sage: Gamma.num_legs(0)
            4
            sage: Gamma.num_legs()
            6
        NcsrNrOr�r�r)r)r*r+krRz'StableGraph.num_legs.<locals>.<genexpr>)r�rr7)r?rGr)r)r*r�\szStableGraph.num_legscCs|tdd�}|jD]!\}}|�||f�|�||f|�|��|�||f|�|��q|��D]
}|�d|f|�|��q.|S)aB
        Return the Sage graph object encoding the stable graph

        This inserts a vertex with label (i,j) in the middle of the edge (i,j).
        Also inserts a vertex with label ('L',i) at the end of each leg l.

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: Gr = StableGraph([3,5],[[1,3,5,7],[2]],[(1,2),(3,5)])
            sage: SageGr = Gr._graph_()
            sage: SageGr
            Multi-graph on 5 vertices
            sage: SageGr.vertices(sort=False)   # random
            [(1, 2), 0, 1, (3, 5), ('L', 7)]
        T)rk�L)rr�
add_vertexrwrsr{)r?rXrGr2r)r)r*�_graph_os
zStableGraph._graph_csH|durt�fdd�tt�j��D��Sd�j|dt�j|�S)ah
        Return dimension of moduli space at vertex v.

        If v=None, return dimension of entire stratum parametrized by graph.

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: Gr = StableGraph([3,5],[[1,3,5,7],[2]],[(1,2),(3,5)])
            sage: Gr.dim(0), Gr.dim(1), Gr.dim()
            (10, 13, 23)
        Ncsg|]}��|��qSr))�dim�r'rErKr)r*r3�r�z#StableGraph.dim.<locals>.<listcomp>r�)r�r�r7rr)r?rEr)rKr*r��s
  zStableGraph.dimcs$tt�fdd�tt�j��D���S)a%
        Return a graph-invariant in form of a tuple of integers or tuples

        At the moment this assumes that we only compare stable graph with same total
        g and set of markings
        currently returns sorted list of (genus,degree) for vertices

        IDEAS:

        * number of self-loops for each vertex

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: Gr = StableGraph([3,5],[[1,3,5,7],[2]],[(1,2),(3,5)])
            sage: Gr.invariant()
            ((3, 4, (7,)), (5, 1, ()))
        c	s4g|]}�j|t�j|�tt��|���f�qSr))rr7rr;rzr{r�rKr)r*r3��4z)StableGraph.invariant.<locals>.<listcomp>)r;rzr�r7rrKr)rKr*�	invariant�s$zStableGraph.invariantcCs�|jstd��tt|j��D]!}|j|d|j|dkr/|j|d|j|df|j|<q|jD]}|��q3|j��tdd�|jD��|_dS)aX
        Sort legs and edges.

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: S = StableGraph([1, 3, 2], [[1, 5, 4], [2, 3, 6], [9, 8, 7]], [(9,3), (1,2)], mutable=True)
            sage: S.tidy_up()
            sage: S
            [1, 3, 2] [[1, 4, 5], [2, 3, 6], [7, 8, 9]] [(1, 2), (3, 9)]
        z*the graph is immutable; use a copy insteadrrcSr-r.r/r1r)r)r*r3�r4z'StableGraph.tidy_up.<locals>.<listcomp>N)	r r8r�r7rrr�r0r)r?rHrAr)r)r*�tidy_up�s"�


zStableGraph.tidy_upcCs.tt|j��D]
}||j|vr|SqdS)a"
        Return vertex number of leg l.

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: Gamma = StableGraph([0,2], [[1,2,3,4],[6,8]], [(4,6)])
            sage: Gamma.vertex(1)
            0
            sage: Gamma.vertex(6)
            1
        �����)r�r7r)r?rFrEr)r)r*rs�s

�zStableGraph.vertexcsV|durt�fdd�tt�j��D��St�j|�}�jD]}|t|�8}qt|�S)aU
        Return the list of markings (non-edge legs) of self at vertex v.

        EXAMPLES::

            sage: from admcycles import StableGraph

            sage: gam = StableGraph([3,5],[[1,3,7],[2,4]],[(1,2)])
            sage: gam.list_markings(0)
            (3, 7)
            sage: gam.list_markings()
            (3, 7, 4)
        Ncs g|]}��|�D]}|�q	qSr))r{)r'rEr2rKr)r*r3�s
�
�z-StableGraph.list_markings.<locals>.<listcomp>)r;r�r7rr�rr)r?rEr�rHr)rKr*r{�s 
zStableGraph.list_markingscCsdd�|jD�S)z�
        Return the list of legs

        EXAMPLES::

            sage: from admcycles import StableGraph

            sage: gam = StableGraph([3,5],[[1,3,7],[2,4]],[(1,2)])
            sage: gam.leglist()
            [1, 3, 7, 2, 4]
        cSsg|]	}|D]}|�qqSr)r))r'�legr2r)r)r*r3�r4z'StableGraph.leglist.<locals>.<listcomp>r�rKr)r)r*�leglist�szStableGraph.leglistcCstdd�|jD��S)a
        Return the tuple containing all half-edges, i.e. legs belonging to an edge

        EXAMPLES::

            sage: from admcycles import StableGraph

            sage: gam = StableGraph([3,5],[[1,3,7],[2,4]],[(1,2)])
            sage: gam.halfedges()
            (1, 2)
        css�|]
}|D]}|VqqdSrOr))r'�edr2r)r)r*r+s�z(StableGraph.halfedges.<locals>.<genexpr>)r;rrKr)r)r*�	halfedges�szStableGraph.halfedgescsP��kr���fdd��jD�S���fdd��jD����fdd��jD�S)a�
        Return the list [(l1,k1),(l2,k2), ...] of edges from i to j, where l1 in i, l2 in j; for i==j return each edge only once

        EXAMPLES::

            sage: from admcycles import StableGraph

            sage: gam = StableGraph([3,5],[[1,3,7],[2,4]],[(1,2)])
            sage: gam.edges_between(0, 1)
            [(1, 2)]
            sage: gam.edges_between(0, 0)
            []
        c�4g|]}|d�j�vr|d�j�vr|�qS�rrr��r'rH�rGr2r?r)r*r3r�z-StableGraph.edges_between.<locals>.<listcomp>crrr�rrr)r*r3r�cs@g|]}|d�j�vr|d�j�vr|d|df�qSrr�rrr)r*r3s@r�)r?rGr2r)rr*�
edges_between
s0zStableGraph.edges_betweencCs2|jstd��|D]
}|j|�|��|�q	|S)a_
        Erase all legs in the list markings, do not check if this gives well-defined graph

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: Gamma = StableGraph([0,2], [[1,2,3,4],[6,8]], [(4,6)], mutable=True)
            sage: Gamma.forget_markings([1,3,8])
            [0, 2] [[2, 4], [6]] [(4, 6)]
        r�)r r8rrs�remove)r?r�r�r)r)r*�forget_markingss
zStableGraph.forget_markingscCs4|jD]\}}||kr|S||kr|Sq|S)a
        Returns l' if l and l' form an edge, otherwise returns l

        EXAMPLES::

            sage: from admcycles import StableGraph

            sage: gam = StableGraph([3,5],[[1,3,7],[2,4]],[(1,2)])
            sage: gam.leginversion(1)
            2
        r�)r?rFr�r�r)r)r*�leginversion.s�zStableGraph.leginversioncs*|jstd��|��}d}ttt|j����i}i�|�s�t|j�}d}d}||k�r�|j|dkr�t|j|�dkr�d}|j|d�|����|�	��}|j�
|���
|�|d8}|j|���|j�
|�z|j���f�W�nty�|j���f�Yn�w|j|dk�r{t|j|�dk�r{d}|j|d�|j|d��|vr��}��|�|����|�	��}|j�
|���
|�|d8}�|v�r	�|�<|j|���|j|�
��|j�
|�z
|j���f�Wn�t�y|j���f�Ynww|����|j�
|�z
|j���f�Wnt�y0|j���f�Ynwz
|j���f�Wnt�yM|j���f�Ynw|j�
��f����<���<�����fdd��D�������fd	d��D��n|d7}||ks+|r�fd
d�tt���D�|�fS)a
        Stabilize this stable graph.

        (all vertices with genus 0 have at least three markings) and returns
        ``(dicv,dicl,dich)`` where

        - dicv a dictionary sending new vertex numbers to the corresponding old
          vertex numbers

        - dicl a dictionary sending new marking names to the label of the last
          half-edge that they replaced during the stabilization this happens
          for instance if a g=0 vertex with marking m and half-edge l
          (belonging to edge (l,l')) is stabilized: l' is replaced by m, so
          dicl[m]=l'

        - dich a dictionary sending half-edges that vanished in the
          stabilization process to the last half-edge that replaced them this
          happens if a g=0 vertex with two half-edges a,b (whose corresponding
          half-edges are c,d) is stabilized: we obtain an edge (c,d) and a ->
          d, b -> d we assume here that a stabilization exists (all connected
          components have 2g-2+n>0)

        EXAMPLES::

            sage: from admcycles import StableGraph

            sage: gam = StableGraph([3,5],[[1,3,7],[2,4]],[(1,2)], mutable=True)
            sage: gam.stabilize()
            ({0: 0, 1: 1}, {}, {})

            sage: gam = StableGraph([3,5],[[1,3,7],[2,4]],[(1,2)])
            sage: gam.stabilize()
            Traceback (most recent call last):
            ...
            ValueError: the graph is not mutable; use a copy instead

            sage: g = StableGraph([0,0,0], [[1,5,6],[2,3],[4,7,8]], [(1,2),(3,4),(5,6),(7,8)], mutable=True)
            sage: g.stabilize()
            ({0: 0, 1: 2}, {}, {2: 4, 3: 1})
            sage: h = StableGraph([0, 0, 0], [[1], [2,3], [4]], [(1,2), (3,4)], mutable=True)
            sage: h.stabilize()
            ({0: 2}, {}, {})
        r�FrTrr,csi|]}�|�kr|��qSr)r)�r'�h)�dichr��e1primer)r*r��r�z)StableGraph.stabilize.<locals>.<dictcomp>csi|]}�|�kr|��qSr)r)r)r
�e0primer�r)r*r��r�csi|]}|�|�qSr)r)r�)�verteximagesr)r*r��r�)r r8r{r5r�r7rrr
rs�poprrrx�update)r?r��stabler��numvert�count�v1�swapr))r
r�rr�rrr*�	stabilizeAs�,

 


�$



�
���� @zStableGraph.stabilizec	cs.�|durt|���D]}|�||�D]}|VqqdS|j|}t|j|�}|dkr>|��}|�|�|s;|��|Vtt	|d�d�D]L}t
t|j|��D]@}|dkr_t|�dks~||krk|t|�dks~d||kr|dkr|j|d|vrqS|��}|�|||�|s�|��|VqSqHdS)a�
        Run through the list of all possible degenerations of this graph.

        A degeneration happens by adding an edge at v or splitting it
        into two vertices connected by an edge the new edge always
        comes last in the list of edges.

        If v is None, return all degenerations at all vertices.

        EXAMPLES::

            sage: from admcycles import StableGraph

            sage: gam = StableGraph([3,5],[[1,3,7],[2,4]],[(1,2)])
            sage: list(gam.degenerations(1))
            [[3, 4] [[1, 3, 7], [2, 4, 8, 9]] [(1, 2), (8, 9)],
             [3, 0, 5] [[1, 3, 7], [2, 4, 8], [9]] [(1, 2), (8, 9)],
             [3, 1, 4] [[1, 3, 7], [8], [2, 4, 9]] [(1, 2), (8, 9)],
             [3, 1, 4] [[1, 3, 7], [2, 8], [4, 9]] [(1, 2), (8, 9)],
             [3, 1, 4] [[1, 3, 7], [4, 8], [2, 9]] [(1, 2), (8, 9)],
             [3, 1, 4] [[1, 3, 7], [2, 4, 8], [9]] [(1, 2), (8, 9)],
             [3, 2, 3] [[1, 3, 7], [8], [2, 4, 9]] [(1, 2), (8, 9)],
             [3, 2, 3] [[1, 3, 7], [2, 8], [4, 9]] [(1, 2), (8, 9)],
             [3, 2, 3] [[1, 3, 7], [4, 8], [2, 9]] [(1, 2), (8, 9)],
             [3, 2, 3] [[1, 3, 7], [2, 4, 8], [9]] [(1, 2), (8, 9)]]

        All these graphs are immutable (or mutable when ``mutable=True``)::

            sage: any(g.is_mutable() for g in gam.degenerations())
            False
            sage: all(g.is_mutable() for g in gam.degenerations(mutable=True))
            True

        TESTS::

            sage: from admcycles import StableGraph
            sage: G = StableGraph([2,2,2], [[1],[2,3],[4]], [(1,2),(3,4)])
            sage: sum(1 for v in range(3) for _ in G.degenerations(v))
            8
            sage: sum(1 for _ in G.degenerations())
            8
        Nrr,r)
r�r_�
degenerationsrr7rrY�degenerate_nonseprLr
rr��degenerate_sep)	r?rErCrr(rFrX�g1�Mr)r)r*r�sH�+�

��
�����zStableGraph.degenerationscCs|jd7_|jd|jfS)a7
        Create two new leg-indices that can be used to create an edge

        This modifies ``self._maxleg``.

        EXAMPLES::

            sage: from admcycles import StableGraph

            sage: gam = StableGraph([3,5],[[1,3,7],[2,4]],[(1,2)])
            sage: gam.newleg()
            (8, 9)
        r,r)rrKr)r)r*�newleg�szStableGraph.newlegcs�|����fdd��D�}�fdd��D�}|rtd�|����dur3�fdd�|jD�}	|	�|�nptt|�����tt	��d�D]}
�|
d�|
krStd	��qC��krfd
d�|jD�}	|	�|�n=d}�fdd��D�}g}
|j
D]}|
�|�qvd
d�|jD�}	�D]}
|
�vs�|
|	vr�q�|||	|
<|d7}q�|	�|�|r�|}|ji|	dd�|r�|�
�n|ji|	ddd�}|r�|�
�|s�|��|r�|||	fS|S)aI
        Rename the markings according to the dictionary ``dic``.

        Return a new stable graph which is isomorphic to self up to the
        change of markings given in ``dic``.

        If ``return_dicts`` is set to ``True``, return a triple
        ``(new_stable_graph, markings_relabelling, all_legs_relabelling)``
        where

        - ``markings_relabelling`` is the dictionary whose keys are the marking of
          the current graph and the values are the new markings

        - ``all_legs_relabelling`` is a dictionary of all the marking relabelling
          (this argument could be forwarded to :meth:`StableGraph.relabel`)

        INPUT:

        dic : dictionary
          the relabeling old label -> new label

        shift : integer
          if provided, perform the corresponding shift on the non-marked legs

        inplace : boolean (default ``True``)
          whether to do an inplace modification or return a new stable graph

        return_dicts : boolean
          whether to return the extra relabelling information

        mutable : boolean (default ``False``)
          whether the return graph should be mutable (ignored when ``inplace=True``)

        EXAMPLES::

            sage: from admcycles import StableGraph

            sage: g = StableGraph([0], [[1,2]], [], mutable=True)
            sage: g.rename_legs({1: 3})
            [0] [[2, 3]] []

            sage: g = StableGraph([0, 0], [[1,3,4],[2,5]], [(4,5)], mutable=True)
            sage: gg, rel, extra = g.rename_legs({1:2, 2:1}, return_dicts=True)
            sage: gg
            [0, 0] [[2, 3, 4], [1, 5]] [(4, 5)]
            sage: rel == {1: 2, 2: 1, 3: 3}
            True

        A graph involving some changes in non-marking legs::

            sage: g = StableGraph([0, 0], [[1, 4], [2, 3, 5]], [(4, 5)])
            sage: g.rename_legs({1: 2, 2: 4, 3: 6}, inplace=False)
            [0, 0] [[1, 2], [4, 5, 6]] [(1, 5)]
            sage: gg, markings_dic, legs_dic = g.rename_legs({1: 2, 2: 4, 3: 6}, inplace=False, return_dicts=True)
            sage: gg
            [0, 0] [[1, 2], [4, 5, 6]] [(1, 5)]
            sage: markings_dic == {1: 2, 2: 4, 3: 6}
            True
            sage: legs_dic == {1: 2, 2: 4, 3: 6, 4: 1, 5: 5}
            True

        Using the ``shift`` argument::

            sage: g = StableGraph([0], [[1, 2, 3]], [(2, 3)], mutable=True)
            sage: g.rename_legs({1: 5}, shift=1, inplace=False)
            [0] [[3, 4, 5]] [(3, 4)]
            sage: gg, markings_dic, legs_dic = g.rename_legs({1: 5}, shift=1, inplace=False, return_dicts=True)
            sage: gg
            [0] [[3, 4, 5]] [(3, 4)]
            sage: markings_dic == {1: 5}
            True
            sage: legs_dic == {1: 5, 2: 3, 3: 4}
            True

        This method forbids renaming internal legs (for that purpose use :meth:`relabel`)::

            sage: g = StableGraph([0], [[1,2,3]], [(2,3)], mutable=True)
            sage: g.rename_legs({2:5})
            Traceback (most recent call last):
            ...
            ValueError: non-marking legs [2] in dic (use the relabel method instead)
        csi|]	}|��||��qSr)r�r�)�dicr)r*r�er4z+StableGraph.rename_legs.<locals>.<dictcomp>c�g|]}|�vr|�qSr)r)r�)r�r)r*r3frVz+StableGraph.rename_legs.<locals>.<listcomp>z;non-marking legs {} in dic (use the relabel method instead)Ncs i|]}|D]}||��qqSr)r)�r'rHrG)�shiftr)r*r�l� rzrepeated marking in the imagecS�i|]
}|D]}||�qqSr)r)r!r)r)r*r�u�rcr r)r)r�)�image_markingsr)r*r3zrVcSr$r)r)r!r)r)r*r�~r%T�r�F)r�rC)r{r8r<rrr;rz�valuesr�r7rr�r�r�rL)r?rr"r�Zreturn_dictsrCZtidyupZmarkings_relabellingZbad_legsZall_legs_relabellingrGro�forgetZall_legsrF�resultr))rr&r�r"r*�rename_legssPT�


�zStableGraph.rename_legscCs�|jstd��|j|}|j|}|��}||j|<|j||g7_t|�|dg|j|<|jtt|�t|��|dgg7_|j|g7_dS)a�
        degenerate vertex v into two vertices with genera g1 and g(v)-g1 and legs M and complement

        add new edge (e[0],e[1]) such that e[0] is in new vertex v, e[1] in last vertex, which is added

        EXAMPLES::

            sage: from admcycles import StableGraph

            sage: G = StableGraph([3,5],[[1,3,7],[2,4]],[(1,2)], mutable=True)
            sage: G.degenerate_sep(1, 2, [4])
            sage: G
            [3, 2, 3] [[1, 3, 7], [4, 8], [2, 9]] [(1, 2), (8, 9)]

            sage: G = StableGraph([3,5],[[1,3,7],[2,4]],[(1,2)])
            sage: G.degenerate_sep(1, 2, [4])
            Traceback (most recent call last):
            ...
            ValueError: the graph is not mutable; use a copy instead
        r�rrN)r r8rrrr5r�r)r?rErrr(ZoldlegrHr)r)r*r�s


*zStableGraph.degenerate_sepcCsZ|jstd��|��}|j|d8<|j||d|dg7<|j|g7_dS)a
        EXAMPLES::

            sage: from admcycles import StableGraph

            sage: G = StableGraph([3,5],[[1,3,7],[2,4]],[(1,2)], mutable=True)
            sage: G.degenerate_nonsep(1)
            sage: G
            [3, 4] [[1, 3, 7], [2, 4, 8, 9]] [(1, 2), (8, 9)]

            sage: G = StableGraph([3,5],[[1,3,7],[2,4]],[(1,2)])
            sage: G.degenerate_nonsep(1)
            Traceback (most recent call last):
            ...
            ValueError: the graph is not mutable; use a copy instead
        r�rrN)r r8rrrr)r?rErHr)r)r*r�szStableGraph.degenerate_nonsepc	Cs�|jstd��|�|d�}|�|d�}||krf|r.t|j|g|j|dd�g|g�}|j|d7<|j|�|d�|j|�|d�|j�|�|rd|||gdd�tt	|j��D�fSdS||kro||}}|r�dd�t|�D�}|||<|�
dd�t|dt	|j��D��t|j||j|g|j|dd�|j|dd�g|g�}|j�|�}|j||7<|j�|�}|j||7<|j|�|d�|j|�|d�|j�|�|r�||||g|fSdS)	a�
        Contracts the edge e=(e0,e1).

        This method returns nothing by default. But if ``adddata`` is set to
        ``True``, then it returns a tuple ``(av, edgegraph, vnum)`` where

          - ``av`` is the the vertex in the modified graph on which previously
            the edge ``e`` had been attached

          - ``edgegraph`` is a stable graph induced in self by the edge e
            (1-edge graph, all legs at the ends of this edge are considered as
            markings)

          - ``vnum`` is a list of one or two vertices e was attached before the
            contraction (in the order in which they appear)

          - ``diccv`` is a dictionary mapping old vertex numbers to new vertex
            numbers

        EXAMPLES::

            sage: from admcycles import StableGraph

            sage: G = StableGraph([3,5],[[1,3,7],[2,4]],[(1,2),(3,7)], mutable=True)
            sage: G.contract_edge((1,2))
            sage: G
            [8] [[3, 7, 4]] [(3, 7)]
            sage: G.contract_edge((3,7))
            sage: G
            [9] [[4]] []

            sage: G2 = StableGraph([3,5],[[1,3,7],[2,4]],[(1,2),(3,7)], mutable=True)
            sage: G2.contract_edge((3,7))
            sage: G2.contract_edge((1,2))
            sage: G == G2
            True

            sage: G2 = StableGraph([3,5],[[1,3,7],[2,4]],[(1,2),(3,7)], mutable=True)
            sage: G2.contract_edge((3,7), adddata=True)
            (0, [3] [[1, 3, 7]] [(3, 7)], [0], {0: 0, 1: 1})

            sage: G = StableGraph([1,1,1,1],[[1],[2,4],[3,5],[6]],[(1,2),(3,4),(5,6)],mutable=True)
            sage: G.contract_edge((3,4),True)
            (1, [1, 1] [[2, 4], [3, 5]] [(3, 4)], [1, 2], {0: 0, 1: 1, 2: 1, 3: 2})

            sage: G = StableGraph([3,5],[[1,3,7],[2,4]],[(1,2)])
            sage: G.contract_edge((1,2))
            Traceback (most recent call last):
            ...
            ValueError: the graph is not mutable; use a copy instead
        r�rrNcS�i|]}||�qSr)r)r�r)r)r*r��z-StableGraph.contract_edge.<locals>.<dictcomp>cSr,r)r)r�r)r)r*r�r-cSsi|]}||d�qS�rr)r�r)r)r*r�r�)r r8rsrrrrrr�r7rr)	r?rHZadddata�v0rr�Zdiccvrr�r)r)r*�
contract_edge�s>4&"�
$>�zStableGraph.contract_edgecCsr|jstd��|��|��|��|�||�}|j�|�|j�|�}|D]}|j�|�q't	|j
|j
�}	|��}
g}|jD]}||7}q@t|�}t|�}
||
}|D]
}|	d7}	|	||<qU|
j
i|dd�t|�D]}|||<qlt|dt|j�d�D]}|d||<qtt|j��D]}t|j�|||<q�|j|
j7_|j|
j7_|j|
j7_|��dS)a	
        Glues the stable graph ``Gr`` at the vertex ``i`` of this stable graph

        optional arguments: if divGr/dil are given they are supposed to be a
        dictionary, which will be cleared and updated with the
        renaming-convention to pass from leg/vertex-names in Gr to
        leg/vertex-names in the glued graph similarly, divs will be a
        dictionary assigning vertex numbers in the old self the corresponding
        number in the new self necessary condition:

        - every leg of i is also a leg in Gr
        - every leg of Gr that is not a leg of i in self gets a new name

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: Gamma = StableGraph([2,1], [[1,2,3,4],[5]], [(2,3),(4,5)], mutable=True)
            sage: Gr = StableGraph([0,1,1], [[1,2,5],[6,3,7],[8,4]], [(5,6),(7,8)])
            sage: divGr=dict()
            sage: divs=dict()
            sage: dil=dict()
            sage: Gamma.glue_vertex(0, Gr, divGr, divs, dil)
            sage: Gamma
            [1, 0, 1, 1] [[5], [1, 2, 10], [3, 11, 12], [4, 9]] [(4, 5), (9, 12), (10, 11)]
            sage: divGr
            {0: 1, 1: 2, 2: 3}
            sage: divs
            {1: 0}
            sage: dil
            {5: 10, 6: 11, 7: 12, 8: 9}
        r�rTr'N)r r8�clearrrrrrrr0rrYr�r�r�r7r�)r?rGZGrZdivGr�divsZdilZ
selfedges_oldZlegs_oldrHr�ZGr_newr�rFr�r2r)r)r*�glue_vertex*s@ 



zStableGraph.glue_vertexcsB�jstd���fdd�|D�}�fdd�|D�}|�_|�_dS)zf
        Reorders vertices according to tuple given (permutation of range(len(self._genera)))
        r�c�g|]}�j|�qSr)r�r1rKr)r*r3�r�z0StableGraph.reorder_vertices.<locals>.<listcomp>cr4r)r�r1rKr)r*r3�r�N)r r8rr)r?�vordr�r�r)rKr*�reorder_verticeszs
zStableGraph.reorder_verticescst�fdd��D����dur1���}�jD]\}}||vr,||vr,|�|�|�|�qt|��t��d�|rG�fdd�t�d�D��n	dd����D���fdd	��D�}���fd
d	��D�}	���fdd	��jD�}
�fdd�tt���D�}t||	|
|d
�|�fS)aD
        Extracts from self a subgraph induced by the list vertices

        if the list outgoing_legs is given, the markings of the subgraph are
        called 1,2,..,m corresponding to the elements of outgoing_legs in this
        case, all edges involving outgoing edges should be cut returns a triple
        (Gamma,dicv,dicl), where

        - Gamma is the induced subgraph

        - dicv, dicl are (surjective) dictionaries associating vertex/leg
          labels in self to the vertex/leg labels in Gamma

        if ``rename=False``, do not rename any legs when extracting

        EXAMPLES::

            sage: from admcycles import StableGraph

            sage: gam = StableGraph([3,5],[[1,3,7],[2,4]],[(1,2)])
            sage: gam.extract_subgraph([0])
            ([3] [[1, 2, 3]] [], {0: 0}, {1: 1, 3: 2, 7: 3})
        c3s$�|]
}�j|D]}|Vq	qdSrOr�)r'rErFrKr)r*r+�r�z/StableGraph.extract_subgraph.<locals>.<genexpr>Nrcsi|]	}�||d�qSr.r)r���
outgoing_legsr)r*r��r4z0StableGraph.extract_subgraph.<locals>.<dictcomp>cSr,r)r)r�r)r)r*r��r-cr4r)r�r�rKr)r*r3�r�z0StableGraph.extract_subgraph.<locals>.<listcomp>cs&g|]}��fdd��j|D��qS)csg|]
}��||���qSr))�
setdefaultr�)r�r"r)r*r3�r%z;StableGraph.extract_subgraph.<locals>.<listcomp>.<listcomp>r�r�)r�r?r"r)r*r3�s&cs@g|]\}}|�vr|�vr|�vr|�vr�|�|f�qSr)r))r'r�r�)�attachedlegsr�r8r)r*r3�s����csi|]}�||�qSr)r)r�)�verticesr)r*r��r��rC)	r�rYrrr5r7r�rr)r?r;r8�renamerCZalllegsr�r�r@rArBr�r))r:r�r8r?r"r;r*�extract_subgraph�s$

�zStableGraph.extract_subgraphcCs�|tkrdS|tkr|��|��|��dkS|tkr&|��|��dkS|tkr@|��|��dkp?tdd�|jD��dkS|t	krIt
|j�Std��)NFrcsrNrO)r>r&r)r)r*r+�rRz'StableGraph.vanishes.<locals>.<genexpr>zunknown moduli)
rrr^r�r_rrr�rrr>rr8)r?�modulir)r)r*�vanishes�s�
zStableGraph.vanishescCs$ddlm}||jdd�|d���S)a"
        Computes the pushforward of a product of tautological classes (one for each vertex) under the
        boundary gluing map for this stable graph.

        INPUT:

        - ``classes``  -- list (default: `None`); list of tautclasses, one for each vertex of the stable
          graph. The genus of the ith class is assumed to be the genus of the ith vertex, the markings
          of the ith class are supposed to be 1, ..., ni where ni is the number of legs at the ith vertex.
          Note: the jth leg at vertex i corresponds to the marking j of the ith class.
          For classes=None, place the fundamental class at each vertex.

        EXAMPLES::

            sage: from admcycles import StableGraph, TautologicalRing
            sage: B=StableGraph([2,1],[[4,1,2],[3,5]],[(4,5)])
            sage: Bclass=B.boundary_pushforward() # class of undecorated boundary divisor
            sage: Bclass*Bclass
            Graph :      [2, 1] [[4, 1, 2], [3, 5]] [(4, 5)]
            Polynomial : -psi_5 - psi_4
            sage: R1 = TautologicalRing(2,3)
            sage: R2 = TautologicalRing(1,2)
            sage: si1=B.boundary_pushforward([R1.fundamental_class(),-R2.psi(2)]); si1
            Graph :      [2, 1] [[4, 1, 2], [3, 5]] [(4, 5)]
            Polynomial : -psi_5
            sage: si2=B.boundary_pushforward([-R1.psi(1), R2.fundamental_class()]); si2
            Graph :      [2, 1] [[4, 1, 2], [3, 5]] [(4, 5)]
            Polynomial : -psi_4
            sage: a = Bclass*Bclass-si1-si2
            sage: a.simplify()
            0
        r��
prodtautclassFr<)Zprotaut)�	admcyclesrBrY�pushforward)r?�classesrBr)r)r*�boundary_pushforward�s!z StableGraph.boundary_pushforwardc
sTddlm}ddlm}t||�r1ddlm}|�jdd�g�}|j��D]	}|��	|�7}q%|St||��r�ddlm
}m}m}m}	m
�m}
t����ttd���d��k}|�|jd|d	�}|�jdd�g�}|D�]2\�}
�}�t�j��d
d����D�}���D]
}||d7<q����D]
}||d7<q�|��}�jD]}||dd
kr�|d|
|d��|
|d��9}q�dd�tt�j��D��
|
D]}�
|
|�|�qӇ���
fdd�tt�j��D��dd�tt|jj��D��|D]}�||�|�q�|	gg�}|jD]<\��}��fdd��D�}|	dd�t��D�|fgdg�}t����fdd�tt|jj��D��}||||7}�q||9}|D]I\��}g}tt�j��D]+�	�fdd��
�	D�}���	fdd���	d
D�}|�|��	d||d���qd|�|g�}||9}||7}�qVqm|Std�|���)a
        Pulls back the TautologicalClass or decstratum other to self and
        returns a prodtautclass with gamma=self.

        EXAMPLES::

            sage: from admcycles import StableGraph, psiclass
            sage: G = StableGraph([0, 2], [[1, 2, 4, 3], [5]], [(3, 5)])
            sage: H = StableGraph([2],[[1,2,4]],[])
            sage: a = H.boundary_pushforward([psiclass(3,2,3)]); a
            Graph :      [2] [[1, 2, 4]] []
            Polynomial : psi_4
            sage: G.boundary_pullback(a)
            Outer graph : [0, 2] [[1, 2, 4, 3], [5]] [(3, 5)]
            Vertex 0 :
            Graph :      [0] [[1, 2, 3, 4]] []
            Polynomial : psi_3
            Vertex 1 :
            Graph :      [2] [[1]] []
            Polynomial : 1
        r)�TautologicalClass)�
decstratumrAFr<)�common_degenerationsrB�	onekppoly�kppoly�kappacl�psiclT)Zmodisor=cSsi|]}|d�qSr.r)r�r)r)r*r�r-z1StableGraph.boundary_pullback.<locals>.<dictcomp>rr,r�cS�g|]}g�qSr)r)r�r)r)r*r3�z1StableGraph.boundary_pullback.<locals>.<listcomp>cs2g|]}�j�|�fdd��j|D�d��qS)crmr)r)r�)�dicl1r)r*r3$rq�<StableGraph.boundary_pullback.<locals>.<listcomp>.<listcomp>r7)r>rr�)�GammarPr?�v1preimr)r*r3#s
�
�cSrNr)r)r�r)r)r*r3&rOcsi|]	}�|�|�qSr)r)r�)�dicl2�psir)r*r�/r4cSrNr)r)r�r)r)r*r30rOcs6g|]�t�����fdd�tt����D���qS)cs6g|]�t���fdd���D������qS)csg|]
}�|�d���qSr.r)�r'�w)rorLrr)r*r32r%zGStableGraph.boundary_pullback.<locals>.<listcomp>.<listcomp>.<listcomp>)r��r')�kapparLrrE�v2preim)ror*r32s.�rQ)rr�r7rX)rYrLrrZ)rEr*r32s
��crmr)r)rV)rYr)r*r3>rqcs*i|]}|�vr��d|�|�qS)r,r)r�)�graphpartitionrUrEr)r*r�?�*)rYrUzinvalid input other={})�tautological_ringrGrCrHr$rBrYZ_termsr(�boundary_pullbackrIrJrKrLrMr�r{r�r��gammar7rrrrx�polyrr6r<)r?r`rGrHrBr*r�rIrJrKrMr=ZcommdegZdicv1Zdicv2ZlegcountrFZ
excesspolyrHrWZ
resultpoly�coeffZpsipolydictZpsipolyZ	kappapolyZdecstratlistZkappavZpsivZtempresur))rRrPrTr[rYrLrrUr?rErSrZr*r^�sn
 $

$��
 � �	zStableGraph.boundary_pullbackcCs.|durddlm}||��|���}||�S)a�
        Return the pure boundary stratum associated to this stable graph.

        Note: does not divide by automorphisms!

        EXAMPLES::

            sage: from admcycles import StableGraph

            sage: g = StableGraph([0,0], [[1,3,4], [2,5,6]], [(3,5),(4,6)])
            sage: g.to_tautological_class()
            Graph :      [0, 0] [[1, 3, 4], [2, 5, 6]] [(3, 5), (4, 6)]
            Polynomial : 1

        TESTS::

             sage: from admcycles import StableGraph

            sage: g = StableGraph([0,0], [[1,3,4], [2,5,6]], [(3,5),(4,6)])
            sage: g.to_tautclass()
            doctest:...: DeprecationWarning: to_tautclass is deprecated. Please use to_tautological_class instead.
            See https://gitlab.com/modulispaces/admcycles/-/merge_requests/109 for details.
            Graph :      [0, 0] [[1, 3, 4], [2, 5, 6]] [(3, 5), (4, 6)]
            Polynomial : 1
       Nr)�TautologicalRing)r]rbr(r�)r?�Rrbr)r)r*�to_tautological_classHsz!StableGraph.to_tautological_class�mcC�t|j�}tt|�SrO)r7rr	r)r?�nvr)r)r*�_vertex_modulei�

zStableGraph._vertex_modulecCrfrO)r7rr	r)r?�ner)r)r*�_edge_modulemrizStableGraph._edge_modulercCs6ddlm}t|j�}t|j�}dd�t|�D�}t|j�D]$\}\}}|�|�}	|�|�}
||	�|
d|f�||
�|	d|f�qdg|}d||<dg|}d	||<|�}
|
�|�t	t|��}|g}|
r�|
�
�}	||	D]#\}
}}||
r�|	||f||
<|
�|
�d	||
<|�|�|�|
�qp|
sh|||fS)
a
        Return a spanning tree.

        INPUT:

        - ``root`` - optional vertex for the root of the spanning tree

        OUTPUT: a triple

        - list of triples ``(vertex ancestor, sign, edge index)`` where the
          triple at position ``i`` correspond to vertex ``i``.

        - the set of indices of extra edges not in the tree

        - vertices sorted according to their heights in the tree (first element is the root
          and the end of the list are the leaves). In other words, a topological sort of
          the vertices with respect to the spanning tree.

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: G = StableGraph([0,0,0], [[1,2],[3,4,5],[6,7]], [(1,3),(4,6),(5,7)])
            sage: G.spanning_tree()
            ([None, (0, -1, 0), (1, -1, 1)], {2}, [0, 1, 2])
            sage: G.spanning_tree(1)
            ([(1, 1, 0), None, (1, -1, 1)], {2}, [1, 0, 2])
            sage: G.spanning_tree(2)
            ([(1, 1, 0), (2, 1, 1), None], {2}, [2, 1, 0])
        r)�dequecSrNr)r)�r'r�r)r)r*r3�rOz-StableGraph.spanning_tree.<locals>.<listcomp>r�rNTF)rrrlr7rrr�r:rsrxr��popleftr)r?�rootrlrgrj�	out_edgesrGr�r�r�rE�	ancestors�unseen�todo�extra_edgesr;r�r)r)r*�
spanning_treess:









��

zStableGraph.spanning_treecCs�t|j�}|��}|��\}}}g}|D]Y}dg|}d||<|j|\}	}
|�|	�}|�|
�}||durK||\}}
}|||
8<||dus6||durf||\}}
}|||
7<||dusQ|�||��q|S)aD
        Return a basis of the cycles as vectors of length the number of edges in the graph.

        The coefficient at a given index in a vector corresponds to the edge
        of this index in the list of edges of the stable graph. The coefficient is
        +1 or -1 depending on the orientation of the edge in the cycle.

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: G = StableGraph([0,0,0], [[1,2,3], [4,5,6], [7,8]], [(1,4),(5,2),(3,7),(6,8)])
            sage: G.cycle_basis()
            [(1, 1, 0, 0), (1, 0, -1, 1)]
        rrN)r7rrkrursrx)r?rj�Vrqrtr��basisrG�vecr�r�r�rEr�r)r)r*�cycle_basis�s(



��zStableGraph.cycle_basiscCs|���|���S)a
        Return the subspace of the edge module generated by the cycles.

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: G = StableGraph([0,0,0], [[1,2,3], [4,5,6], [7,8]], [(1,4),(5,2),(3,7),(6,8)])
            sage: C = G.cycle_space()
            sage: C
            Free module of degree 4 and rank 2 over Integer Ring
            Echelon basis matrix:
            [ 1  0 -1  1]
            [ 0  1  1 -1]
            sage: G.flow_divergence(C.random_element()).is_zero()
            True
        )rk�	submoduleryrKr)r)r*�cycle_space�szStableGraph.cycle_spacecCsn|��|�}|���}t|j�D]$\}\}}|�|�}|�|�}||||7<||||8<q|S)a�
        Return the divergence of the given ``flow``.

        The divergence at a given vertex is the sum of the flow on the outgoing
        edges at this vertex.

        EXAMPLES::

            sage: from admcycles import StableGraph
            sage: G = StableGraph([1,0,0], [[1,2],[3,4,5,6],[7,8]], [(1,3),(4,2),(5,8),(7,6)])
            sage: G.flow_divergence([1,1,1,1])
            (0, 0, 0)
            sage: G.flow_divergence([1,0,0,0])
            (1, -1, 0)
        )rkrhr:rrs)r?�flow�derivrGr�r�r�rEr)r)r*�flow_divergence�s


zStableGraph.flow_divergencec
Cs�t|j�}t|�|kst|�dkrtd��|��\}}}|��|��|��|�}||ur6|��r6|�	�}|}|�
�}|�}|D]}	||	\}
}}|||||	7<||
||	7<qA|S)a�
        Return a solution for the flow equation with given vertex weights.

        EXAMPLES::

            sage: from admcycles import StableGraph

            sage: G = StableGraph([0,0,0], [[1,2,3], [4,5,6], [7,8]], [(1,4),(5,2),(3,7),(6,8)])
            sage: flow = G.flow_solve((-3, 2, 1))
            sage: flow
            (-2, 0, -1, 0)
            sage: G.flow_divergence(flow)
            (-3, 2, 1)
            sage: div = vector((-34, 27, 7))
            sage: flow = G.flow_solve(div)
            sage: G.flow_divergence(flow) == div
            True
            sage: C = G.cycle_space()
            sage: G.flow_divergence(flow + C.random_element()) == div
            True

            sage: G = StableGraph([0,0,0], [[1],[2,3],[4]], [(1,2),(3,4)])
            sage: G.flow_solve((-1, 0, 1))
            (-1, -1)
            sage: G.flow_divergence((-1, -1))
            (-1, 0, 1)
            sage: G.flow_solve((-1, 3, -2))
            (-1, 2)
            sage: G.flow_divergence((-1, 2))
            (-1, 3, -2)

        TESTS::

            sage: V = ZZ**4
            sage: vectors = [V((-1, 0, 0, 1)), V((-3, 1, -2, 4)), V((5, 2, -13, 6))]
            sage: G1 = StableGraph([0,0,0,0], [[1], [2,3], [4,5], [6]], [(1,2), (3,4), (5,6)])
            sage: G2 = StableGraph([0,0,0,0], [[1], [2,3], [4,5], [6]], [(1,2), (3,4), (6,5)])
            sage: G3 = StableGraph([0,0,0,0], [[1], [2,3], [4,5], [6]], [(2,1), (3,4), (6,5)])
            sage: G4 = StableGraph([0,0,0,0], [[1], [2,3], [4,5], [6]], [(1,2), (4,3), (5,6)])
            sage: for G in [G1,G2,G3,G4]:
            ....:     for v in vectors:
            ....:         flow = G.flow_solve(v)
            ....:         assert G.flow_divergence(flow) == v, (v,flow,G)

            sage: V = ZZ**6
            sage: G = StableGraph([0,0,0,0,0,0], [[1,5], [2,3], [4,6,7,10], [8,9,11,13], [14,16], [12,15]], [(1,2),(4,3),(5,6),(7,8),(9,10),(12,11),(13,14),(15,16)])
            sage: for _ in range(10):
            ....:     v0 = V.random_element()
            ....:     for u in V.basis():
            ....:         v = v0 - sum(v0) * u
            ....:         flow = G.flow_solve(v)
            ....:         assert G.flow_divergence(flow) == v, (v, flow)
        rz5vertex_weights must have length nv and sum up to zero)r7rr�r8ru�reverserrhrM�__copy__rk)
r?Zvertex_weightsrgrqr�r;Znew_vertex_weightsrvrxr�rEr�rGr)r)r*�
flow_solve	s"
8zStableGraph.flow_solvecs�d}d}d}d}d}d�|s"ttt|j���}	|dur|	}n||	7}|�����|��s�dg}
tdt|j��D]?}|
|
|dd|t��|d��dt��|d|d��t��|��dt��||��td�|dfg7}
q8�dur|
�n�|
7�|s�fd	d
�tt�j��D�}i}
�j	D]<}��
|d�}��
|d�}||kr�t||�d|t||�t
||�f||
|<|t||�t
||�fd7<q�|dur�|
}n|�|
����fdd�tt�j��D�}g}�j	D]M}��
|d�}��
|d�}||k�rC�|d�|dtd�}�|d�|dtd�||}|t�|||f�|ggd
dd�g7}q�g}tt�j��D]�}��||�}��|�}�|ddt|�t|�d|td�}|D]#}|t�|||f|||f�|ggdd�g7}|d|7}�qt|D]3}|t�|||fgd
dd�t||f|ddd
dd�tt|�||fdd
dd�g7}||7}�q��qMt|�t|�t|�}|�d�|S)a�
        Return a graphics object in which ``self`` is plotted.

        If ``vord`` is ``None``, the method uses the default vertex order.

        If ``vord`` is ``[]``, the parameter ``vord`` is used to give back the vertex order.

        EXAMPLES::

            sage: from admcycles import *
            sage: G = StableGraph([1,2],[[1,2],[3,4,5,6]],[(1,3),(2,4)])
            sage: G.plot()
            Graphics object consisting of 12 graphics primitives

        TESTS::

            sage: from admcycles import *
            sage: G = StableGraph([1,2],[[1,2],[3,4,5,6]],[(1,3),(2,4)])
            sage: vertex_order = []
            sage: P =  G.plot(vord=vertex_order)
            sage: vertex_order
            [0, 1]
        rg�������?gffffff�?g�������?N)rrrr,cs0i|]}t|dt�j��D]}||fd�qqS)rr)r�r7rr�)�
reord_selfr)r*r��	s0z$StableGraph.plot.<locals>.<dictcomp>c
s�g|]Z}t�|d��|d�g�|d��|d�g�|d��|d�g�|d��|d�ggdddddd�tdt�j|��|ddd	d
��qS)rr�whiteT�blackr,)�color�fill�	edgecolor�	thickness�zorderzg=�r���fontsizer�r�)rrr[rr��r��vpos�vsizer)r*r3�	s|�
�"�z$StableGraph.plot.<locals>.<listcomp>r�)r�r�)r�Tr�)r��	facecolorr�r��
r�r�F)r5r�r7rrYr6r{rrrrs�abs�minr0rrrrrr[r��axes)r?r5r�ZeheightZmark_dxZmark_dyZmark_radZv_dxminZed_dyZdefault_vordZdefault_vposrGZnedZdefault_eheightrHZver1Zver2�vertex_graphZ
edge_graphrQr�Z
marking_graphrEZse_listZm_listZv_x0rFrXr)r�r*�plotZ	s�
*�����
�
0 �
�

 ((�
0�
�*���
zStableGraph.plotc
s�ddlm}|��}|����fdd�tt|�|j�D�}�fdd�tt|�|j�D�}gd�}gd�}i�|�}d}t|�D]�}	t|�}
||	D](}t|�}|t	|
d��|<|
d|7<|
dd	d
t	|�d7<qI||	D]}t|�}|
d|7<|
dd
d
t	|�d7<qv|
dt|j
|	�7<tdd�|
D��}
dD]}|
|d|
t	|
|�7<q�dD]}|
|d
|
t	|
|�7<q�td�D]}|
|||7<q�||
d7}|||
�7}q?|��}�fdd�t|�D�}t
|���D]M\}\}}�|}�|}||k�r ||}}t|d|�D]	}d
|||<�q'd|||<d|||<t|d|�D]}d|||<d|||<�qD�qdd�|D�}||t|��S)u%
        Return unicode art for the stable graph ``self``.

        EXAMPLES::

            sage: from admcycles import *
            sage: A = StableGraph([1, 2],[[1, 2, 3], [4]],[(3, 4)])
            sage: unicode_art(A)
             ╭────╮
             3    4
            ╭┴─╮ ╭┴╮
            │1 │ │2│
            ╰┬┬╯ ╰─╯
             12

            sage: A = StableGraph([333, 4444],[[1, 2, 3], [4]],[(3, 4)])
            sage: unicode_art(A)
             ╭─────╮
             3     4
            ╭┴──╮ ╭┴───╮
            │333│ │4444│
            ╰┬┬─╯ ╰────╯
             12

            sage: B = StableGraph([3,5], [[1,3,5],[2]], [(1,2),(3,5)])
            sage: unicode_art(B)
             ╭─────╮
             │╭╮   │
             135   2
            ╭┴┴┴╮ ╭┴╮
            │3  │ │5│
            ╰───╯ ╰─╯

            sage: C = StableGraph([3,5], [[3,1,5],[2,4]], [(1,2),(3,5)])
            sage: unicode_art(C)
              ╭────╮
             ╭─╮   │
             315   2
            ╭┴┴┴╮ ╭┴╮
            │3  │ │5│
            ╰───╯ ╰┬╯
                   4
        r)�
UnicodeArtc�$i|]\}}|�fdd�|D��qS)csg|]}|�vr|�qSr)r)r1��all_half_edgesr)r*r3�	rV�8StableGraph._unicode_art_.<locals>.<dictcomp>.<listcomp>r)�r'rGZlegs_ir�r)r*r��	��z-StableGraph._unicode_art_.<locals>.<dictcomp>cr�)cr r)r)r1r�r)r*r3�	rVr�r)r�r�r)r*r��	r�)rZ�╭�│u╰rZ)�  u╮ u│ u╯ r�ru┴u─�r�u┬r,csrNrOr��r'rr)r)r*r+
rRz,StableGraph._unicode_art_.<locals>.<genexpr>)rr,r�rZ)rr��cs"g|]
}dgdt�����qS)rZr)r0r(rm)�	positionsr)r*r3
s�z-StableGraph._unicode_art_.<locals>.<listcomp>r�u╮r�cSsg|]}d�|��qS)r�)�joinr�r)r)r*r3
r�)�sage.typeset.unicode_artr�r_rryr�rr5�strr7rr0r^r:rB)r?r��NZ
half_edgesZ
open_edgesZleft_boxZ	right_box�boxes�total_widthrsr�rErW�lengthrGr^�	matchingsr�r��xa�xbr2r))r�r�r*�
_unicode_art_�	sl,
�
�""""
�

�zStableGraph._unicode_art_)FT)T�F)FF)NTrO)NF)NTFFT)NTFr.)NNN)J�__name__�
__module__�__qualname__�__doc__�	__slots__rIrLrMrTrYr\rbrdrerfrhrir�r�r�r�r�r�r�r�r�r�r�r(r�r@rBr_rArr^r�r�r�r�r�r�rsr{rrrr	r
rrrr+rrr0r3r6r>r@rFr^rdrZto_tautclassrhrkruryr{r~r�r�r�r)r)r)r*r s�7
5
);'


7
*
+9-/0







uI
!
ZP
/
%
`

?-
NercGsttj�dd�|D���S)Ncss�|]}|��VqdSrO)r�)r'�dctr)r)r*r+'
rRzdicunion.<locals>.<genexpr>)r|�	itertools�chain�
from_iterable)�dictsr)r)r*�dicunion&
sr�Fc
s"|��|��kr
gSg}i}dd�|��D��|��D]6}|�|�}|�|�}|j||j|ksIt|j|�t|j|�ksI||vrM|||krMgS|||<qi�i�tt|j��D]%}||vr�|j|t|j|�f���vr}��|g7<q]|g��<q]tt|j��D]'}||��vr�|j|t|j|�f���vr���|g7<q�|g��<q�t��t��kr�gSg}	�D]3�t���t���kr�gSt	t����}
t
tt������|	����fdd�|
D�g7}	q�tj|	�D�]}d}t
|g|�R��g}
tt|j��D]k}|�||��|��|�|��t��t��k�r.d}nMg}t	t���}
t
tt����}t��ddgg}|
D]*�	tj|�D]!�t
����	fd	d�|D�����	fd
d�|D��}||g7}�qP�qI|
|g7}
�q|�rq�tt|j��D]j}|�r�ndt|dt|j��D]W}|�||��|��|�|��t��t��k�r�d}n9g}t	t���}
t
tt����}|
D]�	t
���	fdd�|D����	fdd�|D��}||g7}�q�|
|g7}
�q��q�|�r�q�|��fd
d�tj|
�D�7}|�r|�r|Sq�|S)NcSr,r)r)r1r)r)r*r�1
r-zGraphIsom.<locals>.<dictcomp>cs$g|]�����fdd��D��qS)cs*i|]}��|���|d�qSr.r)r�)�gd�gdG�gdHr�r)r*r�\
r\z(GraphIsom.<locals>.<listcomp>.<dictcomp>r)rX)r�r�r��ind)r�r*r3\
r�zGraphIsom.<locals>.<listcomp>FTrrcs.i|]}�|d��|d�|�qSrr)r���EdG�EdH�or�r)r*r�{
s&�cs2i|]}�|d��|dd�|�qSr.r)r�r�r)r*r�|
s2cs*i|]}�|d��|dd�qSrr)r��r�r�r�r)r*r��
s"�cs*i|]}�|d��|dd�qSr.r)r�r�r)r*r��
r\cs g|]}�t�g|�R�g�qSr))r�)r'ZLegDics)�IsoL�IsoV2r)r*r3�
r#)r�r{rsrr7rr�r(r�rr5r��productr�r)rXr�rDZIsomlistZIsoVr2ZvGZvHrEZVertIm�P�VIZcontinueloopZLegImZLegImvv�ranZchoic�dirWZLegImvwr))
r�r�r�r�r�r�r�r�r�r�r*�	GraphIsom*
s�

D

�
� �������r�r�)/r�r�rrr�sage.misc.misc_cr�sage.structure.sage_objectr�sage.combinat.permutationr�sage.combinat.subsetr�sage.rings.integer_ringr�sage.modules.free_moduler	�sage.functions.otherr
�sage.graphs.graphr�sage.rings.allr�sage.arith.allr
�$sage.groups.perm_gps.permgroup_namedr�sage.plot.polygonr�sage.plot.textr�sage.plot.bezier_pathr�sage.plot.liner�sage.plot.circler�
supersededrr?rrrrrrr�r�r)r)r)r*�<module>sX