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","."]
docstr: Split the heterogeneous graph into `len(split_ratio)` heterogeneous graphs for 
  the link prediction task. Internally this function splits the edge indices, and 
  the model will only compute loss for the node embeddings in each splitted graph.
  This is only used for the transductive link prediction task.
  In this task, different parts of the graph are observed in train / val / test.
  If during training, we might further split the training graph for the
  message edges and supervision edges.
  
  .. note::
  
      This functon will be called twice.
  
  Args:
      split_types (list): List of message types that will be splitted on.
      split_ratio (float or list): The ratio or list of ratios.
      edge_split_mode (str): "exact" or "approximate".
      shuffle (bool): Whether to shuffle for the splitting.
  
  Returns:
      list: A list of :class:`HeteroGraph` objects.
docstr_tok: ["Split","the","heterogeneous","graph","into","`","len","(","split_ratio",")","`","heterogeneous","graphs","for","the","link","prediction","task",".","Internally","this","function","splits","the","edge","indices",",","and","the","model","will","only","compute","loss","for","the","node","embeddings","in","each","splitted","graph",".","This","is","only","used","for","the","transductive","link","prediction","task",".","In","this","task",",","different","parts","of","the","graph","are","observed","in","train","\/","val","\/","test",".","If","during","training",",","we","might","further","split","the","training","graph","for","the","message","edges","and","supervision","edges",".","..","note",":",":","This","functon","will","be","called","twice",".","Args",":","split_types","(","list",")",":","List","of","message","types","that","will","be","splitted","on",".","split_ratio","(","float","or","list",")",":","The","ratio","or","list","of","ratios",".","edge_split_mode","(","str",")",":","``","exact","''","or","``","approximate","''",".","shuffle","(","bool",")",":","Whether","to","shuffle","for","the","splitting",".","Returns",":","list",":","A","list","of",":","class",":","`","HeteroGraph","`","objects","."]
code: def split_link_pred(
      self,
      split_types: List[tuple],
      split_ratio: Union[float, List[float]],
      edge_split_mode: str = "exact",
      shuffle: bool = True,
  ):
      r"""
      Split the heterogeneous graph into `len(split_ratio)` heterogeneous graphs for
      the link prediction task. Internally this function splits the edge indices, and
      the model will only compute loss for the node embeddings in each splitted graph.
      This is only used for the transductive link prediction task.
      In this task, different parts of the graph are observed in train / val / test.
      If during training, we might further split the training graph for the
      message edges and supervision edges.
  
      .. note::
  
          This functon will be called twice.
  
      Args:
          split_types (list): List of message types that will be splitted on.
          split_ratio (float or list): The ratio or list of ratios.
          edge_split_mode (str): "exact" or "approximate".
          shuffle (bool): Whether to shuffle for the splitting.
  
      Returns:
          list: A list of :class:`HeteroGraph` objects.
  
      """
  
      if isinstance(split_types, list):
          for split_type in split_types:
              if split_type not in self.message_types:
                  raise TypeError(
                      "all split_type in split_types need to be in "
                      f"{self.message_type}, however split type: "
                      "{split_type} is in split_types."
                  )
      elif split_types is None:
          split_types = self.message_types
      else:
          if split_types not in self.message_types:
              raise TypeError(
                  f"split_types need to be in {self.message_type}, "
                  f"however split_types is: {split_types}."
              )
          else:
              split_types = [split_types]
  
      if isinstance(split_ratio, float):
          split_ratio = [split_ratio, 1 - split_ratio]
      if len(split_ratio) < 2 or len(split_ratio) > 3:
          raise ValueError("Unrecoginzed number of splits")
  
      for split_type, num_edge_type in self.num_edges(
          split_types
      ).items():
          if num_edge_type < len(split_ratio):
              raise ValueError(
                  "In _split_link_pred number of edges of message_type: "
                  f"{split_type} is smaller than the number of splitted "
                  "parts."
              )
  
      split_types_all_flag = set(split_types) == set(self.message_types)
  
      if self.G is not None:
          edges = list(self.G.edges(data=True))
          if shuffle:
              random.shuffle(edges)
      else:
          edges = {}
          for message_type in self.message_types:
              if shuffle:
                  edges[message_type] = torch.randperm(
                      self.num_edges(message_type)
                  )
              else:
                  edges[message_type] = torch.arange(
                      self.num_edges(message_type)
                  )
  
      if edge_split_mode == "approximate" and not split_types_all_flag:
          warnings.warn(
              "in _split_edge when edge_split_mode is set to be approximate "
              "split_types does not cover all types, in this case "
              "the splitting is not going to improve much compared "
              "to when _split_edge when edge_split_mode is exact."
          )
  
      # Split edges by going through edges in each edge types and divide them
      # according to split_ratio.
      if edge_split_mode == "exact":
          if self.G is not None:
              edges_train, edges_val, edges_test = [], [], []
          else:
              edges_train, edges_val, edges_test = {}, {}, {}
          edges_split_type_dict = {}
          if self.G is not None:
              for edge in edges:
                  edge_type = edge[2]["edge_type"]
                  source_node_idx = edge[0]
                  target_node_idx = edge[1]
                  source_node_type = self.G.nodes[source_node_idx][
                      "node_type"
                  ]
                  target_node_type = self.G.nodes[target_node_idx][
                      "node_type"
                  ]
                  edge_split_type = (
                      source_node_type,
                      edge_type,
                      target_node_type,
                  )
  
                  if edge_split_type not in edges_split_type_dict:
                      edges_split_type_dict[edge_split_type] = []
                  edges_split_type_dict[edge_split_type].append(edge)
          else:
              edges_split_type_dict = edges
  
          for split_type in self.message_types:
              edges_split_type = edges_split_type_dict[split_type]
              edges_split_type_length = len(edges_split_type)
              if len(split_ratio) == 2:
                  if split_type in split_types:
                      num_edges_train = int(
                          split_ratio[0] * edges_split_type_length
                      )
                      num_edges_val = (
                          edges_split_type_length - num_edges_train
                      )
                      if (num_edges_train == 0) or (num_edges_val == 0):
                          num_edges_train = 1 + int(
                              split_ratio[0]
                              * (edges_split_type_length - 2)
                          )
  
                      if self.G is not None:
                          edges_train += edges_split_type[
                              :num_edges_train
                          ]
                          edges_val += edges_split_type[
                              num_edges_train:
                          ]
                      else:
                          edges_train[split_type] = edges_split_type[
                              :num_edges_train
                          ]
                          edges_val[split_type] = edges_split_type[
                              num_edges_train:
                          ]
                  else:
                      if self.G is not None:
                          edges_train += edges_split_type
                          edges_val += edges_split_type
                      else:
                          edges_train[split_type] = edges_split_type
                          edges_val[split_type] = edges_split_type
  
              # perform `secure split` s.t. guarantees all splitted subgraph
              # of a split type contains at least one edge.
              elif len(split_ratio) == 3:
                  if split_type in split_types:
                      num_edges_train = int(
                          split_ratio[0] * edges_split_type_length
                      )
                      num_edges_val = int(
                          split_ratio[1] * edges_split_type_length
                      )
                      num_edges_test = (
                          edges_split_type_length
                          - num_edges_train
                          - num_edges_val
                      )
                      if (
                          (num_edges_train == 0)
                          or (num_edges_val == 0)
                          or (num_edges_test == 0)
                      ):
                          num_edges_train = 1 + int(
                              split_ratio[0]
                              * (edges_split_type_length - 3)
                          )
                          num_edges_val = 1 + int(
                              split_ratio[1]
                              * (edges_split_type_length - 3)
                          )
  
                      if self.G is not None:
                          edges_train += edges_split_type[
                              :num_edges_train
                          ]
                          edges_val += edges_split_type[
                              num_edges_train : num_edges_train
                              + num_edges_val
                          ]
                          edges_test += edges_split_type[
                              num_edges_train + num_edges_val :
                          ]
                      else:
                          edges_train[split_type] = edges_split_type[
                              :num_edges_train
                          ]
                          edges_val[split_type] = edges_split_type[
                              num_edges_train : num_edges_train
                              + num_edges_val
                          ]
                          edges_test[split_type] = edges_split_type[
                              num_edges_train + num_edges_val :
                          ]
  
                  else:
                      if self.G is not None:
                          edges_train += edges_split_type
                          edges_val += edges_split_type
                          edges_test += edges_split_type
                      else:
                          edges_train[split_type] = edges_split_type
                          edges_val[split_type] = edges_split_type
                          edges_test[split_type] = edges_split_type
  
      elif edge_split_mode == "approximate":
          # if split_types do not cover all edge types in hetero_graph
          # then we need to do the filtering
          if not split_types_all_flag:
              if self.G is not None:
                  edges_split_type = []
                  edges_non_split_type = []
                  for edge in edges:
                      edge_type = edge[2]["edge_type"]
                      source_node_idx = edge[0]
                      target_node_idx = edge[1]
                      source_node_type = self.G.nodes[source_node_idx][
                          "node_type"
                      ]
                      target_node_type = self.G.nodes[target_node_idx][
                          "node_type"
                      ]
                      edge_split_type = (
                          source_node_type,
                          edge_type,
                          target_node_type,
                      )
                      if edge_split_type in split_types:
                          edges_split_type.append(edge)
                      else:
                          edges_non_split_type.append(edge)
  
                  edges_split_type_length = len(edges_split_type)
  
                  # perform `secure split` s.t. guarantees all splitted
                  # subgraph of a split type contains at least one edge.
                  if len(split_ratio) == 2:
                      num_edges_train = int(
                          split_ratio[0] * edges_split_type_length
                      )
                      num_edges_val = (
                          edges_split_type_length - num_edges_train
                      )
                      if (num_edges_train == 0) or (num_edges_val == 0):
                          num_edges_train = 1 + int(
                              split_ratio[0]
                              * (edges_split_type_length - 2)
                          )
  
                      edges_train = (
                          edges_split_type[:num_edges_train]
                          + edges_non_split_type
                      )
                      edges_val = (
                          edges_split_type[num_edges_train:]
                          + edges_non_split_type
                      )
                  elif len(split_ratio) == 3:
                      num_edges_train = int(
                          split_ratio[0] * edges_split_type_length
                      )
                      num_edges_val = int(
                          split_ratio[1] * edges_split_type_length
                      )
                      num_edges_test = (
                          edges_split_type_length
                          - num_edges_train
                          - num_edges_val
                      )
                      if (
                          (num_edges_train == 0)
                          or (num_edges_val == 0)
                          or (num_edges_test == 0)
                      ):
                          num_edges_train = 1 + int(
                              split_ratio[0]
                              * (edges_split_type_length - 3)
                          )
                          num_edges_val = 1 + int(
                              split_ratio[1]
                              * (edges_split_type_length - 3)
                          )
  
                      edges_train = (
                          edges_split_type[:num_edges_train]
                          + edges_non_split_type
                      )
                      edges_val = (
                          edges_split_type[
                              num_edges_train : num_edges_train
                              + num_edges_val
                          ]
                          + edges_non_split_type
                      )
                      edges_test = (
                          edges_split_type[
                              num_edges_train + num_edges_val :
                          ]
                          + edges_non_split_type
                      )
              else:
                  split_offset = 0
                  cumulative_split_type_cnt = []
                  message_types_sorted = sorted(self.message_types)
                  split_types_sorted = [
                      message_type
                      for message_type in message_types_sorted
                      if message_type in split_types
                  ]
  
                  for split_type in split_types_sorted:
                      cumulative_split_type_cnt.append(split_offset)
                      split_offset += edges[split_type].shape[0]
  
                  cumulative_split_type_cnt.append(split_offset)
                  edges_split_type_length = split_offset
                  edge_index = list(range(edges_split_type_length))
                  if shuffle:
                      random.shuffle(edge_index)
  
                  if len(split_ratio) == 2:
                      num_edges_train = int(
                          split_ratio[0] * edges_split_type_length
                      )
                      num_edges_val = (
                          edges_split_type_length - num_edges_train
                      )
                      if (num_edges_train == 0) or (num_edges_val == 0):
                          num_edges_train = 1 + int(
                              split_ratio[0]
                              * (edges_split_type_length - 2)
                          )
  
                      edges_train_index = sorted(
                          edge_index[:num_edges_train]
                      )
                      edges_val_index = sorted(
                          edge_index[num_edges_train:]
                      )
                      edges_train, edges_val = {}, {}
                      split_type_cnt = 0
                      for index in edges_train_index:
                          while not (
                              cumulative_split_type_cnt[split_type_cnt]
                              <= index
                              < cumulative_split_type_cnt[
                                  split_type_cnt + 1
                              ]
                          ):
                              split_type_cnt += 1
                          split_type = split_types_sorted[
                              split_type_cnt
                          ]
                          index_type = (
                              index
                              - cumulative_split_type_cnt[
                                  split_type_cnt
                              ]
                          )
                          if split_type not in edges_train:
                              edges_train[split_type] = []
                          edges_train[split_type].append(index_type)
  
                      split_type_cnt = 0
                      for index in edges_val_index:
                          while not (
                              cumulative_split_type_cnt[split_type_cnt]
                              <= index
                              < cumulative_split_type_cnt[
                                  split_type_cnt + 1
                              ]
                          ):
                              split_type_cnt += 1
                          split_type = split_types_sorted[
                              split_type_cnt
                          ]
                          index_type = (
                              index
                              - cumulative_split_type_cnt[
                                  split_type_cnt
                              ]
                          )
                          if split_type not in edges_val:
                              edges_val[split_type] = []
                          edges_val[split_type].append(index_type)
  
                      for split_type in edges_train:
                          edges_train[split_type] = torch.tensor(
                              edges_train[split_type]
                          )
                      for split_type in edges_val:
                          edges_val[split_type] = torch.tensor(
                              edges_val[split_type]
                          )
  
                      for split_type in message_types_sorted:
                          if split_type not in split_types:
                              edges_train[split_type] = edges[
                                  split_type
                              ]
                              edges_val[split_type] = edges[split_type]
  
                  elif len(split_ratio) == 3:
                      num_edges_train = int(
                          split_ratio[0] * edges_split_type_length
                      )
                      num_edges_val = int(
                          split_ratio[1] * edges_split_type_length
                      )
                      num_edges_test = (
                          edges_split_type_length
                          - num_edges_train
                          - num_edges_val
                      )
                      if (
                          (num_edges_train == 0)
                          or (num_edges_val == 0)
                          or (num_edges_test == 0)
                      ):
                          num_edges_train = 1 + int(
                              split_ratio[0]
                              * (edges_split_type_length - 3)
                          )
                          num_edges_val = 1 + int(
                              split_ratio[1]
                              * (edges_split_type_length - 3)
                          )
  
                      edges_train_index = sorted(
                          edge_index[:num_edges_train]
                      )
                      edges_val_index = sorted(
                          edge_index[
                              num_edges_train : num_edges_train
                              + num_edges_val
                          ]
                      )
                      edges_test_index = sorted(
                          edge_index[num_edges_train + num_edges_val :]
                      )
                      edges_train, edges_val, edges_test = {}, {}, {}
                      split_type_cnt = 0
                      for index in edges_train_index:
                          while not (
                              cumulative_split_type_cnt[split_type_cnt]
                              <= index
                              < cumulative_split_type_cnt[
                                  split_type_cnt + 1
                              ]
                          ):
                              split_type_cnt += 1
                          split_type = split_types_sorted[
                              split_type_cnt
                          ]
                          index_type = (
                              index
                              - cumulative_split_type_cnt[
                                  split_type_cnt
                              ]
                          )
                          if split_type not in edges_train:
                              edges_train[split_type] = []
                          edges_train[split_type].append(index_type)
  
                      split_type_cnt = 0
                      for index in edges_val_index:
                          while not (
                              cumulative_split_type_cnt[split_type_cnt]
                              <= index
                              < cumulative_split_type_cnt[
                                  split_type_cnt + 1
                              ]
                          ):
                              split_type_cnt += 1
                          split_type = split_types_sorted[
                              split_type_cnt
                          ]
                          index_type = (
                              index
                              - cumulative_split_type_cnt[
                                  split_type_cnt
                              ]
                          )
                          if split_type not in edges_val:
                              edges_val[split_type] = []
                          edges_val[split_type].append(index_type)
  
                      split_type_cnt = 0
                      for index in edges_test_index:
                          while not (
                              cumulative_split_type_cnt[split_type_cnt]
                              <= index
                              < cumulative_split_type_cnt[
                                  split_type_cnt + 1
                              ]
                          ):
                              split_type_cnt += 1
                          split_type = split_types_sorted[
                              split_type_cnt
                          ]
                          index_type = (
                              index
                              - cumulative_split_type_cnt[
                                  split_type_cnt
                              ]
                          )
                          if split_type not in edges_test:
                              edges_test[split_type] = []
                          edges_test[split_type].append(index_type)
  
                      for split_type in edges_train:
                          edges_train[split_type] = torch.tensor(
                              edges_train[split_type]
                          )
                      for split_type in edges_val:
                          edges_val[split_type] = torch.tensor(
                              edges_val[split_type]
                          )
                      for split_type in edges_test:
                          edges_test[split_type] = torch.tensor(
                              edges_test[split_type]
                          )
  
                      for split_type in message_types_sorted:
                          if split_type not in split_types:
                              edges_train[split_type] = edges[
                                  split_type
                              ]
                              edges_val[split_type] = edges[split_type]
                              edges_test[split_type] = edges[split_type]
          else:
              # if split_types cover all edge types in hetero_graph then we
              # need not do the filtering and achieve maximal optimization
              # as compared to exact split by splitting all the edges
              # regardless of edge types
  
              if self.G is not None:
                  num_edges = sum(self.num_edges().values())
  
                  # perform `secure split` s.t. guarantees all splitted
                  # subgraph contains at least one edge.
                  if len(split_ratio) == 2:
                      num_edges_train = int(split_ratio[0] * num_edges)
                      num_edges_val = num_edges - num_edges_train
                      if (num_edges_train == 0) or (num_edges_val == 0):
                          num_edges_train = 1 + int(
                              split_ratio[0] * (num_edges - 2)
                          )
  
                      edges_train = edges[:num_edges_train]
                      edges_val = edges[num_edges_train:]
                  elif len(split_ratio) == 3:
                      num_edges_train = int(split_ratio[0] * num_edges)
                      num_edges_val = int(split_ratio[1] * num_edges)
                      num_edges_test = (
                          num_edges - num_edges_train - num_edges_val
                      )
                      if (
                          (num_edges_train == 0)
                          or (num_edges_val == 0)
                          or (num_edges_test == 0)
                      ):
                          num_edges_train = 1 + int(
                              split_ratio[0] * (num_edges - 3)
                          )
                          num_edges_val = 1 + int(
                              split_ratio[1] * (num_edges - 3)
                          )
  
                      edges_train = edges[:num_edges_train]
                      edges_val = edges[
                          num_edges_train : num_edges_train
                          + num_edges_val
                      ]
                      edges_test = edges[
                          num_edges_train + num_edges_val :
                      ]
              else:
                  split_offset = 0
                  cumulative_split_type_cnt = []
                  split_types_sorted = sorted(self.message_types)
                  for split_type in split_types_sorted:
                      cumulative_split_type_cnt.append(split_offset)
                      split_offset += edges[split_type].shape[0]
  
                  cumulative_split_type_cnt.append(split_offset)
                  edges_split_type_length = split_offset
                  edge_index = list(range(edges_split_type_length))
                  if shuffle:
                      random.shuffle(edge_index)
  
                  if len(split_ratio) == 2:
                      num_edges_train = int(
                          split_ratio[0] * edges_split_type_length
                      )
                      num_edges_val = (
                          edges_split_type_length - num_edges_train
                      )
                      if (num_edges_train == 0) or (num_edges_val == 0):
                          num_edges_train = 1 + int(
                              split_ratio[0]
                              * (edges_split_type_length - 2)
                          )
  
                      edges_train_index = sorted(
                          edge_index[:num_edges_train]
                      )
                      edges_val_index = sorted(
                          edge_index[num_edges_train:]
                      )
                      edges_train, edges_val = {}, {}
  
                      split_type_cnt = 0
                      for index in edges_train_index:
                          while not (
                              cumulative_split_type_cnt[split_type_cnt]
                              <= index
                              < cumulative_split_type_cnt[
                                  split_type_cnt + 1
                              ]
                          ):
                              split_type_cnt += 1
                          split_type = split_types_sorted[
                              split_type_cnt
                          ]
                          index_type = (
                              index
                              - cumulative_split_type_cnt[
                                  split_type_cnt
                              ]
                          )
                          if split_type not in edges_train:
                              edges_train[split_type] = []
                          edges_train[split_type].append(index_type)
  
                      split_type_cnt = 0
                      for index in edges_val_index:
                          while not (
                              cumulative_split_type_cnt[split_type_cnt]
                              <= index
                              < cumulative_split_type_cnt[
                                  split_type_cnt + 1
                              ]
                          ):
                              split_type_cnt += 1
                          split_type = split_types_sorted[
                              split_type_cnt
                          ]
                          index_type = (
                              index
                              - cumulative_split_type_cnt[
                                  split_type_cnt
                              ]
                          )
                          if split_type not in edges_val:
                              edges_val[split_type] = []
                          edges_val[split_type].append(index_type)
  
                      for split_type in edges_train:
                          edges_train[split_type] = torch.tensor(
                              edges_train[split_type]
                          )
                      for split_type in edges_val:
                          edges_val[split_type] = torch.tensor(
                              edges_val[split_type]
                          )
  
                  elif len(split_ratio) == 3:
                      num_edges_train = int(
                          split_ratio[0] * edges_split_type_length
                      )
                      num_edges_val = int(
                          split_ratio[1] * edges_split_type_length
                      )
                      num_edges_test = int(
                          edges_split_type_length
                          - num_edges_train
                          - num_edges_val
                      )
                      if (
                          (num_edges_train == 0)
                          or (num_edges_val == 0)
                          or (num_edges_test == 0)
                      ):
                          num_edges_train = 1 + int(
                              split_ratio[0]
                              * (edges_split_type_length - 3)
                          )
                          num_edges_val = 1 + int(
                              split_ratio[1]
                              * (edges_split_type_length - 3)
                          )
  
                      edges_train_index = sorted(
                          edge_index[:num_edges_train]
                      )
                      edges_val_index = sorted(
                          edge_index[
                              num_edges_train : num_edges_train
                              + num_edges_val
                          ]
                      )
                      edges_test_index = sorted(
                          edge_index[num_edges_train + num_edges_val :]
                      )
                      edges_train, edges_val, edges_test = {}, {}, {}
                      split_type_cnt = 0
                      for index in edges_train_index:
                          while not (
                              cumulative_split_type_cnt[split_type_cnt]
                              <= index
                              < cumulative_split_type_cnt[
                                  split_type_cnt + 1
                              ]
                          ):
                              split_type_cnt += 1
                          split_type = split_types_sorted[
                              split_type_cnt
                          ]
                          index_type = (
                              index
                              - cumulative_split_type_cnt[
                                  split_type_cnt
                              ]
                          )
                          if split_type not in edges_train:
                              edges_train[split_type] = []
                          edges_train[split_type].append(index_type)
  
                      split_type_cnt = 0
                      for index in edges_val_index:
                          while not (
                              cumulative_split_type_cnt[split_type_cnt]
                              <= index
                              < cumulative_split_type_cnt[
                                  split_type_cnt + 1
                              ]
                          ):
                              split_type_cnt += 1
                          split_type = split_types_sorted[
                              split_type_cnt
                          ]
                          index_type = (
                              index
                              - cumulative_split_type_cnt[
                                  split_type_cnt
                              ]
                          )
                          if split_type not in edges_val:
                              edges_val[split_type] = []
                          edges_val[split_type].append(index_type)
  
                      split_type_cnt = 0
                      for index in edges_test_index:
                          while not (
                              cumulative_split_type_cnt[split_type_cnt]
                              <= index
                              < cumulative_split_type_cnt[
                                  split_type_cnt + 1
                              ]
                          ):
                              split_type_cnt += 1
                          split_type = split_types_sorted[
                              split_type_cnt
                          ]
                          index_type = (
                              index
                              - cumulative_split_type_cnt[
                                  split_type_cnt
                              ]
                          )
                          if split_type not in edges_test:
                              edges_test[split_type] = []
                          edges_test[split_type].append(index_type)
  
                      for split_type in edges_train:
                          edges_train[split_type] = torch.tensor(
                              edges_train[split_type]
                          )
                      for split_type in edges_val:
                          edges_val[split_type] = torch.tensor(
                              edges_val[split_type]
                          )
                      for split_type in edges_test:
                          edges_test[split_type] = torch.tensor(
                              edges_test[split_type]
                          )
  
      if self.G is not None:
          graph_train = HeteroGraph(
              self._edge_subgraph_with_isonodes(self.G, edges_train)
          )
          if hasattr(self, "negative_label_val"):
              graph_train.negative_label_val = self.negative_label_val
      else:
          graph_train = copy.copy(self)
  
          # update edge_index
          edge_index = {}
          for message_type in edges_train:
              edge_index[message_type] = torch.index_select(
                  self.edge_index[message_type],
                  1,
                  edges_train[message_type],
              )
              if self.is_undirected():
                  edge_index[message_type] = torch.cat(
                      [
                          edge_index[message_type],
                          torch.flip(edge_index[message_type], [0]),
                      ],
                      dim=1,
                  )
  
          # update the other edge_features
          graph_train.edge_index = edge_index
          for key in graph_train.keys:
              if self._is_edge_attribute(key):
                  edge_feature = {}
                  for message_type in edges_train:
                      edge_feature[message_type] = torch.index_select(
                          self[key][message_type],
                          0,
                          edges_train[message_type],
                      )
                      if self.is_undirected():
                          edge_feature[message_type] = torch.cat(
                              [
                                  edge_feature[message_type],
                                  edge_feature[message_type],
                              ],
                              dim=0,
                          )
                  graph_train[key] = edge_feature
  
          # in tensor backend, store the original self.edge_label
          graph_train._edge_label = copy.deepcopy(self.edge_label)
  
      graph_val = copy.copy(graph_train)
      if len(split_ratio) == 3:
          if self.G is not None:
              graph_test = HeteroGraph(
                  self._edge_subgraph_with_isonodes(
                      self.G, edges_train + edges_val
                  )
              )
              if hasattr(self, "negative_label_val"):
                  graph_test.negative_label_val = (
                      self.negative_label_val
                  )
          else:
              graph_test = copy.copy(self)
              edge_index = {}
              for message_type in edges_test:
                  if message_type in split_types:
                      edge_index[message_type] = torch.tensor(
                          [], dtype=self.edge_index[message_type].dtype
                      )
                      if message_type in edges_train:
                          edge_index[message_type] = torch.cat(
                              [
                                  edge_index[message_type],
                                  torch.index_select(
                                      self.edge_index[message_type],
                                      1,
                                      edges_train[message_type],
                                  ),
                              ],
                              dim=1,
                          )
                      if message_type in edges_val:
                          edge_index[message_type] = torch.cat(
                              [
                                  edge_index[message_type],
                                  torch.index_select(
                                      self.edge_index[message_type],
                                      1,
                                      edges_val[message_type],
                                  ),
                              ],
                              dim=1,
                          )
                  else:
                      edge_index[message_type] = torch.index_select(
                          self.edge_index[message_type],
                          1,
                          edges_train[message_type],
                      )
                  if self.is_undirected():
                      edge_index[message_type] = torch.cat(
                          [
                              edge_index[message_type],
                              torch.flip(edge_index[message_type], [0]),
                          ],
                          dim=1,
                      )
  
              graph_test.edge_index = edge_index
              for key in graph_test.keys:
                  if self._is_edge_attribute(key):
                      for message_type in self[key]:
                          if message_type in split_types:
                              edge_feature[message_type] = torch.tensor(
                                  [],
                                  dtype=self[key][message_type].dtype,
                              )
                              if message_type in edges_train:
                                  edge_feature[
                                      message_type
                                  ] = torch.cat(
                                      [
                                          edge_feature[message_type],
                                          torch.index_select(
                                              self[key][message_type],
                                              0,
                                              edges_train[message_type],
                                          ),
                                      ]
                                  )
  
                              if message_type in edges_val:
                                  edge_feature[
                                      message_type
                                  ] = torch.cat(
                                      [
                                          edge_feature[message_type],
                                          torch.index_select(
                                              self[key][message_type],
                                              0,
                                              edges_val[message_type],
                                          ),
                                      ]
                                  )
                          else:
                              edge_feature[
                                  message_type
                              ] = torch.index_select(
                                  self[key][message_type],
                                  0,
                                  edges_train[message_type],
                              )
                          if self.is_undirected():
                              edge_feature[message_type] = torch.cat(
                                  [
                                      edge_feature[message_type],
                                      edge_feature[message_type],
                                  ],
                                  dim=0,
                              )
                      graph_test[key] = edge_feature
  
      # set objective
      if self.G is not None:
          self._create_label_link_pred(
              graph_train, edges_train, list(self.G.nodes(data=True))
          )
          self._create_label_link_pred(
              graph_val, edges_val, list(self.G.nodes(data=True))
          )
      else:
          self._create_label_link_pred(graph_train, edges_train)
          self._create_label_link_pred(graph_val, edges_val)
  
      graph_train._is_train = True
      if len(split_ratio) == 3:
          if self.G is not None:
              self._create_label_link_pred(
                  graph_test, edges_test, list(self.G.nodes(data=True))
              )
          else:
              self._create_label_link_pred(graph_test, edges_test)
          return [graph_train, graph_val, graph_test]
      else:
          return [graph_train, graph_val]
  
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 33 for calling the HeteroGraph obj's underlying member method code with example usage: obj.split_link_pred(split_types, split_ratio, edge_split_mode, shuffle) and returns: list, list