U
    ¹mœdØ  ã                   @   sˆ   d Z ddlmZ ddlmZ ddlmZ ddlZddl	m
Z
 ddlmZ e
ZdgZed	ƒdd
d„ƒZdd„ Zdd„ Zdd„ Zdd„ ZdS )z,
Moody and White algorithm for k-components
é    )Údefaultdict)Úcombinations)Ú
itemgetterN)Úedmonds_karp)Únot_implemented_forÚk_componentsZdirectedc              	      s¦  t tƒ}|dkrt}t ˆ ¡D ]&}t|ƒ}t|ƒdkr|d  |¡ q‡ fdd„t ˆ ¡D ƒ}|D ]&}t|ƒ}t|ƒdkrb|d  |¡ qb|D �]}t|ƒdkr¢qŽtj	||d�}	|	dkrÊ||	  t|ƒ¡ ttj
||	|d�ƒ}
|	t||
|	ƒfg}|rŽ|d \}}zzt|ƒ}| |¡}tj	||d�}||k�rH|dk�rH||  t|ƒ¡ ttj
|||d�ƒ}
|
�rx| |t||
|ƒf¡ W qð tk
�r˜   | ¡  Y qðX qðqŽt|ƒS )	a7  Returns the k-component structure of a graph G.

    A `k`-component is a maximal subgraph of a graph G that has, at least,
    node connectivity `k`: we need to remove at least `k` nodes to break it
    into more components. `k`-components have an inherent hierarchical
    structure because they are nested in terms of connectivity: a connected
    graph can contain several 2-components, each of which can contain
    one or more 3-components, and so forth.

    Parameters
    ----------
    G : NetworkX graph

    flow_func : function
        Function to perform the underlying flow computations. Default value
        :meth:`edmonds_karp`. This function performs better in sparse graphs with
        right tailed degree distributions. :meth:`shortest_augmenting_path` will
        perform better in denser graphs.

    Returns
    -------
    k_components : dict
        Dictionary with all connectivity levels `k` in the input Graph as keys
        and a list of sets of nodes that form a k-component of level `k` as
        values.

    Raises
    ------
    NetworkXNotImplemented
        If the input graph is directed.

    Examples
    --------
    >>> # Petersen graph has 10 nodes and it is triconnected, thus all
    >>> # nodes are in a single component on all three connectivity levels
    >>> G = nx.petersen_graph()
    >>> k_components = nx.k_components(G)

    Notes
    -----
    Moody and White [1]_ (appendix A) provide an algorithm for identifying
    k-components in a graph, which is based on Kanevsky's algorithm [2]_
    for finding all minimum-size node cut-sets of a graph (implemented in
    :meth:`all_node_cuts` function):

        1. Compute node connectivity, k, of the input graph G.

        2. Identify all k-cutsets at the current level of connectivity using
           Kanevsky's algorithm.

        3. Generate new graph components based on the removal of
           these cutsets. Nodes in a cutset belong to both sides
           of the induced cut.

        4. If the graph is neither complete nor trivial, return to 1;
           else end.

    This implementation also uses some heuristics (see [3]_ for details)
    to speed up the computation.

    See also
    --------
    node_connectivity
    all_node_cuts
    biconnected_components : special case of this function when k=2
    k_edge_components : similar to this function, but uses edge-connectivity
        instead of node-connectivity

    References
    ----------
    .. [1]  Moody, J. and D. White (2003). Social cohesion and embeddedness:
            A hierarchical conception of social groups.
            American Sociological Review 68(1), 103--28.
            http://www2.asanet.org/journals/ASRFeb03MoodyWhite.pdf

    .. [2]  Kanevsky, A. (1993). Finding all minimum-size separating vertex
            sets in a graph. Networks 23(6), 533--541.
            http://onlinelibrary.wiley.com/doi/10.1002/net.3230230604/abstract

    .. [3]  Torrents, J. and F. Ferraro (2015). Structural Cohesion:
            Visualization and Heuristics for Fast Computation.
            https://arxiv.org/pdf/1503.04476v1

    Né   c                    s   g | ]}ˆ   |¡‘qS © )Úsubgraph©Ú.0Úc©ÚGr	   úe/home/sam/Atlas/atlas_env/lib/python3.8/site-packages/networkx/algorithms/connectivity/kcomponents.pyÚ
<listcomp>v   s     z k_components.<locals>.<listcomp>é   )Ú	flow_func)Úkr   éÿÿÿÿ)r   ÚlistÚdefault_flow_funcÚnxÚconnected_componentsÚsetÚlenÚappendZbiconnected_componentsZnode_connectivityZall_node_cutsÚ_generate_partitionÚnextr
   ÚStopIterationÚpopÚ_reconstruct_k_components)r   r   r   Ú	componentÚcompZbicomponentsZbicomponentZbicompÚBr   ÚcutsÚstackZparent_kÚ	partitionÚnodesÚCZthis_kr	   r   r   r      sD    Y

	c                 #   sl   t  ¡ }tt| ƒƒ‰| ˆ¡ | ‡ ‡fdd„tˆdƒD ƒ¡ t  |¡D ]}tj	‡fdd„|D ƒŽ V  qHdS )as  Merge sets that share k or more elements.

    See: http://rosettacode.org/wiki/Set_consolidation

    The iterative python implementation posted there is
    faster than this because of the overhead of building a
    Graph and calling nx.connected_components, but it's not
    clear for us if we can use it in NetworkX because there
    is no licence for the code.

    c                 3   s2   | ]*\}}t ˆ| ˆ| @ ƒˆ kr||fV  qd S ©N)r   )r   ÚuÚv©r   r(   r	   r   Ú	<genexpr>¬   s     z_consolidate.<locals>.<genexpr>r   c                    s   g | ]}ˆ | ‘qS r	   r	   ©r   Ún)r(   r	   r   r   °   s     z _consolidate.<locals>.<listcomp>N)
r   ZGraphÚdictÚ	enumerateZadd_nodes_fromZadd_edges_fromr   r   r   Úunion)Zsetsr   r   r"   r	   r-   r   Ú_consolidate�   s    
ÿr4   c                 #   s®   dd„ }g }‡ fdd„|   ¡ D ƒdd„ |D ƒ }|  |¡}t |¡D ]P}t|ƒ}|D ]$}	|	D ]}
|| |
|ƒr\| |
¡ q\qTt|ƒ|  ¡ k rD| |¡ qDt	|ˆ d ƒE d H  d S )Nc                    s   t ‡ fdd„| | D ƒƒS )Nc                 3   s   | ]}|ˆ kV  qd S r*   r	   r/   ©r'   r	   r   r.   µ   s     zE_generate_partition.<locals>.has_nbrs_in_partition.<locals>.<genexpr>©Úany)r   Únoder'   r	   r5   r   Úhas_nbrs_in_partition´   s    z2_generate_partition.<locals>.has_nbrs_in_partitionc                    s   h | ]\}}|ˆ kr|’qS r	   r	   )r   r0   Úd©r   r	   r   Ú	<setcomp>¸   s      z&_generate_partition.<locals>.<setcomp>c                 S   s   h | ]}|D ]}|’qqS r	   r	   )r   Úcutr0   r	   r	   r   r<   ¸   s       r   )
Zdegreer
   r   r   r   Úaddr   Úorderr   r4   )r   r%   r   r9   Ú
componentsr(   ÚHÚccr"   r=   r8   r	   r;   r   r   ³   s    $
r   c                    sÊ   i }t | ƒ}ttd|d ƒƒD ]¦}||krBtt| | |ƒƒ||< q|| krftt||d  |ƒƒ||< qtj| | Ž ‰ ‡ fdd„||d  D ƒ}|r®tt| | | |ƒƒ||< qtt| | |ƒƒ||< q|S )Nr   c                    s&   g | ]}t ‡ fd d„|D ƒƒr|‘qS )c                 3   s   | ]}|ˆ kV  qd S r*   r	   r/   ©Z
nodes_at_kr	   r   r.   Ï   s     z7_reconstruct_k_components.<locals>.<listcomp>.<genexpr>r6   r   rC   r	   r   r   Ï   s      z-_reconstruct_k_components.<locals>.<listcomp>)ÚmaxÚreversedÚranger   r4   r   r3   )Zk_compsÚresultZmax_kr   Zto_addr	   rC   r   r!   Å   s    r!   c                 C   sB   i }t |  ¡ tdƒd�D ]$\}}|D ]}|D ]}|||< q,q$q|S )Nr   )Úkey)ÚsortedÚitemsr   )ZkcompsrG   r   Úcompsr#   r8   r	   r	   r   Úbuild_k_number_dict×   s    rL   )N)Ú__doc__Úcollectionsr   Ú	itertoolsr   Úoperatorr   Znetworkxr   Znetworkx.algorithms.flowr   Znetworkx.utilsr   r   Ú__all__r   r4   r   r!   rL   r	   r	   r	   r   Ú<module>   s    
