
    h1                     :   S r SSKJrJr  SSKJr  SSKrSS/rS r	S r
SS	 jr\R                  R                  S
5      \R                  " SS0SS9 SS j5       5       r\R                  R                  S
5      \R                  " SS0SS9     SS j5       5       rg)z
Functions for hashing graphs to strings.
Isomorphic graphs should be assigned identical hashes.
For now, only Weisfeiler-Lehman hashing is implemented.
    )Counterdefaultdict)blake2bNweisfeiler_lehman_graph_hash!weisfeiler_lehman_subgraph_hashesc                 P    [        U R                  S5      US9R                  5       $ )Nascii)digest_size)r   encode	hexdigest)labelr
   s     S/var/www/html/env/lib/python3.13/site-packages/networkx/algorithms/graph_hashing.py_hash_labelr      s!    5<<(kBLLNN    c           	      $   U(       a0  U R                  SS9 VVs0 s H  u  p4U[        XB   5      _M     snn$ U(       a  U  Vs0 s H  o3S_M     sn$ U R                  5        VVs0 s H  u  p5U[        U5      _M     snn$ s  snnf s  snf s  snnf )NT)data )nodesstrdegree)G	edge_attr	node_attrudddegs         r   _init_node_labelsr      s    347773EF3E%!3r}%%3EFF	 !q!2q!!*+((*5*3s8*55	 G!5s   BB%Bc                     / nU R                  U5       H1  nUc  SO[        X   U   U   5      nUR                  XbU   -   5        M3     X!   SR                  [	        U5      5      -   $ )zW
Compute new labels for given node by aggregating
the labels of each node's neighbors.
r   )	neighborsr   appendjoinsorted)r   nodenode_labelsr   
label_listnbrprefixs          r   _neighborhood_aggregater(      sl    
 J{{4  (c!'#,y2I.J&s#334 ! rwwvj'9:::r   
multigraphr   r   )
edge_attrs
node_attrsc                    ^ SU4S jjn[        XU5      n/ n[        U5       HK  nU" XUS9n[        UR                  5       5      n	UR	                  [        U	R                  5       S S95        MM     [        [        [        U5      5      T5      $ )aS  Return Weisfeiler Lehman (WL) graph hash.

The function iteratively aggregates and hashes neighborhoods of each node.
After each node's neighbors are hashed to obtain updated node labels,
a hashed histogram of resulting labels is returned as the final hash.

Hashes are identical for isomorphic graphs and strong guarantees that
non-isomorphic graphs will get different hashes. See [1]_ for details.

If no node or edge attributes are provided, the degree of each node
is used as its initial label.
Otherwise, node and/or edge labels are used to compute the hash.

Parameters
----------
G : graph
    The graph to be hashed.
    Can have node and/or edge attributes. Can also have no attributes.
edge_attr : string, optional (default=None)
    The key in edge attribute dictionary to be used for hashing.
    If None, edge labels are ignored.
node_attr: string, optional (default=None)
    The key in node attribute dictionary to be used for hashing.
    If None, and no edge_attr given, use the degrees of the nodes as labels.
iterations: int, optional (default=3)
    Number of neighbor aggregations to perform.
    Should be larger for larger graphs.
digest_size: int, optional (default=16)
    Size (in bits) of blake2b hash digest to use for hashing node labels.

Returns
-------
h : string
    Hexadecimal string corresponding to hash of the input graph.

Examples
--------
Two graphs with edge attributes that are isomorphic, except for
differences in the edge labels.

>>> G1 = nx.Graph()
>>> G1.add_edges_from(
...     [
...         (1, 2, {"label": "A"}),
...         (2, 3, {"label": "A"}),
...         (3, 1, {"label": "A"}),
...         (1, 4, {"label": "B"}),
...     ]
... )
>>> G2 = nx.Graph()
>>> G2.add_edges_from(
...     [
...         (5, 6, {"label": "B"}),
...         (6, 7, {"label": "A"}),
...         (7, 5, {"label": "A"}),
...         (7, 8, {"label": "A"}),
...     ]
... )

Omitting the `edge_attr` option, results in identical hashes.

>>> nx.weisfeiler_lehman_graph_hash(G1)
'7bc4dde9a09d0b94c5097b219891d81a'
>>> nx.weisfeiler_lehman_graph_hash(G2)
'7bc4dde9a09d0b94c5097b219891d81a'

With edge labels, the graphs are no longer assigned
the same hash digest.

>>> nx.weisfeiler_lehman_graph_hash(G1, edge_attr="label")
'c653d85538bcf041d88c011f4f905f10'
>>> nx.weisfeiler_lehman_graph_hash(G2, edge_attr="label")
'3dcd84af1ca855d0eff3c978d88e7ec7'

Notes
-----
To return the WL hashes of each subgraph of a graph, use
`weisfeiler_lehman_subgraph_hashes`

Similarity between hashes does not imply similarity between graphs.

References
----------
.. [1] Shervashidze, Nino, Pascal Schweitzer, Erik Jan Van Leeuwen,
   Kurt Mehlhorn, and Karsten M. Borgwardt. Weisfeiler Lehman
   Graph Kernels. Journal of Machine Learning Research. 2011.
   http://www.jmlr.org/papers/volume12/shervashidze11a/shervashidze11a.pdf

See also
--------
weisfeiler_lehman_subgraph_hashes
c                 j   > 0 nU R                  5        H  n[        XXS9n[        UT5      X4'   M     U$ )zl
Apply neighborhood aggregation to each node
in the graph.
Computes a dictionary with labels for each node.
r   )r   r(   r   )r   labelsr   
new_labelsr#   r   r
   s         r   weisfeiler_lehman_step<weisfeiler_lehman_graph_hash.<locals>.weisfeiler_lehman_step   s=     
GGID+AVQE*5+>J  r   r.   c                     U S   $ )Nr    )xs    r   <lambda>.weisfeiler_lehman_graph_hash.<locals>.<lambda>   s    !A$r   )keyN)
r   ranger   valuesextendr"   itemsr   r   tuple)
r   r   r   
iterationsr
   r1   r$   subgraph_hash_counts_counters
       `     r   r   r   (   s    D
 $A)<K:,QyQ+,,./##F7==?$OP	  s5!567EEr   c           	        ^ SU4S jjn[        XU5      nU(       a1  UR                  5        VV	s0 s H  u  pU[        U	T5      /_M     n
nn	O[        [        5      n
[        U5       H  nU" XX5      nM     [        U
5      $ s  sn	nf )a5  
Return a dictionary of subgraph hashes by node.

Dictionary keys are nodes in `G`, and values are a list of hashes.
Each hash corresponds to a subgraph rooted at a given node u in `G`.
Lists of subgraph hashes are sorted in increasing order of depth from
their root node, with the hash at index i corresponding to a subgraph
of nodes at most i edges distance from u. Thus, each list will contain
`iterations` elements - a hash for a subgraph at each depth. If
`include_initial_labels` is set to `True`, each list will additionally
have contain a hash of the initial node label (or equivalently a
subgraph of depth 0) prepended, totalling ``iterations + 1`` elements.

The function iteratively aggregates and hashes neighborhoods of each node.
This is achieved for each step by replacing for each node its label from
the previous iteration with its hashed 1-hop neighborhood aggregate.
The new node label is then appended to a list of node labels for each
node.

To aggregate neighborhoods for a node $u$ at each step, all labels of
nodes adjacent to $u$ are concatenated. If the `edge_attr` parameter is set,
labels for each neighboring node are prefixed with the value of this attribute
along the connecting edge from this neighbor to node $u$. The resulting string
is then hashed to compress this information into a fixed digest size.

Thus, at the $i$-th iteration, nodes within $i$ hops influence any given
hashed node label. We can therefore say that at depth $i$ for node $u$
we have a hash for a subgraph induced by the $i$-hop neighborhood of $u$.

The output can be used to create general Weisfeiler-Lehman graph kernels,
or generate features for graphs or nodes - for example to generate 'words' in
a graph as seen in the 'graph2vec' algorithm.
See [1]_ & [2]_ respectively for details.

Hashes are identical for isomorphic subgraphs and there exist strong
guarantees that non-isomorphic graphs will get different hashes.
See [1]_ for details.

If no node or edge attributes are provided, the degree of each node
is used as its initial label.
Otherwise, node and/or edge labels are used to compute the hash.

Parameters
----------
G : graph
    The graph to be hashed.
    Can have node and/or edge attributes. Can also have no attributes.
edge_attr : string, optional (default=None)
    The key in edge attribute dictionary to be used for hashing.
    If None, edge labels are ignored.
node_attr : string, optional (default=None)
    The key in node attribute dictionary to be used for hashing.
    If None, and no edge_attr given, use the degrees of the nodes as labels.
    If None, and edge_attr is given, each node starts with an identical label.
iterations : int, optional (default=3)
    Number of neighbor aggregations to perform.
    Should be larger for larger graphs.
digest_size : int, optional (default=16)
    Size (in bits) of blake2b hash digest to use for hashing node labels.
    The default size is 16 bits.
include_initial_labels : bool, optional (default=False)
    If True, include the hashed initial node label as the first subgraph
    hash for each node.

Returns
-------
node_subgraph_hashes : dict
    A dictionary with each key given by a node in G, and each value given
    by the subgraph hashes in order of depth from the key node.

Examples
--------
Finding similar nodes in different graphs:

>>> G1 = nx.Graph()
>>> G1.add_edges_from([(1, 2), (2, 3), (2, 4), (3, 5), (4, 6), (5, 7), (6, 7)])
>>> G2 = nx.Graph()
>>> G2.add_edges_from([(1, 3), (2, 3), (1, 6), (1, 5), (4, 6)])
>>> g1_hashes = nx.weisfeiler_lehman_subgraph_hashes(
...     G1, iterations=3, digest_size=8
... )
>>> g2_hashes = nx.weisfeiler_lehman_subgraph_hashes(
...     G2, iterations=3, digest_size=8
... )

Even though G1 and G2 are not isomorphic (they have different numbers of edges),
the hash sequence of depth 3 for node 1 in G1 and node 5 in G2 are similar:

>>> g1_hashes[1]
['a93b64973cfc8897', 'db1b43ae35a1878f', '57872a7d2059c1c0']
>>> g2_hashes[5]
['a93b64973cfc8897', 'db1b43ae35a1878f', '1716d2a4012fa4bc']

The first 2 WL subgraph hashes match. From this we can conclude that it's very
likely the neighborhood of 2 hops around these nodes are isomorphic.

However the 3-hop neighborhoods of ``G1`` and ``G2`` are not isomorphic since the
3rd hashes in the lists above are not equal.

These nodes may be candidates to be classified together since their local topology
is similar.

Notes
-----
To hash the full graph when subgraph hashes are not needed, use
`weisfeiler_lehman_graph_hash` for efficiency.

Similarity between hashes does not imply similarity between graphs.

References
----------
.. [1] Shervashidze, Nino, Pascal Schweitzer, Erik Jan Van Leeuwen,
   Kurt Mehlhorn, and Karsten M. Borgwardt. Weisfeiler Lehman
   Graph Kernels. Journal of Machine Learning Research. 2011.
   http://www.jmlr.org/papers/volume12/shervashidze11a/shervashidze11a.pdf
.. [2] Annamalai Narayanan, Mahinthan Chandramohan, Rajasekar Venkatesan,
   Lihui Chen, Yang Liu and Shantanu Jaiswa. graph2vec: Learning
   Distributed Representations of Graphs. arXiv. 2017
   https://arxiv.org/pdf/1707.05005.pdf

See also
--------
weisfeiler_lehman_graph_hash
c                    > 0 nU R                  5        H0  n[        XXS9n[        UT5      nXtU'   X%   R                  U5        M2     U$ )z
Apply neighborhood aggregation to each node
in the graph.
Computes a dictionary with labels for each node.
Appends the new hashed label to the dictionary of subgraph hashes
originating from and indexed by each node in G
r.   )r   r(   r   r    )	r   r/   node_subgraph_hashesr   r0   r#   r   hashed_labelr
   s	           r   r1   Aweisfeiler_lehman_subgraph_hashes.<locals>.weisfeiler_lehman_step+  sS     
GGID+AVQE&uk:L+t &--l;	 
 r   r9   )r   r=   r   r   listr:   dict)r   r   r   r?   r
   include_initial_labelsr1   r$   kvrE   rA   s       `       r   r   r      s    N  $A)<K9D9J9J9L 
9LAA{+,,9L 	  
  +40:,0
 
 $%% 
s   Br9   )NN      )NNrM   rN   F)__doc__collectionsr   r   hashlibr   networkxnx__all__r   r   r(   utilsnot_implemented_for_dispatchabler   r   r4   r   r   <module>rX      s    -  )+N
OO6	; l+k40[IACwF J ,wFt l+k40[I  b& J ,b&r   