Source code for grakelx.utils.converts

from __future__ import annotations

from collections.abc import Iterable
from typing import TYPE_CHECKING

from grakelx import Graph
from grakelx.utils._type_hints import Label

if TYPE_CHECKING:
    import networkx as nx


[docs] def networkx_from_graph( X: Graph | Iterable[Graph], node_labels_tag: str | None = None, edge_labels_tag: str | None = None, edge_weight_tag: str = "weight", create_using: type[nx.Graph] | None = None, ) -> nx.Graph | list[nx.Graph]: """Transform grakel.Graph objects to networkx graph objects. Inverse operation of :func:`graph_from_networkx`. Iterates the ``edge_dictionary`` of each input graph and writes nodes, edges and (optionally) labels and weights as networkx node/edge attributes. Parameters ---------- X : grakel.Graph or Iterable[grakel.Graph] A GraKeL graph or an iterable of GraKeL graphs. node_labels_tag : str or None, default=None If provided, the dictionary labels of every node are written as a node attribute with this tag. If ``None`` no node attribute is written. edge_labels_tag : str or None, default=None If provided, the dictionary labels of every edge are written as an edge attribute with this tag. If ``None`` no edge attribute is written. edge_weight_tag : str or None, default="weight" If provided, the weight of every edge is written as an edge attribute with this tag. The default ``"weight"`` follows the NetworkX convention. Set to ``None`` to skip writing weights. create_using : type[networkx.Graph] or None, default=None NetworkX graph class used to construct each output graph. Defaults to ``None`` (an undirected ``networkx.Graph``). Any subclass is accepted, e.g. ``nx.DiGraph`` or ``nx.MultiGraph``. GraKeL graphs are inherently non-directed, so when using a directed container the symmetric entries of the edge dictionary are merged into a single canonical edge ``(min(u, v), max(u, v))``. Returns ------- converted_graphs : networkx.Graph or list[networkx.Graph] Returns a networkx.Graph if ``X`` is a single graph and a list of networkx.Graph objects otherwise. """ import networkx as nx if not (node_labels_tag is None or isinstance(node_labels_tag, str)): raise ValueError("node_labels_tag must be a str indicating the tag of the labels inside nodes or None") if not (edge_labels_tag is None or isinstance(edge_labels_tag, str)): raise ValueError("edge_labels_tag must be a str indicating the tag of the labels inside edges or None") if not (edge_weight_tag is None or isinstance(edge_weight_tag, str)): raise ValueError("edge_weight_tag must be a str indicating the tag of the weights inside edges or None") if create_using is not None and not (isinstance(create_using, type) and issubclass(create_using, nx.Graph)): raise ValueError("create_using must be a subclass of networkx.Graph or None") container_cls = nx.Graph if create_using is None else create_using if isinstance(X, Graph): graphs = [X] return_single_graph = True elif isinstance(X, Iterable): graphs = list(X) return_single_graph = False else: raise ValueError("X must be a grakel.Graph or an iterable of grakel.Graph objects") converted_graphs = [] for G in graphs: if not isinstance(G, Graph): raise ValueError("each element of X must be a grakel.Graph") nx_g = container_cls() nl = G.get_labels(purpose="dictionary", return_none=True) if node_labels_tag is not None else None el = G.get_labels(label_type="edge", purpose="dictionary", return_none=True) if edge_labels_tag is not None else None for u in G.get_vertices(purpose="dictionary"): attrs = {} if nl is not None and u in nl: attrs[node_labels_tag] = nl[u] nx_g.add_node(u, **attrs) edge_dictionary = G.get_edge_dictionary() if not isinstance(nx_g, (nx.DiGraph, nx.MultiDiGraph)) and not isinstance(nx_g, (nx.MultiGraph,)): seen = set() for u, nbrs in edge_dictionary.items(): for v, w in nbrs.items(): key = (u, v) if u <= v else (v, u) if key in seen: continue seen.add(key) attrs = {} if edge_weight_tag is not None: attrs[edge_weight_tag] = w if el is not None and key in el: attrs[edge_labels_tag] = el[key] nx_g.add_edge(key[0], key[1], **attrs) else: for u, nbrs in edge_dictionary.items(): for v, w in nbrs.items(): attrs = {} if edge_weight_tag is not None: attrs[edge_weight_tag] = w if el is not None and (u, v) in el: attrs[edge_labels_tag] = el[(u, v)] nx_g.add_edge(u, v, **attrs) converted_graphs.append(nx_g) if return_single_graph: return converted_graphs[0] return converted_graphs
[docs] def graph_from_networkx( X: nx.Graph | Iterable[nx.Graph], node_labels_tag: str | None = None, edge_labels_tag: str | None = None, edge_weight_tag: str | None = None, val_node_labels: Label | None = None, val_edge_labels: Label | None = None, ) -> Graph | list[Graph]: """Transform networkx objects to grakel.Graph objects. A function for helping a user that has a collection of graphs in networkx to use grakel. Parameters ---------- X : networkx.Graph or Iterable[networkx.Graph] A networkx graph or an iterable of networkx graphs. node_labels_tag : str or None Define where to search for labels of nodes, inside the `node` attribute of each graph. If None no labels are assigned. edge_labels_tag : str or None Define where to search for labels of edges, inside the `edge` attribute of each graph. If None no labels are assigned. edge_weight_tag : str or None Define where to search for weights inside the `edge` attribute of each graph. If None 1.0 weights are assigned. val_node_labels : Label, default=None Sets constant value to all nodes labels of a graph if missing. See ``grakelx.utils._type_hints.Label`` for the accepted values. val_edge_labels : Label, default=None Sets constant value to all edges labels of a graph if missing. See ``grakelx.utils._type_hints.Label`` for the accepted values. Returns ------- converted_graphs : grakel.Graph or list[grakel.Graph] Returns a grakel.Graph if ``X`` is a single graph and a list of grakel.Graph objects otherwise. """ import networkx as nx if node_labels_tag is None: def nodel_init(): if val_node_labels is None: return None else: return dict() def nodel_put(nl, u, d): if val_node_labels is not None: nl[u] = val_node_labels elif isinstance(node_labels_tag, str): def nodel_init(): return dict() def nodel_put(nl, u, d): attrs = d[u] # G.nodes[u] -> node attribute dict if node_labels_tag in attrs: nl[u] = attrs[node_labels_tag] elif val_node_labels is not None: nl[u] = val_node_labels else: raise ValueError("could not find node label tag '{0}' for node {1}".format(node_labels_tag, u)) else: raise ValueError("node_labels_tag must be a str indicating the tag of the labels inside nodes or None") if edge_labels_tag is None: def edgel_init(): if val_edge_labels is None: return None else: return dict() def edgel_put(el, u, d): if val_edge_labels is not None: el[u] = val_edge_labels elif isinstance(edge_labels_tag, str): def edgel_init(): return dict() def edgel_put(el, u, d): # d is the adjacency dict G[src]; u is (src, dst); d[u[1]] is edge attr dict attrs = d[u[1]] if edge_labels_tag in attrs: el[u] = attrs[edge_labels_tag] elif val_edge_labels is not None: el[u] = val_edge_labels else: raise ValueError("could not find edge label tag '{0}' for edge {1}".format(edge_labels_tag, u)) else: raise ValueError("edge_labels_tag must be a str indicating the tag of the labels inside edges or None") if edge_weight_tag is None: def get_weight(*args): return 1.0 elif isinstance(edge_weight_tag, str): def get_weight(d, e): # d is adjacency dict G[src]; e is (src, dst); d[e[1]] is edge attr dict attrs = d[e[1]] if edge_weight_tag in attrs: return attrs[edge_weight_tag] return 1.0 else: raise ValueError("weight_labels_tag must be a str indicating tag of the labels inside edges or None (1.0)") if isinstance(X, nx.Graph): graphs = [X] return_single_graph = True elif isinstance(X, Iterable): graphs = list(X) return_single_graph = False else: raise ValueError("X must be a networkx graph or an iterable of networkx graphs") converted_graphs = [] for G in graphs: if not isinstance(G, nx.Graph): raise ValueError("each element of X must be a networkx graph") graph_object = dict() nl = nodel_init() el = edgel_init() for u in G.nodes(): graph_object[u] = dict() nodel_put(nl, u, G.nodes) # G.nodes[u] -> node attribute dict for v in G.neighbors(u): adj = G[u] # adjacency dict for u: adj[v] = edge attr dict graph_object[u][v] = get_weight(adj, (u, v)) edgel_put(el, (u, v), adj) converted_graphs.append(Graph(graph_object, nl, el)) if return_single_graph: return converted_graphs[0] return converted_graphs