U
    td~                     @   s   d dl mZmZ d dlmZ d dlmZ d dlmZ d dl	m
Z
 d(eeed	d
dZd)eedddZd*eedddZd+eedddZd,eedddZd-eeedddZd.eeeee f edddZdedfeeeddd Zd/eeeee f eed!d"d#Zd$d% Zd&d' ZdS )0    )UnionSequence)defaultdict)repeat)warn)UniqueIdGeneratorFnamesourcetarget)directedvertex_name_attr	iterativec              
   C   s  dd }i }d}	|rt |D ]\\}
}| D ]B\}}z|| |
|f W q, tk
rl   |
|fg||< Y q,X q,|	d7 }	q| D ]\}}|||	||< qng ||< ||krt| d|| }t|tt|krtdt|d}|\}}|r| |	g |i |}t |D ]\}
}|| }|| }|| }||	krX|	d ||j
|	 |< |	d7 }	|| }||	kr|	d ||j
|	 |< |	d7 }	||| | D ]\}}||j|
 |< qq|S g }i }d}t |D ]\}
}|||  }|||  }|||f | D ]F\}}z|| |
|f W n$ tk
rL   |
|fg||< Y nX q
|d7 }q| D ]\}}|||||< qft||	krt||	 }dg| }| D ]\}}|| qt| ||< t|}	| |	||i ||S dS )	a	  Constructs a graph from a list-of-dictionaries representation.

    This function is useful when you have two lists of dictionaries, one for
    vertices and one for edges, each containing their attributes (e.g. name,
    weight). Of course, the edge dictionary must also contain two special keys
    that indicate the source and target vertices connected by that edge.
    Non-list iterables should work as long as they yield dictionaries or
    dict-like objects (they should have the 'items' and '__getitem__' methods).
    For instance, a database query result is likely to be fit as long as it's
    iterable and yields dict-like objects with every iteration.

    @param vertices: the list of dictionaries for the vertices or C{None} if
      there are no special attributes assigned to vertices and we
      should simply use the edge list of dicts to infer vertex names.
    @param edges: the list of dictionaries for the edges. Each dict must have
      at least the two keys specified by edge_foreign_keys to label the source
      and target vertices, while additional items will be treated as edge
      attributes.
    @param directed: whether the constructed graph will be directed
    @param vertex_name_attr: the name of the distinguished key in the
      dicts in the vertex data source that contains the vertex names.
      Ignored if C{vertices} is C{None}.
    @param edge_foreign_keys: tuple specifying the attributes in each edge
      dictionary that contain the source (1st) and target (2nd) vertex names.
      These items of each dictionary are also added as edge_attributes.
    @param iterative: whether to add the edges to the graph one by one,
      iteratively, or to build a large edge list first and use that to
      construct the graph. The latter approach is faster but it may
      not be suitable if your dataset is large. The default is to
      add the edges in a batch from an edge list.
    @return: the graph that was constructed

    Example:

    >>> vertices = [{'name': 'apple'}, {'name': 'pear'}, {'name': 'peach'}]
    >>> edges = [{'source': 'apple', 'target': 'pear', 'weight': 1.2},
    ...          {'source': 'apple', 'target': 'peach', 'weight': 0.9}]
    >>> g = Graph.DictList(vertices, edges)

    The graph has three vertices with names and two edges with weights.
    c                 S   s$   d g| }| D ]\}}|||< q|S N )indicesnresultivr   r   J/home/sam/Atlas/atlas_env/lib/python3.8/site-packages/igraph/io/objects.pycreate_list_from_indices<   s    

zA_construct_graph_from_dict_list.<locals>.create_list_from_indicesr      z) is not a key of your vertex dictionarieszvertex names are not unique)initialN)	enumerateitemsappendKeyErrorAttributeErrorlenset
ValueErrorr   Zadd_verticesvsZadd_edgeesextendlistvalues)clsverticesedgesr   r   Zedge_foreign_keysr   r   Zvertex_attrsr   idxZvertex_datakr   Zvertex_namesZvertex_name_mapZefk_srcZefk_destgZ	edge_datasrc_namedst_nameZv1Zv2	edge_list
edge_attrsmdiffmorer   r   r   _construct_graph_from_dict_list	   s    3






r4   N)r   r   c              
   C   s  |dkr(|sd}q4t |ts d}|g}n|r4tdt |trD|g}t }g }i }|D ]}	g ||	< qV|D ]p}
|||
d  ||
d  f t|dD ]B\}}	z||	 |
|  W q tk
r   ||	 d Y qX qqhi }t| ||< t	|}| |||i ||S )a
  Constructs a graph from a list-of-tuples representation.

    This representation assumes that the edges of the graph are encoded
    in a list of tuples (or lists). Each item in the list must have at least
    two elements, which specify the source and the target vertices of the edge.
    The remaining elements (if any) specify the edge attributes of that edge,
    where the names of the edge attributes originate from the C{edge_attrs}
    list. The names of the vertices will be stored in the vertex attribute
    given by C{vertex_name_attr}.

    The default parameters of this function are suitable for creating
    unweighted graphs from lists where each item contains the source vertex
    and the target vertex. If you have a weighted graph, you can use items
    where the third item contains the weight of the edge by setting
    C{edge_attrs} to C{"weight"} or C{["weight"]}. If you have even more
    edge attributes, add them to the end of each item in the C{edges}
    list and also specify the corresponding edge attribute names in
    C{edge_attrs} as a list.

    @param edges: the data source for the edges. This must be a list
      where each item is a tuple (or list) containing at least two
      items: the name of the source and the target vertex. Note that
      names will be assigned to the C{name} vertex attribute (or another
      vertex attribute if C{vertex_name_attr} is specified), even if
      all the vertex names in the list are in fact numbers.
    @param directed: whether the constructed graph will be directed
    @param vertex_name_attr: the name of the vertex attribute that will
      contain the vertex names.
    @param edge_attrs: the names of the edge attributes that are filled
      with the extra items in the edge list (starting from index 2, since
      the first two items are the source and target vertices). If C{None}
      or an empty sequence, only the source and target vertices will be
      extracted and additional tuple items will be ignored. If a string, it is
      interpreted as a single edge attribute.
    @param weights: alternative way to specify that the graph is
      weighted. If you set C{weights} to C{true} and C{edge_attrs} is
      not given, it will be assumed that C{edge_attrs} is C{["weight"]}
      and igraph will parse the third element from each item into an
      edge weight. If you set C{weights} to a string, it will be assumed
      that C{edge_attrs} contains that string only, and igraph will
      store the edge weights in that attribute.
    @return: the graph that was constructed
    Nr   weightz3`weights` must be False if `edge_attrs` is not Noner   r      )

isinstancestrr!   r   r   r   
IndexErrorr%   r&   r   )r'   r)   r   r   r0   weightsZidgenr/   edge_attributesr   itemindexvertex_attributesr   r   r   r    _construct_graph_from_tuple_list   s4    3


r?   c           
         s   t t|d}t|ttfs$tdi }t|trt  g }| D ]*\}} | | fdd|D  qD 	 ||< t
 }	nFg }d}	| D ],\}}t|	|f| }	|tt|| q|	d7 }	| |	||i |i S )a9  Constructs a graph from a dict-of-lists representation.

    This function is used to construct a graph from a dictionary of
    lists. Other, non-list sequences (e.g. tuples) and lazy iterators are
    are accepted. For each key x, its corresponding value must be a sequence of
    multiple values y: the edge (x,y) will be created in the graph. x and y
    must be either one of:

      - two integers: the vertices with those ids will be connected
      - two strings: the vertices with those names will be connected

    If names are used, the order of vertices is not guaranteed, and each
    vertex will be given the vertex_name_attr attribute.

    @param edges: the dict of sequences describing the edges
    @param directed: whether to create a directed graph
    @param vertex_name_attr: vertex attribute that will store the names

    @returns: a Graph object

    Example:

    >>> mydict = {'apple': ['pear', 'peach'], 'pear': ['peach']}
    >>> g = Graph.ListDict(mydict)

    # The graph has three vertices with names and three edges connecting
    # each pair.
    r    Keys must be integers or stringsc                 3   s   | ]} | fV  qd S r   r   ).0r   name_map	source_idr   r   	<genexpr>  s     z2_construct_graph_from_list_dict.<locals>.<genexpr>r   )nextiterr7   intr8   r!   r   r   r$   r&   r   maxzipr   )
r'   r)   r   r   
first_itemr>   r/   r
   sequencer   r   rB   r   _construct_graph_from_list_dict   s&    "

rN   c                 C   sD  t t|d}t|ttfs$tdi }g }t|trt }g }| D ]>\}	}
||	 }|
 D ]$\}}|||| f || q`qH|	 ||< t
|}n\g }d}| D ]B\}	}
t||	f|
 }|
 D ] \}}||	|f || qq|d7 }| |||i |i }t|j|D ]&\}}| D ]\}}|||< q(q|S )a9  Constructs a graph from a dict-of-dicts representation.

    Each key can be an integer or a string and represent a vertex. Each value
    is a dict representing edges (outgoing if the graph is directed) from that
    vertex. Each dict key is an integer/string for a target vertex, such that
    an edge will be created between those two vertices. Integers are
    interpreted as vertex_ids from 0 (as used in igraph), strings are
    interpreted as vertex names, in which case vertices are given separate
    numeric ids. Each value is a dictionary of edge attributes for that edge.

    Example:

      >>> {'Alice': {'Bob': {'weight': 1.5}, 'David': {'weight': 2}}}

    creates a graph with three vertices (Alice, Bob, and David) and two edges:

      - Alice - Bob (with weight 1.5)
      - Alice - David (with weight 2)

    @param edges: the dict of dict of dicts specifying the edges and their
      attributes
    @param directed: whether to create a directed graph
    @param vertex_name_attr: vertex attribute that will store the names

    @returns: a Graph object
    r   r@   rF   r   )rG   rH   r7   rI   r8   r!   r   r   r   r&   r   rJ   rK   r#   )r'   r)   r   r   rL   r>   Zedge_attribute_listrC   r/   r
   target_dictrD   r   r0   r   graphedgekeyvalr   r   r   _construct_graph_from_dict_dict,  s8     

rT   T)r   use_vidsc              	   C   sr  zddl }W n tk
r(   tdY nX zddl}W n tk
rR   tdY nX |jd dk rjtd|dk	r|jd dk rtd|rt|jd d	rt|jd d	r|jddddf 	 j
dd
rtd|jddddf dk j
dd
r.tdntd|j dd  |dk	rl| }|j|jt|jd slt|jjdstd|jj n6|jdk j
dd
rtdntd|jd d  n|jddddf 	 j
dd
rtd | }|jddddf jddd |dkrJ|d||jddddf  i}|jdddf 	 
 rtd | }|jdddf jddd |jdddf  
 rtd|jd dkrd|jdd krtd|j|jd didd
jdd}|j|j|jdddf d}| }|jdddf  |||jd < |jdddf  |||jd < |dkr|jddddf ! ! d }| ||d}	nf|jddddf "|jj#dd
std|jd }| ||d}	|jD ]}
||
  |	j$|
< qt%|jddddf j&ddd}|jd dkr^|jddddf j'd d!nd}|	(|| |	S )"a  Generates a graph from one or two dataframes.

    @param edges: pandas DataFrame containing edges and metadata. The first
      two columns of this DataFrame contain the source and target vertices
      for each edge. These indicate the vertex IDs as nonnegative integers
      rather than vertex names unless C{use_vids} is False. Further columns
      may contain edge attributes.
    @param directed: whether the graph is directed
    @param vertices: None (default) or pandas DataFrame containing vertex
      metadata. The DataFrame's index must contain the vertex IDs as a
      sequence of intergers from 0 to C{len(vertices) - 1}. If C{use_vids}
      is C{False}, the first column must contain the unique vertex names.
      Vertex names should be strings for full compatibility, but many functions
      will work if you set the name with any hashable object. All other columns
      will be added as vertex attributes by column name.
    @param use_vids: whether to interpret the first two columns of the C{edges}
      argument as vertex ids (0-based integers) instead of vertex names.
      If this argument is set to True and the first two columns of C{edges}
      are not integers, an error is thrown.

    @return: the graph

    Vertex names in either the C{edges} or C{vertices} arguments that are set
    to NaN (not a number) will be set to the string "NA". That might lead
    to unexpected behaviour: fill your NaNs with values before calling this
    function to mitigate.
    r   N7You should install pandas in order to use this functionz6You should install numpy in order to use this functionr   r6   z7The 'edges' DataFrame must contain at least two columnsz9The 'vertices' DataFrame must contain at least one column)rI   ZInt)Zaxisz&Source and target IDs must not be nullz*Source and target IDs must not be negativez<Source and target IDs must be 0-based integers, found types rI   z0Vertex IDs must be 0-based integers, found type zVertex IDs must not be negativez1Vertex IDs must be an integer sequence from 0 to zNIn the first two columns of 'edges' NA elements were replaced with string "NA"ZNAT)Zinplacer   zLIn the first column of 'vertices' NA elements were replaced with string "NA"zVertex names must be uniquezCVertex attribute conflict: DataFrame already contains column 'name')Zdropr=   )r   r   zGSome vertices in the edge DataFrame are missing from vertices DataFrameF)r=   r   r%   )Zorient))pandasImportErrornumpyshaper!   r8   Zdtypes
startswithZilocZisnaany	TypeErrortolistZ
sort_indexr=   equalsZ
RangeIndexZ
from_rangerangeZdtyper   copyZfillna	DataFrameuniquer&   ZravelZ
duplicatedcolumnsrenameZreset_indexZSeriesmaprJ   isinallr"   r%   Z
itertuplesto_dictZ	add_edges)r'   r)   r   r(   rU   pdnpZvid_mapnvr,   colZe_listZe_attrr   r   r   _construct_graph_from_dataframes  s    ""$


$ 
*$""
"(

$2ro   )rU   	skip_noner   c                 C   s   g g  }}|s2||   kr(td| | j| }| jD ].}|rTdd | D }n| }|| q8| jD ]^}	|	j\}
}|s||
 ||  }
}|rdd |	 D }n|	 }|
|d< ||d< || qn||fS )a  Export graph as two lists of dictionaries, for vertices and edges.

    This function is the reverse of Graph.DictList.

    Example:

      >>> g = Graph([(0, 1), (1, 2)])
      >>> g.vs["name"] = ["apple", "pear", "peach"]
      >>> g.es["name"] = ["first_edge", "second"]

      >>> g.to_dict_list()
      ([{"name": "apple"}, {"name": "pear"}, {"name": "peach"}],
       [{"source": 0, "target": 1, "name": "first_edge"},
        {"source" 0, "target": 2, name": "second"}])

      >>> g.to_dict_list(use_vids=False)
      ([{"name": "apple"}, {"name": "pear"}, {"name": "peach"}],
       [{"source": "apple", "target": "pear", "name": "first_edge"},
        {"source" "apple", "target": "peach", name": "second"}])

    @param use_vids: whether to label vertices in the output data
      structure by their ids or their vertex_name_attr attribute. If
      use_vids=False but vertices lack a vertex_name_attr attribute, an
      AttributeError is raised.
    @param skip_none: whether to skip, for each edge, attributes that
      have a value of None. This is useful if only some edges are expected to
      possess an attribute.
    @param vertex_name_attr: only used with use_vids=False to choose what
      vertex attribute to use to name your vertices in the output data
      structure.

    @return: a tuple with two lists of dictionaries, representing the vertices
      and the edges, respectively, with their attributes.
    No vertex attribute c                 S   s   i | ]\}}|d k	r||qS r   r   rA   r+   r   r   r   r   
<dictcomp>3  s       z._export_graph_to_dict_list.<locals>.<dictcomp>c                 S   s   i | ]\}}|d k	r||qS r   r   rr   r   r   r   rs   =  s       r
   r   )r>   r   r"   
attributesr   r#   tuple)rP   rU   rp   r   Zres_vsZres_esvs_namesZvertexattrdicrQ   r
   r   r   r   r   _export_graph_to_dict_list   s*    )




rx   )rU   r0   r   c           
         s   g }|dk	rHt |tr|g}tt|t|   }|rLtd| ng }|dkrx||  krntd| | j| }| jD ]P  j	\}}|s|| ||  }}||g}	|	 fdd|D 7 }	|
t	|	 q~|S )aA  Export graph to a list of edge tuples

    This function is the reverse of Graph.TupleList.

    Example:

      >>> g = Graph.Full(3)
      >>> g.vs["name"] = ["apple", "pear", "peach"]
      >>> g.es["name"] = ["first_edge", "second", "third"]

      >>> # Get name of the edge
      >>> g.to_tuple_list(edge_attrs=["name"])
      [(0, 1, "first_edge"), (0, 2, "second"), (1, 2, "third")]

      >>> # Use vertex names, no edge attributes
      >>> g.to_tuple_list(use_vids=False)
      [("apple", "pear"), ("apple", "peach"), ("pear", "peach")]

    @param use_vids: whether to label vertices in the output data
      structure by their ids or their vertex_name_attr attribute. If
      use_vids=False but vertices lack a vertex_name_attr attribute, an
      AttributeError is raised.
    @param edge_attrs: list of edge attributes to export
      in addition to source and target vertex, which are always the first two
      elements of each tuple. None (default) is equivalent to an empty list. A
      string is acceptable to signify a single attribute and will be wrapped in
      a list internally.
    @param vertex_name_attr: only used with use_vids=False to choose what
      vertex attribute to use to name your vertices in the output data
      structure.

    @return: a list of tuples, each representing an edge of the graph.
    NMissing attributes: Frq   c                    s   g | ]} | qS r   r   )rA   attrnamerQ   r   r   
<listcomp>  s     z/_export_graph_to_tuple_list.<locals>.<listcomp>)r7   r8   r%   r    r;   r   r>   r"   r#   ru   r   )
rP   rU   r0   r   resmissing_attrsrv   r
   r   Zattrlistr   r{   r   _export_graph_to_tuple_listH  s(    (



r   )rU   sequence_constructorr   c           	         s   |s*||   kr td| d| j| }tt}| jD ]0}|j\}}|sZ|| }|| }|| | q8 fdd| D }|S )a  Export graph to a dictionary of lists (or other sequences).

    This function is the reverse of Graph.ListDict.

    Example:

      >>> g = Graph.Full(3)
      >>> g.to_sequence_dict() -> {0: [1, 2], 1: [2]}
      >>> g.to_sequence_dict(sequence_constructor=tuple) -> {0: (1, 2), 1: (2,)}
      >>> g.vs['name'] = ['apple', 'pear', 'peach']
      >>> g.to_sequence_dict(use_vids=False)
      {'apple': ['pear', 'peach'], 'pear': ['peach']}

    @param use_vids: whether to label vertices in the output data
      structure by their ids or their vertex_name_attr attribute. If
      use_vids=False but vertices lack a vertex_name_attr attribute, an
      AttributeError is raised.
    @param sequence_constructor: constructor for the data structure
      to be used as values of the dictionary. The default (list) makes a dict
      of lists, with each list representing the neighbors of the vertex
      specified in the respective dictionary key.
    @param vertex_name_attr: only used with use_vids=False to choose what
      vertex attribute to use to name your vertices in the output data
      structure.

    @return: dictionary of sequences, keyed by vertices, with each value
      containing the neighbors of that vertex.
    Vertices do not have a 
 attributec                    s   i | ]\}}| |qS r   r   rA   rR   rS   r   r   r   rs     s      z._export_graph_to_list_dict.<locals>.<dictcomp>)	r>   r   r"   r   r%   r#   ru   r   r   )	rP   rU   r   r   rv   r}   rQ   r
   r   r   r   r   _export_graph_to_list_dict  s    "


r   )rU   r0   rp   r   c                    s   |dk	rBt |tr|g}tt|t|   }|rBtd| |sl||  krbtd| d| j| }tdd }| j	D ]f}|j
\}	}
|s||	 }	||
 }
|  |dk	r fdd|D  |rd	d   D   ||	 |
< q~d
d | D }|S )aZ  Export graph to dictionary of dicts of edge attributes

    This function is the reverse of Graph.DictDict.

    Example:

      >>> g = Graph.Full(3)
      >>> g.es['name'] = ['first_edge', 'second', 'third']
      >>> g.to_dict_dict()
      {0: {1: {'name': 'first_edge'}, 2: {'name': 'second'}}, 1: {2: {'name': 'third'}}}

    @param use_vids: whether to label vertices in the output data
      structure by their ids or their vertex_name_attr attribute. If
      use_vids=False but vertices lack a vertex_name_attr attribute, an
      AttributeError is raised.
    @param edge_attrs: list of edge attributes to export.
      None (default) signified all attributes (unlike Graph.to_tuple_list). A
      string is acceptable to signify a single attribute and will be wrapped
      in a list internally.
    @param skip_none: whether to skip, for each edge, attributes that
      have a value of None. This is useful if only some edges are expected to
      possess an attribute.
    @param vertex_name_attr: only used with use_vids=False to choose what
      vertex attribute to use to name your vertices in the output data
      structure.

    @return: dictionary of dictionaries of dictionaries, with the outer keys
      vertex ids/names, the middle keys ids/names of their neighbors, and the
      innermost dictionary representing attributes of that edge.
    Nry   r   r   c                   S   s   t tS r   )r   dictr   r   r   r   <lambda>      z,_export_graph_to_dict_dict.<locals>.<lambda>c                    s   i | ]}| | qS r   r   )rA   r+   rw   r   r   rs      s      z._export_graph_to_dict_dict.<locals>.<dictcomp>c                 S   s   i | ]\}}|d k	r||qS r   r   rr   r   r   r   rs     s       c                 S   s   i | ]\}}|t |qS r   )r   r   r   r   r   rs     s      )r7   r8   r%   r    r;   r   r>   r"   r   r#   ru   rt   r   )rP   rU   r0   rp   r   r~   rv   r}   rQ   r
   r   r   r   r   _export_graph_to_dict_dict  s0    %



r   c                    sb   zddl }W n tk
r(   tdY nX |j fdd  D tt  d}d|j_|S )a  Export vertices with attributes to pandas.DataFrame

    If you want to use vertex names as index, you can do:

    >>> from string import ascii_letters
    >>> graph = Graph.GRG(25, 0.4)
    >>> graph.vs["name"] = ascii_letters[:graph.vcount()]
    >>> df = graph.get_vertex_dataframe()
    >>> df.set_index('name', inplace=True)

    @return: a pandas.DataFrame representing vertices and their attributes.
      The index uses vertex IDs, from 0 to N - 1 where N is the number of
      vertices.
    r   NrV   c                    s   i | ]}| j | qS r   )r"   rA   attrrP   r   r   rs     s      z,_export_vertex_dataframe.<locals>.<dictcomp>rW   z	vertex ID)	rX   rY   rc   r>   r%   ra   Zvcountr=   r   rP   rk   Zdfr   r   r   _export_vertex_dataframe
  s    r   c                    s   zddl }W n tk
r(   tdY nX |j fdd  D tt  d}d|j_|j	ddd	d
  j
D dd |j	dddd
  j
D dd |S )a  Export edges with attributes to pandas.DataFrame

    If you want to use source and target vertex IDs as index, you can do:

    >>> from string import ascii_letters
    >>> graph = Graph.GRG(25, 0.4)
    >>> graph.vs["name"] = ascii_letters[:graph.vcount()]
    >>> df = graph.get_edge_dataframe()
    >>> df.set_index(['source', 'target'], inplace=True)

    The index will be a pandas.MultiIndex. You can use the C{drop=False}
    option to keep the C{source} and C{target} columns.

    If you want to use vertex names in the source and target columns:

    >>> df = graph.get_edge_dataframe()
    >>> df_vert = graph.get_vertex_dataframe()
    >>> df['source'].replace(df_vert['name'], inplace=True)
    >>> df['target'].replace(df_vert['name'], inplace=True)
    >>> df_vert.set_index('name', inplace=True)  # Optional

    @return: a pandas.DataFrame representing edges and their attributes.
      The index uses edge IDs, from 0 to M - 1 where M is the number of
      edges. The first two columns of the dataframe represent the IDs of
      source and target vertices for each edge. These columns have names
      "source" and "target". If your edges have attributes with the same
      names, they will be present in the dataframe, but not in the first
      two columns.
    r   NrV   c                    s   i | ]}| j | qS r   )r#   r   r   r   r   rs   K  s      z*_export_edge_dataframe.<locals>.<dictcomp>rW   zedge IDr
   c                 S   s   g | ]
}|j qS r   )r
   rA   er   r   r   r|   P  s     z*_export_edge_dataframe.<locals>.<listcomp>T)Zallow_duplicatesr   r   c                 S   s   g | ]
}|j qS r   )r   r   r   r   r   r|   Q  s     )rX   rY   rc   r;   r%   ra   Zecountr=   r   insertr#   r   r   r   r   _export_edge_dataframe'  s    r   )Fr   r	   F)Fr   NF)Fr   )Fr   )TNT)TFr   )TNr   )TNFr   )typingr   r   collectionsr   	itertoolsr   warningsr   Zigraph.datatypesr   boolr8   r4   r?   rN   rT   ro   rx   r   r%   callabler   r   r   r   r   r   r   r   <module>   s            ^  @  J       J   F9    G