We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Batch.from_data_list seems to give different batch results depending on attribute name
### 🐛 Describe the bug

Hi, when using the `torch_geometric.data.Batch` and `torch_geometric.data.Data` class to organize my graph information, I realized the following feature:
```python
import torch
import torch_geometric
from torch_geometric.data import Data, Batch

data_list = []
for i in range(3):
    data = Data(x=torch.randn(5, 3))
    data.image_index = torch.ones(5)
    data_list.append(data)
    
batch = Batch.from_data_list(data_list)
print(batch.image_index)
# Gives tensor([ 1.,  1.,  1.,  1.,  1.,  6.,  6.,  6.,  6.,  6., 11., 11., 11., 11., 11.])

data_list = []
for i in range(3):
    data = Data(x=torch.randn(5, 3))
    data.foo = torch.ones(5)
    data_list.append(data)
    
batch = Batch.from_data_list(data_list)
print(batch.foo)
# Gives tensor([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])
``` 

So it seems that one can get different "batched" results depending on the name of the attribute to which one stores the information. In fact, it seems one gets the former behavior when the string `index` is contained in the attribute name. Was this feature put in place by intention? FYI, I'm using verion `2.2.0`

### Environment

* PyG version: `2.2.0`
* PyTorch version: `1.13.1+cu116`
* OS: Linux
* Python version: `3.8.10`
* CUDA/cuDNN version: `cu116`
* How you installed PyTorch and PyG (`conda`, `pip`, source): `pip`
* Any other relevant information (*e.g.*, version of `torch-scatter`):


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `torch_geometric/data/batch.py`
Content:
```
1 import inspect
2 from collections.abc import Sequence
3 from typing import Any, List, Optional, Union
4 
5 import numpy as np
6 import torch
7 from torch import Tensor
8 
9 from torch_geometric.data.collate import collate
10 from torch_geometric.data.data import BaseData, Data
11 from torch_geometric.data.dataset import IndexType
12 from torch_geometric.data.separate import separate
13 
14 
15 class DynamicInheritance(type):
16     # A meta class that sets the base class of a `Batch` object, e.g.:
17     # * `Batch(Data)` in case `Data` objects are batched together
18     # * `Batch(HeteroData)` in case `HeteroData` objects are batched together
19     def __call__(cls, *args, **kwargs):
20         base_cls = kwargs.pop('_base_cls', Data)
21 
22         if issubclass(base_cls, Batch):
23             new_cls = base_cls
24         else:
25             name = f'{base_cls.__name__}{cls.__name__}'
26 
27             # NOTE `MetaResolver` is necessary to resolve metaclass conflict
28             # problems between `DynamicInheritance` and the metaclass of
29             # `base_cls`. In particular, it creates a new common metaclass
30             # from the defined metaclasses.
31             class MetaResolver(type(cls), type(base_cls)):
32                 pass
33 
34             if name not in globals():
35                 globals()[name] = MetaResolver(name, (cls, base_cls), {})
36             new_cls = globals()[name]
37 
38         params = list(inspect.signature(base_cls.__init__).parameters.items())
39         for i, (k, v) in enumerate(params[1:]):
40             if k == 'args' or k == 'kwargs':
41                 continue
42             if i < len(args) or k in kwargs:
43                 continue
44             if v.default is not inspect.Parameter.empty:
45                 continue
46             kwargs[k] = None
47 
48         return super(DynamicInheritance, new_cls).__call__(*args, **kwargs)
49 
50 
51 class DynamicInheritanceGetter:
52     def __call__(self, cls, base_cls):
53         return cls(_base_cls=base_cls)
54 
55 
56 class Batch(metaclass=DynamicInheritance):
57     r"""A data object describing a batch of graphs as one big (disconnected)
58     graph.
59     Inherits from :class:`torch_geometric.data.Data` or
60     :class:`torch_geometric.data.HeteroData`.
61     In addition, single graphs can be identified via the assignment vector
62     :obj:`batch`, which maps each node to its respective graph identifier.
63     """
64     @classmethod
65     def from_data_list(cls, data_list: List[BaseData],
66                        follow_batch: Optional[List[str]] = None,
67                        exclude_keys: Optional[List[str]] = None):
68         r"""Constructs a :class:`~torch_geometric.data.Batch` object from a
69         Python list of :class:`~torch_geometric.data.Data` or
70         :class:`~torch_geometric.data.HeteroData` objects.
71         The assignment vector :obj:`batch` is created on the fly.
72         In addition, creates assignment vectors for each key in
73         :obj:`follow_batch`.
74         Will exclude any keys given in :obj:`exclude_keys`."""
75 
76         batch, slice_dict, inc_dict = collate(
77             cls,
78             data_list=data_list,
79             increment=True,
80             add_batch=not isinstance(data_list[0], Batch),
81             follow_batch=follow_batch,
82             exclude_keys=exclude_keys,
83         )
84 
85         batch._num_graphs = len(data_list)
86         batch._slice_dict = slice_dict
87         batch._inc_dict = inc_dict
88 
89         return batch
90 
91     def get_example(self, idx: int) -> BaseData:
92         r"""Gets the :class:`~torch_geometric.data.Data` or
93         :class:`~torch_geometric.data.HeteroData` object at index :obj:`idx`.
94         The :class:`~torch_geometric.data.Batch` object must have been created
95         via :meth:`from_data_list` in order to be able to reconstruct the
96         initial object."""
97 
98         if not hasattr(self, '_slice_dict'):
99             raise RuntimeError(
100                 ("Cannot reconstruct 'Data' object from 'Batch' because "
101                  "'Batch' was not created via 'Batch.from_data_list()'"))
102 
103         data = separate(
104             cls=self.__class__.__bases__[-1],
105             batch=self,
106             idx=idx,
107             slice_dict=self._slice_dict,
108             inc_dict=self._inc_dict,
109             decrement=True,
110         )
111 
112         return data
113 
114     def index_select(self, idx: IndexType) -> List[BaseData]:
115         r"""Creates a subset of :class:`~torch_geometric.data.Data` or
116         :class:`~torch_geometric.data.HeteroData` objects from specified
117         indices :obj:`idx`.
118         Indices :obj:`idx` can be a slicing object, *e.g.*, :obj:`[2:5]`, a
119         list, a tuple, or a :obj:`torch.Tensor` or :obj:`np.ndarray` of type
120         long or bool.
121         The :class:`~torch_geometric.data.Batch` object must have been created
122         via :meth:`from_data_list` in order to be able to reconstruct the
123         initial objects."""
124         if isinstance(idx, slice):
125             idx = list(range(self.num_graphs)[idx])
126 
127         elif isinstance(idx, Tensor) and idx.dtype == torch.long:
128             idx = idx.flatten().tolist()
129 
130         elif isinstance(idx, Tensor) and idx.dtype == torch.bool:
131             idx = idx.flatten().nonzero(as_tuple=False).flatten().tolist()
132 
133         elif isinstance(idx, np.ndarray) and idx.dtype == np.int64:
134             idx = idx.flatten().tolist()
135 
136         elif isinstance(idx, np.ndarray) and idx.dtype == bool:
137             idx = idx.flatten().nonzero()[0].flatten().tolist()
138 
139         elif isinstance(idx, Sequence) and not isinstance(idx, str):
140             pass
141 
142         else:
143             raise IndexError(
144                 f"Only slices (':'), list, tuples, torch.tensor and "
145                 f"np.ndarray of dtype long or bool are valid indices (got "
146                 f"'{type(idx).__name__}')")
147 
148         return [self.get_example(i) for i in idx]
149 
150     def __getitem__(self, idx: Union[int, np.integer, str, IndexType]) -> Any:
151         if (isinstance(idx, (int, np.integer))
152                 or (isinstance(idx, Tensor) and idx.dim() == 0)
153                 or (isinstance(idx, np.ndarray) and np.isscalar(idx))):
154             return self.get_example(idx)
155         elif isinstance(idx, str) or (isinstance(idx, tuple)
156                                       and isinstance(idx[0], str)):
157             # Accessing attributes or node/edge types:
158             return super().__getitem__(idx)
159         else:
160             return self.index_select(idx)
161 
162     def to_data_list(self) -> List[BaseData]:
163         r"""Reconstructs the list of :class:`~torch_geometric.data.Data` or
164         :class:`~torch_geometric.data.HeteroData` objects from the
165         :class:`~torch_geometric.data.Batch` object.
166         The :class:`~torch_geometric.data.Batch` object must have been created
167         via :meth:`from_data_list` in order to be able to reconstruct the
168         initial objects."""
169         return [self.get_example(i) for i in range(self.num_graphs)]
170 
171     @property
172     def num_graphs(self) -> int:
173         """Returns the number of graphs in the batch."""
174         if hasattr(self, '_num_graphs'):
175             return self._num_graphs
176         elif hasattr(self, 'ptr'):
177             return self.ptr.numel() - 1
178         elif hasattr(self, 'batch'):
179             return int(self.batch.max()) + 1
180         else:
181             raise ValueError("Can not infer the number of graphs")
182 
183     def __len__(self) -> int:
184         return self.num_graphs
185 
186     def __reduce__(self):
187         state = self.__dict__.copy()
188         return DynamicInheritanceGetter(), self.__class__.__bases__, state
189
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/torch_geometric/data/batch.py b/torch_geometric/data/batch.py
--- a/torch_geometric/data/batch.py
+++ b/torch_geometric/data/batch.py
@@ -60,6 +60,23 @@
     :class:`torch_geometric.data.HeteroData`.
     In addition, single graphs can be identified via the assignment vector
     :obj:`batch`, which maps each node to its respective graph identifier.
+
+    :pyg:`PyG` allows modification to the underlying batching procedure by
+    overwriting the :meth:`~Data.__inc__` and :meth:`~Data.__cat_dim__`
+    functionalities.
+    The :meth:`~Data.__inc__` method defines the incremental count between two
+    consecutive graph attributes.
+    By default, :pyg:`PyG` increments attributes by the number of nodes
+    whenever their attribute names contain the substring :obj:`index`
+    (for historical reasons), which comes in handy for attributes such as
+    :obj:`edge_index` or :obj:`node_index`.
+    However, note that this may lead to unexpected behavior for attributes
+    whose names contain the substring :obj:`index` but should not be
+    incremented.
+    To make sure, it is best practice to always double-check the output of
+    batching.
+    Furthermore, :meth:`~Data.__cat_dim__` defines in which dimension graph
+    tensors of the same attribute should be concatenated together.
     """
     @classmethod
     def from_data_list(cls, data_list: List[BaseData],

{"golden_diff": "diff --git a/torch_geometric/data/batch.py b/torch_geometric/data/batch.py\n--- a/torch_geometric/data/batch.py\n+++ b/torch_geometric/data/batch.py\n@@ -60,6 +60,23 @@\n     :class:`torch_geometric.data.HeteroData`.\n     In addition, single graphs can be identified via the assignment vector\n     :obj:`batch`, which maps each node to its respective graph identifier.\n+\n+    :pyg:`PyG` allows modification to the underlying batching procedure by\n+    overwriting the :meth:`~Data.__inc__` and :meth:`~Data.__cat_dim__`\n+    functionalities.\n+    The :meth:`~Data.__inc__` method defines the incremental count between two\n+    consecutive graph attributes.\n+    By default, :pyg:`PyG` increments attributes by the number of nodes\n+    whenever their attribute names contain the substring :obj:`index`\n+    (for historical reasons), which comes in handy for attributes such as\n+    :obj:`edge_index` or :obj:`node_index`.\n+    However, note that this may lead to unexpected behavior for attributes\n+    whose names contain the substring :obj:`index` but should not be\n+    incremented.\n+    To make sure, it is best practice to always double-check the output of\n+    batching.\n+    Furthermore, :meth:`~Data.__cat_dim__` defines in which dimension graph\n+    tensors of the same attribute should be concatenated together.\n     \"\"\"\n     @classmethod\n     def from_data_list(cls, data_list: List[BaseData],\n", "issue": "Batch.from_data_list seems to give different batch results depending on attribute name\n### \ud83d\udc1b Describe the bug\r\n\r\nHi, when using the `torch_geometric.data.Batch` and `torch_geometric.data.Data` class to organize my graph information, I realized the following feature:\r\n```python\r\nimport torch\r\nimport torch_geometric\r\nfrom torch_geometric.data import Data, Batch\r\n\r\ndata_list = []\r\nfor i in range(3):\r\n    data = Data(x=torch.randn(5, 3))\r\n    data.image_index = torch.ones(5)\r\n    data_list.append(data)\r\n    \r\nbatch = Batch.from_data_list(data_list)\r\nprint(batch.image_index)\r\n# Gives tensor([ 1.,  1.,  1.,  1.,  1.,  6.,  6.,  6.,  6.,  6., 11., 11., 11., 11., 11.])\r\n\r\ndata_list = []\r\nfor i in range(3):\r\n    data = Data(x=torch.randn(5, 3))\r\n    data.foo = torch.ones(5)\r\n    data_list.append(data)\r\n    \r\nbatch = Batch.from_data_list(data_list)\r\nprint(batch.foo)\r\n# Gives tensor([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])\r\n``` \r\n\r\nSo it seems that one can get different \"batched\" results depending on the name of the attribute to which one stores the information. In fact, it seems one gets the former behavior when the string `index` is contained in the attribute name. Was this feature put in place by intention? FYI, I'm using verion `2.2.0`\r\n\r\n### Environment\r\n\r\n* PyG version: `2.2.0`\r\n* PyTorch version: `1.13.1+cu116`\r\n* OS: Linux\r\n* Python version: `3.8.10`\r\n* CUDA/cuDNN version: `cu116`\r\n* How you installed PyTorch and PyG (`conda`, `pip`, source): `pip`\r\n* Any other relevant information (*e.g.*, version of `torch-scatter`):\r\n\n", "before_files": [{"content": "import inspect\nfrom collections.abc import Sequence\nfrom typing import Any, List, Optional, Union\n\nimport numpy as np\nimport torch\nfrom torch import Tensor\n\nfrom torch_geometric.data.collate import collate\nfrom torch_geometric.data.data import BaseData, Data\nfrom torch_geometric.data.dataset import IndexType\nfrom torch_geometric.data.separate import separate\n\n\nclass DynamicInheritance(type):\n    # A meta class that sets the base class of a `Batch` object, e.g.:\n    # * `Batch(Data)` in case `Data` objects are batched together\n    # * `Batch(HeteroData)` in case `HeteroData` objects are batched together\n    def __call__(cls, *args, **kwargs):\n        base_cls = kwargs.pop('_base_cls', Data)\n\n        if issubclass(base_cls, Batch):\n            new_cls = base_cls\n        else:\n            name = f'{base_cls.__name__}{cls.__name__}'\n\n            # NOTE `MetaResolver` is necessary to resolve metaclass conflict\n            # problems between `DynamicInheritance` and the metaclass of\n            # `base_cls`. In particular, it creates a new common metaclass\n            # from the defined metaclasses.\n            class MetaResolver(type(cls), type(base_cls)):\n                pass\n\n            if name not in globals():\n                globals()[name] = MetaResolver(name, (cls, base_cls), {})\n            new_cls = globals()[name]\n\n        params = list(inspect.signature(base_cls.__init__).parameters.items())\n        for i, (k, v) in enumerate(params[1:]):\n            if k == 'args' or k == 'kwargs':\n                continue\n            if i < len(args) or k in kwargs:\n                continue\n            if v.default is not inspect.Parameter.empty:\n                continue\n            kwargs[k] = None\n\n        return super(DynamicInheritance, new_cls).__call__(*args, **kwargs)\n\n\nclass DynamicInheritanceGetter:\n    def __call__(self, cls, base_cls):\n        return cls(_base_cls=base_cls)\n\n\nclass Batch(metaclass=DynamicInheritance):\n    r\"\"\"A data object describing a batch of graphs as one big (disconnected)\n    graph.\n    Inherits from :class:`torch_geometric.data.Data` or\n    :class:`torch_geometric.data.HeteroData`.\n    In addition, single graphs can be identified via the assignment vector\n    :obj:`batch`, which maps each node to its respective graph identifier.\n    \"\"\"\n    @classmethod\n    def from_data_list(cls, data_list: List[BaseData],\n                       follow_batch: Optional[List[str]] = None,\n                       exclude_keys: Optional[List[str]] = None):\n        r\"\"\"Constructs a :class:`~torch_geometric.data.Batch` object from a\n        Python list of :class:`~torch_geometric.data.Data` or\n        :class:`~torch_geometric.data.HeteroData` objects.\n        The assignment vector :obj:`batch` is created on the fly.\n        In addition, creates assignment vectors for each key in\n        :obj:`follow_batch`.\n        Will exclude any keys given in :obj:`exclude_keys`.\"\"\"\n\n        batch, slice_dict, inc_dict = collate(\n            cls,\n            data_list=data_list,\n            increment=True,\n            add_batch=not isinstance(data_list[0], Batch),\n            follow_batch=follow_batch,\n            exclude_keys=exclude_keys,\n        )\n\n        batch._num_graphs = len(data_list)\n        batch._slice_dict = slice_dict\n        batch._inc_dict = inc_dict\n\n        return batch\n\n    def get_example(self, idx: int) -> BaseData:\n        r\"\"\"Gets the :class:`~torch_geometric.data.Data` or\n        :class:`~torch_geometric.data.HeteroData` object at index :obj:`idx`.\n        The :class:`~torch_geometric.data.Batch` object must have been created\n        via :meth:`from_data_list` in order to be able to reconstruct the\n        initial object.\"\"\"\n\n        if not hasattr(self, '_slice_dict'):\n            raise RuntimeError(\n                (\"Cannot reconstruct 'Data' object from 'Batch' because \"\n                 \"'Batch' was not created via 'Batch.from_data_list()'\"))\n\n        data = separate(\n            cls=self.__class__.__bases__[-1],\n            batch=self,\n            idx=idx,\n            slice_dict=self._slice_dict,\n            inc_dict=self._inc_dict,\n            decrement=True,\n        )\n\n        return data\n\n    def index_select(self, idx: IndexType) -> List[BaseData]:\n        r\"\"\"Creates a subset of :class:`~torch_geometric.data.Data` or\n        :class:`~torch_geometric.data.HeteroData` objects from specified\n        indices :obj:`idx`.\n        Indices :obj:`idx` can be a slicing object, *e.g.*, :obj:`[2:5]`, a\n        list, a tuple, or a :obj:`torch.Tensor` or :obj:`np.ndarray` of type\n        long or bool.\n        The :class:`~torch_geometric.data.Batch` object must have been created\n        via :meth:`from_data_list` in order to be able to reconstruct the\n        initial objects.\"\"\"\n        if isinstance(idx, slice):\n            idx = list(range(self.num_graphs)[idx])\n\n        elif isinstance(idx, Tensor) and idx.dtype == torch.long:\n            idx = idx.flatten().tolist()\n\n        elif isinstance(idx, Tensor) and idx.dtype == torch.bool:\n            idx = idx.flatten().nonzero(as_tuple=False).flatten().tolist()\n\n        elif isinstance(idx, np.ndarray) and idx.dtype == np.int64:\n            idx = idx.flatten().tolist()\n\n        elif isinstance(idx, np.ndarray) and idx.dtype == bool:\n            idx = idx.flatten().nonzero()[0].flatten().tolist()\n\n        elif isinstance(idx, Sequence) and not isinstance(idx, str):\n            pass\n\n        else:\n            raise IndexError(\n                f\"Only slices (':'), list, tuples, torch.tensor and \"\n                f\"np.ndarray of dtype long or bool are valid indices (got \"\n                f\"'{type(idx).__name__}')\")\n\n        return [self.get_example(i) for i in idx]\n\n    def __getitem__(self, idx: Union[int, np.integer, str, IndexType]) -> Any:\n        if (isinstance(idx, (int, np.integer))\n                or (isinstance(idx, Tensor) and idx.dim() == 0)\n                or (isinstance(idx, np.ndarray) and np.isscalar(idx))):\n            return self.get_example(idx)\n        elif isinstance(idx, str) or (isinstance(idx, tuple)\n                                      and isinstance(idx[0], str)):\n            # Accessing attributes or node/edge types:\n            return super().__getitem__(idx)\n        else:\n            return self.index_select(idx)\n\n    def to_data_list(self) -> List[BaseData]:\n        r\"\"\"Reconstructs the list of :class:`~torch_geometric.data.Data` or\n        :class:`~torch_geometric.data.HeteroData` objects from the\n        :class:`~torch_geometric.data.Batch` object.\n        The :class:`~torch_geometric.data.Batch` object must have been created\n        via :meth:`from_data_list` in order to be able to reconstruct the\n        initial objects.\"\"\"\n        return [self.get_example(i) for i in range(self.num_graphs)]\n\n    @property\n    def num_graphs(self) -> int:\n        \"\"\"Returns the number of graphs in the batch.\"\"\"\n        if hasattr(self, '_num_graphs'):\n            return self._num_graphs\n        elif hasattr(self, 'ptr'):\n            return self.ptr.numel() - 1\n        elif hasattr(self, 'batch'):\n            return int(self.batch.max()) + 1\n        else:\n            raise ValueError(\"Can not infer the number of graphs\")\n\n    def __len__(self) -> int:\n        return self.num_graphs\n\n    def __reduce__(self):\n        state = self.__dict__.copy()\n        return DynamicInheritanceGetter(), self.__class__.__bases__, state\n", "path": "torch_geometric/data/batch.py"}], "after_files": [{"content": "import inspect\nfrom collections.abc import Sequence\nfrom typing import Any, List, Optional, Union\n\nimport numpy as np\nimport torch\nfrom torch import Tensor\n\nfrom torch_geometric.data.collate import collate\nfrom torch_geometric.data.data import BaseData, Data\nfrom torch_geometric.data.dataset import IndexType\nfrom torch_geometric.data.separate import separate\n\n\nclass DynamicInheritance(type):\n    # A meta class that sets the base class of a `Batch` object, e.g.:\n    # * `Batch(Data)` in case `Data` objects are batched together\n    # * `Batch(HeteroData)` in case `HeteroData` objects are batched together\n    def __call__(cls, *args, **kwargs):\n        base_cls = kwargs.pop('_base_cls', Data)\n\n        if issubclass(base_cls, Batch):\n            new_cls = base_cls\n        else:\n            name = f'{base_cls.__name__}{cls.__name__}'\n\n            # NOTE `MetaResolver` is necessary to resolve metaclass conflict\n            # problems between `DynamicInheritance` and the metaclass of\n            # `base_cls`. In particular, it creates a new common metaclass\n            # from the defined metaclasses.\n            class MetaResolver(type(cls), type(base_cls)):\n                pass\n\n            if name not in globals():\n                globals()[name] = MetaResolver(name, (cls, base_cls), {})\n            new_cls = globals()[name]\n\n        params = list(inspect.signature(base_cls.__init__).parameters.items())\n        for i, (k, v) in enumerate(params[1:]):\n            if k == 'args' or k == 'kwargs':\n                continue\n            if i < len(args) or k in kwargs:\n                continue\n            if v.default is not inspect.Parameter.empty:\n                continue\n            kwargs[k] = None\n\n        return super(DynamicInheritance, new_cls).__call__(*args, **kwargs)\n\n\nclass DynamicInheritanceGetter:\n    def __call__(self, cls, base_cls):\n        return cls(_base_cls=base_cls)\n\n\nclass Batch(metaclass=DynamicInheritance):\n    r\"\"\"A data object describing a batch of graphs as one big (disconnected)\n    graph.\n    Inherits from :class:`torch_geometric.data.Data` or\n    :class:`torch_geometric.data.HeteroData`.\n    In addition, single graphs can be identified via the assignment vector\n    :obj:`batch`, which maps each node to its respective graph identifier.\n\n    :pyg:`PyG` allows modification to the underlying batching procedure by\n    overwriting the :meth:`~Data.__inc__` and :meth:`~Data.__cat_dim__`\n    functionalities.\n    The :meth:`~Data.__inc__` method defines the incremental count between two\n    consecutive graph attributes.\n    By default, :pyg:`PyG` increments attributes by the number of nodes\n    whenever their attribute names contain the substring :obj:`index`\n    (for historical reasons), which comes in handy for attributes such as\n    :obj:`edge_index` or :obj:`node_index`.\n    However, note that this may lead to unexpected behavior for attributes\n    whose names contain the substring :obj:`index` but should not be\n    incremented.\n    To make sure, it is best practice to always double-check the output of\n    batching.\n    Furthermore, :meth:`~Data.__cat_dim__` defines in which dimension graph\n    tensors of the same attribute should be concatenated together.\n    \"\"\"\n    @classmethod\n    def from_data_list(cls, data_list: List[BaseData],\n                       follow_batch: Optional[List[str]] = None,\n                       exclude_keys: Optional[List[str]] = None):\n        r\"\"\"Constructs a :class:`~torch_geometric.data.Batch` object from a\n        Python list of :class:`~torch_geometric.data.Data` or\n        :class:`~torch_geometric.data.HeteroData` objects.\n        The assignment vector :obj:`batch` is created on the fly.\n        In addition, creates assignment vectors for each key in\n        :obj:`follow_batch`.\n        Will exclude any keys given in :obj:`exclude_keys`.\"\"\"\n\n        batch, slice_dict, inc_dict = collate(\n            cls,\n            data_list=data_list,\n            increment=True,\n            add_batch=not isinstance(data_list[0], Batch),\n            follow_batch=follow_batch,\n            exclude_keys=exclude_keys,\n        )\n\n        batch._num_graphs = len(data_list)\n        batch._slice_dict = slice_dict\n        batch._inc_dict = inc_dict\n\n        return batch\n\n    def get_example(self, idx: int) -> BaseData:\n        r\"\"\"Gets the :class:`~torch_geometric.data.Data` or\n        :class:`~torch_geometric.data.HeteroData` object at index :obj:`idx`.\n        The :class:`~torch_geometric.data.Batch` object must have been created\n        via :meth:`from_data_list` in order to be able to reconstruct the\n        initial object.\"\"\"\n\n        if not hasattr(self, '_slice_dict'):\n            raise RuntimeError(\n                (\"Cannot reconstruct 'Data' object from 'Batch' because \"\n                 \"'Batch' was not created via 'Batch.from_data_list()'\"))\n\n        data = separate(\n            cls=self.__class__.__bases__[-1],\n            batch=self,\n            idx=idx,\n            slice_dict=self._slice_dict,\n            inc_dict=self._inc_dict,\n            decrement=True,\n        )\n\n        return data\n\n    def index_select(self, idx: IndexType) -> List[BaseData]:\n        r\"\"\"Creates a subset of :class:`~torch_geometric.data.Data` or\n        :class:`~torch_geometric.data.HeteroData` objects from specified\n        indices :obj:`idx`.\n        Indices :obj:`idx` can be a slicing object, *e.g.*, :obj:`[2:5]`, a\n        list, a tuple, or a :obj:`torch.Tensor` or :obj:`np.ndarray` of type\n        long or bool.\n        The :class:`~torch_geometric.data.Batch` object must have been created\n        via :meth:`from_data_list` in order to be able to reconstruct the\n        initial objects.\"\"\"\n        if isinstance(idx, slice):\n            idx = list(range(self.num_graphs)[idx])\n\n        elif isinstance(idx, Tensor) and idx.dtype == torch.long:\n            idx = idx.flatten().tolist()\n\n        elif isinstance(idx, Tensor) and idx.dtype == torch.bool:\n            idx = idx.flatten().nonzero(as_tuple=False).flatten().tolist()\n\n        elif isinstance(idx, np.ndarray) and idx.dtype == np.int64:\n            idx = idx.flatten().tolist()\n\n        elif isinstance(idx, np.ndarray) and idx.dtype == bool:\n            idx = idx.flatten().nonzero()[0].flatten().tolist()\n\n        elif isinstance(idx, Sequence) and not isinstance(idx, str):\n            pass\n\n        else:\n            raise IndexError(\n                f\"Only slices (':'), list, tuples, torch.tensor and \"\n                f\"np.ndarray of dtype long or bool are valid indices (got \"\n                f\"'{type(idx).__name__}')\")\n\n        return [self.get_example(i) for i in idx]\n\n    def __getitem__(self, idx: Union[int, np.integer, str, IndexType]) -> Any:\n        if (isinstance(idx, (int, np.integer))\n                or (isinstance(idx, Tensor) and idx.dim() == 0)\n                or (isinstance(idx, np.ndarray) and np.isscalar(idx))):\n            return self.get_example(idx)\n        elif isinstance(idx, str) or (isinstance(idx, tuple)\n                                      and isinstance(idx[0], str)):\n            # Accessing attributes or node/edge types:\n            return super().__getitem__(idx)\n        else:\n            return self.index_select(idx)\n\n    def to_data_list(self) -> List[BaseData]:\n        r\"\"\"Reconstructs the list of :class:`~torch_geometric.data.Data` or\n        :class:`~torch_geometric.data.HeteroData` objects from the\n        :class:`~torch_geometric.data.Batch` object.\n        The :class:`~torch_geometric.data.Batch` object must have been created\n        via :meth:`from_data_list` in order to be able to reconstruct the\n        initial objects.\"\"\"\n        return [self.get_example(i) for i in range(self.num_graphs)]\n\n    @property\n    def num_graphs(self) -> int:\n        \"\"\"Returns the number of graphs in the batch.\"\"\"\n        if hasattr(self, '_num_graphs'):\n            return self._num_graphs\n        elif hasattr(self, 'ptr'):\n            return self.ptr.numel() - 1\n        elif hasattr(self, 'batch'):\n            return int(self.batch.max()) + 1\n        else:\n            raise ValueError(\"Can not infer the number of graphs\")\n\n    def __len__(self) -> int:\n        return self.num_graphs\n\n    def __reduce__(self):\n        state = self.__dict__.copy()\n        return DynamicInheritanceGetter(), self.__class__.__bases__, state\n", "path": "torch_geometric/data/batch.py"}]}