code: def bipartite_from_networkx(
      nx_graph,
      utype,
      etype,
      vtype,
      u_attrs=None,
      e_attrs=None,
      v_attrs=None,
      edge_id_attr_name=None,
      idtype=None,
      device=None,
  ):
      """Create a unidirectional bipartite graph from a NetworkX graph and return.
  
      The created graph will have two types of nodes ``utype`` and ``vtype`` as well as one
      edge type ``etype`` whose edges are from ``utype`` to ``vtype``.
  
      .. note::
          Creating a DGLGraph from a NetworkX graph is not fast especially for large scales.
          It is recommended to first convert a NetworkX graph into a tuple of node-tensors
          and then construct a DGLGraph with :func:`dgl.heterograph`.
  
      Parameters
      ----------
      nx_graph : networkx.DiGraph
          The NetworkX graph holding the graph structure and the node/edge attributes.
          DGL will relabel the nodes using consecutive integers starting from zero if it is
          not the case. The graph must follow `NetworkX's bipartite graph convention
          <https://networkx.github.io/documentation/stable/reference/algorithms/bipartite.html>`_,
          and furthermore the edges must be from nodes with attribute ``bipartite=0`` to nodes
          with attribute ``bipartite=1``.
      utype : str, optional
          The name of the source node type.
      etype : str, optional
          The name of the edge type.
      vtype : str, optional
          The name of the destination node type.
      u_attrs : list[str], optional
          The names of the node attributes for node type :attr:`utype` to retrieve from the
          NetworkX graph. If given, DGL stores the retrieved node attributes in
          ``nodes[utype].data`` of the returned graph using their original names. The attribute
          data must be convertible to Tensor type (e.g., scalar, ``numpy.ndarray``, list, etc.).
      e_attrs : list[str], optional
          The names of the edge attributes to retrieve from the NetworkX graph. If given, DGL
          stores the retrieved edge attributes in ``edata`` of the returned graph using their
          original names. The attribute data must be convertible to Tensor type (e.g., scalar,
          numpy.ndarray, list, etc.).
      v_attrs : list[str], optional
          The names of the node attributes for node type :attr:`vtype` to retrieve from the
          NetworkX graph.  If given, DGL stores the retrieved node attributes in
          ``nodes[vtype].data`` of the returned graph using their original names. The attribute
          data must be convertible to Tensor type (e.g., scalar, numpy.array, list, etc.).
      edge_id_attr_name : str, optional
          The name of the edge attribute that stores the edge IDs. If given, DGL will assign edge
          IDs accordingly when creating the graph, so the attribute must be valid IDs, i.e.
          consecutive integers starting from zero. By default, the edge IDs of the returned graph
          can be arbitrary.
      idtype : int32 or int64, optional
          The data type for storing the structure-related graph information such as node and
          edge IDs. It should be a framework-specific data type object (e.g., torch.int32).
          By default, DGL uses int64.
      device : device context, optional
          The device of the resulting graph. It should be a framework-specific device object
          (e.g., torch.device). By default, DGL stores the graph on CPU.
  
      Returns
      -------
      DGLGraph
          The created graph.
  
      Examples
      --------
  
      The following example uses PyTorch backend.
  
      >>> import dgl
      >>> import networkx as nx
      >>> import numpy as np
      >>> import torch
  
      Create a 2-edge unidirectional bipartite graph.
  
      >>> nx_g = nx.DiGraph()
      >>> # Add nodes for the source type
      >>> nx_g.add_nodes_from([1, 3], bipartite=0, feat1=np.zeros((2, 1)), feat2=np.ones((2, 1)))
      >>> # Add nodes for the destination type
      >>> nx_g.add_nodes_from([2, 4, 5], bipartite=1, feat3=np.zeros((3, 1)))
      >>> nx_g.add_edge(1, 4, weight=np.ones((1, 1)), eid=np.array([1]))
      >>> nx_g.add_edge(3, 5, weight=np.ones((1, 1)), eid=np.array([0]))
  
      Convert it into a DGLGraph with structure only.
  
      >>> g = dgl.bipartite_from_networkx(nx_g, utype='_U', etype='_E', vtype='_V')
  
      Retrieve the node/edge features of the graph.
  
      >>> g = dgl.bipartite_from_networkx(nx_g, utype='_U', etype='_E', vtype='_V',
      ...                                 u_attrs=['feat1', 'feat2'],
      ...                                 e_attrs=['weight'],
      ...                                 v_attrs=['feat3'])
  
      Use a pre-specified ordering of the edges.
  
      >>> g.edges()
      (tensor([0, 1]), tensor([1, 2]))
      >>> g = dgl.bipartite_from_networkx(nx_g,
      ...                                 utype='_U', etype='_E', vtype='_V',
      ...                                 edge_id_attr_name='eid')
      (tensor([1, 0]), tensor([2, 1]))
  
      Create a graph on the first GPU with data type int32.
  
      >>> g = dgl.bipartite_from_networkx(nx_g, utype='_U', etype='_E', vtype='_V',
      ...                                 idtype=torch.int32, device='cuda:0')
  
      See Also
      --------
      heterograph
      bipartite_from_scipy
      """
      if not nx_graph.is_directed():
          raise DGLError(
              "Expect nx_graph to be a directed NetworkX graph."
          )
      if (
          edge_id_attr_name is not None
          and not edge_id_attr_name
          in next(iter(nx_graph.edges(data=True)))[-1]
      ):
          raise DGLError(
              "Failed to find the pre-specified edge IDs in the edge features "
              "of the NetworkX graph with name {}".format(
                  edge_id_attr_name
              )
          )
  
      # Get the source and destination node sets
      top_nodes = set()
      bottom_nodes = set()
      for n, ndata in nx_graph.nodes(data=True):
          if "bipartite" not in ndata:
              raise DGLError(
                  "Expect the node {} to have attribute bipartite".format(
                      n
                  )
              )
          if ndata["bipartite"] == 0:
              top_nodes.add(n)
          elif ndata["bipartite"] == 1:
              bottom_nodes.add(n)
          else:
              raise ValueError(
                  "Expect the bipartite attribute of the node {} to be 0 or 1, "
                  "got {}".format(n, ndata["bipartite"])
              )
  
      # Separately relabel the source and destination nodes.
      top_nodes = sorted(top_nodes)
      bottom_nodes = sorted(bottom_nodes)
      top_map = {n: i for i, n in enumerate(top_nodes)}
      bottom_map = {n: i for i, n in enumerate(bottom_nodes)}
  
      # Get the node tensors and the number of nodes
      (sparse_fmt, arrays), urange, vrange = utils.graphdata2tensors(
          nx_graph,
          idtype,
          bipartite=True,
          edge_id_attr_name=edge_id_attr_name,
          top_map=top_map,
          bottom_map=bottom_map,
      )
  
      g = create_from_edges(
          sparse_fmt, arrays, utype, etype, vtype, urange, vrange
      )
  
      # nx_graph.edges(data=True) returns src, dst, attr_dict
      has_edge_id = (
          nx_graph.number_of_edges() > 0
          and edge_id_attr_name is not None
      )
  
      # handle features
      # copy attributes
      if u_attrs is not None:
          # mapping from feature name to a list of tensors to be concatenated
          src_attr_dict = defaultdict(list)
          for nid in top_map.keys():
              for attr in u_attrs:
                  src_attr_dict[attr].append(nx_graph.nodes[nid][attr])
          for attr in u_attrs:
              g.srcdata[attr] = F.copy_to(
                  _batcher(src_attr_dict[attr]), g.device
              )
  
      if v_attrs is not None:
          # mapping from feature name to a list of tensors to be concatenated
          dst_attr_dict = defaultdict(list)
          for nid in bottom_map.keys():
              for attr in v_attrs:
                  dst_attr_dict[attr].append(nx_graph.nodes[nid][attr])
          for attr in v_attrs:
              g.dstdata[attr] = F.copy_to(
                  _batcher(dst_attr_dict[attr]), g.device
              )
  
      if e_attrs is not None:
          # mapping from feature name to a list of tensors to be concatenated
          attr_dict = defaultdict(lambda: [None] * g.num_edges())
          # each defaultdict value is initialized to be a list of None
          # None here serves as placeholder to be replaced by feature with
          # corresponding edge id
          if has_edge_id:
              for _, _, attrs in nx_graph.edges(data=True):
                  for key in e_attrs:
                      attr_dict[key][attrs[edge_id_attr_name]] = attrs[
                          key
                      ]
          else:
              # XXX: assuming networkx iteration order is deterministic
              #      so the order is the same as graph_index.from_networkx
              for eid, (_, _, attrs) in enumerate(
                  nx_graph.edges(data=True)
              ):
                  for key in e_attrs:
                      attr_dict[key][eid] = attrs[key]
          for attr in e_attrs:
              for val in attr_dict[attr]:
                  if val is None:
                      raise DGLError(
                          "Not all edges have attribute {}.".format(
                              attr
                          )
                      )
              g.edata[attr] = F.copy_to(
                  _batcher(attr_dict[attr]), g.device
              )
  
      return g.to(device)
  
imports: from collections import defaultdict
  from collections.abc import Mapping
  import networkx
  import numpy
  from scipy.sparse import spmatrix
  from .None import backend, graph_index, heterograph_index, utils
  from .base import DGLError, EID, ETYPE, NID, NTYPE
  from .heterograph import combine_frames, DGLBlock, DGLGraph
label_desc: Use image node_id 12 for calling a global function with example usage: bipartite_from_networkx(nx_graph, utype, etype, vtype, u_attrs, e_attrs, v_attrs, edge_id_attr_name, idtype, device) and returns: g