code: def metapath_reachable_graph(g, metapath):
      """Return a graph where the successors of any node ``u`` are nodes reachable from ``u`` by
      the given metapath.
  
      If the beginning node type ``s`` and ending node type ``t`` are the same, it will return
      a homogeneous graph with node type ``s = t``.  Otherwise, a unidirectional bipartite graph
      with source node type ``s`` and destination node type ``t`` is returned.
  
      In both cases, two nodes ``u`` and ``v`` will be connected with an edge ``(u, v)`` if
      there exists one path matching the metapath from ``u`` to ``v``.
  
      The result graph keeps the node set of type ``s`` and ``t`` in the original graph even if
      they might have no neighbor.
  
      The features of the source/destination node type in the original graph would be copied to
      the new graph.
  
      Parameters
      ----------
      g : DGLGraph
          The input graph
      metapath : list[str or tuple of str]
          Metapath in the form of a list of edge types
  
      Returns
      -------
      DGLGraph
          A homogeneous or unidirectional bipartite graph. It will be on CPU regardless of
          whether the input graph is on CPU or GPU.
  
      Notes
      -----
  
      This function discards the batch information. Please use
      :func:`dgl.DGLGraph.set_batch_num_nodes`
      and :func:`dgl.DGLGraph.set_batch_num_edges` on the transformed graph
      to maintain the information.
  
      Examples
      --------
      >>> g = dgl.heterograph({
      ...     ('A', 'AB', 'B'): ([0, 1, 2], [1, 2, 3]),
      ...     ('B', 'BA', 'A'): ([1, 2, 3], [0, 1, 2])})
      >>> new_g = dgl.metapath_reachable_graph(g, ['AB', 'BA'])
      >>> new_g.edges(order='eid')
      (tensor([0, 1, 2]), tensor([0, 1, 2]))
      """
      adj = 1
      for etype in metapath:
          adj = adj * g.adj_external(
              etype=etype, scipy_fmt="csr", transpose=False
          )
  
      adj = (adj != 0).tocsr()
      srctype = g.to_canonical_etype(metapath[0])[0]
      dsttype = g.to_canonical_etype(metapath[-1])[2]
      new_g = convert.heterograph(
          {(srctype, "_E", dsttype): adj.nonzero()},
          {srctype: adj.shape[0], dsttype: adj.shape[1]},
          idtype=g.idtype,
          device=g.device,
      )
  
      # copy srcnode features
      new_g.nodes[srctype].data.update(g.nodes[srctype].data)
      # copy dstnode features
      if srctype != dsttype:
          new_g.nodes[dsttype].data.update(g.nodes[dsttype].data)
  
      return new_g
  
imports: import copy
  from collections.abc import Iterable, Mapping
  import numpy
  import scipy.sparse
  import scipy.sparse.linalg
  from ..utils import version
  from ..None import backend, batch, convert, function, ndarray, subgraph, utils
  from .._ffi.function import _init_api
  from ..base import dgl_warning, DGLError, EID, NID
  from ..frame import Frame
  from ..heterograph import DGLGraph
  from ..heterograph_index import create_heterograph_from_relations, create_metagraph_index
  from ..partition import metis_partition, metis_partition_assignment, partition_graph_with_halo
  from ..sampling.neighbor import sample_neighbors
label_desc: Use image node_id 16 for calling a global function with example usage: metapath_reachable_graph(g, metapath) and returns: new_g