
    h                     b    S r S/rSSKrSSKJrJr  SSKJrJr  S r	S r
S rS	 r " S
 S5      rg)a  
ISMAGS Algorithm
================

Provides a Python implementation of the ISMAGS algorithm. [1]_

It is capable of finding (subgraph) isomorphisms between two graphs, taking the
symmetry of the subgraph into account. In most cases the VF2 algorithm is
faster (at least on small graphs) than this implementation, but in some cases
there is an exponential number of isomorphisms that are symmetrically
equivalent. In that case, the ISMAGS algorithm will provide only one solution
per symmetry group.

>>> petersen = nx.petersen_graph()
>>> ismags = nx.isomorphism.ISMAGS(petersen, petersen)
>>> isomorphisms = list(ismags.isomorphisms_iter(symmetry=False))
>>> len(isomorphisms)
120
>>> isomorphisms = list(ismags.isomorphisms_iter(symmetry=True))
>>> answer = [{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}]
>>> answer == isomorphisms
True

In addition, this implementation also provides an interface to find the
largest common induced subgraph [2]_ between any two graphs, again taking
symmetry into account. Given `graph` and `subgraph` the algorithm will remove
nodes from the `subgraph` until `subgraph` is isomorphic to a subgraph of
`graph`. Since only the symmetry of `subgraph` is taken into account it is
worth thinking about how you provide your graphs:

>>> graph1 = nx.path_graph(4)
>>> graph2 = nx.star_graph(3)
>>> ismags = nx.isomorphism.ISMAGS(graph1, graph2)
>>> ismags.is_isomorphic()
False
>>> largest_common_subgraph = list(ismags.largest_common_subgraph())
>>> answer = [{1: 0, 0: 1, 2: 2}, {2: 0, 1: 1, 3: 2}]
>>> answer == largest_common_subgraph
True
>>> ismags2 = nx.isomorphism.ISMAGS(graph2, graph1)
>>> largest_common_subgraph = list(ismags2.largest_common_subgraph())
>>> answer = [
...     {1: 0, 0: 1, 2: 2},
...     {1: 0, 0: 1, 3: 2},
...     {2: 0, 0: 1, 1: 2},
...     {2: 0, 0: 1, 3: 2},
...     {3: 0, 0: 1, 1: 2},
...     {3: 0, 0: 1, 2: 2},
... ]
>>> answer == largest_common_subgraph
True

However, when not taking symmetry into account, it doesn't matter:

>>> largest_common_subgraph = list(ismags.largest_common_subgraph(symmetry=False))
>>> answer = [
...     {1: 0, 0: 1, 2: 2},
...     {1: 0, 2: 1, 0: 2},
...     {2: 0, 1: 1, 3: 2},
...     {2: 0, 3: 1, 1: 2},
...     {1: 0, 0: 1, 2: 3},
...     {1: 0, 2: 1, 0: 3},
...     {2: 0, 1: 1, 3: 3},
...     {2: 0, 3: 1, 1: 3},
...     {1: 0, 0: 2, 2: 3},
...     {1: 0, 2: 2, 0: 3},
...     {2: 0, 1: 2, 3: 3},
...     {2: 0, 3: 2, 1: 3},
... ]
>>> answer == largest_common_subgraph
True
>>> largest_common_subgraph = list(ismags2.largest_common_subgraph(symmetry=False))
>>> answer = [
...     {1: 0, 0: 1, 2: 2},
...     {1: 0, 0: 1, 3: 2},
...     {2: 0, 0: 1, 1: 2},
...     {2: 0, 0: 1, 3: 2},
...     {3: 0, 0: 1, 1: 2},
...     {3: 0, 0: 1, 2: 2},
...     {1: 1, 0: 2, 2: 3},
...     {1: 1, 0: 2, 3: 3},
...     {2: 1, 0: 2, 1: 3},
...     {2: 1, 0: 2, 3: 3},
...     {3: 1, 0: 2, 1: 3},
...     {3: 1, 0: 2, 2: 3},
... ]
>>> answer == largest_common_subgraph
True

Notes
-----
- The current implementation works for undirected graphs only. The algorithm
  in general should work for directed graphs as well though.
- Node keys for both provided graphs need to be fully orderable as well as
  hashable.
- Node and edge equality is assumed to be transitive: if A is equal to B, and
  B is equal to C, then A is equal to C.

References
----------
.. [1] M. Houbraken, S. Demeyer, T. Michoel, P. Audenaert, D. Colle,
   M. Pickavet, "The Index-Based Subgraph Matching Algorithm with General
   Symmetries (ISMAGS): Exploiting Symmetry for Faster Subgraph
   Enumeration", PLoS One 9(5): e97896, 2014.
   https://doi.org/10.1371/journal.pone.0097896
.. [2] https://en.wikipedia.org/wiki/Maximum_common_induced_subgraph
ISMAGS    N)Counterdefaultdict)reducewrapsc                    ^  U R                   n[        U5      S:  a  Sn[        U5      Se [	        U 5      n[        US5      m[        U4S jU 5       5      $ ! [         a     N8f = f)a6  
Returns ``True`` if and only if all elements in `iterable` are equal; and
``False`` otherwise.

Parameters
----------
iterable: collections.abc.Iterable
    The container whose elements will be checked.

Returns
-------
bool
    ``True`` iff all elements in `iterable` compare equal, ``False``
    otherwise.
   z7The function does not works on multidimensional arrays.Nc              3   ,   >#    U  H	  oT:H  v   M     g 7fN ).0itemfirsts     X/var/www/html/env/lib/python3.13/site-packages/networkx/algorithms/isomorphism/ismags.py	<genexpr> are_all_equal.<locals>.<genexpr>   s     2u}s   )shapelenNotImplementedErrorAttributeErroriternextall)iterabler   messageiteratorr   s       @r   are_all_equalr   t   sq     9 u:>OG%g.D8  H~H4 E2222  s   A 
A&%A&c                     / nU  HS  nU H8  n[        [        U5      5      nU" X55      (       d  M&  UR                  U5          M?     UR                  U15        MU     U$ )ab  
Partitions items into sets based on the outcome of ``test(item1, item2)``.
Pairs of items for which `test` returns `True` end up in the same set.

Parameters
----------
items : collections.abc.Iterable[collections.abc.Hashable]
    Items to partition
test : collections.abc.Callable[collections.abc.Hashable, collections.abc.Hashable]
    A function that will be called with 2 arguments, taken from items.
    Should return `True` if those 2 items need to end up in the same
    partition, and `False` otherwise.

Returns
-------
list[set]
    A list of sets, with each set containing part of the items in `items`,
    such that ``all(test(*pair) for pair in  itertools.combinations(set, 2))
    == True``

Notes
-----
The function `test` is assumed to be transitive: if ``test(a, b)`` and
``test(b, c)`` return ``True``, then ``test(a, c)`` must also be ``True``.
)r   r   addappend)itemstest
partitionsr   	partitionp_items         r   make_partitionsr&      s_    4 J#I$y/*FD!!d#	 $ tf%      c                 L    0 n[        U 5       H  u  p#U H  nX!U'   M	     M     U$ )a  
Creates a dictionary that maps each item in each partition to the index of
the partition to which it belongs.

Parameters
----------
partitions: collections.abc.Sequence[collections.abc.Iterable]
    As returned by :func:`make_partitions`.

Returns
-------
dict
)	enumerate)r#   colorscolorkeyskeys        r   partition_to_colorr.      s2     F ,C3K  - Mr'   c                     [        U 5      n U R                  5       n[        [        R                  U [        U5      5      n[        U5      " U5      $ )aL  
Given an collection of sets, returns the intersection of those sets.

Parameters
----------
collection_of_sets: collections.abc.Collection[set]
    A collection of sets.

Returns
-------
set
    An intersection of all sets in `collection_of_sets`. Will have the same
    type as the item initially taken from `collection_of_sets`.
)listpopr   setintersectiontype)collection_of_setsr   outs      r   	intersectr7      sF     01""$E
!!#5s5z
BC;sr'   c                      \ rS rSrSrS(S jr\S 5       r\S 5       r\S 5       r	\S 5       r
\S	 5       r\S
 5       r\S 5       r\S 5       r\S 5       r\S 5       r\S 5       r\S 5       rS)S jr\S 5       rS rS)S jrS rS*S jrS*S jrS)S jrS)S jrS r\S 5       r\S 5       r\S 5       r \!S*S j5       r"S r#S+S  jr$S,S! jr%\S" 5       r&\S# 5       r'\S$ 5       r(S% r)  S+S& jr*S'r+g)-r      a  
Implements the ISMAGS subgraph matching algorithm. [1]_ ISMAGS stands for
"Index-based Subgraph Matching Algorithm with General Symmetries". As the
name implies, it is symmetry aware and will only generate non-symmetric
isomorphisms.

Notes
-----
The implementation imposes additional conditions compared to the VF2
algorithm on the graphs provided and the comparison functions
(:attr:`node_equality` and :attr:`edge_equality`):

 - Node keys in both graphs must be orderable as well as hashable.
 - Equality must be transitive: if A is equal to B, and B is equal to C,
   then A must be equal to C.

Attributes
----------
graph: networkx.Graph
subgraph: networkx.Graph
node_equality: collections.abc.Callable
    The function called to see if two nodes should be considered equal.
    It's signature looks like this:
    ``f(graph1: networkx.Graph, node1, graph2: networkx.Graph, node2) -> bool``.
    `node1` is a node in `graph1`, and `node2` a node in `graph2`.
    Constructed from the argument `node_match`.
edge_equality: collections.abc.Callable
    The function called to see if two edges should be considered equal.
    It's signature looks like this:
    ``f(graph1: networkx.Graph, edge1, graph2: networkx.Graph, edge2) -> bool``.
    `edge1` is an edge in `graph1`, and `edge2` an edge in `graph2`.
    Constructed from the argument `edge_match`.

References
----------
.. [1] M. Houbraken, S. Demeyer, T. Michoel, P. Audenaert, D. Colle,
   M. Pickavet, "The Index-Based Subgraph Matching Algorithm with General
   Symmetries (ISMAGS): Exploiting Symmetry for Faster Subgraph
   Enumeration", PLoS One 9(5): e97896, 2014.
   https://doi.org/10.1371/journal.pone.0097896
Nc                    Xl         X l        XPl        SU l        SU l        SU l        SU l        SU l        SU l        SU l	        SU l
        SU l        SU l        Uck  U R                  S 5      U l        [        U R                  R                   5      /U l        [        U R                   R                   5      /U l        SS0U l        OU R                  U5      U l        Uck  U R#                  S 5      U l        [        U R                  R&                  5      /U l        [        U R                   R&                  5      /U l        SS0U l        gU R#                  U5      U l        g)an  
Parameters
----------
graph: networkx.Graph
subgraph: networkx.Graph
node_match: collections.abc.Callable or None
    Function used to determine whether two nodes are equivalent. Its
    signature should look like ``f(n1: dict, n2: dict) -> bool``, with
    `n1` and `n2` node property dicts. See also
    :func:`~networkx.algorithms.isomorphism.categorical_node_match` and
    friends.
    If `None`, all nodes are considered equal.
edge_match: collections.abc.Callable or None
    Function used to determine whether two edges are equivalent. Its
    signature should look like ``f(e1: dict, e2: dict) -> bool``, with
    `e1` and `e2` edge property dicts. See also
    :func:`~networkx.algorithms.isomorphism.categorical_edge_match` and
    friends.
    If `None`, all edges are considered equal.
cache: collections.abc.Mapping
    A cache used for caching graph symmetries.
Nc                     gNTr   )n1n2s     r   <lambda>!ISMAGS.__init__.<locals>.<lambda>@      tr'   r   c                     gr<   r   )e1e2s     r   r?   r@   G  rA   r'   )graphsubgraph_symmetry_cache_sgn_partitions__sge_partitions__sgn_colors__sge_colors__gn_partitions__ge_partitions__gn_colors__ge_colors__node_compat__edge_compat__node_match_makernode_equalityr2   nodes_edge_match_makeredge_equalityedges)selfrE   rF   
node_match
edge_matchcaches         r   __init__ISMAGS.__init__  s@   2 
 $ !% $  ##!!!%!7!78K!LD%()<)<%=$>D!$'

(8(8$9#:D "#QD!%!7!7
!CD!%!7!78K!LD%()<)<%=$>D!$'

(8(8$9#:D "#QD!%!7!7
!CDr'   c                    ^  T R                   c+  U 4S jn[        T R                  R                  U5      T l         T R                   $ )Nc                 T   > TR                  TR                  U TR                  U5      $ r   )rS   rF   node1node2rX   s     r   	nodematch)ISMAGS._sgn_partitions.<locals>.nodematchR  "    ))$--uUUr'   )rH   r&   rF   rT   rX   rc   s   ` r   _sgn_partitionsISMAGS._sgn_partitionsN  <      (V %4DMM4G4G$SD!$$$r'   c                    ^  T R                   c+  U 4S jn[        T R                  R                  U5      T l         T R                   $ )Nc                 T   > TR                  TR                  U TR                  U5      $ r   )rV   rF   edge1edge2rX   s     r   	edgematch)ISMAGS._sge_partitions.<locals>.edgematch\  re   r'   )rI   r&   rF   rW   rX   ro   s   ` r   _sge_partitionsISMAGS._sge_partitionsX  ri   r'   c                    ^  T R                   c+  U 4S jn[        T R                  R                  U5      T l         T R                   $ )Nc                 T   > TR                  TR                  U TR                  U5      $ r   )rS   rE   r`   s     r   rc   (ISMAGS._gn_partitions.<locals>.nodematchf  "    ))$**eTZZOOr'   )rL   r&   rE   rT   rf   s   ` r   _gn_partitionsISMAGS._gn_partitionsb  <    'P $34::3C3CY#OD ###r'   c                    ^  T R                   c+  U 4S jn[        T R                  R                  U5      T l         T R                   $ )Nc                 T   > TR                  TR                  U TR                  U5      $ r   )rV   rE   rl   s     r   ro   (ISMAGS._ge_partitions.<locals>.edgematchp  rw   r'   )rM   r&   rE   rW   rq   s   ` r   _ge_partitionsISMAGS._ge_partitionsl  rz   r'   c                 h    U R                   c  [        U R                  5      U l         U R                   $ r   )rJ   r.   rg   rX   s    r   _sgn_colorsISMAGS._sgn_colorsv  -    $ 243G3G HD   r'   c                 h    U R                   c  [        U R                  5      U l         U R                   $ r   )rK   r.   rr   r   s    r   _sge_colorsISMAGS._sge_colors|  r   r'   c                 h    U R                   c  [        U R                  5      U l         U R                   $ r   )rN   r.   rx   r   s    r   
_gn_colorsISMAGS._gn_colors  -    #1$2E2EFDr'   c                 h    U R                   c  [        U R                  5      U l         U R                   $ r   )rO   r.   r~   r   s    r   
_ge_colorsISMAGS._ge_colors  r   r'   c                    U R                   b  U R                   $ 0 U l         [        R                  " [        [	        U R
                  5      5      [        [	        U R                  5      5      5       H  u  p[        [        U R
                  U   5      5      n[        [        U R                  U   5      5      nU R                  U R                  X0R                  U5      (       d  Mu  X R                   U'   M     U R                   $ r   )rP   	itertoolsproductranger   rg   rx   r   r   rS   rF   rE   )rX   sgn_part_colorgn_part_colorsgngns        r   _node_compatibilityISMAGS._node_compatibility      )%%%-6->->#d**+,eC8K8K4L.M.
)N tD00@ABCd4..}=>?B!!$--jj"EE5B"">2.
 !!!r'   c                    U R                   b  U R                   $ 0 U l         [        R                  " [        [	        U R
                  5      5      [        [	        U R                  5      5      5       H  u  p[        [        U R
                  U   5      5      n[        [        U R                  U   5      5      nU R                  U R                  X0R                  U5      (       d  Mu  X R                   U'   M     U R                   $ r   )rQ   r   r   r   r   rr   r~   r   r   rV   rF   rE   )rX   sge_part_colorge_part_colorsgeges        r   _edge_compatibilityISMAGS._edge_compatibility  r   r'   c                 0   ^  [        T 5      U 4S j5       nU$ )Nc                 J   > T" U R                   U   UR                   U   5      $ r   )rT   )graph1ra   graph2rb   cmps       r   comparer*ISMAGS._node_match_maker.<locals>.comparer  "    v||E*FLL,?@@r'   r   r   r   s   ` r   rR   ISMAGS._node_match_maker  "    	s	A 
	A r'   c                 0   ^  [        T 5      U 4S j5       nU$ )Nc                 J   > T" U R                   U   UR                   U   5      $ r   )rW   )r   rm   r   rn   r   s       r   r   *ISMAGS._edge_match_maker.<locals>.comparer  r   r'   r   r   s   ` r   rU   ISMAGS._edge_match_maker  r   r'   c              #     ^	#    U R                   (       d  0 v   gU R                  (       d  g[        U R                  5      [        U R                   5      :  a  gU(       aE  U R                  U R                   U R                  U R
                  5      u  p#U R                  U5      nO/ nU R                  5       m	U R                  5       nU R                    H%  nXV   nU(       d  M  T	U   [        U5      1-  T	U'   M'     [        T	R                  5       5      (       a<  [        T	U	4S jS9n[        T	U   5      4T	U'   U R                  UT	U5       Sh  vN   gg N7f)ai  Find all subgraph isomorphisms between subgraph and graph

Finds isomorphisms where :attr:`subgraph` <= :attr:`graph`.

Parameters
----------
symmetry: bool
    Whether symmetry should be taken into account. If False, found
    isomorphisms may be symmetrically equivalent.

Yields
------
dict
    The found isomorphism mappings of {graph_node: subgraph_node}.
Nc                 &   > [        TU    [        S9$ Nr-   minr   n
candidatess    r   r?   *ISMAGS.find_isomorphisms.<locals>.<lambda>  s    c*Q-S6Qr'   r   )rF   rE   r   analyze_symmetryrg   r   _make_constraints_find_nodecolor_candidates_get_lookahead_candidates	frozensetanyvaluesr   r7   
_map_nodes)
rX   symmetry_cosetsconstraintsla_candidatesr   extra_candidates	start_sgnr   s
            @r   find_isomorphismsISMAGS.find_isomorphisms  s-    $ }}H_s4==11--t33T5E5EIA 008KK446
668==C,1",S/Y?O5P4Q"Q
3 !
 z  "##J,QRI%.z)/D%E$GJy!y*kJJJ Ks   CE#A-EEEc                     [        5       nX   nU H)  nX&   nX4U;   a  X1U4   nOX6U4   nXHU4==   S-  ss'   M+     U$ )zk
For `node` in `graph`, count the number of edges of a specific color
it has to nodes of a specific color.
r	   )r   )	rE   node
node_color
edge_colorcounts	neighborsneighborn_colore_colors	            r   _find_neighbor_color_count!ISMAGS._find_neighbor_color_count  sa     K	!H *G:-$8^4$t^4G#$)$ " r'   c                   ^^ 0 nU R                    H6  nU R                  U R                   X R                  U R                  5      X'   M8     [	        [
        5      nU R                   H  nU R                  U R                  X@R                  U R                  5      n[        5       mUR                  5        H-  u  u  pgn U R                  U   n	U R                  U   n
UTX4'   M/     UR                  5        H6  u  nm[        UU4S jT 5       5      (       d  M#  X4   R                  U5        M8     M     U$ ! [         a     M  f = f)z
Returns a mapping of {subgraph node: collection of graph nodes} for
which the graph nodes are feasible candidates for the subgraph node, as
determined by looking ahead one edge.
c              3   :   >#    U  H  nTU   TU   :*  v   M     g 7fr   r   )r   xg_countnew_sg_counts     r   r   3ISMAGS._get_lookahead_candidates.<locals>.<genexpr>  s     Kl|A'!*4ls   )rE   r   r   r   r   r2   rF   r   r   r   r!   r   r   KeyErrorr   r   )rX   g_countsr   r   r   sg_count	sge_color	sgn_colorcountge_colorgn_colorr   r   s              @@r   r    ISMAGS._get_lookahead_candidates  s)    **B::

BHL  !%
==C66s$4$4d6F6FH #9L191A-&=#77	BH#77	BH 8=L!34 2B  (~~/GKlKKKO''+  0 !$    s   =D55
EEc              #     #    U R                   (       d  0 v   gU R                  (       d  gU(       aE  U R                  U R                   U R                  U R                  5      u  p#U R                  U5      nO/ nU R                  5       n[        UR                  5       5      (       a  U R                  XT5       Sh  vN   gg N7f)aU  
Find the largest common induced subgraphs between :attr:`subgraph` and
:attr:`graph`.

Parameters
----------
symmetry: bool
    Whether symmetry should be taken into account. If False, found
    largest common subgraphs may be symmetrically equivalent.

Yields
------
dict
    The found isomorphism mappings of {graph_node: subgraph_node}.
N)
rF   rE   r   rg   r   r   r   r   r   _largest_common_subgraph)rX   r   r   r   r   r   s         r   largest_common_subgraphISMAGS.largest_common_subgraph  s     $ }}H--t33T5E5EIA 008KK446
z  "##44ZMMM Ns   B9C;C<Cc           
         U R                   b  [        [        UR                  5      [        UR                  5      [        [        [        U5      5      [        UR                  5       5      45      nX@R                   ;   a  U R                   U   $ [        U R                  XU5      5      n[        U5      S:X  d   eUS   nU R                  XX#5      u  pVU R                   b  XV4U R                   W'   XV4$ )a  
Find a minimal set of permutations and corresponding co-sets that
describe the symmetry of `graph`, given the node and edge equalities
given by `node_partitions` and `edge_colors`, respectively.

Parameters
----------
graph : networkx.Graph
    The graph whose symmetry should be analyzed.
node_partitions : list of sets
    A list of sets containing node keys. Node keys in the same set
    are considered equivalent. Every node key in `graph` should be in
    exactly one of the sets. If all nodes are equivalent, this should
    be ``[set(graph.nodes)]``.
edge_colors : dict mapping edges to their colors
    A dict mapping every edge in `graph` to its corresponding color.
    Edges with the same color are considered equivalent. If all edges
    are equivalent, this should be ``{e: 0 for e in graph.edges}``.


Returns
-------
set[frozenset]
    The found permutations. This is a set of frozensets of pairs of node
    keys which can be exchanged without changing :attr:`subgraph`.
dict[collections.abc.Hashable, set[collections.abc.Hashable]]
    The found co-sets. The co-sets is a dictionary of
    ``{node key: set of node keys}``.
    Every key-value pair describes which ``values`` can be interchanged
    without changing nodes less than ``key``.
r	   r   )rG   hashtuplerT   rW   mapr!   r0   _refine_node_partitionsr    _process_ordered_pair_partitions)rX   rE   node_partitionsedge_colorsr-   permutationsr   s          r   r   ISMAGS.analyze_symmetryB  s    @ +%++&%++&#e_56+++-.	C ***++C00((M
 ?#q((()!,#DDO 
 +(4(<D  %##r'   c                     [        U R                  5      [        U R                  5      :H  =(       a    U R                  U5      $ )zl
Returns True if :attr:`graph` is isomorphic to :attr:`subgraph` and
False otherwise.

Returns
-------
bool
)r   rF   rE   subgraph_is_isomorphicrX   r   s     r   is_isomorphicISMAGS.is_isomorphicy  s7     4==!S_4 
9T9T:
 	
r'   c                 <    [        U R                  US9S5      nUSL$ )zz
Returns True if a subgraph of :attr:`graph` is isomorphic to
:attr:`subgraph` and False otherwise.

Returns
-------
bool
r   N)r   subgraph_isomorphisms_iter)rX   r   isoms      r   r   ISMAGS.subgraph_is_isomorphic  s)     D33X3FM4r'   c              #      #    [        U R                  5      [        U R                  5      :X  a  U R                  US9 Sh  vN   gg N7f)zq
Does the same as :meth:`find_isomorphisms` if :attr:`graph` and
:attr:`subgraph` have the same number of nodes.
r   N)r   rE   rF   r   r   s     r   isomorphisms_iterISMAGS.isomorphisms_iter  s?     
 tzz?c$--00666III 1Is   ?A
AA
c                 $    U R                  U5      $ )z/Alternative name for :meth:`find_isomorphisms`.)r   r   s     r   r   !ISMAGS.subgraph_isomorphisms_iter  s    %%h//r'   c                    [        [        5      nU R                  R                   Hv  nU R                  U   nX0R
                  ;   a:  U R
                  U   nX   R                  [        U R                  U   5      5        M[  X   R                  [        5       5        Mx     [        U5      nUR                  5        H  u  p%[        U5      X'   M     U$ )zH
Per node in subgraph find all nodes in graph that have the same color.
)r   r2   rF   rT   r   r   r   r   rx   dictr!   )rX   r   r   r   r   optionss         r   r   !ISMAGS._find_nodecolor_candidates  s     !%
==&&C((-I44433I>##Id.A.A(.K$LM##IK0 ' *%
&,,.LC'0JO /r'   c                     / nU R                  5        H'  u  p#U H  nX$:w  d  M
  UR                  X$45        M     M)     U$ )z
Turn cosets into constraints.
)r!   r    )r   r   node_inode_tsnode_ts        r   r   ISMAGS._make_constraints  sD    
 %||~OF!#&&'78 "  .
 r'   c                 (   [        S 5      nU R                   H?  u  pEXE4U;   a  X$U4   nOX%U4   nX4   XaU   4==   S-  ss'   X5   XaU   4==   S-  ss'   MA     0 nU R                   H$  nX   [        X8   R	                  5       5      4Xx'   M&     U$ )z
For every node in graph, come up with a color that combines 1) the
color of the node, and 2) the number of edges of a color to each type
of node.
c                       [        [        5      $ r   )r   intr   r'   r   r?   .ISMAGS._find_node_edge_color.<locals>.<lambda>  s	    [%5r'   r	   )r   rW   rT   r2   r!   )	rE   node_colorsr   r   ra   rb   ecolornode_edge_colorsr   s	            r   _find_node_edge_colorISMAGS._find_node_edge_color  s     56!KKLE~,$E\2$E\2 M&e"445:5M&e"445:5 ( KKD%0%6FL<N<N<P8Q%Q"    r'   c              #      ^#    [        [        5      mU  H   nT[        U5         R                  U5        M"     [        R
                  " U4S j[        T5       5       6  Sh  vN   g N7f)am  
Get all permutations of items, but only permute items with the same
length.

>>> found = list(ISMAGS._get_permutations_by_length([[1], [2], [3, 4], [4, 5]]))
>>> answer = [
...     (([1], [2]), ([3, 4], [4, 5])),
...     (([1], [2]), ([4, 5], [3, 4])),
...     (([2], [1]), ([3, 4], [4, 5])),
...     (([2], [1]), ([4, 5], [3, 4])),
... ]
>>> found == answer
True
c              3   V   >#    U  H  n[         R                  " TU   5      v   M      g 7fr   )r   r   )r   lby_lens     r   r   5ISMAGS._get_permutations_by_length.<locals>.<genexpr>  s#     HAi$$VAY//s   &)N)r   r0   r   r    r   r   sorted)r!   r   r  s     @r   _get_permutations_by_length"ISMAGS._get_permutations_by_length  sY       T"D3t9$$T*  $$HH
 	
 	
s   AA*"A(#A*c           
   #   J  ^#    U4S jn[        U5      n[        U5      nU R                  XU5      m[        U4S jU 5       5      (       a  Uv   g/ nU/nU GH  n	[	        U4S jU	 5       5      (       d  [        X5      n
U(       a  [        U
5      S:w  a  [        U
 Vs1 s H  n[        U5      iM     sn5      [        U
 Vs/ s H  n[        U5      PM     sn5      :w  aI  U R                  U
5      n/ nU H,  nU H#  nUR                  U[        US   5      -   5        M%     M.     UnM  U H   nUR                  [        U
[        S95        M"     M  U H  nUR                  U	5        M     GM     U H  nU R                  XX45       Sh  vN   M     gs  snf s  snf  N7f)z
Given a partition of nodes in graph, make the partitions smaller such
that all nodes in a partition have 1) the same color, and 2) the same
number of edges to specific other partitions.
c                    > TU    TU   :H  $ r   r   )ra   rb   r  s     r   equal_color3ISMAGS._refine_node_partitions.<locals>.equal_color  s    #E*.>u.EEEr'   c              3   N   >#    U  H  n[        U4S  jU 5       5      v   M     g7f)c              3   .   >#    U  H
  nTU   v   M     g 7fr   r   r   r   r  s     r   r   ;ISMAGS._refine_node_partitions.<locals>.<genexpr>.<genexpr>  s     GYT*40Y   N)r   )r   r$   r  s     r   r   1ISMAGS._refine_node_partitions.<locals>.<genexpr>  s&      
,	 GYGGG,s   "%Nc              3   .   >#    U  H
  nTU   v   M     g 7fr   r   r#  s     r   r   r&    s      NID!1$!7Ir%  r	   r   r   )r0   r.   r  r   r   r&   r   r  r    extendr  r   )clsrE   r   r   branchr  r  new_partitionsoutputr$   refinedrr   
new_outputn_ppermutationr  s                   @r   r   ISMAGS._refine_node_partitions  s    	F /(944UU 
,
 
 
 "! !(I  NI NNN))AG)W5WSVW56#w>Ww!s1vw>W:XX $'#B#B7#KL!#J%+7K&--cDQ4H.HI ,8  & (F%

6's#;<  & "CJJy) "/ )2 C225{SSS ' 6>W( Ts+   BF# F
5F#FB4F#F!F#c                     X4U R                   ;   a  U R                   X4   nOU R                   X!4   nX0R                  ;   a   U R                  U   nU R                  U   nU$ / nU$ )zu
Returns all edges in :attr:`graph` that have the same colour as the
edge between sgn1 and sgn2 in :attr:`subgraph`.
)r   r   r~   )rX   sgn1sgn2r   r   g_edgess         r   _edges_of_same_colorISMAGS._edges_of_same_color#  sw    
 <4+++((4I((4I000//	:H))(3G  Gr'   c           	   #     ^#    Uc  0 nOUR                  5       nUc  [        U R                  R                  5      n[	        X!   5      n[        U/5      X!'   U GH  nXtR                  5       ;   d  X;  a  M  XtU'   U[        UR                  5       5      :X  a)  UR                  5        VV	s0 s H  u  pX_M	     sn	nv   Mh  U[        UR                  5       5      -
  n
UR                  5       m[        U R                  U   5      n[        U R                  R                  5      [        U R                  U   5      -
  nU
 H  nX;  a  UnO7U R                  X5      nU VVs1 s H  nU  H  nUU;   d  M  UiM     M     nnnTU   R                  [        U5      /5      TU'   X4U;   a%  U R                   Vs1 s H  nUU:  d  M  UiM     nnO.X4U;   a%  U R                   Vs1 s H  nUU:  d  M  UiM     nnOM  TU   R                  [        U5      /5      TU'   M     [        U
U4S jS9nU R                  UTUUUS9 Sh  vN   GM     gs  sn	nf s  snnf s  snf s  snf  N 7f)z6
Find all subgraph isomorphisms honoring constraints.
Nc                 &   > [        TU    [        S9$ r   r   )r   new_candidatess    r   r?   #ISMAGS._map_nodes.<locals>.<lambda>u  s    c.:KQT6Ur'   r   )mappingto_be_mapped)copyr2   rF   rT   r7   r   r   r,   r!   rE   r7  unionr   r   )rX   r   r   r   r=  r>  sgn_candidatesr   kvleft_to_mapsgn_nbrsnot_gn_nbrsr5  gn2_optionsr6  er   gn2next_sgnr;  s                       @r   r   ISMAGS._map_nodes4  sO     ?GllnGt}}223L
 #:?3#^$45
 B^^%%)@ CLs7<<>22(/8qt88&W\\^)<<K'__.N4==-.Hdjj../#djjn2EEK#'"-K #77BG /6"Lgq!B!G1q1gK"L (6d';'A'A{+,(t$ ;+-26**"I*3b3*K"IK[K/26**"I*3b3*K"IK'5d';'A'A{+,(t$5 $@ ;,UVH) '   e ! 9 #M #J"IsV   B(I(+I9B'I( I
4
I
>9I(7
III(#
I!1I!7AI(I&!I(c              #   Z  ^#    Uc   [        U R                  R                  5      1n[        [	        [        U5      / 5      5      nSnU[        U R                  5      ::  aP  [        U[        S9 H>  n[        UU4S jS9nU R                  UTX&S9n [	        U5      n	U	v   U Sh  vN   SnM@     U(       d  US:X  a  g[        5       n
U H/  nU H&  nU R                  XU5      nU
R                  U5        M(     M1     U R                  TX*S9 Sh  vN   g Np! [         a     M  f = f N7f)z9
Find all largest common subgraphs honoring constraints.
NFr   c                 &   > [        TU    [        S9$ r   r   r   s    r   r?   1ISMAGS._largest_common_subgraph.<locals>.<lambda>  s    C
134Or'   )r>  Tr	   )r   rF   rT   r   r   r   rE   r  r   r   StopIterationr2   _remove_noder   r   )rX   r   r   r>  current_size	found_isorT   rJ  	isomorphsr   left_to_be_mappedr   	new_nodess    `           r   r   ISMAGS._largest_common_subgraph  s@     %dmm&9&9:;L 4\ 2B78	3tzz?*  &9u*OP OOj+ , 	%	?D J((( $I! :& ) E!E !--c+F	!%%i0  " 00 1 
 	
 	
3 )	 % :	
sC   BD+D	D+%D&A*D+D)D+
D&"D+%D&&D+c                 \     U H  u  p4X0:X  d  M  XA;   d  M  Un   O   OM  [        X1-
  5      $ )z
Returns a new set where node has been removed from nodes, subject to
symmetry constraints. We know, that for every constraint we have
those subgraph nodes are equal. So whenever we would remove the
lower part of a constraint, remove the higher instead.
)r   )r   rT   r   lowhighs        r   rP  ISMAGS._remove_node  s?     (	;4=D )
   ((r'   c                 .   [        5       n[        X5       H{  u  p4[        U5      S:w  d  [        U5      S:w  a  [        SU  SU 35      eX4:w  d  M;  UR	                  [        [        [        U5      5      [        [        U5      5      45      5        M}     U$ )z
Return the pairs of top/bottom partitions where the partitions are
different. Ensures that all partitions in both top and bottom
partitions have size 1.
r	   z/Not all nodes are coupled. This is impossible: z, )r2   zipr   
IndexErrorr   r   r   r   )top_partitionsbottom_partitionsr   topbots        r   _find_permutationsISMAGS._find_permutations  s     uN>HC3x1}CA $$2#326G5HJ  z  DcOT$s)_+M!NO ? r'   c                     U HT  nUu  p4S=pV[        U 5       H  u  pxUb  Ub    OX8;   a  UnXH;   d  M  UnM     XV:w  d  M=  X   R                  X   5        X	 MV     g)z
Update orbits based on permutations. Orbits is modified in place.
For every pair of items in permutations their respective orbits are
merged.
N)r)   update)	orbitsr   r1  r   rb   r   secondidxorbits	            r   _update_orbitsISMAGS._update_orbits  sp     (K%KD "!E'/
$);=E> F 0 $$V^4N (r'   c              #     #    X   nX#   n	XH;   a  XY;   d   eU V
s/ s H  oR                  5       PM     nn
U Vs/ s H  oR                  5       PM     nnU1X1-
  4nU1X1-
  4nX	 X	 XX3& XX3& U R                  XkU5      nU R                  XmUSS9n[        U5      n[        U5      S:X  d   eUS   nU H  n[        U5      U4v   M     gs  sn
f s  snf 7f)z
Generate new partitions from top and bottom_partitions where t_node is
coupled to b_node. pair_idx is the index of the partitions where t_ and
b_node can be found.
T)r*  r	   r   N)r?  r   r0   r   )rX   r^  r_  pair_idxt_nodeb_noderE   r   t_partitionb_partitionr`  new_top_partitionsra  new_bottom_partitionsnew_t_groupsnew_b_groupss                   r   _couple_nodesISMAGS._couple_nodes   s      %.'1$)>>>4BCNShhjNC7H I7H7H Ixx!77xx!77(!+0<8-3?h0!99{
 !% < <+d != !
 ""45%&!+++/2(C)*C// )' D Is   CCCCBCc           
        ^^ Uc  UR                    Vs/ s H  ow1PM     nnOUnUc  0 nOUR                  5       n[        S [        X#5       5       5      (       d   e[        S U 5       5      (       a2  U R	                  X#5      nU R                  XX5        U(       a  U/U4$ / U4$ / n[        U5       V	V
Vs1 s H#  u  pU
  H  n[        U
5      S:  d  M  Xy4iM     M%     nn
n	n[        U5      u  mnX<   n[        U5       H  m[        U5      S:X  a  M  TT:w  a  [        UU4S jU 5       5      (       a  M7  U R                  UUUTTUU5      nU H7  nUu  nnU R                  UUUUUU5      u  nnUU-  nUR                  U5        M9     M     [        X#5       VVVs1 s H+  u  nnU  H  n[        U5      S:X  d  M  UU:X  d  M  UiM      M-     nnnnUR                    Vs1 s H  nUT:  d  M  UiM     nnUU:*  =(       a    TU;  nU(       a$  U H  nTU;   d  M  UR                  5       UT'   M      X4$ s  snf s  snn
n	f s  snnnf s  snf )z
Processes ordered pair partitions as per the reference paper. Finds and
returns all permutations and cosets that leave the graph unchanged.
c              3   T   #    U  H  u  p[        U5      [        U5      :H  v   M      g 7fr   r   )r   t_pb_ps      r   r   :ISMAGS._process_ordered_pair_partitions.<locals>.<genexpr>B  s#      
1WXSCHC 1Ws   &(c              3   >   #    U  H  n[        U5      S :H  v   M     g7f)r	   Nrz  )r   r`  s     r   r   r}  G  s     7s3x1}s   r	   c              3   F   >#    U  H  nTU;   =(       a    TU;   v   M     g 7fr   r   )r   ri  r   rb   s     r   r   r}  ^  s$      %>DU0%5.0fs   !)rT   r?  r   r\  rb  rj  r)   r   r   r  r   rv  r   re  )rX   rE   r^  r_  r   rf  r   r   r   rh  rp  unmapped_nodesrm  rq  r#   opprr  rs  	new_perms
new_cosetsr`  bottomrB  mappedks
find_cosetri  rb   s          `                   @r   r   'ISMAGS._process_ordered_pair_partitions)  s    >).5fF5F
 F>F[[]F 
14^1W
 
 
 	
 

 777722>UL5$~v--6z! %.n$=
$= #;!# TK# $= 	 
 ^,h'1K(E;1$u} %>D% " "  ++!J "<?9"$9(,(M(M&))%	: 	)j) ") )J  #>E
EV3x1}  "%  E 	 
 1ADa16\8d&&8
5=#(::<F4L   ##i 64
X
 2s/   H97H>H> I I
I$
I2I)rQ   rO   rM   rN   rL   rP   rK   rI   rJ   rH   rG   rV   rE   rS   rF   )NNN)T)F)NNr   ),__name__
__module____qualname____firstlineno____doc__r\   propertyrg   rr   rx   r~   r   r   r   r   r   r   staticmethodrR   rU   r   r   r   r   r   r   r   r   r   r   r   r  r  classmethodr   r7  r   r   rP  rb  rj  rv  r   __static_attributes__r   r'   r   r   r      s   (T?DB % % % % $ $ $ $ ! !
 ! !
    
    
 " " " "    .`   @%N5$n
 J0" 
 
    0 
 
. 0T 0Td"HZA
F ) )   & # #,'0^ b$r'   )r  __all__r   collectionsr   r   	functoolsr   r   r   r&   r.   r7   r   r   r'   r   <module>r     s?   jX *  , #3<#L**i$ i$r'   