README.md 9.03 KB
Newer Older
rusty1s's avatar
rusty1s committed
1
2
3
4
5
6
7
[pypi-image]: https://badge.fury.io/py/torch-cluster.svg
[pypi-url]: https://pypi.python.org/pypi/torch-cluster
[build-image]: https://travis-ci.org/rusty1s/pytorch_cluster.svg?branch=master
[build-url]: https://travis-ci.org/rusty1s/pytorch_cluster
[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
8
# PyTorch Cluster
rusty1s's avatar
rusty1s committed
9

rusty1s's avatar
rusty1s committed
10
11
12
13
[![PyPI Version][pypi-image]][pypi-url]
[![Build Status][build-image]][build-url]
[![Code Coverage][coverage-image]][coverage-url]

rusty1s's avatar
rusty1s committed
14
15
--------------------------------------------------------------------------------

rusty1s's avatar
rusty1s committed
16
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
17
The package consists of the following clustering algorithms:
rusty1s's avatar
rusty1s committed
18

rusty1s's avatar
credit  
rusty1s committed
19
* **[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
20
* **[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
21
22
* **[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
23
* Clustering based on **[Nearest](#nearest)** points
rusty1s's avatar
rusty1s committed
24
* **[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
25
26
27
28
29

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

## Installation

rusty1s's avatar
rusty1s committed
30
31
32
### Binaries

We provide pip wheels for all major OS/PyTorch/CUDA combinations, see [here](https://s3.eu-central-1.amazonaws.com/pytorch-geometric.com/whl/index.html).
rusty1s's avatar
rusty1s committed
33

rusty1s's avatar
rusty1s committed
34
35
36
37
38
#### PyTorch 1.8.0

To install the binaries for PyTorch 1.8.0, simply run

```
rusty1s's avatar
rusty1s committed
39
pip install torch-cluster -f https://pytorch-geometric.com/whl/torch-1.8.0+${CUDA}.html
rusty1s's avatar
rusty1s committed
40
41
42
43
44
45
46
47
48
49
```

where `${CUDA}` should be replaced by either `cpu`, `cu101`, `cu102`, or `cu111` depending on your PyTorch installation.

|             | `cpu` | `cu101` | `cu102` | `cu111` |
|-------------|-------|---------|---------|---------|
| **Linux**   | ✅    | ✅      | ✅      | ✅      |
| **Windows** | ✅    | ✅      | ✅      | ✅      |
| **macOS**   | ✅    |         |         |         |

rusty1s's avatar
rusty1s committed
50
#### PyTorch 1.7.0
rusty1s's avatar
rusty1s committed
51

rusty1s's avatar
rusty1s committed
52
To install the binaries for PyTorch 1.7.0, simply run
rusty1s's avatar
rusty1s committed
53
54

```
55
pip install torch-cluster -f https://pytorch-geometric.com/whl/torch-1.7.0+${CUDA}.html
rusty1s's avatar
rusty1s committed
56
57
```

rusty1s's avatar
rusty1s committed
58
where `${CUDA}` should be replaced by either `cpu`, `cu92`, `cu101`, `cu102`, or `cu110` depending on your PyTorch installation.
rusty1s's avatar
rusty1s committed
59

rusty1s's avatar
rusty1s committed
60
61
62
63
64
|             | `cpu` | `cu92` | `cu101` | `cu102` | `cu110` |
|-------------|-------|--------|---------|---------|---------|
| **Linux**   | ✅    | ✅     | ✅      | ✅      | ✅      |
| **Windows** | ✅    | ❌     | ✅      | ✅      | ✅      |
| **macOS**   | ✅    |        |         |         |         |
rusty1s's avatar
rusty1s committed
65

rusty1s's avatar
rusty1s committed
66
**Note:** Binaries of older versions are also provided for PyTorch 1.4.0, PyTorch 1.5.0 and PyTorch 1.6.0 (following the same procedure).
rusty1s's avatar
rusty1s committed
67
68
69
70
71
72
73
74

### 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
75
76

$ python -c "import torch; print(torch.__version__)"
rusty1s's avatar
rusty1s committed
77
>>> 1.1.0
rusty1s's avatar
rusty1s committed
78
79
80
81
82

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

$ echo $CPATH
rusty1s's avatar
rusty1s committed
83
>>> /usr/local/cuda/include:...
rusty1s's avatar
rusty1s committed
84
85
```

rusty1s's avatar
rusty1s committed
86
87
88
Then run:

```
rusty1s's avatar
rusty1s committed
89
pip install torch-cluster
rusty1s's avatar
rusty1s committed
90
91
```

rusty1s's avatar
rusty1s committed
92
When running in a docker container without NVIDIA driver, PyTorch needs to evaluate the compute capabilities and may fail.
rusty1s's avatar
rusty1s committed
93
94
95
96
97
98
99
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
100

rusty1s's avatar
rusty1s committed
101
### Graclus
rusty1s's avatar
rusty1s committed
102
103

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
104
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
105
106
107
108
109

```python
import torch
from torch_cluster import graclus_cluster

rusty1s's avatar
rusty1s committed
110
111
row = torch.tensor([0, 1, 1, 2])
col = torch.tensor([1, 0, 2, 1])
rusty1s's avatar
rusty1s committed
112
weight = torch.tensor([1., 1., 1., 1.])  # Optional edge weights.
rusty1s's avatar
rusty1s committed
113
114
115
116
117
118

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

```
print(cluster)
rusty1s's avatar
rusty1s committed
119
tensor([0, 0, 1])
rusty1s's avatar
rusty1s committed
120
121
```

rusty1s's avatar
rusty1s committed
122
### VoxelGrid
rusty1s's avatar
rusty1s committed
123
124

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
125
126
127
128
129

```python
import torch
from torch_cluster import grid_cluster

rusty1s's avatar
rusty1s committed
130
pos = torch.tensor([[0., 0.], [11., 9.], [2., 8.], [2., 2.], [8., 3.]])
rusty1s's avatar
new api  
rusty1s committed
131
size = torch.Tensor([5, 5])
rusty1s's avatar
rusty1s committed
132
133
134
135
136
137

cluster = grid_cluster(pos, size)
```

```
print(cluster)
rusty1s's avatar
rusty1s committed
138
tensor([0, 5, 3, 0, 1])
rusty1s's avatar
rusty1s committed
139
```
rusty1s's avatar
rusty1s committed
140

rusty1s's avatar
rusty1s committed
141
### FarthestPointSampling
rusty1s's avatar
rusty1s committed
142

rusty1s's avatar
rusty1s committed
143
A sampling algorithm, which iteratively samples the most distant point with regard to the rest points.
rusty1s's avatar
rusty1s committed
144
145
146
147
148

```python
import torch
from torch_cluster import fps

rusty1s's avatar
rusty1s committed
149
x = torch.tensor([[-1., -1.], [-1., 1.], [1., -1.], [1., 1.]])
rusty1s's avatar
rusty1s committed
150
batch = torch.tensor([0, 0, 0, 0])
rusty1s's avatar
rusty1s committed
151
index = fps(x, batch, ratio=0.5, random_start=False)
rusty1s's avatar
rusty1s committed
152
153
154
```

```
JunZe Yu's avatar
JunZe Yu committed
155
print(index)
rusty1s's avatar
rusty1s committed
156
157
158
tensor([0, 3])
```

rusty1s's avatar
rusty1s committed
159
### kNN-Graph
rusty1s's avatar
rusty1s committed
160

rusty1s's avatar
rusty1s committed
161
Computes graph edges to the nearest *k* points.
rusty1s's avatar
rusty1s committed
162

163
164
165
**Args:**

* **x** *(Tensor)*: Node feature matrix of shape `[N, F]`.
luwei0917's avatar
luwei0917 committed
166
* **k** *(int)*: The number of neighbors.
167
168
169
170
171
172
* **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
173
174
175
176
```python
import torch
from torch_cluster import knn_graph

rusty1s's avatar
rusty1s committed
177
x = torch.tensor([[-1., -1.], [-1., 1.], [1., -1.], [1., 1.]])
rusty1s's avatar
rusty1s committed
178
179
180
181
182
183
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
184
185
tensor([[1, 2, 0, 3, 0, 3, 1, 2],
        [0, 0, 1, 1, 2, 2, 3, 3]])
rusty1s's avatar
rusty1s committed
186
187
```

rusty1s's avatar
rusty1s committed
188
### Radius-Graph
rusty1s's avatar
rusty1s committed
189

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

192
193
194
195
196
197
198
199
200
201
**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`)
* **max_num_neighbors** *(int, optional)*: The maximum number of neighbors to return for each element. (default: `32`)
* **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
202
203
204
205
```python
import torch
from torch_cluster import radius_graph

rusty1s's avatar
rusty1s committed
206
x = torch.tensor([[-1., -1.], [-1., 1.], [1., -1.], [1., 1.]])
rusty1s's avatar
rusty1s committed
207
batch = torch.tensor([0, 0, 0, 0])
rusty1s's avatar
rusty1s committed
208
edge_index = radius_graph(x, r=2.5, batch=batch, loop=False)
rusty1s's avatar
rusty1s committed
209
210
211
212
```

```
print(edge_index)
Matthias Fey's avatar
Matthias Fey committed
213
214
tensor([[1, 2, 0, 3, 0, 3, 1, 2],
        [0, 0, 1, 1, 2, 2, 3, 3]])
rusty1s's avatar
rusty1s committed
215
216
```

rusty1s's avatar
rusty1s committed
217
### Nearest
rusty1s's avatar
rusty1s committed
218

rusty1s's avatar
rusty1s committed
219
Clusters points in *x* together which are nearest to a given query point in *y*.
rusty1s's avatar
rusty1s committed
220
`batch_{x,y}` vectors need to be sorted.
rusty1s's avatar
rusty1s committed
221
222
223
224
225
226

```python
import torch
from torch_cluster import nearest

x = torch.Tensor([[-1, -1], [-1, 1], [1, -1], [1, 1]])
rusty1s's avatar
rusty1s committed
227
228
229
230
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
231
232
233
234
235
236
237
```

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

rusty1s's avatar
rusty1s committed
238
### RandomWalk-Sampling
rusty1s's avatar
rusty1s committed
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254

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
255
tensor([[0, 1, 2, 4],
rusty1s's avatar
rusty1s committed
256
        [1, 3, 4, 2],
Maurits's avatar
Maurits committed
257
258
259
        [2, 4, 2, 1],
        [3, 4, 2, 4],
        [4, 3, 1, 0]])
rusty1s's avatar
rusty1s committed
260
261
```

rusty1s's avatar
rusty1s committed
262
263
264
265
266
## Running tests

```
python setup.py test
```
rusty1s's avatar
rusty1s committed
267
268
269
270
271
272
273
274
275
276
277
278
279

## C++ API

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

```
mkdir build
cd build
# Add -DWITH_CUDA=on support for the CUDA if needed
cmake ..
make
make install
```