+
    �°³jÐ ã                   ó‚   € R t R.t^ RIt^ RIt^ RIHtHt ^ RItR t	RR lt
R tR tR t ! R	 R
4      t ! R R4      tR# )a)  
ISMAGS Algorithm
================

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

ISMAGS does a symmetry analysis to find the constraints on isomorphisms if
we preclude yielding isomorphisms that differ by a symmetry of the subgraph.
For example, if the subgraph contains a 4-cycle, every isomorphism would have a
symmetric version with the nodes rotated relative to the original isomorphism.
By encoding these symmetries as constraints we reduce the search space for
isomorphisms and we also simplify processing the resulting isomorphisms.

ISMAGS finds (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 are an exponential number of isomorphisms that are symmetrically
equivalent. In that case, the ISMAGS algorithm will provide only one isomorphism
per symmetry group, speeding up finding isomorphisms and avoiding the task of
post-processing many effectively identical isomorphisms.

>>> 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
-----
- 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.

- With a method that yields subgraph isomorphisms, we can construct functions like
  ``is_subgraph_isomorphic`` by checking for any yielded mapping. And functions like
  ``is_isomorphic`` by checking whether the subgraph has the same number of nodes as
  the graph and is also subgraph isomorphic. This subpackage also allows a
  ``symmetry`` bool keyword argument so you can find isomorphisms with or
  without the symmetry constraints.

- For more information, see [2]_ and the documentation for :class:`ISMAGS`
  which includes a description of the algorithm.

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
ÚISMAGSN)ÚCounterÚdefaultdictc                ó   a€  V P                   p\        V4      ^8”  d   Rp\        V4      Rh \	        V 4      p\        VR4      o\        ;QJ d    V3R lV 4       F  '       d   K   R# 	  R# ! V3R lV 4       4      #   \         d     L^i ; i)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   ó,   <"  € T F	  qS8H  x € K  	  R # 5i©N© )Ú.0ÚitemÚfirsts   & €Úg/home/luca/screen-checking/.venv/lib/python3.14/site-packages/networkx/algorithms/isomorphism/ismags.pyÚ	<genexpr>Ú are_all_equal.<locals>.<genexpr>Ÿ   s   øé € Ð2© �uŽ}«ùs   ƒFT)ÚshapeÚlenÚNotImplementedErrorÚAttributeErrorÚiterÚnextÚall)Úiterabler   ÚmessageÚiteratorr   s   &   @r   Úare_all_equalr   „   s‰   ø€ ð 9Ø—‘ˆô ˆu‹:˜Œ>ØOˆGÜ% gÓ.°DÐ8ð ô �H‹~€HÜ�˜4Ó €Eß‹3Ô2©Ó2�3Œ3Ð2Š3Ð2ˆ3Ô2©Ó2Ó2Ð2øô ô Ùðús   ƒA? Á?BÂBc                ó†  aa€ . oV  FT  pS F9  p\        \        V4      4      pS! W54      '       g   K'  VP                  V4        K@  	  SP                  V04       KV  	  V'       d½   \        ;QJ d    V3R lS 4       F  '       d   K   RM	  RM! V3R lS 4       4      '       g   \
        P                  ! RS RS: 24      h\        ;QJ d     VV3R lS 4       F  '       d   K   RM	  RM! VV3R lS 4       4      '       g   \
        P                  ! RS RS: 24      hS Uu. uF  p\        V4      NK  	  up# u upi )aD  
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 match/tests so need to end up in the same
    part of the partition, and `False` otherwise.
check : bool optional (default: True)
    If ``True``, check that the resulting partition satisfies the match criteria.
    Every item should match every item in its part and none outside the part.

Returns
-------
list[set]
    A partition as a list of sets (the parts). Each set contains some of
    the items in `items`, such that all items are in exactly one part and every
    pair of items in each part matches. The following will be true:
    ``all(thing_matcher(*pair) for pair in itertools.combinations(items, 2))``

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``.
The function `test` is assumed to be commutative: if ``test(a, b)``
returns ``True`` then ``test(b, a)`` returns ``True``.
c              3   ó”   <"  € T F=  p\         P                  ! V^4       F  w  r#S! W#4      ;'       d	    S! W24      x € K!  	  K?  	  R# 5i)é   N)Ú	itertoolsÚcombinations)r	   ÚpartÚt1Út2Útests   &   €r   r   Ú!make_partition.<locals>.<genexpr>Í   sG   øé € ð 
á!�Ü#×0Ò0°°qÖ9‘�ñ �‹L×)Ð)™T "›\Ô)á9ñ *Û!ùs
   ƒ/A³AFTz 
Invalid partition created with z=.
Some items in a part do not match. This leads to
partition=c              3   óÈ   <"  € T FW  pS FN  pW8w  g   K  \         P                  ! W4       F)  w  r4S! W44      '       * ;'       d    S! WC4      '       * x € K+  	  KP  	  KY  	  R # 5ir   )r   Úproduct)r	   Úp1Úp2r    r!   Ú	partitionr"   s   &    €€r   r   r#   ×   sf   øé € ð 
á�Û�Ø‰xô 2ô $×+Ò+¨BÖ3‘�ñ	 �R“Ô×1Ð1¡T¨"£\Ô!1Ô1ñ 4ñ	 2áñ 2Ûùs   ƒA"•.A"ÁA"z;.
Some items match multiple parts. This leads to
partition=)r   r   ÚaddÚappendr   ÚnxÚNetworkXErrorÚset)Úitemsr"   Úcheckr
   r   Úp_itemr(   s   &f&   @r   Úmake_partitionr1   ¢   s1  ù€ ð@ €IÛˆÛˆDÜœ$˜t›*Ó%ˆFÙ�D×!Ô!Ø—‘˜”Úñ	 ð ×Ñ˜d˜VÖ$ñ ÷ ß‹sô 
á!ó
�s�sŠsô 
á!ó
÷ 
ò 
ô
 ×"Ò"Ø3°D°6ð :à‘,ð óð ÷
 ‹sõ 
áó
�s�sŠsõ 
áó
÷ 
ò 
ô ×"Ò"Ø3°D°6ð :à‘,ð óð ñ
 #,Ó,¡)˜$ŒC�ŽI¡)Ñ,Ð,ùÒ,s   Ä'D>c                ód   € \        V 4       UUUu/ uF  w  rV F  q3VbK  	  K  	  uppp# u upppi )zû
Creates a dictionary that maps each item in each part to the index of
the part to which it belongs.

Parameters
----------
partition: collections.abc.Sequence[collections.abc.Iterable]
    As returned by :func:`make_partition`.

Returns
-------
dict
)Ú	enumerate)r(   ÚIDr   Únodes   &   r   Únode_to_part_ID_dictr6   æ   s/   € ô &/¨yÔ%9ÕKÑ%9™˜Ãd¸d�"ŠHÁd‰DÑ%9ÓKÐKùÔKs   �+c                ó®  aaa€ V P                  4       '       gF   V P                  4        UaUu/ uF'  w  opSSS,          \        VVV3R lV 4       4      3bK)  	  upp# V P                  4        UaUu/ uFN  w  opSSS,          \        VVV3R lV 4       4      \        VVV3R lV P                  S,           4       4      3bKP  	  upp# u uppi u uppi )a·  Returns a dict keyed by node to counts of edge and nbr color combos.

Similar to color_degree_by_node except that multiedges are not included in
the color tuples.

Not used for largest_common_subgraph. Only for _all_morphisms when considering
problem_type == "MONO", or cases with no multigraphs present. MONO needs to allow
lower numbers of multiedges in the subgraph, so we can't use that as a color (which
are essentially treated using equality checks for isomorphisms. We handle
multiedge checks for MONO during mapping update checks rather than candidate
identifying part of the loop. Ignoring the multiedges gives the same code we
can use when both graph and subgraph are not multigraphs.
c              3   óL   <"  € T F  pSSV3,          SV,          3x € K  	  R # 5ir   r   ©r	   ÚvÚe_colorsÚn_colorsÚus   & €€€r   r   Ú0color_degree_by_node_no_multi.<locals>.<genexpr>  s"   øé € Ð$QÉDÀq h¨q°!¨t¥n°h¸qµkÕ%BËDùó   ƒ!$c              3   óL   <"  € T F  pSSV3,          SV,          3x € K  	  R # 5ir   r   r9   s   & €€€r   r   r>     s"   øé € Ð@¹4°a�X˜a ˜d•^ X¨a¥[Õ1»4ùr?   c              3   óL   <"  € T F  pSVS3,          SV,          3x € K  	  R # 5ir   r   r9   s   & €€€r   r   r>     s"   øé € ÐF¹:°a�X˜a ˜d•^ X¨a¥[Õ1»:ùr?   )Úis_directedÚ	adjacencyr   Ú_pred)ÚGr<   r;   r=   Únbrss   &ff` r   Úcolor_degree_by_node_no_multirG   ÷   sÄ   ú€ ð �=‰=�?Š?ð Ÿ;™;œ=õ
á(‘��4ð �˜•œWÖ$QÉDÓ$QÓQÐRÒRÙ(ò
ð 	
ð —{‘{”}õñ %‰GˆAˆtð 	
Ø�Q�KÜÖ@¹4Ó@Ó@ÜÖF¸1¿7¹7À1¾:ÓFÓFð
ò 	
ñ
 %òð ùó
ùó
s   ®-CÁ3ACc                ó@  a aaa€ \        S4      \        S 4      8  d7   S P                  4        F"  w  r4VS9  g   K  RSV&   V F	  pRSW53&   K  	  K$  	  S P                  4       '       g¢   S P                  4       '       dG   S P                  4        UaUu/ uF(  w  opSSS,          \	        V VVV3R lV 4       4      3bK*  	  upp# S P                  4        UaUu/ uF'  w  opSSS,          \	        VVV3R lV 4       4      3bK)  	  upp# S P                  4       '       d|   S P                  4        UaUu/ uF]  w  opSSS,          \	        V VVV3R lV 4       4      \	        VVV3R lS P
                  S,          P                  4        4       4      3bK_  	  upp# S P                  4        UaUu/ uFN  w  opSSS,          \	        VVV3R lV 4       4      \	        VVV3R lS P
                  S,           4       4      3bKP  	  upp# u uppi u uppi u uppi u uppi )a*  Returns a dict keyed by node to counts of edge/nbr color for that node.

The inputs are G -- graph of interest; `n_colors` -- a dict keyed by node
to the node color/label associated with that node; `e_colors` -- a dict
keyed by edge to the edge color/label associated with that edge.

The returned dict is keyed by node to a 2-tuple of node color and degree
dict. The degree dict holds counts for each edge-key which is a
3-tuple: (edge color, number of multiedges, nbr color). For example,
``{0: (1, {(0, 1, 2): 5})}`` means that node ``0`` has node color 1 and
has 5 edges of color 0 that have 1 multiedge to nodes of color 2.
This dict is a measure of degree (edge count) by color of edge, number of
multiedges and color of the node on the other side of that edge.

For directed graphs the degree counts are a 2-tuple of 2 dicts indicating
(in, out) degree counts respectively.

When both graph and subgraph are not multigraphs, the multiedge indicator
is not relevant. Use `color_degree_by_node_no_multi` to make the keys of
the inner dict 2-tuples with edge color and nbr node color.

TODO: if edge_match is None, this could get simplified to just the node
color on the other end of the edge. Similarly if node_match is None then only
edge color is tracked. And if both are None, we simply count the number of edges.
Nc              3   ó|   <"  € T F1  pSSV3,          \        SS,          V,          4      SV,          3x € K3  	  R # 5ir   ©r   ©r	   r:   rE   r;   r<   r=   s   & €€€€r   r   Ú'color_degree_by_node.<locals>.<genexpr>=  s2   øé € ÐVÑQUÈA˜X a¨ d�^¬S°°1µ°aµ«\¸8ÀA½;ÕGÓQUùó   ƒ9<c              3   óN   <"  € T F  pSSV3,          ^SV,          3x € K  	  R# 5i©é   Nr   r9   s   & €€€r   r   rL   B  s$   øé € Ð$TÉtÈ! h¨q°!¨t¥n°a¸À!½Õ%EËtùó   ƒ"%c              3   ó|   <"  € T F1  pSSV3,          \        SS,          V,          4      SV,          3x € K3  	  R # 5ir   rJ   rK   s   & €€€€r   r   rL   J  s0   øé € ÐRÉTÈ˜ ! Q $�¬¨Q¨q­T°!­W«°xÀµ{ÕCËTùrM   c              3   ód   <"  € T F%  w  rSVS3,          \        V4      SV,          3x € K'  	  R # 5ir   rJ   )r	   r:   Úkdr;   r<   r=   s   &  €€€r   r   rL   K  s1   øé € ð á!3™˜ð ˜a ˜d•^¤S¨£W¨h°q­kÕ:Û!3ùs   ƒ-0c              3   óN   <"  € T F  pSSV3,          ^SV,          3x € K  	  R# 5irO   r   r9   s   & €€€r   r   rL   U  s$   øé € ÐC¹d¸�X˜a ˜d•^ Q¨°­Õ4»dùrQ   c              3   óN   <"  € T F  pSVS3,          ^SV,          3x € K  	  R# 5irO   r   r9   s   & €€€r   r   rL   V  s$   øé € ÐI¹j¸�X˜a ˜d•^ Q¨°­Õ4»jùrQ   )r   rC   rB   Úis_multigraphr   rD   r.   )rE   r<   r;   ÚnrF   r:   r=   s   fff   `r   Úcolor_degree_by_noderY     sù  û€ ô6 ˆ8ƒ}”s˜1“vÔØ—{‘{–}‰GˆAØ˜Ö Ø"�˜‘Û�AØ%)�H˜Q˜T“Nó ñ %ð �=‰=�?Š?Ø�?‰?×Òð  !Ÿ{™{œ}õñ
  -‘G�A�tð	 Ø˜Q•KÜ×VÑQUÓVÓVðò ñ  -òð ð Ÿ;™;œ=õ
á(‘��4ð �˜•œWÖ$TÉtÓ$TÓTÐUÒUÙ(ò
ð 	
ð
 	‡�×Òð Ÿ;™;œ=õ

ñ )‘��4ð Ø˜•Ü×RÉTÓRÓRÜö à!"§¡¨¥×!1Ñ!1Ô!3óó ðò ñ )ò

ð 
	
ð" —{‘{”}õñ %‰GˆAˆtð 	
Ø�Q�KÜÖC¹dÓCÓCÜÖI¸a¿g¹gÀa¾jÓIÓIð
ò 	
ñ
 %òð ùó1ùó
ùó

ùós   Â.HÃ-HÄ5A#HÆ0AHc                   ó6   a € ] tR tRt o RtR tR tR tRtV t	R# )Ú
EdgeLookupi\  a‹  Class to handle getitem for undirected edges.

Note that ``items()`` iterates over one of the two representations of the edge
(u, v) and (v, u). So this technically doesn't violate the Mapping
invariant that (k,v) pairs reported by ``items()`` satisfy ``.__getitem__(k) == v``.
But we are violating the spirit of the protocol by having keys available
for lookup by ``__getitem__`` that are not reported by ``items()``.

Note that if we used frozensets for undirected edges we would have the same
behavior we see here. You could ``__getitem__`` either ``{u, v}`` or ``{v, u}``
and get the same value -- yet ``items()`` would only report one of the two.
So from that perspective we *are* following the Mapping protocol. Our keys
are undirected edges. We are using 2-tuples as an imperfect representation
of these edges. We are not using 2-tuples as keys. Only as imperfect edges
and we use the edges as keys.
c                ó   € Wn         R # r   ©Ú	edge_dict)Úselfr^   s   &&r   Ú__init__ÚEdgeLookup.__init__n  s   € Ø"Žó    c                ó‚   € WP                   9   d   V P                   V,          # V P                   VR R R1,          ,          # )Néÿÿÿÿr]   )r_   Úedges   &&r   Ú__getitem__ÚEdgeLookup.__getitem__q  s2   € Ø—>‘>Ô!Ø—>‘> $Õ'Ð'Ø�~‰~˜d¡4 R 4�jÕ)Ð)rb   c                ó6   € V P                   P                  4       # r   )r^   r.   )r_   s   &r   r.   ÚEdgeLookup.itemsv  s   € Ø�~‰~×#Ñ#Ó%Ð%rb   r]   N)
Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__r`   rf   r.   Ú__static_attributes__Ú__classdictcell__©Ú__classdict__s   @r   r[   r[   \  s   ø‡ € ñò"#ò*÷
&ð &rb   r[   c                   óü   a € ] tR tRt o RtRR ltR tRR ltR tR t	RR	 lt
R
 tRR ltRR ltRR ltRR ltRR ltRR ltR t]R 4       tRR ltRR lt]R 4       t]R 4       tR t]R 4       tR tRtV tR# )r   iz  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.

Attributes
----------
graph: networkx.Graph
subgraph: networkx.Graph

Notes
-----
ISMAGS does a symmetry analysis to find the constraints on isomorphisms if
we preclude yielding isomorphisms that differ by a symmetry of the subgraph.
For example, if the subgraph is a 4-cycle, every isomorphism would have a
symmetric version with the nodes rotated relative to the original isomorphism.
By encoding these symmetries as constraints we reduce the search space for
isomorphisms and we also simplify processing the resulting isomorphisms.

**Symmetry Analysis**

The constraints in ISMAGS are based off the handling in ``nauty`` and its many
variants, in particular ``saucy``, as discussed in the ISMAGS paper [1]_.
That paper cites [3]_ for details on symmetry handling. Figure 2 of [3]_
describes the DFS approach to symmetries used here and relying on a data structure
called an Ordered Pair Partitions(OPP). This consists of a pair of partitions
where each part represents nodes with the same degree-by-color over all colors.
We refine these partitions simultaneously in a way to result in permutations
of the nodes that preserve the graph structure. We thus find automorphisms
for the subgraph of interest. From those we identify pairs of nodes which
are structurally equivalent. We then constrain our problem by requiring the
first of the pair to always be assigned first in the isomorphism -- thus
constraining the isomorphisms reported to only one example from the set of all
symmetrically equivalent isomorphisms. These constraints are computed once
based on the subgraph symmetries and then used throughout the DFS search for
isomorphisms.

Finding the symmetries involves a DFS of the OPP wherein we "couple" a node
to a node in its degree-by-color part of the partition. This "coupling" is done
via assigning a new color in the top partition to the node being coupled,
and the same new color in the bottom partition to the node being coupled to.
This new color has only one node in each partition. The new color also requires
that we "refine" both top and bottom partitions by splitting parts until each
part represents a common degree-by-color value. Those refinements introduce
new colors as the parts are split during refinement. Parts do not get combined
during refinement. So the coupling/refining process always results in at least
one new part with only one node in both the top and bottom partition. In practice
we usually refine into many new one-node parts in both partitions.
We continue in this way until each node has its own part/color in the top partition
-- and the node in the bottom partition with that color is the symmetric node.
That is, an OPP represents an automorphism, and thus a symmetry
of the subgraph when each color has a single node in the top partition and a single
node in the bottom partition. From those automorphisms we build up a set of nodes
that can be obtained from each other by symmetry (they are mutually symmetric).
That set of nodes is called an "orbit" of the subgraph under symmetry.

After finding the orbits for one symmetry, we backtrack in the DFS by removing the
latest coupling and replacing it with a coupling from the same top node to a new
bottom node in its degree-by-color grouping. When all possible couplings for that
node are considered we backtrack to the previously coupled node and recouple in
a DFS manner.

We can prune the DFS search tree in helpful ways. The paper [2]_ demonstrates 6
situations of interest in the DFS where pruning is possible:

- An **Automorphism OPP** is an OPP where every part in both partitions
  contains a single node. The mapping/automorphism is found by mapping
  each top node to the bottom node in the same color part. For example,
  ``[({1}, {2}, {3}); ({2}, {3}, {1})]`` represents the mapping of each
  node to the next in a triangle. It rotates the nodes around the triangle.
- The **Identity OPP** is the first automorphism found during the DFS. It
  appears on the left side of the DFS tree and is first due to our ordering of
  coupling nodes to be in an arbitrary but fixed ordering of the nodes. This
  automorphism does not show any symmetries, but it ensures the orbit for each
  node includes itself and it sets us up for handling the symmetries. Note that
  a subgraph with no symmetries will only have the identity automorphism.
- A **Non-isomorphic OPP** occurs when refinement creates a different number of
  parts in the top partition than in the bottom partition. This means no symmetries
  will be found during further processing of that subtree of the DFS. We prune
  the subtree and continue.
- A **Matching OPP** is such that each top part that has more than one node is
  in fact equal to the bottom part with the same color. The many-node-parts match
  exactly. The single-node parts then represent symmetries that do not permute
  any matching nodes. Matching OPPs arise while finding the Identity Mapping. But
  the single-node parts are identical in the two partitions, so no useful symmetries
  are found. But after the Identity Mapping is found, every Matching OPP encountered
  will have different nodes in at least two single-node parts of the same color.
  So these positions in the DFS provide us with symmetries without any
  need to find the whole automorphism. We can prune the subtree, update the orbits
  and backtrack. Any larger symmetries that combine these symmetries with symmetries
  of the many-node-parts do not need to be explored because the symmetry "generators"
  found in this way provide a basis for all symmetries. We will find the symmetry
  generators of the many-node-parts at another subtree of the DFS.
- An **Orbit Pruning OPP** is an OPP where the node coupling to be considered next
  has both nodes already known to be in the same orbit. We have already identified
  those permutations when we discovered the orbit. So we can prune the resulting
  subtree. This is the primary pruning discussed in [1]_.
- A **Coset Point** in the DFS is a point of the tree when a node is first
  back-tracked. That is, its couplings have all been analyzed once and we backtrack
  to its parent. So, said another way, when a node is backtracked to and is about to
  be mapped to a different node for the first time, its child in the DFS has been
  completely analysed. Thus the orbit for that child at this point in the DFS is
  the full orbit for symmetries involving only that child or larger nodes in the
  node order. All smaller nodes are mapped to themselves.
  This orbit is due to symmetries not involving smaller nodes. Such an orbit is
  called the "coset" of that node. The Coset Point does not lead to pruning or to
  more symmetries. It is the point in the process where we store the **coset** of
  the node being backtracked. We use the cosets to construct the symmetry
  constraints.

Once the pruned DFS tree has been traversed, we have collected the cosets of some
special nodes. Often most nodes are not coupled during the progression down the left
side of the DFS. They are separated from other nodes during the partition refinement
process after coupling. So they never get coupled directly. Thus the number of cosets
we find is typically many fewer than the number of nodes.

We turn those cosets into constraints on the nodes when building non-symmetric
isomorphisms. The node whose coset is used is paired with each other node in the
coset. These node-pairs form the constraints. During isomorphism construction we
always select the first of the constraint before the other. This removes subtrees
from the DFS traversal space used to build isomorphisms.

The constraints we obtain via symmetry analysis of the subgraph are used for
finding non-symmetric isomorphisms. We prune the isomorphism tree based on
the constraints we obtain from the symmetry analysis.

**Isomorphism Construction**

Once we have symmetry constraints on the isomorphisms, ISMAGS constructs the allowed
isomorphisms by mapping each node of the subgraph to all possible nodes (with the
same degree-by-color) from the graph. We partition all nodes into degree-by-color
parts and order the subgraph nodes we consider using smallest part size first.
The idea is to try to map the most difficult subgraph nodes first (most difficult
here means least number of graph candidates).

By considering each potential subgraph node to graph candidate mapping image in turn,
we perform a DFS traversal of partial mappings. If the mapping is rejected due to
the graph neighbors not matching the degree-by-color of the subgraph neighbors, or
rejected due to the constraints imposed from symmetry, we prune that subtree and
consider a new graph candidate node for that subgraph node. When no more graph
candidates remain we backtrack to the previous node in the mapping and consider a
new graph candidate for that node. If we ever get to a depth where all subgraph nodes
are mapped and no structural requirements or symmetry constraints are violated,
we have found an isomorphism. We yield that mapping and backtrack to find other
isomorphisms.

As we visit more neighbors, the graph candidate nodes have to satisfy more structural
restrictions. As described in the ISMAGS paper, [1]_, we store each set of structural
restrictions separately as a set of possible candidate nodes rather than computing
the intersection of that set with the known graph candidates for the subgraph node.
We delay taking the intersection until that node is selected to be in the mapping.
While choosing the node with fewest candidates, we avoid computing the intersection
by using the size of the minimal set to be intersected rather than the size of the
intersection. This may make the node ordering slightly worse via a savings of
many intersections most of which are not ever needed.

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
.. [3] Hadi Katebi, Karem A. Sakallah and Igor L. Markov
   "Graph Symmetry Detection and Canonical Labeling: Differences and Synergies"
   in "Turing-100. The Alan Turing Centenary" Ed: A. Voronkov p. 181 -- 195, (2012).
   https://doi.org/10.29007/gzc1 https://arxiv.org/abs/1208.6271
Nc                ór  € VP                  4       VP                  4       8w  d   \        R4      hWn        W n        WPn        V P                  W0P                  P                  V P                  P                  4      pVw  V n        V n        V n	        \        V P                  4      V n        \        V P                  4      V n        V P                  W@P                  P                  4       V P                  P                  4       4      pVw  V n        V n        V n        V P                  P                  4       '       d7   \        V P                  4      V n        \        V P                  4      V n        R# \'        \        V P                  4      4      V n        \'        \        V P                  4      4      V n        R# )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.
z2Directed and undirected graphs cannot be compared.N)rB   Ú
ValueErrorÚgraphÚsubgraphÚ_symmetry_cacheÚcreate_aligned_partitionsÚnodesÚ_sgn_partitionÚ_gn_partitionÚN_node_colorsr6   Ú_sgn_colorsÚ
_gn_colorsÚedgesÚ_sge_partitionÚ_ge_partitionÚN_edge_colorsÚ_sge_colorsÚ
_ge_colorsr[   )r_   rv   rw   Ú
node_matchÚ
edge_matchÚcacheÚ
node_partsÚedge_partitionss   &&&&&&  r   r`   ÚISMAGS.__init__&  sV  € ð. ×ÑÓ (×"6Ñ"6Ó"8Ô8ÜÐQÓRÐRð Œ
Ø ŒØ$Ôð ×3Ñ3ØŸ™×+Ñ+¨T¯Z©Z×-=Ñ-=ó
ˆ
ð GQÑCˆÔ˜TÔ/°Ô1CÜ/°×0CÑ0CÓDˆÔÜ.¨t×/AÑ/AÓBˆŒà×8Ñ8ØŸ™×+Ñ+Ó-¨t¯z©z×/?Ñ/?Ó/Aó
ˆð GVÑCˆÔ˜TÔ/°Ô1CØ�:‰:×!Ñ!×#Ò#Ü3°D×4GÑ4GÓHˆDÔÜ2°4×3EÑ3EÓFˆDŽOä)Ô*>¸t×?RÑ?RÓ*SÓTˆDÔÜ(Ô)=¸d×>PÑ>PÓ)QÓRˆDŽOrb   c                óD  a aaa€ Sf   \        S4      .p\        S4      .pWE^3# \        S\        P                  P                  P
                  4      p\        S\        P                  P                  P
                  4      pV'       g	   VV3R lpMV V3R lpV'       g	   VV3R lp	MV V3R lp	\        SV4      p\        SV	4      p/ p
/ p\        V4      \        V4      rÜ\        P                  ! \        V4      \        V4      4       EF[  w  rï\        \        WN,          4      4      p\        \        W_,          4      4      pV'       g
   SV,          M'S P                  V^ ,          ,          V^,          ,          pV'       g
   SV,          M'S P                  V^ ,          ,          V^,          ,          pS! VV4      '       g   K½  Wê9   dD   \        P                  ! RS RV: RV RWN,           R	W_,           R
WZV,          ,           R24      hWû9   dH   \        P                  ! RS RV: RV: RV RW_,           RWN,           R
WKV,          ,           R24      hWúV&   WëV&   EK^  	  V
P!                  4        UUu. uF  w  rïWN,          W_,          3NK  	  ppp\        V4      pV'       d'   \#        V!   Uu. uF  p\%        V4      NK  	  upw  ppM. . ppVV8  dh   \        V4       Uu. uF  pVV
9  g   K  VV,          NK  	  pp\%        V4      V,           p\%        V4      \        4       .\        V4      ,          ,           pVV8  d=   \        V4       Uu. uF  pVV9  g   K  VV,          NK  	  pp\%        V4      V,           pVVV3# u uppi u upi u upi u upi )aš  Partitions of "things" (nodes or edges) from subgraph and graph
based on function `thing_matcher`.

Returns: sg_partition, g_partition, number_of_matched_parts

The first `number_of_matched_parts` parts in each partition
match in order, e.g. 2nd part matches other's 2nd part.
Warning: nodes in parts after that have no matching nodes in the other graph.
For morphisms those nodes can't appear in the mapping.
c                 ó2   <€ S! SV ,          SV,          4      # r   r   )Úthing1Úthing2Ú	sg_thingsÚthing_matchers   &&€€r   Úsg_matchÚ2ISMAGS.create_aligned_partitions.<locals>.sg_matchs  s   ø€ Ù$ Y¨vÕ%6¸	À&Õ8IÓJÐJrb   c                 ó†   <€ Wuw  r#w  rES! SP                   V,          V,          SP                   V,          V,          4      # r   )rw   ©rŽ   r�   Úu1Úv1Úu2Úv2r_   r‘   s   &&    €€r   r’   r“   x  s:   ø€ Ø%+Ð"‘�™(˜2Ù$ T§]¡]°2Õ%6°rÕ%:¸D¿M¹MÈ"Õ<MÈbÕ<QÓRÐRrb   c                 ó2   <€ S! SV ,          SV,          4      # r   r   )rŽ   r�   Úg_thingsr‘   s   &&€€r   Úg_matchÚ1ISMAGS.create_aligned_partitions.<locals>.g_match~  s   ø€ Ù$ X¨fÕ%5°xÀÕ7GÓHÐHrb   c                 ó†   <€ Wuw  r#w  rES! SP                   V,          V,          SP                   V,          V,          4      # r   )rv   r•   s   &&    €€r   rœ   r�   ƒ  s8   ø€ Ø%+Ð"‘�™(˜2Ù$ T§Z¡Z°¥^°BÕ%7¸¿¹ÀB½ÈÕ9KÓLÐLrb   z
Matching function z- seems faulty.
Partition found: sg_partition=z
So z in subgraph part z matches two graph parts z and Ú
z!
Matching function seems broken: z
Partitions found: g_partition=z sg_partition=z in graph part z matches two subgraph parts )r-   Ú
isinstancer+   ÚclassesÚreportviewsÚOutEdgeDataViewr1   r   r   r%   Úranger   r   rw   rv   r,   r.   ÚzipÚlist)r_   r‘   r�   r›   Úsg_partitionÚg_partitionÚsg_multiedgeÚg_multiedger’   rœ   Ú	sgc_to_gcÚ	gc_to_sgcÚsNÚNÚsgcÚgcÚsgtÚgtÚsgt_Úgt_Ú	new_orderÚNcolorsÚxÚnew_sg_pÚnew_g_pÚcÚextras   ffff                       r   ry   Ú ISMAGS.create_aligned_partitions]  s]  û€ ð Ò Ü 	›NÐ+ˆLÜ˜x›=˜/ˆKØ¨aÐ/Ð/ô " )¬R¯Z©Z×-CÑ-C×-SÑ-SÓTˆÜ  ¬2¯:©:×+AÑ+A×+QÑ+QÓRˆß÷Kö
S÷ ÷Iö
Mô & i°Ó:ˆÜ$ X¨wÓ7ˆð ˆ	Øˆ	Ü�LÓ!¤3 {Ó#3ˆAÜ ×(Ò(¬¨r«´E¸!³H×=‰GˆCÜ”t˜LÕ-Ó.Ó/ˆCÜ”d˜;�?Ó+Ó,ˆBß)5�9˜S–>¸4¿=¹=ÈÈQÍÕ;PÐQTÐUVÕQWÕ;XˆDß&1�(˜2–,°t·z±zÀ"ÀQÅ%Õ7HÈÈAÍÕ7OˆCÙ˜T 3×'Ô'ð Ô#Ü×*Ò*Ø.¨}¨oð >:Ø,8©?ð ;Ø!˜UÐ"4°\Õ5FÐ4Gð H'Ø'2¥Ð&7°uØ&°¥~Õ6Ð7°rð	;óð ð ”?Ü×*Ò*Ø<¸]¸Oð L:Ø-8©N¸/¸L¹?ð KØ ˜T °µÐ0Að B*Ø*6Õ*;Ð)<¸EØ'°"­Õ6Ð7°rð	;óð ð "$˜#‘Ø #˜"”ñ3 >ð8 AJÇÁÔ@Qô
Ù@Q±W°Sˆ\Õ ¥Ó0Ñ@Qð 	ñ 
ô �i“.ˆßÜ25°y²/Ó B±/¨Q¤ a¦±/Ñ BÑˆH‘gà " B�gˆHØ�RŒ<Ü.3°B¬iÓN©i¨¸1ÀIÑ;M”_�\ !—_�_©iˆEÐNÜ˜H“~¨Õ-ˆHÜ˜7“m¤s£u g´°E³
Õ&:Õ:ˆGØ�QŒ;Ü-2°1¬XÓL©X¨¸À)Ñ9K”^�[ —^�^©XˆEÐLÜ˜7“m eÕ+ˆGð ˜ 'Ð)Ð)ùó#
ùò
 !Cùò Oùò Ms$   É<NÊ8NË+	NË9NÍ	NÍ'Nc              #  óH   "  € V P                  VRR7       Rj  x€L
  R#  L5i)z?left for backward compatibility. Use subgraph_isomorphisms_iterÚSUB©Úproblem_typeN©Ú_all_morphisms©r_   Úsymmetrys   &&r   Úfind_isomorphismsÚISMAGS.find_isomorphisms¼  s'   é € à×&Ñ& x¸eÐ&ÓD×DÐDÙñ 	Eùs   ‚"™ š"c              #  ój  a"  € VR8X  d   \         P                  ;r4MQVR8X  d"   \         P                  p\         P                  pM)VR8X  d   \         P                  ;r4M\        RV: R24      hV P                  '       g   / x € R# V P
                  '       g   R# V! \        V P                  4      \        V P
                  4      4      '       g   R# V! \        V P                  4      V P                  4      '       g   R# V! \        V P                  4      V P                  4      '       g   R# VR8X  d]   V! \        V P                  4      V P                  4      '       g   R# V! \        V P                  4      V P                  4      '       g   R# V'       dI   V P                  4       pVP                  4        UUUu. uF  w  rgV F  q†V8w  g   K  Wh3NK  	  K  	  p	pppM. p	V P                  V4      oV P!                  W44      p
V
P                  4        F&  w  r¼SV,          P#                  \%        V4      4       K(  	  \'        SP)                  4       4      '       dL   \+        SV3R lR7      p\$        P,                  ! SV,          !  3SV&   V P/                  VSW”4       Rj  x€L
  R# u upppi  L5i)	a	  Find all morphisms between :attr:`graph` and :attr:`subgraph`

Yield isomorphisms or monomorphisms depending on `problem_type`.

Parameters
----------
symmetry : bool
    Whether symmetry should be taken into account.
    If False, morphisms may be symmetrically equivalent.
problem_type : string
    The problem type to be used:
    - "ISO" for graph isomorphism,
    - "SUB" for subgraph isomorphism,
    - "MONO" for monomorphism.

Yields
------
dict
    The isomorphism mappings in form: {graph_node: subgraph_node}.
ÚISOr¾   ÚMONOzInvalid problem_type=z . Must be "ISO", "SUB" or "MONO"Nc                 ó6   <€ \        R  SV ,           4       4      # )c              3   ó8   "  € T F  p\        V4      x € K  	  R # 5ir   rJ   ©r	   r·   s   & r   r   Ú:ISMAGS._all_morphisms.<locals>.<lambda>.<locals>.<genexpr>  s   é € Ð8VÉÀA¼¸Q¿¸Ëùó   ‚©Úmin©rX   Ú	cand_setss   &€r   Ú<lambda>Ú'ISMAGS._all_morphisms.<locals>.<lambda>  s   ø€ ´SÑ8VÈÐSTÎÓ8VÔ5Vrb   ©Úkey)ÚoperatorÚeqÚleru   rw   rv   r   r{   r}   r�   rƒ   r|   r‚   Úanalyze_subgraph_symmetryr.   Ú_get_node_color_candidate_setsÚ_get_color_degree_candidatesr)   Ú	frozensetÚanyÚvaluesrÐ   ÚintersectionÚ
_map_nodes)r_   rÄ   rÀ   ÚSG_fitsÚ	MONO_fitsÚcosetsrX   ÚcsÚcoÚconstraintsÚlookahead_candidatesÚsgnÚlookahead_candsÚ	start_sgnrÒ   s   &&&           @r   rÂ   ÚISMAGS._all_morphismsÁ  s+  øé € ð* ˜5Ô Ü"*§+¡+Ð-ˆG�iØ˜UÔ"Ü—k‘kˆGÜ Ÿ™‰IØ˜VÔ#Ü"*§+¡+Ð-ˆG�iäÐ5¨¡Ð6VÐWÓXÐXð �}�}ˆ}ØŠHÙØ——�ÙÙœ˜TŸ]™]Ó+¬S°·±«_×=Ò=ÙÙœ˜T×0Ñ0Ó1°4×3EÑ3E×FÒFáÙœ˜T×0Ñ0Ó1°4×3EÑ3E×FÒFáØ˜5Ô Ùœ3˜t×1Ñ1Ó2°D×4FÑ4F×GÒGÙÙœ3˜t×1Ñ1Ó2°D×4FÑ4F×GÒGÙçØ×3Ñ3Ó5ˆFà06·±´ÕW±¡u qËÀ2ÐTVÉwœ7˜A›7É™7±ˆKÒWˆKàˆKà×7Ñ7¸	ÓBˆ	à#×@Ñ@ÀÓTÐØ$8×$>Ñ$>Ö$@Ñ ˆCØ�c�N×Ñœy¨Ó9Ö:ñ %Aô ˆy×ÑÓ!×"Ò"ô
 ˜IÔ+VÔWˆIÜ$-×$:Ò$:¸IÀiÕ<PÑ$QÐ#SˆI�iÑ Ø—‘ y°)¸[ÓT×TÐTÙùô% Xñ" Uùs8   ƒA<J3Â J3ÂC?J3Æ$J3Æ<J*ÇJ*ÇCJ3Ê#J1Ê$J3c                óî  aa€ V\         P                  8H  pV P                  P                  4       '       g!   V P                  P                  4       '       d   V'       g   \
        pM\        pV! V P                  V P                  V P                  4      pV! V P                  V P                  V P                  4      pVP                  4        UUU	U
Uau/ uFˆ  w  pvr‰TVP                  4        U
UUau0 uF`  w  p
vpo\        ;QJ d)    VV3R l\        V	4       4       F  '       d   K   RM	  RM! VV3R l\        V	4       4       4      '       g   K^  V
kKb  	  uppp
bKŠ  	  upp
p	pp# u uppp
i u upp
p	ppi )z¸
Returns a mapping of {subgraph node: set of graph nodes} for
which the graph nodes are feasible mapping candidate_sets for the
subgraph node, as determined by looking ahead one edge.
c              3   óˆ   <"  € T F7  w  rVP                  4        F  w  r4S! VSV,          V,          4      x € K   	  K9  	  R # 5ir   )r.   )r	   ÚidxÚcountsÚcolorÚsg_cntrâ   Úg_countss   &    €€r   r   Ú6ISMAGS._get_color_degree_candidates.<locals>.<genexpr>  sB   øé € ð á'?™˜Ø)/¯©®™˜ñ ˜F H¨S¥M°%Õ$8×9Ð9á)7ñ :Û'?ùs   ƒ?AFT)r×   rÙ   rv   rW   rw   rY   rG   r   r…   r~   r„   r.   r   r3   )r_   râ   rã   rÉ   Úcolor_countÚg_degÚsg_degré   Ú_Úneeded_countsÚgnró   s   &f&        `r   rÜ   Ú#ISMAGS._get_color_degree_candidates  s%  ù€ ð œHŸK™KÑ'ˆØ�J‰J×$Ñ$×&Ò&¨$¯-©-×*EÑ*E×*GÒ*G×QUÜ.‰Kä7ˆKÙ˜DŸJ™J¨¯©¸¿¹ÓIˆÙ˜TŸ]™]¨D×,<Ñ,<¸d×>NÑ>NÓOˆð -3¯L©L¬N÷
ð 
ñ -;Ñ(�Ð(�að à*/¯+©+¬-öá*7Ñ&�B˜˜˜Xß“3õ ä'0°Ô'?ó—3—3’3õ ä'0°Ô'?ó÷ ÷ �Ù*7óò ñ -;õ
ð 	
ùôùö
s*   ÃE.Ã1E'ÄE'Ä'%E'ÅE'Å	E.Å'E.c              #  ó˜  "  € V P                   '       g   / x € R# V P                  '       g   R# V'       dI   V P                  4       pVP                  4        UUUu. uF  w  r4V F  qSV8w  g   K  W53NK  	  K  	  ppppM. pV P	                  \
        P                  R7      p\        ;QJ d*    R VP                  4        4       F  '       g   K   RM	  RM! R VP                  4        4       4      '       dI   V P                  RV P                   p\        R V 4       4      0p	V P                  WvV	4       Rj  x€L
  R# R# u upppi  L5i)aV  
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)rã   c              3   ó8   "  € T F  p\        V4      x € K  	  R # 5ir   )r   )r	   Ú
constraints   & r   r   Ú1ISMAGS.largest_common_subgraph.<locals>.<genexpr>H  s   é € ÐIÑ1H :Œs�:�ˆÓ1HùrÎ   TFc              3   ó4   "  € T F  q F  q"x € K  	  K  	  R # 5ir   r   )r	   ÚprX   s   &  r   r   rÿ   J  s   é € Ð%K±¨AÊÀA¤aÉ¡a³ùs   ‚)rw   rv   rÚ   r.   rÛ   r×   rØ   rÞ   rß   r{   r}   rÝ   Ú_largest_common_subgraph)
r_   rÄ   rä   rX   rå   Úcnrç   Úcandidate_setsÚrelevant_partsÚto_be_mappeds
   &&        r   Úlargest_common_subgraphÚISMAGS.largest_common_subgraph&  s  é € ð$ �}�}ˆ}ØŠHÙØ——�ÙçØ×3Ñ3Ó5ˆFà06·±´ÕW±¡u qËÀ2ÐTVÉwœ7˜A›7É™7±ˆKÒWˆKàˆKà×<Ñ<ÄxÇ{Á{Ð<ÓSˆ÷ ‹3ÑI°×1FÑ1FÔ1HÓI�3�3Š3ÑI°×1FÑ1FÔ1HÓI×IÒIØ!×0Ñ0Ð1E°4×3EÑ3EÐFˆNÜ%Ñ%K±Ó%KÓKÐLˆLØ×4Ñ4Ø¨\ó÷ ò ñ
 ùô Xñùs@   ‚*E
­	E
·$E
ÁEÁ/EÁ:1E
Â,E
Ã(E
Ã7AE
Ä8EÄ9E
c           
     óh  € V P                   V P                  r!V P                  e¸   \        \	        V P
                  P                  4      \	        V P
                  P                  4      \	        \        \        \        4      4      \	        VP                  4       4      V P
                  P                  4       34      pW0P                  9   d   V P                  V,          # V P                  V P
                  W4      pV P                  V P
                  WV4      pV P                  e   W@P                  X&   V# )aî  
Find a minimal set of permutations and corresponding co-sets that
describe the symmetry of ``self.subgraph``, given the node and edge
equalities given by `node_partition` and `edge_colors`, respectively.

Returns
-------
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„   rx   ÚhashÚtuplerw   rz   r€   ÚmapÚnode_partitionr.   rB   Ú_refine_node_partitionÚ _process_ordered_pair_partitions)r_   r(   Úedge_colorsrÖ   rä   s   &    r   rÚ   Ú ISMAGS.analyze_subgraph_symmetryR  s÷   € ð "&×!4Ñ!4°d×6FÑ6F�;à×ÑÒ+Üä˜$Ÿ-™-×-Ñ-Ó.Ü˜$Ÿ-™-×-Ñ-Ó.Üœ#œe¤^Ó4Ó5Ü˜+×+Ñ+Ó-Ó.Ø—M‘M×-Ñ-Ó/ðóˆCð ×*Ñ*Ô*Ø×+Ñ+¨CÕ0Ð0Ø×/Ñ/°·±¸yÓVˆ	Ø×6Ñ6Ø�M‰M˜9°ó
ˆð ×ÑÒ+Ø(.× Ñ  Ñ%Øˆrb   c                óB   € \        V P                  VR7      R4      pVRJ# )aW  Returns True if the class input graphs are isomorphic, False otherwise.

Isomorphic means there is a mapping of nodes from ``graph`` to ``subgraph``
that maintains connectivity and node/edge matchings.
Note: `symmetry` is only used for testing. We only find 1 isomorphism
so there is no gain from looking for symmetries.

Returns
-------
bool
©rÄ   N)r   Úisomorphisms_iter©r_   rÄ   Úisoms   && r   Úis_isomorphicÚISMAGS.is_isomorphicv  s)   € ô �D×*Ñ*°HÐ*Ó=¸tÓDˆØ˜4ÐÐrb   c                óB   € \        V P                  VR7      R4      pVRJ# )a€  Returns True if the class input graphs are subgraph isomorphic, False otherwise.

Subgraph isomorphic means there is a mapping of nodes from an induced
subgraph of ``graph`` to ``subgraph`` that maintains connectivity and
node/edge matching.

Note: `symmetry` is only used for testing. We only find 1 isomorphism
so there is no gain from looking for symmetries.

Returns
-------
bool
r  N)r   Úsubgraph_isomorphisms_iterr  s   && r   Úsubgraph_is_isomorphicÚISMAGS.subgraph_is_isomorphic…  s)   € ô �D×3Ñ3¸XÐ3ÓFÈÓMˆØ˜4ÐÐrb   c                óB   € \        V P                  VR7      R4      pVRJ# )aµ  Returns True if ``graph`` contains a monomorphic copy of ``subgraph``.

All nodes and edges in ``subgraph`` must be matched, respecting node/edge
matching. Extra edges between matched nodes in ``graph`` are allowed.
For example, a triangle contains a three-node path as a non-induced
subgraph.

Note: `symmetry` is only used for testing. We only find 1 monomorphism
so there is no gain from looking for symmetries.

Returns
-------
bool
r  N)r   Úmonomorphisms_iter)r_   rÄ   Úmoms   && r   Úis_monomorphicÚISMAGS.is_monomorphic–  s(   € ô �4×*Ñ*°HÐ*Ó=¸tÓDˆØ˜$ˆÐrb   c              #  ó¦   "  € \        V P                  4      \        V P                  4      8X  d   V P                  VRR7       Rj  x€L
  R# R#  L5i)a"  Yields all isomorphisms from :attr:`graph` to :attr:`subgraph`

An isomorphism is an all-to-all mapping of the nodes that maintains
connectivity and node/edge matchings.

Symmetric isomorphisms can be ignored for the symmetries of :attr:`subgraph`.

Parameters
----------
symmetry : bool, optional (default: True)
    Whether symmetries of :attr:`subgraph` should be taken into account.
    If False, yielded isomorphisms may be symmetrically equivalent.

Yields
------
dict
    The isomorphism mappings in form: ``{graph_node: subgraph_node}``.
rÈ   r¿   N)r   rv   rw   rÂ   rÃ   s   &&r   r  ÚISMAGS.isomorphisms_iter¨  sA   é € ô& ˆt�z‰z‹?œc $§-¡-Ó0Ô0Ø×*Ñ*¨8À%Ð*ÓH×HÒHñ 1ÙHùs   ‚AAÁAÁ	Ac                ó(   € V P                  VRR7      # )ah  Yields all subgraph isomorphisms from :attr:`graph` to :attr:`subgraph`

An isomorphism is a mapping of nodes that maintains connectivity and
node/edge matchings. Subgraph isomorphism means an isomorphism from an
induced subgraph of ``graph`` to ``subgraph``.

Symmetric isomorphisms can be ignored for the symmetries of ``subgraph``.

Parameters
----------
symmetry : bool, optional (default: True)
    Whether symmetries of ``subgraph`` should be taken into account.
    If False, isomorphisms may be symmetrically equivalent.

Yields
------
dict
    The isomorphism mappings in form: {graph_node: subgraph_node}.
r¾   r¿   rÁ   rÃ   s   &&r   r  Ú!ISMAGS.subgraph_isomorphisms_iter¾  s   € ð( ×"Ñ" 8¸%Ð"Ó@Ð@rb   c                ó(   € V P                  VRR7      # )a¬  Yields all monomorphisms from :attr:`graph` to :attr:`subgraph`

Each mapping assigns distinct nodes in ``graph`` to all nodes in
``subgraph``, preserving the connectivity and node/edge matches required
by ``subgraph``. Extra edges between matched nodes in ``graph`` are
allowed, so the matched subgraph need not be induced. For multigraphs,
``graph`` must supply at least the required number of parallel edges
and self-loops, subject to the edge-matching function.

Symmetric monomorphisms can be ignored for the symmetries of ``subgraph``.

Parameters
----------
symmetry : bool, optional (default: True)
    Whether symmetries of ``subgraph`` should be taken into account.
    If False, monomorphisms may be symmetrically equivalent.

Yields
------
dict
    The monomorphism mappings in form: ``{graph_node: subgraph_node}``.

Examples
--------
A triangle contains a path as a non-induced subgraph. Accounting for
the path's reflection symmetry halves the number of reported mappings.

>>> matcher = ISMAGS(nx.cycle_graph(3), nx.path_graph(3))
>>> matcher.subgraph_is_isomorphic()
False
>>> len(list(matcher.monomorphisms_iter(symmetry=False)))
6
>>> len(list(matcher.monomorphisms_iter()))
3
rÉ   r¿   rÁ   rÃ   s   &&r   r  ÚISMAGS.monomorphisms_iterÔ  s   € ðH ×"Ñ" 8¸&Ð"ÓAÐArb   c                ó²  aaa€ V P                    Uu/ uF  q"V P                   P                  W"4      bK   	  upo/ pV P                  P                   F}  pV P                  V,          pWPP
                  8¼  d   \        4       0W4&   K5  V P                  P                  WD4      oVVV3R lV P                  V,           4       p\        V4      0W4&   K  	  V# u upi )aB  
For each node in subgraph store all nodes in graph with same color
and self-loop count. These sets should not change so we cache them.
Stored as a dict-of-set-of-frozenset. The dict is keyed by node to a
collection of frozensets of graph nodes. Each of these frozensets are
a restriction. The node can be mapped only to nodes in the frozenset.
Thus it must be mapped to nodes in the intersection of all these sets.
We store the sets to delay taking the intersection of them. This helps
for two reasons: Firstly any duplicate restriction sets can be ignored;
Secondly, some nodes will not need the intersection to be constructed.
Note: a dict-of-list-of-set would store duplicate sets in the list and
we want to avoid that. But I wonder if checking hash/equality when `add`ing
removes the benefit of avoiding computing intersections.
c              3   óX   <"  € T F  pS! SSV,          4      '       g   K  Vx € K!  	  R # 5ir   r   )r	   rX   ÚLrã   Úloopss   & €€€r   r   Ú8ISMAGS._get_node_color_candidate_sets.<locals>.<genexpr>  s#   øé € ÐTÑ9�q¹YÀqÈ%ÐPQÍ(×=S—’Ó9ùs   ƒ* 
*)rv   Únumber_of_edgesrw   rz   r~   r}   rÝ   r|   )	r_   rã   rX   r  ré   Ú	sgn_colorrº   r*  r+  s	   &f     @@r   rÛ   Ú%ISMAGS._get_node_color_candidate_setsú  s¹   ú€ ð ?C¿jºjÓI¹j¸�D—J‘J×.Ñ.¨qÓ4Ò4¹jÑIˆàˆØ—=‘=×&Ô&ˆCØ×(Ñ(¨Õ-ˆIØ×.Ñ.Ô.ä'0£{ m�Ñ#ÙØ—‘×-Ñ-¨cÓ7ˆAÞT˜D×.Ñ.¨yÖ9ÓTˆAÜ#,¨Q£< .ˆNÓñ 'ð Ðùò Js   ’$Cc                ó¨  a€ V3R  lp\        V4      p\        WV4      o\        ;QJ d    V3R lV 4       F  '       d   K   RM	  RM! V3R lV 4       4      '       gn   V UUu. uFF  p\        V3R lV 4       4      '       d   V.M\	        \        WdRR7      \        R7       F  pVNK  	  KH  	  ppp\        V4      p\        WV4      oK­  V# u uppi )c                 ó*   <€ SV ,          SV,          8H  # r   r   ©Únode1Únode2Úcolor_degrees   &&€r   Úequal_colorÚ2ISMAGS._refine_node_partition.<locals>.equal_color  ó   ø€ Ø Õ&¨,°uÕ*=Ñ=Ð=rb   c              3   óN   <"  € T F  p\        V3R  lV 4       4      x € K  	  R# 5i)c              3   ó6   <"  € T F  pSV,          x € K  	  R # 5ir   r   ©r	   rX   r5  s   & €r   r   Ú:ISMAGS._refine_node_partition.<locals>.<genexpr>.<genexpr>  s   øé € Ð#?¹Q¸ L°§O¢O»Qùó   ƒN©r   ©r	   r  r5  s   & €r   r   Ú0ISMAGS._refine_node_partition.<locals>.<genexpr>  s   øé € ÐSÉÀA”mÔ#?¹QÓ#?×?Ð?ËùrQ   FTc              3   ó6   <"  € T F  pSV,          x € K  	  R # 5ir   r   r;  s   & €r   r   r@  $  s   øé € Ð$C¹d¸ \°!§_¢_»dùr=  ©r/   rÕ   )r6   rY   r   r   Úsortedr1   r   )	Úclsrv   r(   r  r6  Únode_colorsr   r  r5  s	   &&&&    @r   r  ÚISMAGS._refine_node_partition  sÈ   ø€ õ	>ô +¨9Ó5ˆÜ+¨EÀÓLˆß“#ÔSÉÓS—#—#’#ÔSÉÓS×SÒSñ &ôá%�Dô %Ô$C¹dÓ$C×CÒCð ‘Fä¤¨tÈÔ NÔTWÔXòYð ó ñYñ Ù%ð ñ ô /¨yÓ9ˆKÜ/°ÀKÓPŠLØÐùós   Á$ACc              #  óú  aaa a!a"a#"  € V P                   pVP                  pV P                  o!S!P                  o"V P                  pVP	                  4       p	\        S!4      p
\        S!4       UUu/ uF  w  r¼WËbK	  	  ppp/ o#/ pVf   VP                  4       p\        P                  ! VS,          !  pV0VS&   SV\        V4      3.pV'       EdŒ   VR,          w  oppV EFM  o V\        P                  8X  dÉ   \        ;QJ d)    V V"V#V3R lVS,           4       F  '       g   K   RM	  RM! V V"V#V3R lVS,           4       4      '       d   Ko  V	'       dk   \        ;QJ d3    V V!V#V3R lVP                  S,           4       F  '       g   K   RM(	  RM$! V V!V#V3R lVP                  S,           4       4      '       d   Ká  S V9   d   Kê  SS#9   d   S#S,          pVV S S#S&   SVS &   \        S#4      \        V4      8X  d   VP!                  4       x € S#S VS  EK;  VS#P                  4       ,
          pVP!                  4       oV	'       Eg"   VS,          pV\        P"                  8X  d6   \        S"P                  4       S"S ,          P                  4       ,
          4      pV FÆ  pVV9  d.   V\        P"                  8X  d   SV,          X0,          SV&   K5  K7  VP%                  SV4      pVSV3,          pS"S ,           Uu0 uFB  pV P&                  S V3,          V8X  g   K  V! VS!P%                  S V4      4      '       g   K@  VkKD  	  ppSV,          \        V4      0,          SV&   KÈ  	  EMúVS,          pVP                  S,          pV\        P"                  8X  d\   \        S"P                  4       S"S ,          P                  4       ,
          S!P                  S ,          P                  4       ,
          4      pV EFg  pVV9  dÐ   VV9  d.   V\        P"                  8X  d   SV,          X0,          SV&   K=  K?  VP%                  VS4      pVVS3,          pS!P                  S ,           Uu0 uFB  pV P&                  VS 3,          V8X  g   K  V! VS!P%                  VS 4      4      '       g   K@  VkKD  	  ppSV,          \        V4      0,          SV&   KÚ  VV9  dt   VP%                  SV4      pVSV3,          pS"S ,           Uu0 uFB  pV P&                  S V3,          V8X  g   K  V! VS!P%                  S V4      4      '       g   K@  VkKD  	  ppMöVP%                  SV4      pVP%                  VS4      pVSV3,          pS"S ,           Uu0 uFB  pV P&                  S V3,          V8X  g   K  V! VS!P%                  S V4      4      '       g   K@  VkKD  	  ppVVS3,          pTS!P                  S ,           Uu0 uFB  pV P&                  VS 3,          V8X  g   K  V! VS!P%                  VS 4      4      '       g   K@  VkKD  	  up,          pSV,          \        V4      0,          SV&   EKj  	  V Ff  pSV3V9   d   \)        W­S ,          ^,           R 4      pM!VS3V9   d   \)        V
RVS ,           4      pMKI  SV,          \        V4      0,          SV&   Kh  	  \+        VV3R lR7      p\        P                  ! SV,          !  pV'       g   EK)  V0SV&   VP-                  VS\        V4      34        EKh  	  VP/                  4        SS#9   g   EK„  VS#S,           S#S EK”  R# u uppi u upi u upi u upi u upi u upi 5i)	aŠ  
Find all subgraph isomorphisms honoring constraints.
The collection `candidate_sets` is stored as a dict-of-set-of-frozenset.
The dict is keyed by node to a collection of candidate frozensets. Any
viable candidate must belong to all the frozensets in the collection.
So each frozenset added to the collection is a restriction on the candidates.

According to the paper, we store the collection of sets rather than their
intersection to delay computing many intersections with the hope of avoiding
them completely. Having the middle collection be a set also means that
duplicate restrictions on candidates are ignored, avoiding another intersection.
Nc              3   ó~   <"  € T F2  pSV8g  ;'       d!    VS9   ;'       d    SV,          SS,          9  x € K4  	  R # 5ir   r   )r	   Úsgn2rú   Ú	graph_adjÚmappingré   s   & €€€€r   r   Ú$ISMAGS._map_nodes.<locals>.<genexpr>]  sP   øé € ð ñ %6˜Dð ˜t™÷ ?ð ?Ø  G™O÷?ð ?à# D�M°¸2µÑ>ô?ó %6ùs   ƒ=”=¡=TFc              3   ó’   <"  € T F<  pSV8g  ;'       d+    VS9   ;'       d    SV,          SP                   S,          9  x € K>  	  R # 5ir   )rD   )r	   rI  rú   rv   rK  ré   s   & €€€€r   r   rL  e  sY   øé € ð ñ )< ð   4™K÷ Eð EØ $¨¡÷Eð Eà '¨¥°U·[±[Àµ_Ñ DôEó )<ùs   ƒA”A¡&Ac                 ó6   <€ \        R  SV ,           4       4      # )c              3   ó8   "  € T F  p\        V4      x € K  	  R # 5ir   rJ   rÌ   s   & r   r   Ú6ISMAGS._map_nodes.<locals>.<lambda>.<locals>.<genexpr>æ  s   é € Ð2PÁ<¸a´3°q·6°6Ã<ùrÎ   rÏ   rÑ   s   &€r   rÓ   Ú#ISMAGS._map_nodes.<locals>.<lambda>æ  s   ø€ ¬sÑ2PÀ9ÈQÆ<Ó2PÔ/Prb   rÕ   rd   )rw   Ú_adjrv   r„   rB   r¦   r3   ÚkeysrÝ   rà   r   r×   rÙ   rÞ   rD   r   ÚcopyrØ   r-  r…   r-   rÐ   r*   Úpop)$r_   ré   r  rç   rã   r  rw   Úsubgraph_adjÚself_sge_colorsrB   Úgn_ID_to_nodeÚidrX   Úgn_node_to_IDÚrev_mappingÚsgn_candidatesÚqueueÚsgn_cand_iterÚold_gnÚleft_to_mapÚsgn_nbrsÚnot_gn_nbrsrI  Ú	sgn_e_cntÚ	sge_colorÚ	gn2_candsÚ	sgn_predsÚsgn_inÚsgn_outÚnew_sgnÚnew_sgn_candidatesrÒ   rú   rv   rJ  rK  s$   &f&&&&                         @@@@@r   rá   ÚISMAGS._map_nodes,  s%  ýé € ð  —=‘=ˆØ—}‘}ˆØ—
‘
ˆØ—J‘Jˆ	Ø×*Ñ*ˆØ×*Ñ*Ó,ˆä˜U›ˆÜ,5°eÔ,<Ô=Ñ,<¡5 2˜šÑ,<ˆÑ=àˆØˆØÒØ'×,Ñ,Ó.ˆLô #×/Ò/°ÀÕ1DÑEˆØ-Ð.ˆ�sÑØ�~¤t¨NÓ';Ð<Ð=ˆßˆeØ16°rµÑ.ˆC� ä#�Ø¤§¡Ô+ß“s÷ ð %1°Ö$5ó	—s—s’s÷ ð %1°Ö$5ó	÷ ò ñ !ß"ß›3÷ ð )1¯©°sÖ(;ó	Ÿ3Ÿ3š3÷ ð )1¯©°sÖ(;ó	÷ ò ñ %à˜Ô$Ùð ˜'”>Ø$ S�\�FØ# FÐ+Ø!�˜‘Ø"%�˜B‘ä�w“<¤3 |Ó#4Ô4Ø%×*Ñ*Ó,Ò,Ø ˜Ø# B˜ÚØ*¨W¯\©\«^Õ;�ð +×/Ñ/Ó1�	÷ #�{Ø+¨CÕ0�HØ ¤H§K¡KÔ/Ü&/°	·±Ó0@À9ÈRÅ=×CUÑCUÓCWÕ0WÓ&X˜Û +˜Ø xÔ/à(¬H¯K©KÔ7Ø2;¸Dµ/À[ÀMÕ2Q 	¨$£ñ  8ð
 )1×(@Ñ(@ÀÀdÓ(K˜IØ(7¸¸T¸	Õ(B˜Ið *3°2®ó)á)6 AØ#'§?¡?°2°q°5Õ#9¸YÑ#Fô !"ñ $-¨Y¸×8MÑ8MÈbÐRSÓ8T×#U÷ !" Ù)6ð &ð )ð /8¸­oÄÈ9ÓAUÐ@VÕ.V˜I d›Oó# !,ð&  ,¨CÕ0�HØ (§¡¨sÕ 3�IØ ¤H§K¡KÔ/Ü&/Ø%ŸN™NÓ,Ø'¨�m×0Ñ0Ó2õ3à#Ÿk™k¨"�o×2Ñ2Ó4õ5ó'˜ô
 !,˜à xÔ/Ø#¨9Ô4Ø#,´·±Ô#;à6?ÀµoÈÈÕ6U I¨d£Oñ $<ð *2×)AÑ)AÀ$ÈÓ)L Ø,;¸DÀ#¸IÕ,F 	ð .3¯[©[¸®_ó-"á-<¨Ø'+§¡°q¸"°uÕ'=ÀÑ'Jô %&ñ (1°¸×9NÑ9NÈqÐRTÓ9U×'V÷ %& AÙ-<ð !*ð -"ð 3<¸Dµ/Ü$-¨iÓ$8ðE"õ 3" 	¨$£ð  $¨9Ô4Ø*2×*BÑ*BÀ3ÈÓ*M Ø,;¸CÀ¸IÕ,F 	ð .7°r®]ó-"á-:¨Ø'+§¡°r¸1°uÕ'=ÀÑ'Jô %&ñ (1°¸%×:OÑ:OÐPRÐTUÓ:V×'W÷ %& AÙ-:ð !*ð -" 	ð +3×*BÑ*BÀ3ÈÓ*M Ø)1×)AÑ)AÀ$ÈÓ)L à,;¸CÀ¸IÕ,F 	ð .7°r®]ó-"á-:¨Ø'+§¡°r¸1°uÕ'=ÀÑ'Jô %&ñ (1°¸%×:OÑ:OÐPRÐTUÓ:V×'W÷ %& AÙ-:ð !*ð -"ð -<¸DÀ#¸IÕ,F 	Ø )à-2¯[©[¸®_ó."á-<¨Ø'+§¡°q¸"°uÕ'=ÀÑ'Jô %&ñ (1°¸×9NÑ9NÈqÐRTÓ9U×'V÷ %& AÙ-<ñ."õ !" 	ð /8¸­oÄÈ9ÓAUÐ@VÕ.V˜I dœOñc !,óf (�Dà˜T�{ kÔ1Ü$'¨ÀBÕ6GÈ!Õ6KÐ6MÐ(NÓ$O™	Ø ˜¨Ô3Ü$'¨Ð6I¸ÀbÕ8IÐ(JÓ$K™	á à&/°¥o¼À9Ó9MÐ8NÕ&N�I˜d“Oñ (ô ØÔ%Pô�ô &/×%;Ò%;¸YÀwÕ=OÑ%PÐ"ß)ÚØ&8Ð%9�	˜'Ñ"Ø—‘˜g y´$Ð7IÓ2JÐKÔLÛñe $ðh —	‘	”Ø˜'—>Ø# G¨C¥LÐ1Ø ›ñu ùó) >ùò^)ùò6-"ùò-"ùò-"ùò."ùsÌ   ˆA&];Á.]Á;A];Ã2];Ä];Ä#'];Å	];Å
];Å (];Æ1];Æ=B];É	B4];Ë=]"Ì]"Ì?]"ÍD$];Ñ)]'Ò
]'Ò+]'Ò1A];Ô],Ô"],Õ],Õ	=];Ö]1Ö']1×]1×#];×1]6
Ø]6
Ø3]6
Ø9D];Ý
1];c           	   #  ó¢  a"  € \         P                  pVf!   \        V P                  P                  4      0p\        \        \        V4      . 4      4      pRpV\        V P                  4      8:  d[   \        V\        R7       FE  p\        VV3R lR7      pV P                  VSW$VR7      p	 \        V	4      p
V
x € T	 Rj  x€L
  RpKG  	  V'       g   V^8X  d   R# \        4       pV F/  pV F&  pV P                  WÇV4      pVP                  V4       K(  	  K1  	  V P!                  SW+R7       Rj  x€L
  R#  Lw  \         d     KÆ  i ; i L5i)z9
Find all largest common subgraphs honoring constraints.
NFrÕ   c                 ó6   <€ \        R  SV ,           4       4      # )c              3   ó8   "  € T F  p\        V4      x € K  	  R # 5ir   rJ   rÌ   s   & r   r   ÚDISMAGS._largest_common_subgraph.<locals>.<lambda>.<locals>.<genexpr>  s   é € Ð7VÉÀ1¼¸A¿¸ËùrÎ   rÏ   )rX   Ú
candidatess   &€r   rÓ   Ú1ISMAGS._largest_common_subgraph.<locals>.<lambda>  s   ø€ ´CÑ7VÈ
ÐSTÎÓ7VÔ4Vrb   )r  T)r×   rØ   rÝ   rw   rz   r   r   r   rv   rC  rÐ   rá   ÚStopIterationr-   Ú_remove_noder)   r  )r_   rp  rç   r  rã   Úcurrent_sizeÚ	found_isorz   Únext_sgnÚ	isomorphsr
   Úleft_to_be_mappedré   Ú	new_nodess   &f&&          r   r  ÚISMAGS._largest_common_subgraphô  sK  øé € ô —K‘Kˆ	àÒÜ% d§m¡m×&9Ñ&9Ó:Ð;ˆLô œ4¤ \Ó 2°BÓ7Ó8ˆàˆ	Øœ3˜tŸz™z›?Ô*ô   ´&×9�ä˜uÔ*VÔW�Ø ŸO™OØ˜j¨+Èuð ,ó �	ð%Ü 	›?�Dð ’JØ(×(Ð(Ø $’Iñ! :÷& ˜¨Ô)ñ ä›EÐÛ!ˆEÛ�ð !×-Ñ-¨c¸+ÓF�	Ø!×%Ñ% iÖ0ó ñ "ð ×0Ñ0Ø˜ð 1ó 
÷ 	
ò 	
ñ3 )øô	 %ô Úðúñ:	
ùsI   ƒB)EÂ-D;Â8	EÃD9ÃEÃAEÄ2EÄ3EÄ;E
ÅEÅ	E
Å
Ec                ód   €  V F  w  r4W08X  g   K  WA9   g   K  Tp  K  	  \        W0,
          4      # )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Ý   )r5   rz   rç   ÚlowÚhighs   &&&  r   rs  ÚISMAGS._remove_node8  s5   € ð Û(‘	�Ø–; 4¦=Ø�DÚñ )ô
 ! ¨¥Ó0Ð0rb   c                óÖ   a€ \        \        4      oV  F$  pS\        V4      ,          P                  V4       K&  	  \        \        P
                  ! V3R l\        S4       4       !  4      # )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   ó^   <"  € T F"  p\         P                  ! SV,          4      x € K$  	  R # 5ir   )r   Úpermutations)r	   ÚlÚby_lens   & €r   r   Ú5ISMAGS._get_permutations_by_length.<locals>.<genexpr>^  s#   øé € ÐL¹^¸”)×(Ò(¨°­×3Ð3»^ùs   ƒ*-)r   r¦   r   r*   r   r%   rC  )r.   r
   rƒ  s   & @r   Ú_get_permutations_by_lengthÚ"ISMAGS._get_permutations_by_lengthH  sY   ø€ ô  œTÓ"ˆÛˆDØ”3�t“9Õ×$Ñ$ TÖ*ñ ô Ü×ÒÜL¼VÀF¼^ÓLñó
ð 	
rb   c           
   #  óþ  a"  € V3R  lpV P                  WV4      pV.pV'       EdÉ   VP                  4       p\        V4      p\        WV4      o\        ;QJ d    V3R lV 4       F  '       d   K   RM	  RM! V3R lV 4       4      '       d!   \        V4      \        V4      8X  d   W#3x € K�  . .pV EF  p	\        V	4      ^8X  g   \        V3R lV	 4       4      '       d   V F  p
V
P                  V	4       K  	  KK  \        W•RR7      p\        V4      pV^8X  g*   T\        V Uu0 uF  p\        V4      kK  	  up4      8X  d,   V F#  pVP                  \        V\
        R7      4       K%  	  K¾  V P                  V4      p. pV F@  pV F7  pV UUu. uF  pV F  pVNK  	  K  	  pppVP                  VV,           4       K9  	  KB  	  TpEK  	  VP                  VRRR1,          4       EKÑ  R# u upi u uppi 5i)	c                 ó*   <€ SV ,          SV,          8H  # r   r   r2  s   &&€r   r6  Ú'ISMAGS._refine_opp.<locals>.equal_colorc  r8  rb   c              3   óN   <"  € T F  p\        V3R  lV 4       4      x € K  	  R# 5i)c              3   ó6   <"  € T F  pSV,          x € K  	  R # 5ir   r   r;  s   & €r   r   Ú/ISMAGS._refine_opp.<locals>.<genexpr>.<genexpr>m  s   øé € Ð <¹!°Q ¨a§¢»!ùr=  Nr>  r?  s   & €r   r   Ú%ISMAGS._refine_opp.<locals>.<genexpr>m  s   øé € ÐMÁfÀ”=Ô <¹!Ó <×<Ð<ÃfùrQ   FTc              3   ó6   <"  € T F  pSV,          x € K  	  R # 5ir   r   )r	   r5   r5  s   & €r   r   r�  v  s   øé € Ð2WÑRVÈ$°<À×3EÒ3EÓRVùr=  rB  rÕ   Nrd   )r  rU  r6   rY   r   r   r   r*   r1   ÚextendrC  r…  )rD  rv   ÚtopÚbottomr  r6  Úpossible_bottomsrE  Úmore_bottomsr   Ú
new_bottomÚrefined_partÚRr  Ún_pr�  Únew_partitionsÚnew_partitionÚtupÚsÚflat_pr5  s   &&&&&                @r   Ú_refine_oppÚISMAGS._refine_oppb  s½  øé € õ	>ð ×(Ñ(¨°[ÓAˆà"˜8ÐßÐØ%×)Ñ)Ó+ˆFÜ.¨vÓ6ˆKÜ/°ÀKÓPˆLß‹sÔMÁfÓM�s�sŠsÔMÁfÓM×MÒMÜ�s“8œs 6›{Ô*Ø˜+Ò%ñ à˜4ˆLÜ�Ü�t“9 ”>¤]Ô2WÑRVÓ2W×%WÒ%WÛ&2˜
Ø"×)Ñ)¨$Ö/ó '3ô $2°$È5Ô#Q�LÜ˜LÓ)�AØ˜A”v ¤c¹<Ó*H¹<°a¬3¨q®6¹<Ñ*HÓ&IÔ!Iã#/˜CØŸJ™J¤v¨lÄÔ'DÖEó $0ð (+×'FÑ'FÀ|Ó'T˜ð *,˜Û-9˜MÛ%1 á78Ô)F±q°Ã#¸Q«!Á#©!±q Ñ)FØ .× 5Ñ 5°mÀfÕ6LÖ Mó &2ñ .:ð
 (6›ñ5 ð: ×#Ñ# L±°2°Õ$6×7ñS ùò( +Iùó  *Gùs2   ƒ$G=¨AG=Á1G=ÂBG=Ä(G2Ä<AG=ÆG7Æ(AG=c                óD  € \        4       p\        W4       F†  w  r4\        V4      ^8”  g   \        V4      ^8”  d   W48X  d   K-  \        RV  RV 24      hW48w  g   KF  VP	                  \        \        \        V4      4      \        \        V4      4      34      4       Kˆ  	  V# )a  
Return a set of 2-tuples of nodes. These nodes are not equal
but are mapped to each other in the symmetry represented by this OPP.
Swapping all the 2-tuples of nodes in this set permutes the nodes
but retains the graph structure. Thus it is a symmetry of the subgraph.
z/Not all nodes are matched. This is impossible: z, )r-   r¥   r   Ú
IndexErrorr)   rÝ   r   r   )Útop_partitionÚbottom_partitionr�  r�  Úbots   &&   r   Ú_find_permutationsÚISMAGS._find_permutations”  s—   € ô “uˆÜ˜MÖ<‰HˆCÜ�3‹x˜!Œ|œs 3›x¨!œ|ð
 ”:ÙÜ ð$Ø$1 ?°"Ð5EÐ4FðHóð ð
 –Ø× Ñ ¤¬D´°c³«O¼TÄ$ÀsÃ)»_Ð+MÓ!NÖOñ =ð Ðrb   c           	     ó‚  a"a#a$€ \         ;QJ d    R  V 4       F  '       d   K   RM	  RM! R  V 4       4      '       d   / # Rp\        V4       UUu/ uF  w  rgWvbK	  	  pppV Uu. uF  qw0NK  	  p	p/ p
\        V4       UUu/ uF  w  r¼WËbK	  	  uppo"S"P                  o$V"V$3R lp. pV! WâV4       V'       Edl   VR,          w  pppppV EF  pVV8w  d   W‡,          VV,          8X  d   K!  V0pV0pV Uu. uF  pVP                  4       NK  	  ppVV;;,          V,          uu&   VP	                  VV4       V Uu. uF  pVP                  4       NK  	  ppVV;;,          V,          uu&   VP	                  VV4       V P                  VVVV4      p. pV EF.  pV'       dN   \         ;QJ d#    R V^ ,           4       F  '       d   K   RM	  RM! R V^ ,           4       4      '       d   RpKX  MË\         ;QJ d#    R \        V!   4       F  '       d   K   RM	  RM! R \        V!   4       4      '       d„   V P                  ! V!  pV Fj  w  ppVV,          o#VV,          p S#V 8w  g   K!  V	V ,          p!V	S#,          P                  V!4       \        4       V	V &   VP                  V#3R lV! 4       4       Kl  	  EK$  V! V.VO5!   EK1  	  VP                  VRRR1,          4        EK7  	  VP                  4        Wz9  g   EKR  W˜V,          ,          P                  4       W§&   EKt  V
# u uppi u upi u uppi u upi u upi )	c              3   ó>   "  € T F  p\        V4      ^8*  x € K  	  R# 5irO   rJ   ©r	   r�  s   & r   r   Ú:ISMAGS._process_ordered_pair_partitions.<locals>.<genexpr>¶  s   é € Ð6© Œs�3‹x˜1Ž}«ùó   ‚FTc                 ó²   <€ V	3R  l\        V4       4       p\        V4      w  rEpW&,          p\        \        VS
R7      4      pV P	                  WWVV.4       R# )c              3   ót   <"  € T F-  w  rV F"  p\        V4      ^8”  g   K  SV,          W13x € K$  	  K/  	  R# 5irO   rJ   )r	   rï   Út_partr5   Ú
node_to_IDs   &   €r   r   ÚZISMAGS._process_ordered_pair_partitions.<locals>._load_next_queue_entry.<locals>.<genexpr>Æ  s>   øé € ð á#;‘K�CÛ"�DÜ�v“; ‘?ô .�˜DÕ! 4Õ-á"ñ .Û#;ùs   ƒ8¡8rÕ   N)r3   rÐ   r   rC  r*   )r]  r¡  r¢  Úunmapped_nodesrø   r5   Úpart_iÚb_partÚ
node2_iterr®  Ú
sort_by_IDs   &&&      €€r   Ú_load_next_queue_entryÚGISMAGS._process_ordered_pair_partitions.<locals>._load_next_queue_entryÄ  sS   ø€ ôä#,¨]Ô#;óˆNô " .Ó1‰OˆA�VØ%Õ-ˆFÜœf V°Ô<Ó=ˆJà�L‰L˜-¸4ÈÐTÖUrb   c              3   ó>   "  € T F  p\        V4      ^8*  x € K  	  R# 5irO   rJ   r¨  s   & r   r   r©  õ  s   é € Ð?¹°œs 3›x¨1ž}»ùrª  c              3   óZ   "  € T F!  w  r\        V4      ^8*  ;'       g    W8H  x € K#  	  R# 5irO   rJ   )r	   ÚtÚbs   &  r   r   r©  ù  s'   é € ÐI¹y±t°qœS ›V q™[×2Ð2¨A©FÔ2»yùs   ‚+ž+c              3   ó*   <"  € T F  qS3x € K
  	  R # 5ir   r   )r	   rX   Úorb1s   & €r   r   r©    s   øé € Ð/NÁ:¸a°Dµ	Ã:ùs   ƒNrd   )r   r3   rf   rT  Úinsertr�  r¥   r¤  Úupdater-   r�  rU  )%r_   rv   r¡  r¢  r  Úfinding_identityÚorbit_ir5   Úorbit_idÚorbitsrä   ÚirX   rµ  r]  ÚtopsÚbottomsr±  r³  r4  Únew_top_partÚnew_bot_partr�  Únew_topr£  Únew_botÚoppsÚnew_qÚoppr�  Ún1Ún2Úorb2Ú
orbit_set2r®  r¼  r´  s%   &&&&&                             @@@r   r  Ú'ISMAGS._process_ordered_pair_partitions¯  sÛ  ú€ ÷ ‹3Ñ6©Ó6�3�3Š3Ñ6©Ó6×6Ò6àˆIð  Ðä7@ÀÔ7GÔHÑ7G¡m g�D’MÑ7GˆÑHÙ%*Ó+¡U˜T“&¡UˆÐ+Øˆä'0°Ô'7Ô8Ñ'7™t˜q�a’dÑ'7Ò8ˆ
Ø×+Ñ+ˆ
ö	Vð ˆÙ˜uÐ5EÔFçˆeØ6;¸BµiÑ3ˆD�'˜4 ¨ä#�Ø˜5”= X¥^°xÀµÔ%Fáð !%˜v�Ø %˜w�á15Ó6±¨#˜3Ÿ8™8ž:±�Ð6Ø˜— <Õ/“Ø—‘˜v |Ô4á18Ó9±¨#˜3Ÿ8™8ž:±�Ð9Ø˜— <Õ/“Ø—‘˜v |Ô4ð ×'Ñ'¨¨w¸ÀÓM�Ø�Ü�C÷ (÷ ›3Ñ?¸¸A¾Ó?Ÿ3Ÿ3š3Ñ?¸¸A¾Ó?×?Ò?à/4Ð,Ù$ð @÷ ›ÑI¼sÀCºyÓIŸŸšÑI¼sÀCºyÓI×IÒIð (,×'>Ò'>ÀÑ'D˜Û&2™F˜B Ø#+¨B¥<˜DØ#+¨B¥<˜DØ# tž|Ø-3°D­\ 
Ø & t¥× 3Ñ 3°JÔ ?Ü/2«u  t¡Ø (§¡Ô/NÁ:Ó/NÖ Nñ '3ò !á*¨5Ð7°3×7ñ7  ð: —‘˜U¡4 R 4�[Ô)Ûñe $ðh —	‘	”Ø×%ð $*°4­.Õ#9×#>Ñ#>Ó#@�F”LØˆùók IùÚ+ùó 9ùò@ 7ùò :s   ÁL&Á$L,ÂL1Ã5L7Ä;L<)rƒ   r}   r…   r‚   r   r|   r„   r�   r~   r{   rx   rv   rw   )NNN©T)Fr   )rj   rk   rl   rm   rn   r`   ry   rÅ   rÂ   rÜ   r  rÚ   r  r  r   r  r  r  rÛ   Úclassmethodr  rá   r  Ústaticmethodrs  r…  r�  r¤  r  ro   rp   rq   s   @r   r   r   z  sÎ   ø‡ € ñiôV5Sòn]*ô~ò
HòT
ô6*òX"ôH ô ô"ô$Iô,Aô,$BòLð: ñó ðô(F%ôPB
ðH ñ1ó ð1ð ñ
ó ð
ò208ðd ñó ð÷4cð crb   rÒ  )rn   Ú__all__r   r×   Úcollectionsr   r   Únetworkxr+   r   r1   r6   rG   rY   r[   r   r   rb   r   Ú<module>rØ     sU   ðñxðt ˆ*€ã Û ß ,ã ò3ô<A-òHLò"ò>C÷L&ñ &÷<Xó Xrb   