README.md 8.96 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
#### PyTorch 1.7.0
rusty1s's avatar
rusty1s committed
35

rusty1s's avatar
rusty1s committed
36
To install the binaries for PyTorch 1.7.0, simply run
rusty1s's avatar
rusty1s committed
37
38

```
rusty1s's avatar
rusty1s committed
39
pip install torch-scatter==latest+${CUDA} torch-sparse==latest+${CUDA} -f https://pytorch-geometric.com/whl/torch-1.7.0.html
rusty1s's avatar
rusty1s committed
40
41
```

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

rusty1s's avatar
rusty1s committed
44
45
46
47
48
|             | `cpu` | `cu92` | `cu101` | `cu102` | `cu110` |
|-------------|-------|--------|---------|---------|---------|
| **Linux**   | ✅    | ✅     | ✅      | ✅      | ✅      |
| **Windows** | ✅    | ❌     | ✅      | ✅      | ✅      |
| **macOS**   | ✅    |        |         |         |         |
rusty1s's avatar
rusty1s committed
49

rusty1s's avatar
rusty1s committed
50

rusty1s's avatar
rusty1s committed
51
52
53
#### PyTorch 1.6.0

To install the binaries for PyTorch 1.6.0, simply run
rusty1s's avatar
rusty1s committed
54
55

```
rusty1s's avatar
rusty1s committed
56
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
57
58
59
60
61
62
63
64
65
66
```

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
67
**Note:** Binaries of older versions are also provided for PyTorch 1.4.0 and PyTorch 1.5.0 (following the same procedure).
rusty1s's avatar
rusty1s committed
68
69
70

### From source

rusty1s's avatar
rusty1s committed
71
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
72
73

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

rusty1s's avatar
rusty1s committed
77
$ echo $PATH
rusty1s's avatar
rusty1s committed
78
>>> /usr/local/cuda/bin:...
rusty1s's avatar
rusty1s committed
79
80

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

rusty1s's avatar
rusty1s committed
84
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
85
Note that METIS needs to be installed with 64 bit `IDXTYPEWIDTH` by changing `include/metis.h`.
rusty1s's avatar
rusty1s committed
86
Afterwards, set the environment variable `WITH_METIS=1`.
rusty1s's avatar
rusty1s committed
87

rusty1s's avatar
rusty1s committed
88
89
90
Then run:

```
rusty1s's avatar
rusty1s committed
91
pip install torch-scatter torch-sparse
rusty1s's avatar
rusty1s committed
92
93
```

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

```
rusty1s's avatar
rusty1s committed
98
export TORCH_CUDA_ARCH_LIST="6.0 6.1 7.2+PTX 7.5+PTX"
rusty1s's avatar
rusty1s committed
99
100
101
```

## Functions
rusty1s's avatar
links  
rusty1s committed
102

rusty1s's avatar
rusty1s committed
103
### Coalesce
rusty1s's avatar
rusty1s committed
104

rusty1s's avatar
docs  
rusty1s committed
105
```
rusty1s's avatar
rusty1s committed
106
torch_sparse.coalesce(index, value, m, n, op="add") -> (torch.LongTensor, torch.Tensor)
rusty1s's avatar
docs  
rusty1s committed
107
108
```

109
Row-wise sorts `index` and removes duplicate entries.
rusty1s's avatar
rusty1s committed
110
111
112
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
113
#### Parameters
rusty1s's avatar
rusty1s committed
114
115
116

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

rusty1s's avatar
rusty1s committed
121
#### Returns
rusty1s's avatar
rusty1s committed
122

rusty1s's avatar
docs  
rusty1s committed
123
124
* **index** *(LongTensor)* - The coalesced index tensor of sparse matrix.
* **value** *(Tensor)* - The coalesced value tensor of sparse matrix.
rusty1s's avatar
rusty1s committed
125

rusty1s's avatar
rusty1s committed
126
#### Example
rusty1s's avatar
docs  
rusty1s committed
127
128

```python
ekka's avatar
ekka committed
129
import torch
rusty1s's avatar
rusty1s committed
130
131
132
133
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
134
value = torch.Tensor([[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7]])
rusty1s's avatar
docs  
rusty1s committed
135

rusty1s's avatar
rusty1s committed
136
index, value = coalesce(index, value, m=3, n=2)
rusty1s's avatar
docs  
rusty1s committed
137
138
```

rusty1s's avatar
rusty1s committed
139
140
141
142
143
```
print(index)
tensor([[0, 1, 1, 2],
        [1, 0, 1, 0]])
print(value)
rusty1s's avatar
rusty1s committed
144
145
146
147
tensor([[6.0, 8.0],
        [7.0, 9.0],
        [3.0, 4.0],
        [5.0, 6.0]])
rusty1s's avatar
rusty1s committed
148
```
rusty1s's avatar
docs  
rusty1s committed
149

rusty1s's avatar
rusty1s committed
150
### Transpose
rusty1s's avatar
rusty1s committed
151

rusty1s's avatar
docs  
rusty1s committed
152
```
rusty1s's avatar
rusty1s committed
153
torch_sparse.transpose(index, value, m, n) -> (torch.LongTensor, torch.Tensor)
rusty1s's avatar
docs  
rusty1s committed
154
155
```

rusty1s's avatar
rusty1s committed
156
157
Transposes dimensions 0 and 1 of a sparse matrix.

rusty1s's avatar
rusty1s committed
158
#### Parameters
rusty1s's avatar
rusty1s committed
159
160
161

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

rusty1s's avatar
rusty1s committed
166
#### Returns
rusty1s's avatar
rusty1s committed
167

rusty1s's avatar
docs  
rusty1s committed
168
169
* **index** *(LongTensor)* - The transposed index tensor of sparse matrix.
* **value** *(Tensor)* - The transposed value tensor of sparse matrix.
rusty1s's avatar
rusty1s committed
170

rusty1s's avatar
rusty1s committed
171
#### Example
rusty1s's avatar
docs  
rusty1s committed
172
173

```python
ekka's avatar
ekka committed
174
import torch
rusty1s's avatar
rusty1s committed
175
176
177
178
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
179
value = torch.Tensor([[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7]])
rusty1s's avatar
docs  
rusty1s committed
180

rusty1s's avatar
docs  
rusty1s committed
181
index, value = transpose(index, value, 3, 2)
rusty1s's avatar
docs  
rusty1s committed
182
183
```

rusty1s's avatar
rusty1s committed
184
185
186
187
188
```
print(index)
tensor([[0, 0, 1, 1],
        [1, 2, 0, 1]])
print(value)
rusty1s's avatar
rusty1s committed
189
190
191
192
tensor([[7.0, 9.0],
        [5.0, 6.0],
        [6.0, 8.0],
        [3.0, 4.0]])
rusty1s's avatar
rusty1s committed
193
```
rusty1s's avatar
docs  
rusty1s committed
194

rusty1s's avatar
rusty1s committed
195
### Sparse Dense Matrix Multiplication
rusty1s's avatar
rusty1s committed
196

rusty1s's avatar
docs  
rusty1s committed
197
```
198
torch_sparse.spmm(index, value, m, n, matrix) -> torch.Tensor
rusty1s's avatar
docs  
rusty1s committed
199
200
```

rusty1s's avatar
rusty1s committed
201
202
Matrix product of a sparse matrix with a dense matrix.

rusty1s's avatar
rusty1s committed
203
#### Parameters
rusty1s's avatar
docs  
rusty1s committed
204

rusty1s's avatar
rusty1s committed
205
206
* **index** *(LongTensor)* - The index tensor of sparse matrix.
* **value** *(Tensor)* - The value tensor of sparse matrix.
207
208
* **m** *(int)* - The first dimension of corresponding dense matrix.
* **n** *(int)* - The second dimension of corresponding dense matrix.
rusty1s's avatar
docs  
rusty1s committed
209
* **matrix** *(Tensor)* - The dense matrix.
rusty1s's avatar
rusty1s committed
210

rusty1s's avatar
rusty1s committed
211
#### Returns
rusty1s's avatar
rusty1s committed
212

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

rusty1s's avatar
rusty1s committed
215
#### Example
rusty1s's avatar
rusty1s committed
216
217

```python
ekka's avatar
ekka committed
218
import torch
rusty1s's avatar
rusty1s committed
219
220
221
222
from torch_sparse import spmm

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

rusty1s's avatar
rusty1s committed
226
out = spmm(index, value, 3, 3, matrix)
rusty1s's avatar
rusty1s committed
227
228
229
230
```

```
print(out)
231
232
233
tensor([[7.0, 16.0],
        [8.0, 20.0],
        [7.0, 19.0]])
rusty1s's avatar
docs  
rusty1s committed
234
```
rusty1s's avatar
rusty1s committed
235

rusty1s's avatar
rusty1s committed
236
### Sparse Sparse Matrix Multiplication
rusty1s's avatar
rusty1s committed
237
238
239
240
241
242

```
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
243
Both input sparse matrices need to be **coalesced** (use the `coalesced` attribute to force).
rusty1s's avatar
rusty1s committed
244

rusty1s's avatar
rusty1s committed
245
#### Parameters
rusty1s's avatar
rusty1s committed
246
247
248
249
250

* **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.
251
252
253
* **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
254
* **coalesced** *(bool, optional)*: If set to `True`, will coalesce both input sparse matrices. (default: `False`)
rusty1s's avatar
rusty1s committed
255

rusty1s's avatar
rusty1s committed
256
#### Returns
rusty1s's avatar
rusty1s committed
257

rusty1s's avatar
docs  
rusty1s committed
258
259
* **index** *(LongTensor)* - The output index tensor of sparse matrix.
* **value** *(Tensor)* - The output value tensor of sparse matrix.
rusty1s's avatar
rusty1s committed
260

rusty1s's avatar
rusty1s committed
261
#### Example
rusty1s's avatar
rusty1s committed
262
263

```python
ekka's avatar
ekka committed
264
import torch
rusty1s's avatar
docs  
rusty1s committed
265
266
from torch_sparse import spspmm

rusty1s's avatar
rusty1s committed
267
indexA = torch.tensor([[0, 0, 1, 2, 2], [1, 2, 0, 0, 1]])
rusty1s's avatar
rusty1s committed
268
valueA = torch.Tensor([1, 2, 3, 4, 5])
rusty1s's avatar
rusty1s committed
269
270

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

rusty1s's avatar
rusty1s committed
273
274
275
276
indexC, valueC = spspmm(indexA, valueA, indexB, valueB, 3, 3, 2)
```

```
ekka's avatar
ekka committed
277
print(indexC)
rusty1s's avatar
rusty1s committed
278
279
tensor([[0, 1, 2],
        [0, 1, 1]])
ekka's avatar
ekka committed
280
print(valueC)
281
tensor([8.0, 6.0, 8.0])
rusty1s's avatar
docs  
rusty1s committed
282
283
```

rusty1s's avatar
rusty1s committed
284
285
286
287
288
## Running tests

```
python setup.py test
```