code: def to_networkx(
      data: Union[
          "torch_geometric.data.Data",
          "torch_geometric.data.HeteroData",
      ],
      node_attrs: Optional[Iterable[str]] = None,
      edge_attrs: Optional[Iterable[str]] = None,
      graph_attrs: Optional[Iterable[str]] = None,
      to_undirected: Optional[Union[bool, str]] = False,
      to_multi: bool = False,
      remove_self_loops: bool = False,
  ) -> Any:
      r"""Converts a :class:`torch_geometric.data.Data` instance to a
      :obj:`networkx.Graph` if :attr:`to_undirected` is set to :obj:`True`, or
      a directed :obj:`networkx.DiGraph` otherwise.
  
      Args:
          data (torch_geometric.data.Data or torch_geometric.data.HeteroData): A
              homogeneous or heterogeneous data object.
          node_attrs (iterable of str, optional): The node attributes to be
              copied. (default: :obj:`None`)
          edge_attrs (iterable of str, optional): The edge attributes to be
              copied. (default: :obj:`None`)
          graph_attrs (iterable of str, optional): The graph attributes to be
              copied. (default: :obj:`None`)
          to_undirected (bool or str, optional): If set to :obj:`True`, will
              return a :class:`networkx.Graph` instead of a
              :class:`networkx.DiGraph`.
              By default, will include all edges and make them undirected.
              If set to :obj:`"upper"`, the undirected graph will only correspond
              to the upper triangle of the input adjacency matrix.
              If set to :obj:`"lower"`, the undirected graph will only correspond
              to the lower triangle of the input adjacency matrix.
              Only applicable in case the :obj:`data` object holds a homogeneous
              graph. (default: :obj:`False`)
          to_multi (bool, optional): if set to :obj:`True`, will return a
              :class:`networkx.MultiGraph` or a :class:`networkx:MultiDiGraph`
              (depending on the :obj:`to_undirected` option), which will not drop
              duplicated edges that may exist in :obj:`data`.
              (default: :obj:`False`)
          remove_self_loops (bool, optional): If set to :obj:`True`, will not
              include self-loops in the resulting graph. (default: :obj:`False`)
  
      Examples:
          >>> edge_index = torch.tensor([
          ...     [0, 1, 1, 2, 2, 3],
          ...     [1, 0, 2, 1, 3, 2],
          ... ])
          >>> data = Data(edge_index=edge_index, num_nodes=4)
          >>> to_networkx(data)
          <networkx.classes.digraph.DiGraph at 0x2713fdb40d0>
  
      """
      import networkx as nx
  
      from torch_geometric.data import HeteroData
  
      to_undirected_upper: bool = to_undirected == "upper"
      to_undirected_lower: bool = to_undirected == "lower"
  
      to_undirected = to_undirected is True
      to_undirected |= to_undirected_upper or to_undirected_lower
      assert isinstance(to_undirected, bool)
  
      if isinstance(data, HeteroData) and to_undirected:
          raise ValueError(
              "'to_undirected' is not supported in "
              "'to_networkx' for heterogeneous graphs"
          )
  
      if to_undirected:
          G = nx.MultiGraph() if to_multi else nx.Graph()
      else:
          G = nx.MultiDiGraph() if to_multi else nx.DiGraph()
  
      def to_networkx_value(value: Any) -> Any:
          return value.tolist() if isinstance(value, Tensor) else value
  
      for key in graph_attrs or []:
          G.graph[key] = to_networkx_value(data[key])
  
      node_offsets = data.node_offsets
      for node_store in data.node_stores:
          start = node_offsets[node_store._key]
          assert node_store.num_nodes is not None
          for i in range(node_store.num_nodes):
              node_kwargs: Dict[str, Any] = {}
              if isinstance(data, HeteroData):
                  node_kwargs["type"] = node_store._key
              for key in node_attrs or []:
                  node_kwargs[key] = to_networkx_value(
                      node_store[key][i]
                  )
  
              G.add_node(start + i, **node_kwargs)
  
      for edge_store in data.edge_stores:
          for i, (v, w) in enumerate(
              edge_store.edge_index.t().tolist()
          ):
              if to_undirected_upper and v > w:
                  continue
              elif to_undirected_lower and v < w:
                  continue
              elif (
                  remove_self_loops
                  and v == w
                  and not edge_store.is_bipartite()
              ):
                  continue
  
              edge_kwargs: Dict[str, Any] = {}
              if isinstance(data, HeteroData):
                  v = v + node_offsets[edge_store._key[0]]
                  w = w + node_offsets[edge_store._key[-1]]
                  edge_kwargs["type"] = edge_store._key
              for key in edge_attrs or []:
                  edge_kwargs[key] = to_networkx_value(
                      edge_store[key][i]
                  )
  
              G.add_edge(v, w, **edge_kwargs)
  
      return G
  
file_path: pytorch_geometric/torch_geometric/utils/convert.py
imports: from collections import defaultdict
  from typing import Any, Dict, Iterable, List, Literal, Optional, Tuple, Union
  import scipy.sparse
  import torch
  from torch import Tensor
  from torch.utils.dlpack import from_dlpack, to_dlpack
  import torch_geometric
  from torch_geometric.utils.num_nodes import maybe_num_nodes
label_desc: Use image node_id 3 for calling a global function with example usage: to_networkx(data, node_attrs, edge_attrs, graph_attrs, to_undirected, to_multi, remove__loops) and returns: G, unknown