README.md 9.98 KB
Newer Older
rusty1s's avatar
rusty1s committed
1
2
[pypi-image]: https://badge.fury.io/py/torch-cluster.svg
[pypi-url]: https://pypi.python.org/pypi/torch-cluster
rusty1s's avatar
rusty1s committed
3
4
5
6
[testing-image]: https://github.com/rusty1s/pytorch_cluster/actions/workflows/testing.yml/badge.svg
[testing-url]: https://github.com/rusty1s/pytorch_cluster/actions/workflows/testing.yml
[linting-image]: https://github.com/rusty1s/pytorch_cluster/actions/workflows/linting.yml/badge.svg
[linting-url]: https://github.com/rusty1s/pytorch_cluster/actions/workflows/linting.yml
rusty1s's avatar
rusty1s committed
7
8
9
[coverage-image]: https://codecov.io/gh/rusty1s/pytorch_cluster/branch/master/graph/badge.svg
[coverage-url]: https://codecov.io/github/rusty1s/pytorch_cluster?branch=master

rusty1s's avatar
rusty1s committed
10
# PyTorch Cluster
rusty1s's avatar
rusty1s committed
11

rusty1s's avatar
rusty1s committed
12
[![PyPI Version][pypi-image]][pypi-url]
rusty1s's avatar
rusty1s committed
13
14
[![Testing Status][testing-image]][testing-url]
[![Linting Status][linting-image]][linting-url]
rusty1s's avatar
rusty1s committed
15
16
[![Code Coverage][coverage-image]][coverage-url]

rusty1s's avatar
rusty1s committed
17
18
--------------------------------------------------------------------------------

rusty1s's avatar
rusty1s committed
19
This package consists of a small extension library of highly optimized graph cluster algorithms for the use in [PyTorch](http://pytorch.org/).
rusty1s's avatar
typo  
rusty1s committed
20
The package consists of the following clustering algorithms:
rusty1s's avatar
rusty1s committed
21

rusty1s's avatar
credit  
rusty1s committed
22
* **[Graclus](#graclus)** from Dhillon *et al.*: [Weighted Graph Cuts without Eigenvectors: A Multilevel Approach](http://www.cs.utexas.edu/users/inderjit/public_papers/multilevel_pami.pdf) (PAMI 2007)
rusty1s's avatar
rusty1s committed
23
* **[Voxel Grid Pooling](#voxelgrid)** from, *e.g.*, Simonovsky and Komodakis: [Dynamic Edge-Conditioned Filters in Convolutional Neural Networks on Graphs](https://arxiv.org/abs/1704.02901) (CVPR 2017)
rusty1s's avatar
rusty1s committed
24
25
* **[Iterative Farthest Point Sampling](#farthestpointsampling)** from, *e.g.* Qi *et al.*: [PointNet++: Deep Hierarchical Feature Learning on Point Sets in a Metric Space](https://arxiv.org/abs/1706.02413) (NIPS 2017)
* **[k-NN](#knn-graph)** and **[Radius](#radius-graph)** graph generation
rusty1s's avatar
rusty1s committed
26
* Clustering based on **[Nearest](#nearest)** points
rusty1s's avatar
rusty1s committed
27
* **[Random Walk Sampling](#randomwalk-sampling)** from, *e.g.*, Grover and Leskovec: [node2vec: Scalable Feature Learning for Networks](https://arxiv.org/abs/1607.00653) (KDD 2016)
rusty1s's avatar
rusty1s committed
28
29
30
31
32

All included operations work on varying data types and are implemented both for CPU and GPU.

## Installation

rusty1s's avatar
rusty1s committed
33
34
### Anaconda

rusty1s's avatar
rusty1s committed
35
**Update:** You can now install `pytorch-cluster` via [Anaconda](https://anaconda.org/pyg/pytorch-cluster) for all major OS/PyTorch/CUDA combinations 🤗
rusty1s's avatar
rusty1s committed
36
37
38
Given that you have [`pytorch >= 1.8.0` installed](https://pytorch.org/get-started/locally/), simply run

```
rusty1s's avatar
rusty1s committed
39
conda install pytorch-cluster -c pyg
rusty1s's avatar
rusty1s committed
40
41
```

rusty1s's avatar
rusty1s committed
42
43
### Binaries

rusty1s's avatar
rusty1s committed
44
We alternatively provide pip wheels for all major OS/PyTorch/CUDA combinations, see [here](https://data.pyg.org/whl).
rusty1s's avatar
rusty1s committed
45

46
#### PyTorch 2.1
rusty1s's avatar
rusty1s committed
47

48
To install the binaries for PyTorch 2.1.0, simply run
rusty1s's avatar
rusty1s committed
49
50

```
51
pip install torch-cluster -f https://data.pyg.org/whl/torch-2.1.0+${CUDA}.html
rusty1s's avatar
rusty1s committed
52
53
```

54
where `${CUDA}` should be replaced by either `cpu`, `cu118`, or `cu121` depending on your PyTorch installation.
rusty1s's avatar
rusty1s committed
55

56
|             | `cpu` | `cu118` | `cu121` |
Matthias Fey's avatar
Matthias Fey committed
57
58
59
60
|-------------|-------|---------|---------|
| **Linux**   | ✅    | ✅      | ✅      |
| **Windows** | ✅    | ✅      | ✅      |
| **macOS**   | ✅    |         |         |
rusty1s's avatar
rusty1s committed
61

62
#### PyTorch 2.0
rusty1s's avatar
rusty1s committed
63

64
To install the binaries for PyTorch 2.0.0, simply run
rusty1s's avatar
rusty1s committed
65
66

```
67
pip install torch-cluster -f https://data.pyg.org/whl/torch-2.0.0+${CUDA}.html
rusty1s's avatar
rusty1s committed
68
69
```

70
where `${CUDA}` should be replaced by either `cpu`, `cu117`, or `cu118` depending on your PyTorch installation.
rusty1s's avatar
rusty1s committed
71

72
|             | `cpu` | `cu117` | `cu118` |
Matthias Fey's avatar
Matthias Fey committed
73
74
75
76
|-------------|-------|---------|---------|
| **Linux**   | ✅    | ✅      | ✅      |
| **Windows** | ✅    | ✅      | ✅      |
| **macOS**   | ✅    |         |         |
rusty1s's avatar
rusty1s committed
77

78
**Note:** Binaries of older versions are also provided for PyTorch 1.4.0, PyTorch 1.5.0, PyTorch 1.6.0, PyTorch 1.7.0/1.7.1, PyTorch 1.8.0/1.8.1, PyTorch 1.9.0, PyTorch 1.10.0/1.10.1/1.10.2, PyTorch 1.11.0, PyTorch 1.12.0/1.12.1 and PyTorch 1.13.0/1.13.1 (following the same procedure).
Matthias Fey's avatar
Matthias Fey committed
79
For older versions, you need to explicitly specify the latest supported version number or install via `pip install --no-index` in order to prevent a manual installation from source.
rusty1s's avatar
rusty1s committed
80
You can look up the latest supported version number [here](https://data.pyg.org/whl).
rusty1s's avatar
rusty1s committed
81
82
83
84
85
86
87
88

### From source

Ensure that at least PyTorch 1.4.0 is installed and verify that `cuda/bin` and `cuda/include` are in your `$PATH` and `$CPATH` respectively, *e.g.*:

```
$ python -c "import torch; print(torch.__version__)"
>>> 1.4.0
rusty1s's avatar
rusty1s committed
89
90

$ python -c "import torch; print(torch.__version__)"
rusty1s's avatar
rusty1s committed
91
>>> 1.1.0
rusty1s's avatar
rusty1s committed
92
93
94
95
96

$ echo $PATH
>>> /usr/local/cuda/bin:...

$ echo $CPATH
rusty1s's avatar
rusty1s committed
97
>>> /usr/local/cuda/include:...
rusty1s's avatar
rusty1s committed
98
99
```

rusty1s's avatar
rusty1s committed
100
101
102
Then run:

```
rusty1s's avatar
rusty1s committed
103
pip install torch-cluster
rusty1s's avatar
rusty1s committed
104
105
```

rusty1s's avatar
rusty1s committed
106
When running in a docker container without NVIDIA driver, PyTorch needs to evaluate the compute capabilities and may fail.
rusty1s's avatar
rusty1s committed
107
108
109
110
111
112
113
In this case, ensure that the compute capabilities are set via `TORCH_CUDA_ARCH_LIST`, *e.g.*:

```
export TORCH_CUDA_ARCH_LIST = "6.0 6.1 7.2+PTX 7.5+PTX"
```

## Functions
rusty1s's avatar
rusty1s committed
114

rusty1s's avatar
rusty1s committed
115
### Graclus
rusty1s's avatar
rusty1s committed
116
117

A greedy clustering algorithm of picking an unmarked vertex and matching it with one its unmarked neighbors (that maximizes its edge weight).
rusty1s's avatar
credit  
rusty1s committed
118
The GPU algorithm is adapted from Fagginger Auer and Bisseling: [A GPU Algorithm for Greedy Graph Matching](http://www.staff.science.uu.nl/~bisse101/Articles/match12.pdf) (LNCS 2012)
rusty1s's avatar
rusty1s committed
119
120
121
122
123

```python
import torch
from torch_cluster import graclus_cluster

rusty1s's avatar
rusty1s committed
124
125
row = torch.tensor([0, 1, 1, 2])
col = torch.tensor([1, 0, 2, 1])
rusty1s's avatar
rusty1s committed
126
weight = torch.tensor([1., 1., 1., 1.])  # Optional edge weights.
rusty1s's avatar
rusty1s committed
127
128
129
130
131
132

cluster = graclus_cluster(row, col, weight)
```

```
print(cluster)
rusty1s's avatar
rusty1s committed
133
tensor([0, 0, 1])
rusty1s's avatar
rusty1s committed
134
135
```

rusty1s's avatar
rusty1s committed
136
### VoxelGrid
rusty1s's avatar
rusty1s committed
137
138

A clustering algorithm, which overlays a regular grid of user-defined size over a point cloud and clusters all points within a voxel.
rusty1s's avatar
rusty1s committed
139
140
141
142
143

```python
import torch
from torch_cluster import grid_cluster

rusty1s's avatar
rusty1s committed
144
pos = torch.tensor([[0., 0.], [11., 9.], [2., 8.], [2., 2.], [8., 3.]])
rusty1s's avatar
new api  
rusty1s committed
145
size = torch.Tensor([5, 5])
rusty1s's avatar
rusty1s committed
146
147
148
149
150
151

cluster = grid_cluster(pos, size)
```

```
print(cluster)
rusty1s's avatar
rusty1s committed
152
tensor([0, 5, 3, 0, 1])
rusty1s's avatar
rusty1s committed
153
```
rusty1s's avatar
rusty1s committed
154

rusty1s's avatar
rusty1s committed
155
### FarthestPointSampling
rusty1s's avatar
rusty1s committed
156

rusty1s's avatar
rusty1s committed
157
A sampling algorithm, which iteratively samples the most distant point with regard to the rest points.
rusty1s's avatar
rusty1s committed
158
159
160
161
162

```python
import torch
from torch_cluster import fps

rusty1s's avatar
rusty1s committed
163
x = torch.tensor([[-1., -1.], [-1., 1.], [1., -1.], [1., 1.]])
rusty1s's avatar
rusty1s committed
164
batch = torch.tensor([0, 0, 0, 0])
rusty1s's avatar
rusty1s committed
165
index = fps(x, batch, ratio=0.5, random_start=False)
rusty1s's avatar
rusty1s committed
166
167
168
```

```
JunZe Yu's avatar
JunZe Yu committed
169
print(index)
rusty1s's avatar
rusty1s committed
170
171
172
tensor([0, 3])
```

rusty1s's avatar
rusty1s committed
173
### kNN-Graph
rusty1s's avatar
rusty1s committed
174

rusty1s's avatar
rusty1s committed
175
Computes graph edges to the nearest *k* points.
rusty1s's avatar
rusty1s committed
176

177
178
179
**Args:**

* **x** *(Tensor)*: Node feature matrix of shape `[N, F]`.
luwei0917's avatar
luwei0917 committed
180
* **k** *(int)*: The number of neighbors.
181
182
183
184
185
186
* **batch** *(LongTensor, optional)*: Batch vector of shape `[N]`, which assigns each node to a specific example. `batch` needs to be sorted. (default: `None`)
* **loop** *(bool, optional)*: If `True`, the graph will contain self-loops. (default: `False`)
* **flow** *(string, optional)*: The flow direction when using in combination with message passing (`"source_to_target"` or `"target_to_source"`). (default: `"source_to_target"`)
* **cosine** *(boolean, optional)*: If `True`, will use the Cosine distance instead of Euclidean distance to find nearest neighbors. (default: `False`)
* **num_workers** *(int)*: Number of workers to use for computation. Has no effect in case `batch` is not `None`, or the input lies on the GPU. (default: `1`)

rusty1s's avatar
rusty1s committed
187
188
189
190
```python
import torch
from torch_cluster import knn_graph

rusty1s's avatar
rusty1s committed
191
x = torch.tensor([[-1., -1.], [-1., 1.], [1., -1.], [1., 1.]])
rusty1s's avatar
rusty1s committed
192
193
194
195
196
197
batch = torch.tensor([0, 0, 0, 0])
edge_index = knn_graph(x, k=2, batch=batch, loop=False)
```

```
print(edge_index)
Matthias Fey's avatar
Matthias Fey committed
198
199
tensor([[1, 2, 0, 3, 0, 3, 1, 2],
        [0, 0, 1, 1, 2, 2, 3, 3]])
rusty1s's avatar
rusty1s committed
200
201
```

rusty1s's avatar
rusty1s committed
202
### Radius-Graph
rusty1s's avatar
rusty1s committed
203

rusty1s's avatar
rusty1s committed
204
Computes graph edges to all points within a given distance.
rusty1s's avatar
rusty1s committed
205

206
207
208
209
210
211
**Args:**

* **x** *(Tensor)*: Node feature matrix of shape `[N, F]`.
* **r** *(float)*: The radius.
* **batch** *(LongTensor, optional)*: Batch vector of shape `[N]`, which assigns each node to a specific example. `batch` needs to be sorted. (default: `None`)
* **loop** *(bool, optional)*: If `True`, the graph will contain self-loops. (default: `False`)
rusty1s's avatar
rusty1s committed
212
* **max_num_neighbors** *(int, optional)*: The maximum number of neighbors to return for each element. If the number of actual neighbors is greater than `max_num_neighbors`, returned neighbors are picked randomly. (default: `32`)
213
214
215
* **flow** *(string, optional)*: The flow direction when using in combination with message passing (`"source_to_target"` or `"target_to_source"`). (default: `"source_to_target"`)
* **num_workers** *(int)*: Number of workers to use for computation. Has no effect in case `batch` is not `None`, or the input lies on the GPU. (default: `1`)

rusty1s's avatar
rusty1s committed
216
217
218
219
```python
import torch
from torch_cluster import radius_graph

rusty1s's avatar
rusty1s committed
220
x = torch.tensor([[-1., -1.], [-1., 1.], [1., -1.], [1., 1.]])
rusty1s's avatar
rusty1s committed
221
batch = torch.tensor([0, 0, 0, 0])
rusty1s's avatar
rusty1s committed
222
edge_index = radius_graph(x, r=2.5, batch=batch, loop=False)
rusty1s's avatar
rusty1s committed
223
224
225
226
```

```
print(edge_index)
Matthias Fey's avatar
Matthias Fey committed
227
228
tensor([[1, 2, 0, 3, 0, 3, 1, 2],
        [0, 0, 1, 1, 2, 2, 3, 3]])
rusty1s's avatar
rusty1s committed
229
230
```

rusty1s's avatar
rusty1s committed
231
### Nearest
rusty1s's avatar
rusty1s committed
232

rusty1s's avatar
rusty1s committed
233
Clusters points in *x* together which are nearest to a given query point in *y*.
rusty1s's avatar
rusty1s committed
234
`batch_{x,y}` vectors need to be sorted.
rusty1s's avatar
rusty1s committed
235
236
237
238
239
240

```python
import torch
from torch_cluster import nearest

x = torch.Tensor([[-1, -1], [-1, 1], [1, -1], [1, 1]])
rusty1s's avatar
rusty1s committed
241
242
243
244
batch_x = torch.tensor([0, 0, 0, 0])
y = torch.Tensor([[-1, 0], [1, 0]])
batch_y = torch.tensor([0, 0])
cluster = nearest(x, y, batch_x, batch_y)
rusty1s's avatar
rusty1s committed
245
246
247
248
249
250
251
```

```
print(cluster)
tensor([0, 0, 1, 1])
```

rusty1s's avatar
rusty1s committed
252
### RandomWalk-Sampling
rusty1s's avatar
rusty1s committed
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268

Samples random walks of length `walk_length` from all node indices in `start` in the graph given by `(row, col)`.

```python
import torch
from torch_cluster import random_walk

row = torch.tensor([0, 1, 1, 1, 2, 2, 3, 3, 4, 4])
col = torch.tensor([1, 0, 2, 3, 1, 4, 1, 4, 2, 3])
start = torch.tensor([0, 1, 2, 3, 4])

walk = random_walk(row, col, start, walk_length=3)
```

```
print(walk)
Maurits's avatar
Maurits committed
269
tensor([[0, 1, 2, 4],
rusty1s's avatar
rusty1s committed
270
        [1, 3, 4, 2],
Maurits's avatar
Maurits committed
271
272
273
        [2, 4, 2, 1],
        [3, 4, 2, 4],
        [4, 3, 1, 0]])
rusty1s's avatar
rusty1s committed
274
275
```

rusty1s's avatar
rusty1s committed
276
277
278
## Running tests

```
rusty1s's avatar
rusty1s committed
279
pytest
rusty1s's avatar
rusty1s committed
280
```
rusty1s's avatar
rusty1s committed
281
282
283
284
285
286

## C++ API

`torch-cluster` also offers a C++ API that contains C++ equivalent of python models.

```
287
export Torch_DIR=`python -c 'import torch;print(torch.utils.cmake_prefix_path)'`
rusty1s's avatar
rusty1s committed
288
289
290
291
292
293
294
mkdir build
cd build
# Add -DWITH_CUDA=on support for the CUDA if needed
cmake ..
make
make install
```