class_docstr: Duck-typed version of the DGLHeteroGraph class made for cuGraph
  for storing graph structure and node/edge feature data.
  
  This object is wrapper around cugraph's Multi GPU MultiGraph and returns samples
  that conform with `DGLHeteroGraph`
  See: https://docs.rapids.ai/api/cugraph/nightly/api_docs/cugraph_dgl.html
class_docstr_tok: ["Duck-typed","version","of","the","DGLHeteroGraph","class","made","for","cuGraph","for","storing","graph","structure","and","node\/edge","feature","data",".","This","object","is","wrapper","around","cugraph","'s","Multi","GPU","MultiGraph","and","returns","samples","that","conform","with","`","DGLHeteroGraph","`","See",":","https",":","\/\/docs.rapids.ai\/api\/cugraph\/nightly\/api_docs\/cugraph_dgl.html"]
docstr: Return a DGLGraph which is a subgraph induced by sampling neighboring
  edges of the given nodes.
  See ``dgl.sampling.sample_neighbors`` for detailed semantics.
  Parameters
  ----------
  nodes : Tensor or dict[str, Tensor]
      Node IDs to sample neighbors from.
      This argument can take a single ID tensor or a dictionary of node
      types and ID tensors. If a single tensor is given, the graph must
      only have one type of nodes.
  fanout : int or dict[etype, int]
      The number of edges to be sampled for each node on each edge type.
      This argument can take a single int or a dictionary of edge types
      and ints. If a single int is given, DGL will sample this number of
      edges for each node for every edge type.
      If -1 is given for a single edge type, all the neighboring edges
      with that edge type will be selected.
  edge_dir: 'in' or 'out'
      The direction of edges to import
  prob : str, optional
      Feature name used as the (un-normalized) probabilities associated
      with each neighboring edge of a node.  The feature must have only
      one element for each edge.
      The features must be non-negative floats, and the sum of the
      features of inbound/outbound edges for every node must be positive
      (though they don't have to sum up to one).  Otherwise, the result
      will be undefined. If :attr:`prob` is not None, GPU sampling is
      not supported.
  exclude_edges: tensor or dict
      Edge IDs to exclude during sampling neighbors for the seed nodes.
      This argument can take a single ID tensor or a dictionary of edge
      types and ID tensors. If a single tensor is given, the graph must
      only have one type of nodes.
  replace : bool, optional
      If True, sample with replacement.
  output_device : Framework-specific device context object, optional
      The output device.  Default is the same as the input graph.
  Returns
  -------
  DGLGraph
      A sampled subgraph with the same nodes as the original graph, but
      only the sampled neighboring edges.  The induced edge IDs will be
      in ``edata[dgl.EID]``.
code: def sample_neighbors(
      self,
      nodes,
      fanout: int,
      edge_dir: str = "in",
      prob: Optional[str] = None,
      exclude_edges=None,
      replace: bool = False,
      output_device=None,
  ):
      """
      Return a DGLGraph which is a subgraph induced by sampling neighboring
      edges of the given nodes.
      See ``dgl.sampling.sample_neighbors`` for detailed semantics.
      Parameters
      ----------
      nodes : Tensor or dict[str, Tensor]
          Node IDs to sample neighbors from.
          This argument can take a single ID tensor or a dictionary of node
          types and ID tensors. If a single tensor is given, the graph must
          only have one type of nodes.
      fanout : int or dict[etype, int]
          The number of edges to be sampled for each node on each edge type.
          This argument can take a single int or a dictionary of edge types
          and ints. If a single int is given, DGL will sample this number of
          edges for each node for every edge type.
          If -1 is given for a single edge type, all the neighboring edges
          with that edge type will be selected.
      edge_dir: 'in' or 'out'
          The direction of edges to import
      prob : str, optional
          Feature name used as the (un-normalized) probabilities associated
          with each neighboring edge of a node.  The feature must have only
          one element for each edge.
          The features must be non-negative floats, and the sum of the
          features of inbound/outbound edges for every node must be positive
          (though they don't have to sum up to one).  Otherwise, the result
          will be undefined. If :attr:`prob` is not None, GPU sampling is
          not supported.
      exclude_edges: tensor or dict
          Edge IDs to exclude during sampling neighbors for the seed nodes.
          This argument can take a single ID tensor or a dictionary of edge
          types and ID tensors. If a single tensor is given, the graph must
          only have one type of nodes.
      replace : bool, optional
          If True, sample with replacement.
      output_device : Framework-specific device context object, optional
          The output device.  Default is the same as the input graph.
      Returns
      -------
      DGLGraph
          A sampled subgraph with the same nodes as the original graph, but
          only the sampled neighboring edges.  The induced edge IDs will be
          in ``edata[dgl.EID]``.
      """
      if self.uniform_sampler is None:
          self.uniform_sampler = DGLUniformSampler(
              self._edges_dict,
              self._etype_range_d,
              self._etype_id_dict,
              self.single_gpu,
          )
  
      if prob is not None:
          raise NotImplementedError(
              "prob is not currently supported",
              " for sample_neighbors in CuGraphStorage",
          )
  
      if exclude_edges is not None:
          raise NotImplementedError(
              "exclude_edges is not currently supported",
              " for sample_neighbors in CuGraphStorage",
          )
  
      if not isinstance(nodes, dict):
          if len(self.ntypes) > 1:
              raise dgl.DGLError(
                  "Must specify node type when the graph is not homogeneous."
              )
          nodes = cp.asarray(nodes)
          nodes = {self.ntypes[0]: nodes}
      else:
          nodes = {
              k: self.dgl_n_id_to_cugraph_id(F.tensor(n), k)
              for k, n in nodes.items()
          }
          nodes = {k: cp.asarray(F.tensor(n)) for k, n in nodes.items()}
  
      sampled_obj = self.uniform_sampler.sample_neighbors(
          nodes,
          fanout,
          edge_dir=edge_dir,
          prob=prob,
          replace=replace,
      )
      # heterograph case
      if len(self.etypes) > 1:
          graph_data_d, graph_eid_d = self.__convert_to_dgl_tensor_d(
              sampled_obj, self.idtype
          )
          sampled_graph = dgl.heterograph(
              data_dict=graph_data_d,
              num_nodes_dict=self.num_nodes_dict,
              idtype=self.idtype,
          )
          sampled_graph.edata[dgl.EID] = graph_eid_d
      else:
          src_ids, dst_ids, edge_ids = sampled_obj
          src_ids = torch.as_tensor(src_ids, device="cuda")
          dst_ids = torch.as_tensor(dst_ids, device="cuda")
          edge_ids = torch.as_tensor(edge_ids, device="cuda")
          total_number_of_nodes = self.total_number_of_nodes
          sampled_graph = dgl.graph(
              (src_ids, dst_ids),
              num_nodes=total_number_of_nodes,
              idtype=self.idtype,
          )
          sampled_graph.edata[dgl.EID] = edge_ids
  
      # to device function move the dgl graph to desired devices
      if output_device is not None:
          sampled_graph.to(output_device)
      return sampled_graph
  
imports: from __future__ import annotations
  from typing import Optional, Sequence, Tuple, Dict, Union
  from functools import cached_property
  from cugraph.utilities.utils import import_optional, MissingModule
  from cugraph.gnn import FeatureStore
  from cugraph.gnn.dgl_extensions.dgl_uniform_sampler import DGLUniformSampler
  import cudf
  import dask_cudf
  import cupy
  from cugraph_dgl.utils.cugraph_storage_utils import _assert_valid_canonical_etype, backend_dtype_to_np_dtype_dict, add_edge_ids_to_edges_dict, add_node_offset_to_edges_dict
  from cugraph_dgl.utils.feature_storage import dgl_FeatureStorage
label_desc: Use image node_id 4 for calling the CuGraphStorage obj's underlying member method code with example usage: obj.sample_neighbors(nodes, fanout, edge_dir, prob, exclude_edges, replace, output_device) and returns: sampled_graph