We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Typo (?) in Aggr.Aggregation's description
### 📚 Describe the documentation issue

First of all, I would like to thank everyone who has helped me in the last weeks to get going with Pytorch-Geometric. I really appreciate it.

There might be a minor typo in the documentation regarding the abstract class `aggr.Aggregation`. 

https://pytorch-geometric.readthedocs.io/en/latest/generated/torch_geometric.nn.aggr.Aggregation.html#torch_geometric.nn.aggr.Aggregation

See second code box:

```python
# Feature matrix holding 10 elements with 64 features each:
x = torch.randn(10, 64)

# Define the boundary indices for three sets:
ptr = torch.tensor([0, 4, 7, 10])

output = aggr(x, ptr=ptr)  #  Output shape: [4, 64]
```


### Suggest a potential alternative/fix

I believe it should be `#  Output shape: [3, 64]` in the last line. It says *three sets* just above. The previous example on indices also has output shape `[3, 64]`.

--- 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/nn/aggr/base.py`
Content:
```
1 from typing import Final, Optional, Tuple
2 
3 import torch
4 from torch import Tensor
5 
6 from torch_geometric.experimental import disable_dynamic_shapes
7 from torch_geometric.utils import scatter, segment, to_dense_batch
8 
9 
10 class Aggregation(torch.nn.Module):
11     r"""An abstract base class for implementing custom aggregations.
12 
13     Aggregation can be either performed via an :obj:`index` vector, which
14     defines the mapping from input elements to their location in the output:
15 
16     |
17 
18     .. image:: https://raw.githubusercontent.com/rusty1s/pytorch_scatter/
19             master/docs/source/_figures/add.svg?sanitize=true
20         :align: center
21         :width: 400px
22 
23     |
24 
25     Notably, :obj:`index` does not have to be sorted (for most aggregation
26     operators):
27 
28     .. code-block::
29 
30        # Feature matrix holding 10 elements with 64 features each:
31        x = torch.randn(10, 64)
32 
33        # Assign each element to one of three sets:
34        index = torch.tensor([0, 0, 1, 0, 2, 0, 2, 1, 0, 2])
35 
36        output = aggr(x, index)  #  Output shape: [3, 64]
37 
38     Alternatively, aggregation can be achieved via a "compressed" index vector
39     called :obj:`ptr`. Here, elements within the same set need to be grouped
40     together in the input, and :obj:`ptr` defines their boundaries:
41 
42     .. code-block::
43 
44        # Feature matrix holding 10 elements with 64 features each:
45        x = torch.randn(10, 64)
46 
47        # Define the boundary indices for three sets:
48        ptr = torch.tensor([0, 4, 7, 10])
49 
50        output = aggr(x, ptr=ptr)  #  Output shape: [4, 64]
51 
52     Note that at least one of :obj:`index` or :obj:`ptr` must be defined.
53 
54     Shapes:
55         - **input:**
56           node features :math:`(*, |\mathcal{V}|, F_{in})` or edge features
57           :math:`(*, |\mathcal{E}|, F_{in})`,
58           index vector :math:`(|\mathcal{V}|)` or :math:`(|\mathcal{E}|)`,
59         - **output:** graph features :math:`(*, |\mathcal{G}|, F_{out})` or
60           node features :math:`(*, |\mathcal{V}|, F_{out})`
61     """
62     def __init__(self) -> None:
63         super().__init__()
64 
65         self._deterministic: Final[bool] = (
66             torch.are_deterministic_algorithms_enabled()
67             or torch.is_deterministic_algorithms_warn_only_enabled())
68 
69     def forward(
70         self,
71         x: Tensor,
72         index: Optional[Tensor] = None,
73         ptr: Optional[Tensor] = None,
74         dim_size: Optional[int] = None,
75         dim: int = -2,
76         max_num_elements: Optional[int] = None,
77     ) -> Tensor:
78         r"""Forward pass.
79 
80         Args:
81             x (torch.Tensor): The source tensor.
82             index (torch.Tensor, optional): The indices of elements for
83                 applying the aggregation.
84                 One of :obj:`index` or :obj:`ptr` must be defined.
85                 (default: :obj:`None`)
86             ptr (torch.Tensor, optional): If given, computes the aggregation
87                 based on sorted inputs in CSR representation.
88                 One of :obj:`index` or :obj:`ptr` must be defined.
89                 (default: :obj:`None`)
90             dim_size (int, optional): The size of the output tensor at
91                 dimension :obj:`dim` after aggregation. (default: :obj:`None`)
92             dim (int, optional): The dimension in which to aggregate.
93                 (default: :obj:`-2`)
94             max_num_elements: (int, optional): The maximum number of elements
95                 within a single aggregation group. (default: :obj:`None`)
96         """
97         pass
98 
99     def reset_parameters(self):
100         r"""Resets all learnable parameters of the module."""
101         pass
102 
103     @disable_dynamic_shapes(required_args=['dim_size'])
104     def __call__(
105         self,
106         x: Tensor,
107         index: Optional[Tensor] = None,
108         ptr: Optional[Tensor] = None,
109         dim_size: Optional[int] = None,
110         dim: int = -2,
111         **kwargs,
112     ) -> Tensor:
113 
114         if dim >= x.dim() or dim < -x.dim():
115             raise ValueError(f"Encountered invalid dimension '{dim}' of "
116                              f"source tensor with {x.dim()} dimensions")
117 
118         if index is None and ptr is None:
119             index = x.new_zeros(x.size(dim), dtype=torch.long)
120 
121         if ptr is not None:
122             if dim_size is None:
123                 dim_size = ptr.numel() - 1
124             elif dim_size != ptr.numel() - 1:
125                 raise ValueError(f"Encountered invalid 'dim_size' (got "
126                                  f"'{dim_size}' but expected "
127                                  f"'{ptr.numel() - 1}')")
128 
129         if index is not None and dim_size is None:
130             dim_size = int(index.max()) + 1 if index.numel() > 0 else 0
131 
132         try:
133             return super().__call__(x, index=index, ptr=ptr, dim_size=dim_size,
134                                     dim=dim, **kwargs)
135         except (IndexError, RuntimeError) as e:
136             if index is not None:
137                 if index.numel() > 0 and dim_size <= int(index.max()):
138                     raise ValueError(f"Encountered invalid 'dim_size' (got "
139                                      f"'{dim_size}' but expected "
140                                      f">= '{int(index.max()) + 1}')")
141             raise e
142 
143     def __repr__(self) -> str:
144         return f'{self.__class__.__name__}()'
145 
146     # Assertions ##############################################################
147 
148     def assert_index_present(self, index: Optional[Tensor]):
149         # TODO Currently, not all aggregators support `ptr`. This assert helps
150         # to ensure that we require `index` to be passed to the computation:
151         if index is None:
152             raise NotImplementedError(
153                 "Aggregation requires 'index' to be specified")
154 
155     def assert_sorted_index(self, index: Optional[Tensor]):
156         if index is not None and not torch.all(index[:-1] <= index[1:]):
157             raise ValueError("Can not perform aggregation since the 'index' "
158                              "tensor is not sorted. Specifically, if you use "
159                              "this aggregation as part of 'MessagePassing`, "
160                              "ensure that 'edge_index' is sorted by "
161                              "destination nodes, e.g., by calling "
162                              "`data.sort(sort_by_row=False)`")
163 
164     def assert_two_dimensional_input(self, x: Tensor, dim: int):
165         if x.dim() != 2:
166             raise ValueError(f"Aggregation requires two-dimensional inputs "
167                              f"(got '{x.dim()}')")
168 
169         if dim not in [-2, 0]:
170             raise ValueError(f"Aggregation needs to perform aggregation in "
171                              f"first dimension (got '{dim}')")
172 
173     # Helper methods ##########################################################
174 
175     def reduce(self, x: Tensor, index: Optional[Tensor] = None,
176                ptr: Optional[Tensor] = None, dim_size: Optional[int] = None,
177                dim: int = -2, reduce: str = 'sum') -> Tensor:
178 
179         if ptr is not None:
180             if index is None or self._deterministic:
181                 ptr = expand_left(ptr, dim, dims=x.dim())
182                 return segment(x, ptr, reduce=reduce)
183 
184         if index is None:
185             raise RuntimeError("Aggregation requires 'index' to be specified")
186 
187         return scatter(x, index, dim, dim_size, reduce)
188 
189     def to_dense_batch(
190         self,
191         x: Tensor,
192         index: Optional[Tensor] = None,
193         ptr: Optional[Tensor] = None,
194         dim_size: Optional[int] = None,
195         dim: int = -2,
196         fill_value: float = 0.0,
197         max_num_elements: Optional[int] = None,
198     ) -> Tuple[Tensor, Tensor]:
199 
200         # TODO Currently, `to_dense_batch` can only operate on `index`:
201         self.assert_index_present(index)
202         self.assert_sorted_index(index)
203         self.assert_two_dimensional_input(x, dim)
204 
205         return to_dense_batch(
206             x,
207             index,
208             batch_size=dim_size,
209             fill_value=fill_value,
210             max_num_nodes=max_num_elements,
211         )
212 
213 
214 ###############################################################################
215 
216 
217 def expand_left(ptr: Tensor, dim: int, dims: int) -> Tensor:
218     for _ in range(dims + dim if dim < 0 else dim):
219         ptr = ptr.unsqueeze(0)
220     return ptr
221
```
--- 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/nn/aggr/base.py b/torch_geometric/nn/aggr/base.py
--- a/torch_geometric/nn/aggr/base.py
+++ b/torch_geometric/nn/aggr/base.py
@@ -47,7 +47,7 @@
        # Define the boundary indices for three sets:
        ptr = torch.tensor([0, 4, 7, 10])
 
-       output = aggr(x, ptr=ptr)  #  Output shape: [4, 64]
+       output = aggr(x, ptr=ptr)  #  Output shape: [3, 64]
 
     Note that at least one of :obj:`index` or :obj:`ptr` must be defined.

{"golden_diff": "diff --git a/torch_geometric/nn/aggr/base.py b/torch_geometric/nn/aggr/base.py\n--- a/torch_geometric/nn/aggr/base.py\n+++ b/torch_geometric/nn/aggr/base.py\n@@ -47,7 +47,7 @@\n        # Define the boundary indices for three sets:\n        ptr = torch.tensor([0, 4, 7, 10])\n \n-       output = aggr(x, ptr=ptr)  #  Output shape: [4, 64]\n+       output = aggr(x, ptr=ptr)  #  Output shape: [3, 64]\n \n     Note that at least one of :obj:`index` or :obj:`ptr` must be defined.\n", "issue": "Typo (?) in Aggr.Aggregation's description\n### \ud83d\udcda Describe the documentation issue\r\n\r\nFirst of all, I would like to thank everyone who has helped me in the last weeks to get going with Pytorch-Geometric. I really appreciate it.\r\n\r\nThere might be a minor typo in the documentation regarding the abstract class `aggr.Aggregation`. \r\n\r\nhttps://pytorch-geometric.readthedocs.io/en/latest/generated/torch_geometric.nn.aggr.Aggregation.html#torch_geometric.nn.aggr.Aggregation\r\n\r\nSee second code box:\r\n\r\n```python\r\n# Feature matrix holding 10 elements with 64 features each:\r\nx = torch.randn(10, 64)\r\n\r\n# Define the boundary indices for three sets:\r\nptr = torch.tensor([0, 4, 7, 10])\r\n\r\noutput = aggr(x, ptr=ptr)  #  Output shape: [4, 64]\r\n```\r\n\r\n\r\n### Suggest a potential alternative/fix\r\n\r\nI believe it should be `#  Output shape: [3, 64]` in the last line. It says *three sets* just above. The previous example on indices also has output shape `[3, 64]`.\n", "before_files": [{"content": "from typing import Final, Optional, Tuple\n\nimport torch\nfrom torch import Tensor\n\nfrom torch_geometric.experimental import disable_dynamic_shapes\nfrom torch_geometric.utils import scatter, segment, to_dense_batch\n\n\nclass Aggregation(torch.nn.Module):\n    r\"\"\"An abstract base class for implementing custom aggregations.\n\n    Aggregation can be either performed via an :obj:`index` vector, which\n    defines the mapping from input elements to their location in the output:\n\n    |\n\n    .. image:: https://raw.githubusercontent.com/rusty1s/pytorch_scatter/\n            master/docs/source/_figures/add.svg?sanitize=true\n        :align: center\n        :width: 400px\n\n    |\n\n    Notably, :obj:`index` does not have to be sorted (for most aggregation\n    operators):\n\n    .. code-block::\n\n       # Feature matrix holding 10 elements with 64 features each:\n       x = torch.randn(10, 64)\n\n       # Assign each element to one of three sets:\n       index = torch.tensor([0, 0, 1, 0, 2, 0, 2, 1, 0, 2])\n\n       output = aggr(x, index)  #  Output shape: [3, 64]\n\n    Alternatively, aggregation can be achieved via a \"compressed\" index vector\n    called :obj:`ptr`. Here, elements within the same set need to be grouped\n    together in the input, and :obj:`ptr` defines their boundaries:\n\n    .. code-block::\n\n       # Feature matrix holding 10 elements with 64 features each:\n       x = torch.randn(10, 64)\n\n       # Define the boundary indices for three sets:\n       ptr = torch.tensor([0, 4, 7, 10])\n\n       output = aggr(x, ptr=ptr)  #  Output shape: [4, 64]\n\n    Note that at least one of :obj:`index` or :obj:`ptr` must be defined.\n\n    Shapes:\n        - **input:**\n          node features :math:`(*, |\\mathcal{V}|, F_{in})` or edge features\n          :math:`(*, |\\mathcal{E}|, F_{in})`,\n          index vector :math:`(|\\mathcal{V}|)` or :math:`(|\\mathcal{E}|)`,\n        - **output:** graph features :math:`(*, |\\mathcal{G}|, F_{out})` or\n          node features :math:`(*, |\\mathcal{V}|, F_{out})`\n    \"\"\"\n    def __init__(self) -> None:\n        super().__init__()\n\n        self._deterministic: Final[bool] = (\n            torch.are_deterministic_algorithms_enabled()\n            or torch.is_deterministic_algorithms_warn_only_enabled())\n\n    def forward(\n        self,\n        x: Tensor,\n        index: Optional[Tensor] = None,\n        ptr: Optional[Tensor] = None,\n        dim_size: Optional[int] = None,\n        dim: int = -2,\n        max_num_elements: Optional[int] = None,\n    ) -> Tensor:\n        r\"\"\"Forward pass.\n\n        Args:\n            x (torch.Tensor): The source tensor.\n            index (torch.Tensor, optional): The indices of elements for\n                applying the aggregation.\n                One of :obj:`index` or :obj:`ptr` must be defined.\n                (default: :obj:`None`)\n            ptr (torch.Tensor, optional): If given, computes the aggregation\n                based on sorted inputs in CSR representation.\n                One of :obj:`index` or :obj:`ptr` must be defined.\n                (default: :obj:`None`)\n            dim_size (int, optional): The size of the output tensor at\n                dimension :obj:`dim` after aggregation. (default: :obj:`None`)\n            dim (int, optional): The dimension in which to aggregate.\n                (default: :obj:`-2`)\n            max_num_elements: (int, optional): The maximum number of elements\n                within a single aggregation group. (default: :obj:`None`)\n        \"\"\"\n        pass\n\n    def reset_parameters(self):\n        r\"\"\"Resets all learnable parameters of the module.\"\"\"\n        pass\n\n    @disable_dynamic_shapes(required_args=['dim_size'])\n    def __call__(\n        self,\n        x: Tensor,\n        index: Optional[Tensor] = None,\n        ptr: Optional[Tensor] = None,\n        dim_size: Optional[int] = None,\n        dim: int = -2,\n        **kwargs,\n    ) -> Tensor:\n\n        if dim >= x.dim() or dim < -x.dim():\n            raise ValueError(f\"Encountered invalid dimension '{dim}' of \"\n                             f\"source tensor with {x.dim()} dimensions\")\n\n        if index is None and ptr is None:\n            index = x.new_zeros(x.size(dim), dtype=torch.long)\n\n        if ptr is not None:\n            if dim_size is None:\n                dim_size = ptr.numel() - 1\n            elif dim_size != ptr.numel() - 1:\n                raise ValueError(f\"Encountered invalid 'dim_size' (got \"\n                                 f\"'{dim_size}' but expected \"\n                                 f\"'{ptr.numel() - 1}')\")\n\n        if index is not None and dim_size is None:\n            dim_size = int(index.max()) + 1 if index.numel() > 0 else 0\n\n        try:\n            return super().__call__(x, index=index, ptr=ptr, dim_size=dim_size,\n                                    dim=dim, **kwargs)\n        except (IndexError, RuntimeError) as e:\n            if index is not None:\n                if index.numel() > 0 and dim_size <= int(index.max()):\n                    raise ValueError(f\"Encountered invalid 'dim_size' (got \"\n                                     f\"'{dim_size}' but expected \"\n                                     f\">= '{int(index.max()) + 1}')\")\n            raise e\n\n    def __repr__(self) -> str:\n        return f'{self.__class__.__name__}()'\n\n    # Assertions ##############################################################\n\n    def assert_index_present(self, index: Optional[Tensor]):\n        # TODO Currently, not all aggregators support `ptr`. This assert helps\n        # to ensure that we require `index` to be passed to the computation:\n        if index is None:\n            raise NotImplementedError(\n                \"Aggregation requires 'index' to be specified\")\n\n    def assert_sorted_index(self, index: Optional[Tensor]):\n        if index is not None and not torch.all(index[:-1] <= index[1:]):\n            raise ValueError(\"Can not perform aggregation since the 'index' \"\n                             \"tensor is not sorted. Specifically, if you use \"\n                             \"this aggregation as part of 'MessagePassing`, \"\n                             \"ensure that 'edge_index' is sorted by \"\n                             \"destination nodes, e.g., by calling \"\n                             \"`data.sort(sort_by_row=False)`\")\n\n    def assert_two_dimensional_input(self, x: Tensor, dim: int):\n        if x.dim() != 2:\n            raise ValueError(f\"Aggregation requires two-dimensional inputs \"\n                             f\"(got '{x.dim()}')\")\n\n        if dim not in [-2, 0]:\n            raise ValueError(f\"Aggregation needs to perform aggregation in \"\n                             f\"first dimension (got '{dim}')\")\n\n    # Helper methods ##########################################################\n\n    def reduce(self, x: Tensor, index: Optional[Tensor] = None,\n               ptr: Optional[Tensor] = None, dim_size: Optional[int] = None,\n               dim: int = -2, reduce: str = 'sum') -> Tensor:\n\n        if ptr is not None:\n            if index is None or self._deterministic:\n                ptr = expand_left(ptr, dim, dims=x.dim())\n                return segment(x, ptr, reduce=reduce)\n\n        if index is None:\n            raise RuntimeError(\"Aggregation requires 'index' to be specified\")\n\n        return scatter(x, index, dim, dim_size, reduce)\n\n    def to_dense_batch(\n        self,\n        x: Tensor,\n        index: Optional[Tensor] = None,\n        ptr: Optional[Tensor] = None,\n        dim_size: Optional[int] = None,\n        dim: int = -2,\n        fill_value: float = 0.0,\n        max_num_elements: Optional[int] = None,\n    ) -> Tuple[Tensor, Tensor]:\n\n        # TODO Currently, `to_dense_batch` can only operate on `index`:\n        self.assert_index_present(index)\n        self.assert_sorted_index(index)\n        self.assert_two_dimensional_input(x, dim)\n\n        return to_dense_batch(\n            x,\n            index,\n            batch_size=dim_size,\n            fill_value=fill_value,\n            max_num_nodes=max_num_elements,\n        )\n\n\n###############################################################################\n\n\ndef expand_left(ptr: Tensor, dim: int, dims: int) -> Tensor:\n    for _ in range(dims + dim if dim < 0 else dim):\n        ptr = ptr.unsqueeze(0)\n    return ptr\n", "path": "torch_geometric/nn/aggr/base.py"}], "after_files": [{"content": "from typing import Final, Optional, Tuple\n\nimport torch\nfrom torch import Tensor\n\nfrom torch_geometric.experimental import disable_dynamic_shapes\nfrom torch_geometric.utils import scatter, segment, to_dense_batch\n\n\nclass Aggregation(torch.nn.Module):\n    r\"\"\"An abstract base class for implementing custom aggregations.\n\n    Aggregation can be either performed via an :obj:`index` vector, which\n    defines the mapping from input elements to their location in the output:\n\n    |\n\n    .. image:: https://raw.githubusercontent.com/rusty1s/pytorch_scatter/\n            master/docs/source/_figures/add.svg?sanitize=true\n        :align: center\n        :width: 400px\n\n    |\n\n    Notably, :obj:`index` does not have to be sorted (for most aggregation\n    operators):\n\n    .. code-block::\n\n       # Feature matrix holding 10 elements with 64 features each:\n       x = torch.randn(10, 64)\n\n       # Assign each element to one of three sets:\n       index = torch.tensor([0, 0, 1, 0, 2, 0, 2, 1, 0, 2])\n\n       output = aggr(x, index)  #  Output shape: [3, 64]\n\n    Alternatively, aggregation can be achieved via a \"compressed\" index vector\n    called :obj:`ptr`. Here, elements within the same set need to be grouped\n    together in the input, and :obj:`ptr` defines their boundaries:\n\n    .. code-block::\n\n       # Feature matrix holding 10 elements with 64 features each:\n       x = torch.randn(10, 64)\n\n       # Define the boundary indices for three sets:\n       ptr = torch.tensor([0, 4, 7, 10])\n\n       output = aggr(x, ptr=ptr)  #  Output shape: [3, 64]\n\n    Note that at least one of :obj:`index` or :obj:`ptr` must be defined.\n\n    Shapes:\n        - **input:**\n          node features :math:`(*, |\\mathcal{V}|, F_{in})` or edge features\n          :math:`(*, |\\mathcal{E}|, F_{in})`,\n          index vector :math:`(|\\mathcal{V}|)` or :math:`(|\\mathcal{E}|)`,\n        - **output:** graph features :math:`(*, |\\mathcal{G}|, F_{out})` or\n          node features :math:`(*, |\\mathcal{V}|, F_{out})`\n    \"\"\"\n    def __init__(self) -> None:\n        super().__init__()\n\n        self._deterministic: Final[bool] = (\n            torch.are_deterministic_algorithms_enabled()\n            or torch.is_deterministic_algorithms_warn_only_enabled())\n\n    def forward(\n        self,\n        x: Tensor,\n        index: Optional[Tensor] = None,\n        ptr: Optional[Tensor] = None,\n        dim_size: Optional[int] = None,\n        dim: int = -2,\n        max_num_elements: Optional[int] = None,\n    ) -> Tensor:\n        r\"\"\"Forward pass.\n\n        Args:\n            x (torch.Tensor): The source tensor.\n            index (torch.Tensor, optional): The indices of elements for\n                applying the aggregation.\n                One of :obj:`index` or :obj:`ptr` must be defined.\n                (default: :obj:`None`)\n            ptr (torch.Tensor, optional): If given, computes the aggregation\n                based on sorted inputs in CSR representation.\n                One of :obj:`index` or :obj:`ptr` must be defined.\n                (default: :obj:`None`)\n            dim_size (int, optional): The size of the output tensor at\n                dimension :obj:`dim` after aggregation. (default: :obj:`None`)\n            dim (int, optional): The dimension in which to aggregate.\n                (default: :obj:`-2`)\n            max_num_elements: (int, optional): The maximum number of elements\n                within a single aggregation group. (default: :obj:`None`)\n        \"\"\"\n        pass\n\n    def reset_parameters(self):\n        r\"\"\"Resets all learnable parameters of the module.\"\"\"\n        pass\n\n    @disable_dynamic_shapes(required_args=['dim_size'])\n    def __call__(\n        self,\n        x: Tensor,\n        index: Optional[Tensor] = None,\n        ptr: Optional[Tensor] = None,\n        dim_size: Optional[int] = None,\n        dim: int = -2,\n        **kwargs,\n    ) -> Tensor:\n\n        if dim >= x.dim() or dim < -x.dim():\n            raise ValueError(f\"Encountered invalid dimension '{dim}' of \"\n                             f\"source tensor with {x.dim()} dimensions\")\n\n        if index is None and ptr is None:\n            index = x.new_zeros(x.size(dim), dtype=torch.long)\n\n        if ptr is not None:\n            if dim_size is None:\n                dim_size = ptr.numel() - 1\n            elif dim_size != ptr.numel() - 1:\n                raise ValueError(f\"Encountered invalid 'dim_size' (got \"\n                                 f\"'{dim_size}' but expected \"\n                                 f\"'{ptr.numel() - 1}')\")\n\n        if index is not None and dim_size is None:\n            dim_size = int(index.max()) + 1 if index.numel() > 0 else 0\n\n        try:\n            return super().__call__(x, index=index, ptr=ptr, dim_size=dim_size,\n                                    dim=dim, **kwargs)\n        except (IndexError, RuntimeError) as e:\n            if index is not None:\n                if index.numel() > 0 and dim_size <= int(index.max()):\n                    raise ValueError(f\"Encountered invalid 'dim_size' (got \"\n                                     f\"'{dim_size}' but expected \"\n                                     f\">= '{int(index.max()) + 1}')\")\n            raise e\n\n    def __repr__(self) -> str:\n        return f'{self.__class__.__name__}()'\n\n    # Assertions ##############################################################\n\n    def assert_index_present(self, index: Optional[Tensor]):\n        # TODO Currently, not all aggregators support `ptr`. This assert helps\n        # to ensure that we require `index` to be passed to the computation:\n        if index is None:\n            raise NotImplementedError(\n                \"Aggregation requires 'index' to be specified\")\n\n    def assert_sorted_index(self, index: Optional[Tensor]):\n        if index is not None and not torch.all(index[:-1] <= index[1:]):\n            raise ValueError(\"Can not perform aggregation since the 'index' \"\n                             \"tensor is not sorted. Specifically, if you use \"\n                             \"this aggregation as part of 'MessagePassing`, \"\n                             \"ensure that 'edge_index' is sorted by \"\n                             \"destination nodes, e.g., by calling \"\n                             \"`data.sort(sort_by_row=False)`\")\n\n    def assert_two_dimensional_input(self, x: Tensor, dim: int):\n        if x.dim() != 2:\n            raise ValueError(f\"Aggregation requires two-dimensional inputs \"\n                             f\"(got '{x.dim()}')\")\n\n        if dim not in [-2, 0]:\n            raise ValueError(f\"Aggregation needs to perform aggregation in \"\n                             f\"first dimension (got '{dim}')\")\n\n    # Helper methods ##########################################################\n\n    def reduce(self, x: Tensor, index: Optional[Tensor] = None,\n               ptr: Optional[Tensor] = None, dim_size: Optional[int] = None,\n               dim: int = -2, reduce: str = 'sum') -> Tensor:\n\n        if ptr is not None:\n            if index is None or self._deterministic:\n                ptr = expand_left(ptr, dim, dims=x.dim())\n                return segment(x, ptr, reduce=reduce)\n\n        if index is None:\n            raise RuntimeError(\"Aggregation requires 'index' to be specified\")\n\n        return scatter(x, index, dim, dim_size, reduce)\n\n    def to_dense_batch(\n        self,\n        x: Tensor,\n        index: Optional[Tensor] = None,\n        ptr: Optional[Tensor] = None,\n        dim_size: Optional[int] = None,\n        dim: int = -2,\n        fill_value: float = 0.0,\n        max_num_elements: Optional[int] = None,\n    ) -> Tuple[Tensor, Tensor]:\n\n        # TODO Currently, `to_dense_batch` can only operate on `index`:\n        self.assert_index_present(index)\n        self.assert_sorted_index(index)\n        self.assert_two_dimensional_input(x, dim)\n\n        return to_dense_batch(\n            x,\n            index,\n            batch_size=dim_size,\n            fill_value=fill_value,\n            max_num_nodes=max_num_elements,\n        )\n\n\n###############################################################################\n\n\ndef expand_left(ptr: Tensor, dim: int, dims: int) -> Tensor:\n    for _ in range(dims + dim if dim < 0 else dim):\n        ptr = ptr.unsqueeze(0)\n    return ptr\n", "path": "torch_geometric/nn/aggr/base.py"}]}