code: def collate(
      cls: Type[T],
      data_list: List[BaseData],
      increment: bool = True,
      add_batch: bool = True,
      follow_batch: Optional[Iterable[str]] = None,
      exclude_keys: Optional[Iterable[str]] = None,
  ) -> Tuple[T, SliceDictType, IncDictType]:
      # Collates a list of `data` objects into a single object of type `cls`.
      # `collate` can handle both homogeneous and heterogeneous data objects by
      # individually collating all their stores.
      # In addition, `collate` can handle nested data structures such as
      # dictionaries and lists.
  
      if not isinstance(data_list, (list, tuple)):
          # Materialize `data_list` to keep the `_parent` weakref alive.
          data_list = list(data_list)
  
      if cls != data_list[0].__class__:  # Dynamic inheritance.
          out = cls(_base_cls=data_list[0].__class__)  # type: ignore
      else:
          out = cls()
  
      # Create empty stores:
      out.stores_as(data_list[0])  # type: ignore
  
      follow_batch = set(follow_batch or [])
      exclude_keys = set(exclude_keys or [])
  
      # Group all storage objects of every data object in the `data_list` by key,
      # i.e. `key_to_stores = { key: [store_1, store_2, ...], ... }`:
      key_to_stores = defaultdict(list)
      for data in data_list:
          for store in data.stores:
              key_to_stores[store._key].append(store)
  
      # With this, we iterate over each list of storage objects and recursively
      # collate all its attributes into a unified representation:
  
      # We maintain two additional dictionaries:
      # * `slice_dict` stores a compressed index representation of each attribute
      #    and is needed to re-construct individual elements from mini-batches.
      # * `inc_dict` stores how individual elements need to be incremented, e.g.,
      #   `edge_index` is incremented by the cumulated sum of previous elements.
      #   We also need to make use of `inc_dict` when re-constructuing individual
      #   elements as attributes that got incremented need to be decremented
      #   while separating to obtain original values.
      device: Optional[torch.device] = None
      slice_dict: SliceDictType = {}
      inc_dict: IncDictType = {}
      for out_store in out.stores:  # type: ignore
          key = out_store._key
          stores = key_to_stores[key]
          for attr in stores[0].keys():
              if (
                  attr in exclude_keys
              ):  # Do not include top-level attribute.
                  continue
  
              values = [store[attr] for store in stores]
  
              # The `num_nodes` attribute needs special treatment, as we need to
              # sum their values up instead of merging them to a list:
              if attr == "num_nodes":
                  out_store._num_nodes = values
                  out_store.num_nodes = sum(values)
                  continue
  
              # Skip batching of `ptr` vectors for now:
              if attr == "ptr":
                  continue
  
              # Collate attributes into a unified representation:
              value, slices, incs = _collate(
                  attr, values, data_list, stores, increment
              )
  
              # If parts of the data are already on GPU, make sure that auxiliary
              # data like `batch` or `ptr` are also created on GPU:
              if isinstance(value, Tensor) and value.is_cuda:
                  device = value.device
  
              out_store[attr] = value
  
              if key is not None:  # Heterogeneous:
                  store_slice_dict = slice_dict.get(key, {})
                  assert isinstance(store_slice_dict, dict)
                  store_slice_dict[attr] = slices
                  slice_dict[key] = store_slice_dict
  
                  store_inc_dict = inc_dict.get(key, {})
                  assert isinstance(store_inc_dict, dict)
                  store_inc_dict[attr] = incs
                  inc_dict[key] = store_inc_dict
              else:  # Homogeneous:
                  slice_dict[attr] = slices
                  inc_dict[attr] = incs
  
              # Add an additional batch vector for the given attribute:
              if attr in follow_batch:
                  batch, ptr = _batch_and_ptr(slices, device)
                  out_store[f"{attr}_batch"] = batch
                  out_store[f"{attr}_ptr"] = ptr
  
          # In case of node-level storages, we add a top-level batch vector it:
          if (
              add_batch
              and isinstance(stores[0], NodeStorage)
              and stores[0].can_infer_num_nodes
          ):
              repeats = [store.num_nodes or 0 for store in stores]
              out_store.batch = repeat_interleave(
                  repeats, device=device
              )
              out_store.ptr = cumsum(
                  torch.tensor(repeats, device=device)
              )
  
      return out, slice_dict, inc_dict
  
file_path: pytorch_geometric/torch_geometric/data/collate.py
imports: from collections import defaultdict
  from collections.abc import Mapping, Sequence
  from typing import Any, Dict, Iterable, List, Optional, Tuple, Type, TypeVar, Union
  import torch
  from torch import Tensor
  import torch_geometric.typing
  from torch_geometric import EdgeIndex
  from torch_geometric.data.data import BaseData
  from torch_geometric.data.storage import BaseStorage, NodeStorage
  from torch_geometric.edge_index import SortOrder
  from torch_geometric.typing import SparseTensor, TensorFrame, torch_frame, torch_sparse
  from torch_geometric.utils import cumsum, is_sparse, is_torch_sparse_tensor
  from torch_geometric.utils.sparse import cat
label_desc: Use image node_id 1 for calling a global function with example usage: collate(cls, data_list, increment, add_batch, follow_batch, exclude_keys) and returns: out,  slice_dict,  inc_dict