README.md 8.77 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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

#### 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
52
To install the binaries for PyTorch 1.4.0, simply run
rusty1s's avatar
rusty1s committed
53
54

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

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
64
| **macOS**   | ✅    |        |         |         |
rusty1s's avatar
rusty1s committed
65
66
67

### From source

rusty1s's avatar
rusty1s committed
68
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
69
70

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

rusty1s's avatar
rusty1s committed
74
$ echo $PATH
rusty1s's avatar
rusty1s committed
75
>>> /usr/local/cuda/bin:...
rusty1s's avatar
rusty1s committed
76
77

$ echo $CPATH
rusty1s's avatar
rusty1s committed
78
>>> /usr/local/cuda/include:...
rusty1s's avatar
rusty1s committed
79
80
```

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

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

```
rusty1s's avatar
rusty1s committed
88
pip install torch-scatter torch-sparse
rusty1s's avatar
rusty1s committed
89
90
```

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

```
rusty1s's avatar
rusty1s committed
95
export TORCH_CUDA_ARCH_LIST="6.0 6.1 7.2+PTX 7.5+PTX"
rusty1s's avatar
rusty1s committed
96
97
98
```

## Functions
rusty1s's avatar
links  
rusty1s committed
99

rusty1s's avatar
rusty1s committed
100
### Coalesce
rusty1s's avatar
rusty1s committed
101

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

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

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

rusty1s's avatar
rusty1s committed
118
#### Returns
rusty1s's avatar
rusty1s committed
119

rusty1s's avatar
docs  
rusty1s committed
120
121
* **index** *(LongTensor)* - The coalesced index tensor of sparse matrix.
* **value** *(Tensor)* - The coalesced value tensor of sparse matrix.
rusty1s's avatar
rusty1s committed
122

rusty1s's avatar
rusty1s committed
123
#### Example
rusty1s's avatar
docs  
rusty1s committed
124
125

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

rusty1s's avatar
rusty1s committed
133
index, value = coalesce(index, value, m=3, n=2)
rusty1s's avatar
docs  
rusty1s committed
134
135
```

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

rusty1s's avatar
rusty1s committed
147
### Transpose
rusty1s's avatar
rusty1s committed
148

rusty1s's avatar
docs  
rusty1s committed
149
```
rusty1s's avatar
rusty1s committed
150
torch_sparse.transpose(index, value, m, n) -> (torch.LongTensor, torch.Tensor)
rusty1s's avatar
docs  
rusty1s committed
151
152
```

rusty1s's avatar
rusty1s committed
153
154
Transposes dimensions 0 and 1 of a sparse matrix.

rusty1s's avatar
rusty1s committed
155
#### Parameters
rusty1s's avatar
rusty1s committed
156
157
158

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

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

rusty1s's avatar
docs  
rusty1s committed
165
166
* **index** *(LongTensor)* - The transposed index tensor of sparse matrix.
* **value** *(Tensor)* - The transposed value tensor of sparse matrix.
rusty1s's avatar
rusty1s committed
167

rusty1s's avatar
rusty1s committed
168
#### Example
rusty1s's avatar
docs  
rusty1s committed
169
170

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

rusty1s's avatar
docs  
rusty1s committed
178
index, value = transpose(index, value, 3, 2)
rusty1s's avatar
docs  
rusty1s committed
179
180
```

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

rusty1s's avatar
rusty1s committed
192
### Sparse Dense Matrix Multiplication
rusty1s's avatar
rusty1s committed
193

rusty1s's avatar
docs  
rusty1s committed
194
```
195
torch_sparse.spmm(index, value, m, n, matrix) -> torch.Tensor
rusty1s's avatar
docs  
rusty1s committed
196
197
```

rusty1s's avatar
rusty1s committed
198
199
Matrix product of a sparse matrix with a dense matrix.

rusty1s's avatar
rusty1s committed
200
#### Parameters
rusty1s's avatar
docs  
rusty1s committed
201

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

rusty1s's avatar
rusty1s committed
208
#### Returns
rusty1s's avatar
rusty1s committed
209

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

rusty1s's avatar
rusty1s committed
212
#### Example
rusty1s's avatar
rusty1s committed
213
214

```python
ekka's avatar
ekka committed
215
import torch
rusty1s's avatar
rusty1s committed
216
217
218
219
from torch_sparse import spmm

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

rusty1s's avatar
rusty1s committed
223
out = spmm(index, value, 3, 3, matrix)
rusty1s's avatar
rusty1s committed
224
225
226
227
```

```
print(out)
228
229
230
tensor([[7.0, 16.0],
        [8.0, 20.0],
        [7.0, 19.0]])
rusty1s's avatar
docs  
rusty1s committed
231
```
rusty1s's avatar
rusty1s committed
232

rusty1s's avatar
rusty1s committed
233
### Sparse Sparse Matrix Multiplication
rusty1s's avatar
rusty1s committed
234
235
236
237
238
239

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

rusty1s's avatar
rusty1s committed
242
#### Parameters
rusty1s's avatar
rusty1s committed
243
244
245
246
247

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

rusty1s's avatar
rusty1s committed
253
#### Returns
rusty1s's avatar
rusty1s committed
254

rusty1s's avatar
docs  
rusty1s committed
255
256
* **index** *(LongTensor)* - The output index tensor of sparse matrix.
* **value** *(Tensor)* - The output value tensor of sparse matrix.
rusty1s's avatar
rusty1s committed
257

rusty1s's avatar
rusty1s committed
258
#### Example
rusty1s's avatar
rusty1s committed
259
260

```python
ekka's avatar
ekka committed
261
import torch
rusty1s's avatar
docs  
rusty1s committed
262
263
from torch_sparse import spspmm

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

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

rusty1s's avatar
rusty1s committed
270
271
272
273
indexC, valueC = spspmm(indexA, valueA, indexB, valueB, 3, 3, 2)
```

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

rusty1s's avatar
rusty1s committed
281
282
283
284
285
## Running tests

```
python setup.py test
```