class_docstr: A plain python object modeling a heterogeneous graph with various
  attributes (Node types in :class:`HeteroGraph` require string type).
  
  Args:
      G (:class:`networkx.classes.graph`): A NetworkX graph object which
          contains features and labels for each node type and edge type.
      **kwargs: Keyworded argument list with keys such
          as :obj:`"node_feature"`, :obj:`"node_label"` and
          corresponding attributes.
class_docstr_tok: ["A","plain","python","object","modeling","a","heterogeneous","graph","with","various","attributes","(","Node","types","in",":","class",":","`","HeteroGraph","`","require","string","type",")",".","Args",":","G","(",":","class",":","`","networkx.classes.graph","`",")",":","A","NetworkX","graph","object","which","contains","features","and","labels","for","each","node","type","and","edge","type",".","*","*","kwargs",":","Keyworded","argument","list","with","keys","such","as",":","obj",":","`","``","node_feature","''","`",",",":","obj",":","`","``","node_label","''","`","and","corresponding","attributes","."]
code: def __init__(self, G=None, **kwargs):
      self.G = G
      keys = [
          "node_feature",
          "node_label",
          "edge_feature",
          "edge_label",
          "graph_feature",
          "graph_label",
          "edge_index",
          "edge_label_index",
          "node_label_index",
          "custom",
      ]
      for key in keys:
          self[key] = None
  
      self._is_train = False
      self._num_positive_examples = None
  
      for key, item in kwargs.items():
          self[key] = item
  
      if G is None and kwargs:
          if "directed" not in kwargs:
              self.directed = True
          if "edge_index" not in kwargs:
              raise ValueError(
                  "A dictionary of tensor of edge_index is required by "
                  "using the tensor backend."
              )
          # check for undirected edge_index format
          if not self.directed:
              for message_type in self.edge_index:
                  edge_index_length = self.edge_index[
                      message_type
                  ].shape[1]
                  edge_index_first_half, _ = torch.sort(
                      self.edge_index[message_type][
                          :, : int(edge_index_length / 2)
                      ]
                  )
                  edge_index_second_half, _ = torch.sort(
                      self.edge_index[message_type][
                          :, int(edge_index_length / 2) :
                      ]
                  )
                  if not torch.equal(
                      edge_index_first_half,
                      torch.flip(edge_index_second_half, [0]),
                  ):
                      raise ValueError(
                          "In tensor backend mode with undirected graph, "
                          "the user provided edge_index for each "
                          "message_type should contain "
                          "undirected edges for both directions."
                          "the first half of edge_index should contain "
                          "unique edges in one direction and the second "
                          "half of edge_index should contain the same set "
                          "of unique edges of another direction."
                          "The corresponding message_type of edge_index "
                          f"that fails this check is: {message_type}."
                      )
  
      if G is not None or kwargs:
          self._update_tensors(init=True)
  
imports: import math
  import copy
  import random
  import torch
  import networkx
  import numpy
  from deepsnap.graph import Graph
  from typing import Dict, List, Union
  import warnings
label_desc: Use image node_id 1 to create a new HeteroGraph object from inherited base classes: Graph with example: obj = HeteroGraph(G)