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: Constructor for creating a object of instance CuGraphStorage
  
  See also ``cugraph_dgl.cugraph_storage_from_heterograph``
  to convert from DGLHeteroGraph to CuGraphStorage
  
  Parameters
  ----------
   data_dict:
      The dictionary data for constructing a heterogeneous graph.
      The keys are in the form of string triplets (src_type, edge_type, dst_type),
      specifying the source node, edge, and destination node types.
      The values are graph data is a dataframe with 2 columns form of (𝑈,𝑉),
      where (𝑈[𝑖],𝑉[𝑖]) forms the edge with ID 𝑖.
  
   num_nodes_dict: dict[str, int]
      The number of nodes for some node types, which is a
      dictionary mapping a node type T to the number of T-typed nodes.
  
  single_gpu: bool
      Whether to create the cugraph Property Graph
      on a single GPU or multiple GPUs
      single GPU = True
      single GPU = False
  
  device_id: int
      If specified, must be the integer ID of the GPU device to have the
      results being created on
  
  idtype: Framework-specific device object,
      The data type for storing the structure-related graph
      information this can be ``torch.int32`` or ``torch.int64``
      for PyTorch.
      Defaults to ``torch.int64`` if pytorch is installed
  
  
   Examples
   --------
   The following example uses `CuGraphStorage` :
      >>> from cugraph_dgl.cugraph_storage import CuGraphStorage
      >>> import cudf
      >>> import torch
      >>> num_nodes_dict={"drug": 3, "gene": 2, "disease": 1}
      >>> drug_interacts_drug_df = cudf.DataFrame({"src": [0, 1], "dst": [1, 2]})
      >>> drug_interacts_gene = cudf.DataFrame({"src": [0, 1], "dst": [0, 1]})
      >>> drug_treats_disease = cudf.DataFrame({"src": [1], "dst": [0]})
      >>> data_dict = {("drug", "interacts", "drug"):drug_interacts_drug_df,
           ("drug", "interacts", "gene"):drug_interacts_gene,
           ("drug", "treats", "disease"):drug_treats_disease }
      >>> gs = CuGraphStorage(data_dict=data_dict, num_nodes_dict=num_nodes_dict)
      >>> gs.add_node_data(ntype='drug', feat_name='node_feat',
                                    feat_obj=torch.as_tensor([0.1, 0.2, 0.3]))
      >>> gs.add_edge_data(canonical_etype=("drug", "interacts", "drug"),
                                    feat_name='edge_feat',
                                    feat_obj=torch.as_tensor([0.2, 0.4]))
      >>> gs.ntypes
      ['disease', 'drug', 'gene']
      >>> gs.etypes
      ['interacts', 'interacts', 'treats']
      >>> gs.canonical_etypes
      [('drug', 'interacts', 'drug'),
       ('drug', 'interacts', 'gene'),
       ('drug', 'treats', 'disease')]
  
      >>> gs.sample_neighbors({'disease':[0]},
                              1)
      Graph(num_nodes={'disease': 1, 'drug': 3, 'gene': 2},
      num_edges={('drug', 'interacts', 'drug'): 0,
                 ('drug', 'interacts', 'gene'): 0,
                 ('drug', 'treats', 'disease'): 1},
      metagraph=[('drug', 'drug', 'interacts'),
                 ('drug', 'gene', 'interacts'),
                 ('drug', 'disease', 'treats')])
  
      >>> gs.get_node_storage(key='node_feat',
                              ntype='drug').fetch([0,1,2])
      tensor([0.1000, 0.2000, 0.3000], device='cuda:0',
       dtype=torch.float64)
  
      >>> es = gs.get_edge_storage(key='edge_feat',
                              etype=('drug', 'interacts', 'drug'))
      >>> es.fetch([0,1])
      tensor([0.2000, 0.4000], device='cuda:0', dtype=torch.float64)
code: def __init__(
      self,
      data_dict: Dict[
          Tuple[str, str, str],
          Union[cudf.DataFrame, dask_cudf.DataFrame],
      ],
      num_nodes_dict: Dict[str, int],
      single_gpu: bool = True,
      device_id: int = 0,
      idtype=None if isinstance(F, MissingModule) else F.int64,
  ):
      """
      Constructor for creating a object of instance CuGraphStorage
  
      See also ``cugraph_dgl.cugraph_storage_from_heterograph``
      to convert from DGLHeteroGraph to CuGraphStorage
  
      Parameters
      ----------
       data_dict:
          The dictionary data for constructing a heterogeneous graph.
          The keys are in the form of string triplets (src_type, edge_type, dst_type),
          specifying the source node, edge, and destination node types.
          The values are graph data is a dataframe with 2 columns form of (𝑈,𝑉),
          where (𝑈[𝑖],𝑉[𝑖]) forms the edge with ID 𝑖.
  
       num_nodes_dict: dict[str, int]
          The number of nodes for some node types, which is a
          dictionary mapping a node type T to the number of T-typed nodes.
  
      single_gpu: bool
          Whether to create the cugraph Property Graph
          on a single GPU or multiple GPUs
          single GPU = True
          single GPU = False
  
      device_id: int
          If specified, must be the integer ID of the GPU device to have the
          results being created on
  
      idtype: Framework-specific device object,
          The data type for storing the structure-related graph
          information this can be ``torch.int32`` or ``torch.int64``
          for PyTorch.
          Defaults to ``torch.int64`` if pytorch is installed
  
  
       Examples
       --------
       The following example uses `CuGraphStorage` :
          >>> from cugraph_dgl.cugraph_storage import CuGraphStorage
          >>> import cudf
          >>> import torch
          >>> num_nodes_dict={"drug": 3, "gene": 2, "disease": 1}
          >>> drug_interacts_drug_df = cudf.DataFrame({"src": [0, 1], "dst": [1, 2]})
          >>> drug_interacts_gene = cudf.DataFrame({"src": [0, 1], "dst": [0, 1]})
          >>> drug_treats_disease = cudf.DataFrame({"src": [1], "dst": [0]})
          >>> data_dict = {("drug", "interacts", "drug"):drug_interacts_drug_df,
               ("drug", "interacts", "gene"):drug_interacts_gene,
               ("drug", "treats", "disease"):drug_treats_disease }
          >>> gs = CuGraphStorage(data_dict=data_dict, num_nodes_dict=num_nodes_dict)
          >>> gs.add_node_data(ntype='drug', feat_name='node_feat',
                                        feat_obj=torch.as_tensor([0.1, 0.2, 0.3]))
          >>> gs.add_edge_data(canonical_etype=("drug", "interacts", "drug"),
                                        feat_name='edge_feat',
                                        feat_obj=torch.as_tensor([0.2, 0.4]))
          >>> gs.ntypes
          ['disease', 'drug', 'gene']
          >>> gs.etypes
          ['interacts', 'interacts', 'treats']
          >>> gs.canonical_etypes
          [('drug', 'interacts', 'drug'),
           ('drug', 'interacts', 'gene'),
           ('drug', 'treats', 'disease')]
  
          >>> gs.sample_neighbors({'disease':[0]},
                                  1)
          Graph(num_nodes={'disease': 1, 'drug': 3, 'gene': 2},
          num_edges={('drug', 'interacts', 'drug'): 0,
                     ('drug', 'interacts', 'gene'): 0,
                     ('drug', 'treats', 'disease'): 1},
          metagraph=[('drug', 'drug', 'interacts'),
                     ('drug', 'gene', 'interacts'),
                     ('drug', 'disease', 'treats')])
  
          >>> gs.get_node_storage(key='node_feat',
                                  ntype='drug').fetch([0,1,2])
          tensor([0.1000, 0.2000, 0.3000], device='cuda:0',
           dtype=torch.float64)
  
          >>> es = gs.get_edge_storage(key='edge_feat',
                                  etype=('drug', 'interacts', 'drug'))
          >>> es.fetch([0,1])
          tensor([0.2000, 0.4000], device='cuda:0', dtype=torch.float64)
      """
      # Order is very important
      # do this first before cuda work
      # Create cuda context on the right gpu,
      # defaults to gpu-0
      import numba.cuda as cuda
  
      cuda.select_device(device_id)
  
      self.idtype = idtype
      self.id_np_type = backend_dtype_to_np_dtype_dict[idtype]
      self.num_nodes_dict = num_nodes_dict
      self._ntype_offset_d = self.__get_ntype_offset_d(
          self.num_nodes_dict
      )
      # Todo: Can possibly optimize by persisting edge-list
      # Trade-off memory for run-time
      self.num_edges_dict = {k: len(v) for k, v in data_dict.items()}
      self._etype_offset_d = self.__get_etype_offset_d(
          self.num_edges_dict
      )
      self.single_gpu = single_gpu
  
      self.ndata_storage = FeatureStore(backend="torch")
      self.ndata = self.ndata_storage.fd
      self.edata_storage = FeatureStore(backend="torch")
      self.edata = self.edata_storage.fd
  
      self._etype_range_d = self.__get_etype_range_d(
          self._etype_offset_d, self.num_canonical_edges_dict
      )
      _edges_dict = add_edge_ids_to_edges_dict(
          data_dict, self._etype_offset_d, self.id_np_type
      )
  
      self._edges_dict = add_node_offset_to_edges_dict(
          _edges_dict, self._ntype_offset_d
      )
      self._etype_id_dict = {
          etype: etype_id
          for etype_id, etype in enumerate(self.canonical_etypes)
      }
      self.uniform_sampler = None
  
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 1 to create a new CuGraphStorage object  with example: obj = CuGraphStorage(data_dict, num_nodes_dict, single_gpu, device_id, idtype)