README.md 9.36 KB
Newer Older
rusty1s's avatar
rusty1s committed
1
2
3
4
5
6
[pypi-image]: https://badge.fury.io/py/torch-sparse.svg
[pypi-url]: https://pypi.python.org/pypi/torch-sparse
[build-image]: https://travis-ci.org/rusty1s/pytorch_sparse.svg?branch=master
[build-url]: https://travis-ci.org/rusty1s/pytorch_sparse
[coverage-image]: https://codecov.io/gh/rusty1s/pytorch_sparse/branch/master/graph/badge.svg
[coverage-url]: https://codecov.io/github/rusty1s/pytorch_sparse?branch=master
rusty1s's avatar
rusty1s committed
7

rusty1s's avatar
rusty1s committed
8
# PyTorch Sparse
rusty1s's avatar
rusty1s committed
9
10
11
12
13
14

[![PyPI Version][pypi-image]][pypi-url]
[![Build Status][build-image]][build-url]
[![Code Coverage][coverage-image]][coverage-url]

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

rusty1s's avatar
rusty1s committed
16
This package consists of a small extension library of optimized sparse matrix operations with autograd support.
rusty1s's avatar
typos  
rusty1s committed
17
This package currently consists of the following methods:
rusty1s's avatar
rusty1s committed
18

rusty1s's avatar
rusty1s committed
19
20
21
* **[Coalesce](#coalesce)**
* **[Transpose](#transpose)**
* **[Sparse Dense Matrix Multiplication](#sparse-dense-matrix-multiplication)**
rusty1s's avatar
docs  
rusty1s committed
22
* **[Sparse Sparse Matrix Multiplication](#sparse-sparse-matrix-multiplication)**
rusty1s's avatar
rusty1s committed
23
24

All included operations work on varying data types and are implemented both for CPU and GPU.
rusty1s's avatar
rusty1s committed
25
26
To avoid the hazzle of creating [`torch.sparse_coo_tensor`](https://pytorch.org/docs/stable/torch.html?highlight=sparse_coo_tensor#torch.sparse_coo_tensor), this package defines operations on sparse tensors by simply passing `index` and `value` tensors as arguments ([with same shapes as defined in PyTorch](https://pytorch.org/docs/stable/sparse.html)).
Note that only `value` comes with autograd support, as `index` is discrete and therefore not differentiable.
rusty1s's avatar
rusty1s committed
27
28
29

## Installation

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

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

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

To install the binaries for PyTorch 1.6.0, simply run

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

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

|             | `cpu` | `cu92` | `cu101` | `cu102` |
|-------------|-------|--------|---------|---------|
| **Linux**   | ✅    | ✅     | ✅      | ✅      |
| **Windows** | ✅    | ❌     | ✅      | ✅      |
| **macOS**   | ✅    |        |         |         |

rusty1s's avatar
rusty1s committed
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#### PyTorch 1.5.0

To install the binaries for PyTorch 1.5.0, simply run

```
pip install torch-scatter==latest+${CUDA} torch-sparse==latest+${CUDA} -f https://pytorch-geometric.com/whl/torch-1.5.0.html
```

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

|             | `cpu` | `cu92` | `cu101` | `cu102` |
|-------------|-------|--------|---------|---------|
| **Linux**   | ✅    | ✅     | ✅      | ✅      |
| **Windows** | ✅    | ❌     | ✅      | ✅      |
| **macOS**   | ✅    |        |         |         |

#### PyTorch 1.4.0

rusty1s's avatar
rusty1s committed
68
To install the binaries for PyTorch 1.4.0, simply run
rusty1s's avatar
rusty1s committed
69
70

```
rusty1s's avatar
rusty1s committed
71
pip install torch-scatter==latest+${CUDA} torch-sparse==latest+${CUDA} -f https://pytorch-geometric.com/whl/torch-1.4.0.html
rusty1s's avatar
rusty1s committed
72
73
74
75
76
77
78
79
```

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

|             | `cpu` | `cu92` | `cu100` | `cu101` |
|-------------|-------|--------|---------|---------|
| **Linux**   | ✅    | ✅     | ✅      | ✅      |
| **Windows** | ✅    | ❌     | ❌      | ✅      |
rusty1s's avatar
rusty1s committed
80
| **macOS**   | ✅    |        |         |         |
rusty1s's avatar
rusty1s committed
81
82
83

### From source

rusty1s's avatar
rusty1s committed
84
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.*:
rusty1s's avatar
rusty1s committed
85
86

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

rusty1s's avatar
rusty1s committed
90
$ echo $PATH
rusty1s's avatar
rusty1s committed
91
>>> /usr/local/cuda/bin:...
rusty1s's avatar
rusty1s committed
92
93

$ echo $CPATH
rusty1s's avatar
rusty1s committed
94
>>> /usr/local/cuda/include:...
rusty1s's avatar
rusty1s committed
95
96
```

rusty1s's avatar
rusty1s committed
97
If you want to additionally build `torch-sparse` with METIS support, *e.g.* for partioning, please download and install the [METIS library](http://glaros.dtc.umn.edu/gkhome/metis/metis/download) by following the instructions in the `Install.txt` file.
rusty1s's avatar
rusty1s committed
98
Note that METIS needs to be installed with 64 bit `IDXTYPEWIDTH` by changing `include/metis.h`.
rusty1s's avatar
rusty1s committed
99
Afterwards, set the environment variable `WITH_METIS=1`.
rusty1s's avatar
rusty1s committed
100

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

```
rusty1s's avatar
rusty1s committed
104
pip install torch-scatter torch-sparse
rusty1s's avatar
rusty1s committed
105
106
```

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

```
rusty1s's avatar
rusty1s committed
111
export TORCH_CUDA_ARCH_LIST="6.0 6.1 7.2+PTX 7.5+PTX"
rusty1s's avatar
rusty1s committed
112
113
114
```

## Functions
rusty1s's avatar
links  
rusty1s committed
115

rusty1s's avatar
rusty1s committed
116
### Coalesce
rusty1s's avatar
rusty1s committed
117

rusty1s's avatar
docs  
rusty1s committed
118
```
rusty1s's avatar
rusty1s committed
119
torch_sparse.coalesce(index, value, m, n, op="add") -> (torch.LongTensor, torch.Tensor)
rusty1s's avatar
docs  
rusty1s committed
120
121
```

122
Row-wise sorts `index` and removes duplicate entries.
rusty1s's avatar
rusty1s committed
123
124
125
Duplicate entries are removed by scattering them together.
For scattering, any operation of [`torch_scatter`](https://github.com/rusty1s/pytorch_scatter) can be used.

rusty1s's avatar
rusty1s committed
126
#### Parameters
rusty1s's avatar
rusty1s committed
127
128
129

* **index** *(LongTensor)* - The index tensor of sparse matrix.
* **value** *(Tensor)* - The value tensor of sparse matrix.
130
131
* **m** *(int)* - The first dimension of corresponding dense matrix.
* **n** *(int)* - The second dimension of corresponding dense matrix.
rusty1s's avatar
docs  
rusty1s committed
132
* **op** *(string, optional)* - The scatter operation to use. (default: `"add"`)
rusty1s's avatar
rusty1s committed
133

rusty1s's avatar
rusty1s committed
134
#### Returns
rusty1s's avatar
rusty1s committed
135

rusty1s's avatar
docs  
rusty1s committed
136
137
* **index** *(LongTensor)* - The coalesced index tensor of sparse matrix.
* **value** *(Tensor)* - The coalesced value tensor of sparse matrix.
rusty1s's avatar
rusty1s committed
138

rusty1s's avatar
rusty1s committed
139
#### Example
rusty1s's avatar
docs  
rusty1s committed
140
141

```python
ekka's avatar
ekka committed
142
import torch
rusty1s's avatar
rusty1s committed
143
144
145
146
from torch_sparse import coalesce

index = torch.tensor([[1, 0, 1, 0, 2, 1],
                      [0, 1, 1, 1, 0, 0]])
rusty1s's avatar
rusty1s committed
147
value = torch.Tensor([[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7]])
rusty1s's avatar
docs  
rusty1s committed
148

rusty1s's avatar
rusty1s committed
149
index, value = coalesce(index, value, m=3, n=2)
rusty1s's avatar
docs  
rusty1s committed
150
151
```

rusty1s's avatar
rusty1s committed
152
153
154
155
156
```
print(index)
tensor([[0, 1, 1, 2],
        [1, 0, 1, 0]])
print(value)
rusty1s's avatar
rusty1s committed
157
158
159
160
tensor([[6.0, 8.0],
        [7.0, 9.0],
        [3.0, 4.0],
        [5.0, 6.0]])
rusty1s's avatar
rusty1s committed
161
```
rusty1s's avatar
docs  
rusty1s committed
162

rusty1s's avatar
rusty1s committed
163
### Transpose
rusty1s's avatar
rusty1s committed
164

rusty1s's avatar
docs  
rusty1s committed
165
```
rusty1s's avatar
rusty1s committed
166
torch_sparse.transpose(index, value, m, n) -> (torch.LongTensor, torch.Tensor)
rusty1s's avatar
docs  
rusty1s committed
167
168
```

rusty1s's avatar
rusty1s committed
169
170
Transposes dimensions 0 and 1 of a sparse matrix.

rusty1s's avatar
rusty1s committed
171
#### Parameters
rusty1s's avatar
rusty1s committed
172
173
174

* **index** *(LongTensor)* - The index tensor of sparse matrix.
* **value** *(Tensor)* - The value tensor of sparse matrix.
175
176
* **m** *(int)* - The first dimension of corresponding dense matrix.
* **n** *(int)* - The second dimension of corresponding dense matrix.
rusty1s's avatar
typo  
rusty1s committed
177
* **coalesced** *(bool, optional)* - If set to `False`, will not coalesce the output. (default: `True`)
rusty1s's avatar
rusty1s committed
178

rusty1s's avatar
rusty1s committed
179
#### Returns
rusty1s's avatar
rusty1s committed
180

rusty1s's avatar
docs  
rusty1s committed
181
182
* **index** *(LongTensor)* - The transposed index tensor of sparse matrix.
* **value** *(Tensor)* - The transposed value tensor of sparse matrix.
rusty1s's avatar
rusty1s committed
183

rusty1s's avatar
rusty1s committed
184
#### Example
rusty1s's avatar
docs  
rusty1s committed
185
186

```python
ekka's avatar
ekka committed
187
import torch
rusty1s's avatar
rusty1s committed
188
189
190
191
from torch_sparse import transpose

index = torch.tensor([[1, 0, 1, 0, 2, 1],
                      [0, 1, 1, 1, 0, 0]])
rusty1s's avatar
rusty1s committed
192
value = torch.Tensor([[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7]])
rusty1s's avatar
docs  
rusty1s committed
193

rusty1s's avatar
docs  
rusty1s committed
194
index, value = transpose(index, value, 3, 2)
rusty1s's avatar
docs  
rusty1s committed
195
196
```

rusty1s's avatar
rusty1s committed
197
198
199
200
201
```
print(index)
tensor([[0, 0, 1, 1],
        [1, 2, 0, 1]])
print(value)
rusty1s's avatar
rusty1s committed
202
203
204
205
tensor([[7.0, 9.0],
        [5.0, 6.0],
        [6.0, 8.0],
        [3.0, 4.0]])
rusty1s's avatar
rusty1s committed
206
```
rusty1s's avatar
docs  
rusty1s committed
207

rusty1s's avatar
rusty1s committed
208
### Sparse Dense Matrix Multiplication
rusty1s's avatar
rusty1s committed
209

rusty1s's avatar
docs  
rusty1s committed
210
```
211
torch_sparse.spmm(index, value, m, n, matrix) -> torch.Tensor
rusty1s's avatar
docs  
rusty1s committed
212
213
```

rusty1s's avatar
rusty1s committed
214
215
Matrix product of a sparse matrix with a dense matrix.

rusty1s's avatar
rusty1s committed
216
#### Parameters
rusty1s's avatar
docs  
rusty1s committed
217

rusty1s's avatar
rusty1s committed
218
219
* **index** *(LongTensor)* - The index tensor of sparse matrix.
* **value** *(Tensor)* - The value tensor of sparse matrix.
220
221
* **m** *(int)* - The first dimension of corresponding dense matrix.
* **n** *(int)* - The second dimension of corresponding dense matrix.
rusty1s's avatar
docs  
rusty1s committed
222
* **matrix** *(Tensor)* - The dense matrix.
rusty1s's avatar
rusty1s committed
223

rusty1s's avatar
rusty1s committed
224
#### Returns
rusty1s's avatar
rusty1s committed
225

rusty1s's avatar
docs  
rusty1s committed
226
* **out** *(Tensor)* - The dense output matrix.
rusty1s's avatar
rusty1s committed
227

rusty1s's avatar
rusty1s committed
228
#### Example
rusty1s's avatar
rusty1s committed
229
230

```python
ekka's avatar
ekka committed
231
import torch
rusty1s's avatar
rusty1s committed
232
233
234
235
from torch_sparse import spmm

index = torch.tensor([[0, 0, 1, 2, 2],
                      [0, 2, 1, 0, 1]])
rusty1s's avatar
rusty1s committed
236
237
value = torch.Tensor([1, 2, 4, 1, 3])
matrix = torch.Tensor([[1, 4], [2, 5], [3, 6]])
rusty1s's avatar
rusty1s committed
238

rusty1s's avatar
rusty1s committed
239
out = spmm(index, value, 3, 3, matrix)
rusty1s's avatar
rusty1s committed
240
241
242
243
```

```
print(out)
244
245
246
tensor([[7.0, 16.0],
        [8.0, 20.0],
        [7.0, 19.0]])
rusty1s's avatar
docs  
rusty1s committed
247
```
rusty1s's avatar
rusty1s committed
248

rusty1s's avatar
rusty1s committed
249
### Sparse Sparse Matrix Multiplication
rusty1s's avatar
rusty1s committed
250
251
252
253
254
255

```
torch_sparse.spspmm(indexA, valueA, indexB, valueB, m, k, n) -> (torch.LongTensor, torch.Tensor)
```

Matrix product of two sparse tensors.
rusty1s's avatar
typo  
rusty1s committed
256
Both input sparse matrices need to be **coalesced** (use the `coalesced` attribute to force).
rusty1s's avatar
rusty1s committed
257

rusty1s's avatar
rusty1s committed
258
#### Parameters
rusty1s's avatar
rusty1s committed
259
260
261
262
263

* **indexA** *(LongTensor)* - The index tensor of first sparse matrix.
* **valueA** *(Tensor)* - The value tensor of first sparse matrix.
* **indexB** *(LongTensor)* - The index tensor of second sparse matrix.
* **valueB** *(Tensor)* - The value tensor of second sparse matrix.
264
265
266
* **m** *(int)* - The first dimension of first corresponding dense matrix.
* **k** *(int)* - The second dimension of first corresponding dense matrix and first dimension of second corresponding dense matrix.
* **n** *(int)* - The second dimension of second corresponding dense matrix.
rusty1s's avatar
typo  
rusty1s committed
267
* **coalesced** *(bool, optional)*: If set to `True`, will coalesce both input sparse matrices. (default: `False`)
rusty1s's avatar
rusty1s committed
268

rusty1s's avatar
rusty1s committed
269
#### Returns
rusty1s's avatar
rusty1s committed
270

rusty1s's avatar
docs  
rusty1s committed
271
272
* **index** *(LongTensor)* - The output index tensor of sparse matrix.
* **value** *(Tensor)* - The output value tensor of sparse matrix.
rusty1s's avatar
rusty1s committed
273

rusty1s's avatar
rusty1s committed
274
#### Example
rusty1s's avatar
rusty1s committed
275
276

```python
ekka's avatar
ekka committed
277
import torch
rusty1s's avatar
docs  
rusty1s committed
278
279
from torch_sparse import spspmm

rusty1s's avatar
rusty1s committed
280
indexA = torch.tensor([[0, 0, 1, 2, 2], [1, 2, 0, 0, 1]])
rusty1s's avatar
rusty1s committed
281
valueA = torch.Tensor([1, 2, 3, 4, 5])
rusty1s's avatar
rusty1s committed
282
283

indexB = torch.tensor([[0, 2], [1, 0]])
rusty1s's avatar
rusty1s committed
284
valueB = torch.Tensor([2, 4])
rusty1s's avatar
docs  
rusty1s committed
285

rusty1s's avatar
rusty1s committed
286
287
288
289
indexC, valueC = spspmm(indexA, valueA, indexB, valueB, 3, 3, 2)
```

```
ekka's avatar
ekka committed
290
print(indexC)
rusty1s's avatar
rusty1s committed
291
292
tensor([[0, 1, 2],
        [0, 1, 1]])
ekka's avatar
ekka committed
293
print(valueC)
294
tensor([8.0, 6.0, 8.0])
rusty1s's avatar
docs  
rusty1s committed
295
296
```

rusty1s's avatar
rusty1s committed
297
298
299
300
301
## Running tests

```
python setup.py test
```