U
    tdx                     @   sN   d dl Z d dlmZmZ G dd deZG dd deZdddZd	d
 ZdS )    N)EdgeSeq	VertexSeqc                   @   s0   e Zd ZdZdd Zdd Zdd Zdd	 Zd
S )r   a  Class representing a sequence of vertices in the graph.

    This class is most easily accessed by the C{vs} field of the
    L{Graph} object, which returns an ordered sequence of all vertices in
    the graph. The vertex sequence can be refined by invoking the
    L{VertexSeq.select()} method. L{VertexSeq.select()} can also be
    accessed by simply calling the L{VertexSeq} object.

    An alternative way to create a vertex sequence referring to a given
    graph is to use the constructor directly:

      >>> g = Graph.Full(3)
      >>> vs = VertexSeq(g)
      >>> restricted_vs = VertexSeq(g, [0, 1])

    The individual vertices can be accessed by indexing the vertex sequence
    object. It can be used as an iterable as well, or even in a list
    comprehension:

      >>> g=Graph.Full(3)
      >>> for v in g.vs:
      ...   v["value"] = v.index ** 2
      ...
      >>> [v["value"] ** 0.5 for v in g.vs]
      [0.0, 1.0, 2.0]

    The vertex set can also be used as a dictionary where the keys are the
    attribute names. The values corresponding to the keys are the values
    of the given attribute for every vertex selected by the sequence.

      >>> g=Graph.Full(3)
      >>> for idx, v in enumerate(g.vs):
      ...   v["weight"] = idx*(idx+1)
      ...
      >>> g.vs["weight"]
      [0, 2, 6]
      >>> g.vs.select(1,2)["weight"] = [10, 20]
      >>> g.vs["weight"]
      [0, 10, 20]

    If you specify a sequence that is shorter than the number of vertices in
    the VertexSeq, the sequence is reused:

      >>> g = Graph.Tree(7, 2)
      >>> g.vs["color"] = ["red", "green"]
      >>> g.vs["color"]
      ['red', 'green', 'red', 'green', 'red', 'green', 'red']

    You can even pass a single string or integer, it will be considered as a
    sequence of length 1:

      >>> g.vs["color"] = "red"
      >>> g.vs["color"]
      ['red', 'red', 'red', 'red', 'red', 'red', 'red']

    Some methods of the vertex sequences are simply proxy methods to the
    corresponding methods in the L{Graph} object. One such example is
    C{VertexSeq.degree()}:

      >>> g=Graph.Tree(7, 2)
      >>> g.vs.degree()
      [2, 3, 3, 1, 1, 1, 1]
      >>> g.vs.degree() == g.degree()
      True
    c                 C   s
   | j  S )zfReturns the list of all the vertex attributes in the graph
        associated to this vertex sequence.)graphZvertex_attributesself r   C/home/sam/Atlas/atlas_env/lib/python3.8/site-packages/igraph/seq.py
attributesL   s    zVertexSeq.attributesc                 O   s   |sRd|kr| d}nd|kr,| d}nd}|dk	rRt|trJ|g}n||d< |rtj| f| }|sn|S | jj|j}n| }|jf |}|r|d S t	ddS )a  Returns the first vertex of the vertex sequence that matches some
        criteria.

        The selection criteria are equal to the ones allowed by L{VertexSeq.select}.
        See L{VertexSeq.select} for more details.

        For instance, to find the first vertex with name C{foo} in graph C{g}:

            >>> g.vs.find(name="foo")            #doctest:+SKIP

        To find an arbitrary isolated vertex:

            >>> g.vs.find(_degree=0)             #doctest:+SKIP
        nameZname_eqNr   zno such vertex)
pop
isinstancestr
_VertexSeqfindr   vsselectindex
ValueError)r   argskwdsr
   Zvertexr   r   r   r   r   Q   s(    
zVertexSeq.findc              	      s  t j| f| }tjtjtjtjtjtjdd dd d}|	 D ]\}d|ks`|
ddkrh|d7 }|d\}}}z||  W n( tk
r   |d|d   }} Y nX |d dkrt|j|d	d
 |}	n|| }	 fddt|	D }
||
}qB|S )a  Selects a subset of the vertex sequence based on some criteria

        The selection criteria can be specified by the positional and the keyword
        arguments. Positional arguments are always processed before keyword
        arguments.

          - If the first positional argument is C{None}, an empty sequence is
            returned.

          - If the first positional argument is a callable object, the object
            will be called for every vertex in the sequence. If it returns
            C{True}, the vertex will be included, otherwise it will
            be excluded.

          - If the first positional argument is an iterable, it must return
            integers and they will be considered as indices of the current
            vertex set (NOT the whole vertex set of the graph -- the
            difference matters when one filters a vertex set that has
            already been filtered by a previous invocation of
            L{VertexSeq.select()}. In this case, the indices do not refer
            directly to the vertices of the graph but to the elements of
            the filtered vertex sequence.

          - If the first positional argument is an integer, all remaining
            arguments are expected to be integers. They are considered as
            indices of the current vertex set again.

        Keyword arguments can be used to filter the vertices based on their
        attributes. The name of the keyword specifies the name of the attribute
        and the filtering operator, they should be concatenated by an
        underscore (C{_}) character. Attribute names can also contain
        underscores, but operator names don't, so the operator is always the
        largest trailing substring of the keyword name that does not contain
        an underscore. Possible operators are:

          - C{eq}: equal to

          - C{ne}: not equal to

          - C{lt}: less than

          - C{gt}: greater than

          - C{le}: less than or equal to

          - C{ge}: greater than or equal to

          - C{in}: checks if the value of an attribute is in a given list

          - C{notin}: checks if the value of an attribute is not in a given
            list

        For instance, if you want to filter vertices with a numeric C{age}
        property larger than 200, you have to write:

          >>> g.vs.select(age_gt=200)                   #doctest: +SKIP

        Similarly, to filter vertices whose C{type} is in a list of predefined
        types:

          >>> list_of_types = ["HR", "Finance", "Management"]
          >>> g.vs.select(type_in=list_of_types)        #doctest: +SKIP

        If the operator is omitted, it defaults to C{eq}. For instance, the
        following selector selects vertices whose C{cluster} property equals
        to 2:

          >>> g.vs.select(cluster=2)                    #doctest: +SKIP

        In the case of an unknown operator, it is assumed that the
        recognized operator is part of the attribute name and the actual
        operator is C{eq}.

        Attribute names inferred from keyword arguments are treated specially
        if they start with an underscore (C{_}). These are not real attributes
        but refer to specific properties of the vertices, e.g., its degree.
        The rule is as follows: if an attribute name starts with an underscore,
        the rest of the name is interpreted as a method of the L{Graph} object.
        This method is called with the vertex sequence as its first argument
        (all others left at default values) and vertices are filtered
        according to the value returned by the method. For instance, if you
        want to exclude isolated vertices:

          >>> g = Graph.Famous("zachary")
          >>> non_isolated = g.vs.select(_degree_gt=0)

        For properties that take a long time to be computed (e.g., betweenness
        centrality for large graphs), it is advised to calculate the values
        in advance and store it in a graph attribute. The same applies when
        you are selecting based on the same property more than once in the
        same C{select()} call to avoid calculating it twice unnecessarily.
        For instance, the following would calculate betweenness centralities
        twice:

          >>> edges = g.vs.select(_betweenness_gt=10, _betweenness_lt=30)

        It is advised to use this instead:

          >>> g.vs["bs"] = g.betweenness()
          >>> edges = g.vs.select(bs_gt=10, bs_lt=30)

        @return: the new, filtered vertex sequencec                 S   s   | |kS Nr   abr   r   r   <lambda>       z"VertexSeq.select.<locals>.<lambda>c                 S   s   | |kS r   r   r   r   r   r   r      r   ltgtlegeeqneinnotin_r   _eqr!      Nc                    s   g | ]\}} |r|qS r   r   .0ivfuncvaluer   r   
<listcomp>  s     
 z$VertexSeq.select.<locals>.<listcomp>)r   r   operatorr   r   r   r    r!   r"   itemsrindex
rpartitionKeyErrorgetattrr   	enumerate)r   r   r   r   	operatorskeywordattrr%   opvaluesfiltered_idxsr   r,   r   r      s0    g
zVertexSeq.selectc                 O   s   | j ||S )zvShorthand notation to select()

        This method simply passes all its arguments to L{VertexSeq.select()}.
        r   r   r   r   r   r   r   __call__  s    zVertexSeq.__call__N__name__
__module____qualname____doc__r	   r   r   r?   r   r   r   r   r   	   s   B3 r   c                   @   s0   e Zd ZdZdd Zdd Zdd Zdd	 Zd
S )r   a  Class representing a sequence of edges in the graph.

    This class is most easily accessed by the C{es} field of the
    L{Graph} object, which returns an ordered sequence of all edges in
    the graph. The edge sequence can be refined by invoking the
    L{EdgeSeq.select()} method. L{EdgeSeq.select()} can also be
    accessed by simply calling the L{EdgeSeq} object.

    An alternative way to create an edge sequence referring to a given
    graph is to use the constructor directly:

      >>> g = Graph.Full(3)
      >>> es = EdgeSeq(g)
      >>> restricted_es = EdgeSeq(g, [0, 1])

    The individual edges can be accessed by indexing the edge sequence
    object. It can be used as an iterable as well, or even in a list
    comprehension:

      >>> g=Graph.Full(3)
      >>> for e in g.es:
      ...   print(e.tuple)
      ...
      (0, 1)
      (0, 2)
      (1, 2)
      >>> [max(e.tuple) for e in g.es]
      [1, 2, 2]

    The edge sequence can also be used as a dictionary where the keys are the
    attribute names. The values corresponding to the keys are the values
    of the given attribute of every edge in the graph:

      >>> g=Graph.Full(3)
      >>> for idx, e in enumerate(g.es):
      ...   e["weight"] = idx*(idx+1)
      ...
      >>> g.es["weight"]
      [0, 2, 6]
      >>> g.es["weight"] = range(3)
      >>> g.es["weight"]
      [0, 1, 2]

    If you specify a sequence that is shorter than the number of edges in
    the EdgeSeq, the sequence is reused:

      >>> g = Graph.Tree(7, 2)
      >>> g.es["color"] = ["red", "green"]
      >>> g.es["color"]
      ['red', 'green', 'red', 'green', 'red', 'green']

    You can even pass a single string or integer, it will be considered as a
    sequence of length 1:

      >>> g.es["color"] = "red"
      >>> g.es["color"]
      ['red', 'red', 'red', 'red', 'red', 'red']

    Some methods of the edge sequences are simply proxy methods to the
    corresponding methods in the L{Graph} object. One such example is
    C{EdgeSeq.is_multiple()}:

      >>> g=Graph(3, [(0,1), (1,0), (1,2)])
      >>> g.es.is_multiple()
      [False, True, False]
      >>> g.es.is_multiple() == g.is_multiple()
      True
    c                 C   s
   | j  S )zbReturns the list of all the edge attributes in the graph
        associated to this edge sequence.)r   Zedge_attributesr   r   r   r   r	   Y  s    zEdgeSeq.attributesc                 O   sV   |r.t j| f| }|s|S | jj|j}n| }|jf |}|rJ|d S tddS )ax  Returns the first edge of the edge sequence that matches some
        criteria.

        The selection criteria are equal to the ones allowed by L{VertexSeq.select}.
        See L{VertexSeq.select} for more details.

        For instance, to find the first edge with weight larger than 5 in graph C{g}:

            >>> g.es.find(weight_gt=5)           #doctest:+SKIP
        r   zno such edgeN)_EdgeSeqr   r   esr   r   r   )r   r   r   edgerF   r   r   r   r   ^  s    zEdgeSeq.findc              	      s  t j| f| | j }dd }tjtjtjtjtj	tj
dd dd d}| D ]\}d|kst|ddkr||d	7 }|d}|d| ||d
 d  }}	z||	 W n( tk
r   |d|d   }}	Y nX |d dkr|dkrH|sH|	dkr
tdd}|d krHtdr>tts>tn
tg|dkr r|	dkrtjjdd}
dn*dd D }|	dks|	dkr|q|dkr r|	dkrtjjdd}
dn*dd D }|	dks|	dkr|q|dkr|d|t  D ]} j| q4 sp fddtD }
nt }
q|dkrd|t  D ]} j| q sڇ fddtD }
nfdd D }
n|dkrtd krtd!d|d |d
 t  D ]} j| q6D ]} j| qT sfd"dtD }
nfd#d D }
ntj|d
d }n| }dk	rfd$dt|D }
|
qTS )%a;  Selects a subset of the edge sequence based on some criteria

        The selection criteria can be specified by the positional and the
        keyword arguments. Positional arguments are always processed before
        keyword arguments.

          - If the first positional argument is C{None}, an empty sequence is
            returned.

          - If the first positional argument is a callable object, the object
            will be called for every edge in the sequence. If it returns
            C{True}, the edge will be included, otherwise it will
            be excluded.

          - If the first positional argument is an iterable, it must return
            integers and they will be considered as indices of the current
            edge set (NOT the whole edge set of the graph -- the
            difference matters when one filters an edge set that has
            already been filtered by a previous invocation of
            L{EdgeSeq.select()}. In this case, the indices do not refer
            directly to the edges of the graph but to the elements of
            the filtered edge sequence.

          - If the first positional argument is an integer, all remaining
            arguments are expected to be integers. They are considered as
            indices of the current edge set again.

        Keyword arguments can be used to filter the edges based on their
        attributes and properties. The name of the keyword specifies the name
        of the attribute and the filtering operator, they should be
        concatenated by an underscore (C{_}) character. Attribute names can
        also contain underscores, but operator names don't, so the operator is
        always the largest trailing substring of the keyword name that does not
        contain an underscore. Possible operators are:

          - C{eq}: equal to

          - C{ne}: not equal to

          - C{lt}: less than

          - C{gt}: greater than

          - C{le}: less than or equal to

          - C{ge}: greater than or equal to

          - C{in}: checks if the value of an attribute is in a given list

          - C{notin}: checks if the value of an attribute is not in a given
            list

        For instance, if you want to filter edges with a numeric C{weight}
        property larger than 50, you have to write:

          >>> g.es.select(weight_gt=50)            #doctest: +SKIP

        Similarly, to filter edges whose C{type} is in a list of predefined
        types:

          >>> list_of_types = ["inhibitory", "excitatory"]
          >>> g.es.select(type_in=list_of_types)   #doctest: +SKIP

        If the operator is omitted, it defaults to C{eq}. For instance, the
        following selector selects edges whose C{type} property is
        C{intracluster}:

          >>> g.es.select(type="intracluster")     #doctest: +SKIP

        In the case of an unknown operator, it is assumed that the
        recognized operator is part of the attribute name and the actual
        operator is C{eq}.

        Keyword arguments are treated specially if they start with an
        underscore (C{_}). These are not real attributes but refer to specific
        properties of the edges, e.g., their centrality.  The rules are as
        follows:

          1. C{_source} or {_from} means the source vertex of an edge. For
             undirected graphs, only the C{eq} operator is supported and it
             is treated as {_incident} (since undirected graphs have no notion
             of edge directionality).

          2. C{_target} or {_to} means the target vertex of an edge. For
             undirected graphs, only the C{eq} operator is supported and it
             is treated as {_incident} (since undirected graphs have no notion
             of edge directionality).

          3. C{_within} ignores the operator and checks whether both endpoints
             of the edge lie within a specified set.

          4. C{_between} ignores the operator and checks whether I{one}
             endpoint of the edge lies within a specified set and the I{other}
             endpoint lies within another specified set. The two sets must be
             given as a tuple.

          5. C{_incident} ignores the operator and checks whether the edge is
             incident on a specific vertex or a set of vertices.

          6. Otherwise, the rest of the name is interpreted as a method of the
             L{Graph} object. This method is called with the edge sequence as
             its first argument (all others left at default values) and edges
             are filtered according to the value returned by the method.

        For instance, if you want to exclude edges with a betweenness
        centrality less than 2:

          >>> g = Graph.Famous("zachary")
          >>> excl = g.es.select(_edge_betweenness_ge = 2)

        To select edges originating from vertices 2 and 4:

          >>> edges = g.es.select(_source_in = [2, 4])

        To select edges lying entirely within the subgraph spanned by vertices
        2, 3, 4 and 7:

          >>> edges = g.es.select(_within = [2, 3, 4, 7])

        To select edges with one endpoint in the vertex set containing vertices
        2, 3, 4 and 7 and the other endpoint in the vertex set containing
        vertices 8 and 9:

          >>> edges = g.es.select(_between = ([2, 3, 4, 7], [8, 9]))

        For properties that take a long time to be computed (e.g., betweenness
        centrality for large graphs), it is advised to calculate the values
        in advance and store it in a graph attribute. The same applies when
        you are selecting based on the same property more than once in the
        same C{select()} call to avoid calculating it twice unnecessarily.
        For instance, the following would calculate betweenness centralities
        twice:

          >>> edges = g.es.select(_edge_betweenness_gt=10,       # doctest:+SKIP
          ...                     _edge_betweenness_lt=30)

        It is advised to use this instead:

          >>> g.es["bs"] = g.edge_betweenness()
          >>> edges = g.es.select(bs_gt=10, bs_lt=30)

        @return: the new, filtered edge sequence
        c                 S   s8   t | trtdd | D } nt | ttfs4t| } | S )Nc                 s   s   | ]}|j V  qd S r   r   )r)   r+   r   r   r   	<genexpr>  s     z6EdgeSeq.select.<locals>._ensure_set.<locals>.<genexpr>)r   r   set	frozenset)r.   r   r   r   _ensure_set  s
    
z#EdgeSeq.select.<locals>._ensure_setc                 S   s   | |kS r   r   r   r   r   r   r     r   z EdgeSeq.select.<locals>.<lambda>c                 S   s   | |kS r   r   r   r   r   r   r     r   r   r%   r   r&   r'   Nr!   )_source_from_target_to)r!   r#   z!unsupported for undirected graphsZ	_incident__iter__)rM   rN   out)modec                 S   s   g | ]
}|j qS r   )sourcer)   er   r   r   r/   D  s     z"EdgeSeq.select.<locals>.<listcomp>r#   r$   )rO   rP   c                 S   s   g | ]
}|j qS r   )targetrU   r   r   r   r/   P  s     c                    s   g | ]\}}|j  kr|qS r   rH   r)   r*   rV   )
candidatesr   r   r/   `  s    
 Z_withinc                    s2   g | ]*\}}|j  kr|jkr|jkr|qS r   )r   rT   rW   rX   )rY   r.   r   r   r/   s  s
   


c                    s,   g | ]$} | j kr | jkr|qS r   rT   rW   r)   r*   )rF   r.   r   r   r/   }  s    Z_between   z._between selector requires two vertex ID listsc                    s<   g | ]4\}}|j  kr |jks4|j kr|j kr|qS r   rZ   rX   )set1set2r   r   r/     s   
 

 
c                    sH   g | ]@} | j kr$ | jks@ | jkr | j kr|qS r   rZ   r[   )rF   r]   r^   r   r   r/     s     c                    s   g | ]\}} |r|qS r   r   r(   r,   r   r   r/     s     
 )rE   r   r   is_directedr0   r   r   r   r    r!   r"   r1   r2   r4   RuntimeErrorhasattrr   r   rJ   Zis_allsortedZincidentupdater6   lenr   r5   )r   r   r   r_   rL   r7   r8   posr9   r:   r<   r;   r+   r   )rY   rF   r-   r]   r^   r.   r   r   y  s     














		
zEdgeSeq.selectc                 O   s   | j ||S )ztShorthand notation to select()

        This method simply passes all its arguments to L{EdgeSeq.select()}.
        r=   r>   r   r   r   r?     s    zEdgeSeq.__call__Nr@   r   r   r   r   r     s   E  >r   c                    sb   ddl m} |dkr j}t||t dr> fdd}nfdd}||_dd	|i |_|S )
a  Auxiliary decorator

    This decorator allows some methods of L{VertexSeq} and L{EdgeSeq} to
    call their respective counterparts in L{Graph} to avoid code duplication.

    @param func: the function being decorated. This function will be
      called on the results of the original L{Graph} method.
      If C{None}, defaults to the identity function.
    @param name: the name of the corresponding method in L{Graph}. If
      C{None}, it defaults to the name of the decorated function.
    @return: the decorated function
    r   )GraphNr?   c                     s$   | d j } | d |f| |S Nr   r   r   r   r   r-   methodr   r   	decorated  s    
z_graphmethod.<locals>.decoratedc                     s   | d j } |f| |S rg   rh   ri   )rk   r   r   rl     s    
zProxy method to L{Graph.%(name)s()}

This method calls the C{%(name)s()} method of the L{Graph} class
restricted to this sequence, and returns the result.

@see: Graph.%(name)s() for details.
r
   )Zigraphrf   rA   r5   ra   rD   )r-   r
   rf   rl   r   rj   r   _graphmethod  s    

 
rm   c                  C   s   i } ddddddddd	d
dddddddddddddg| t < ddddddg| t< i }ddi|t < ddd|t< |  D ]4\}}|D ]&}|| ||}t||td | qqtttd td!d" d  d S )#NZdegreeZbetweennessZbibcouplingZ	closenessZ
cocitation
constraintZ	distancesZ	diversityZeccentricityZget_shortest_pathsZ	maxdegreeZpagerankZpersonalized_pagerankZshortest_pathsZsimilarity_diceZsimilarity_jaccardZsubgraphZindegreeZ	outdegreeZisoclassZdelete_verticesZis_separatorZis_minimal_separatorZcount_multipledelete_edgesZis_loopZis_multipleZ	is_mutualsubgraph_edgesdelete)ro   rp   Zedge_betweennessc                    s    fdd| j D S )Nc                    s   g | ]} | qS r   r   r[   resultr   r   r/   "  s     z8_add_proxy_methods.<locals>.<lambda>.<locals>.<listcomp>)indices)r   rs   r   rr   r   r   "  r   z$_add_proxy_methods.<locals>.<lambda>)r   r   r1   getsetattrrm   )Zdecorated_methodsZrename_methodsclsmethodsrk   Znew_method_namer   r   r   _add_proxy_methods  s^    	 ry   )NN)r0   Zigraph._igraphr   rE   r   r   rm   ry   r   r   r   r   <module>   s        -
.