README.md 8.68 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
82
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.
Afterwards, set the environment variable `WITH_METIS=1`.
rusty1s's avatar
rusty1s committed
83

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

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

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

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

## Functions
rusty1s's avatar
links  
rusty1s committed
98

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

```
python setup.py test
```