code: def khop_graph(g, k, copy_ndata=True):
      """Return the graph whose edges connect the :attr:`k`-hop neighbors of the original graph.
  
      More specifically, an edge from node ``u`` and node ``v`` exists in the new graph if
      and only if a path with length :attr:`k` exists from node ``u`` to node ``v`` in the
      original graph.
  
      The adjacency matrix of the returned graph is :math:`A^k`
      (where :math:`A` is the adjacency matrix of :math:`g`).
  
      Parameters
      ----------
      g : DGLGraph
          The input graph.
      k : int
          The :math:`k` in `k`-hop graph.
      copy_ndata: bool, optional
          If True, the node features of the new graph are copied from the
          original graph.
  
          If False, the new graph will not have any node features.
  
          (Default: True)
  
      Returns
      -------
      DGLGraph
          The returned graph.
  
      Notes
      -----
      If :attr:`copy_ndata` is True, the resulting graph will share the node feature
      tensors with the input graph. Hence, users should try to avoid in-place operations
      which will be visible to both graphs.
  
      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
      --------
  
      Below gives an easy example:
  
      >>> import dgl
      >>> g = dgl.graph(([0, 1], [1, 2]))
      >>> g_2 = dgl.transforms.khop_graph(g, 2)
      >>> print(g_2.edges())
      (tensor([0]), tensor([2]))
  
      A more complicated example:
  
      >>> import dgl
      >>> g = dgl.graph(([0,1,2,3,4,0,1,2,3,4], [0,1,2,3,4,1,2,3,4,0]))
      >>> dgl.khop_graph(g, 1)
      DGLGraph(num_nodes=5, num_edges=10,
               ndata_schemes={}
               edata_schemes={})
      >>> dgl.khop_graph(g, 3)
      DGLGraph(num_nodes=5, num_edges=40,
               ndata_schemes={}
               edata_schemes={})
      """
      assert g.is_homogeneous, "only homogeneous graph is supported"
      n = g.num_nodes()
      adj_k = (
          g.adj_external(
              transpose=False, scipy_fmt=g.formats()["created"][0]
          )
          ** k
      )
      adj_k = adj_k.tocoo()
      multiplicity = adj_k.data
      row = np.repeat(adj_k.row, multiplicity)
      col = np.repeat(adj_k.col, multiplicity)
      # TODO(zihao): we should support creating multi-graph from scipy sparse matrix
      # in the future.
      new_g = convert.graph(
          (row, col), num_nodes=n, idtype=g.idtype, device=g.device
      )
  
      # handle ndata
      if copy_ndata:
          node_frames = utils.extract_node_subframes(g, None)
          utils.set_new_frames(new_g, node_frames=node_frames)
  
      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 12 for calling a global function with example usage: khop_graph(g, k, copy_ndata) and returns: new_g