Commit cadfd303 authored by rusty1s's avatar rusty1s
Browse files

running tests, bugfixes

parent 3fc02192
[report]
exclude_lines =
pragma: no cover
def backward
cuda
#include <torch/torch.h>
#define CHECK_CUDA(x) AT_ASSERT(x.type().is_cuda(), #x " must be a CUDA tensor")
#define CHECK_CUDA(x) AT_ASSERTM(x.type().is_cuda(), #x " must be CUDA tensor")
std::tuple<at::Tensor, at::Tensor> spspmm_cuda(at::Tensor A, at::Tensor B);
std::tuple<at::Tensor, at::Tensor>
spspmm_cuda(at::Tensor indexA, at::Tensor valueA, at::Tensor indexB,
at::Tensor valueB, int m, int k, int n);
std::tuple<at::Tensor, at::Tensor> spspmm(at::Tensor A, at::Tensor B) {
CHECK_CUDA(A);
CHECK_CUDA(B);
return spspmm_cuda(A, B);
std::tuple<at::Tensor, at::Tensor> spspmm(at::Tensor indexA, at::Tensor valueA,
at::Tensor indexB, at::Tensor valueB,
int m, int k, int n) {
CHECK_CUDA(indexA);
CHECK_CUDA(valueA);
CHECK_CUDA(indexB);
CHECK_CUDA(valueB);
return spspmm_cuda(indexA, valueA, indexB, valueB, m, k, n);
}
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
......
......@@ -27,28 +27,27 @@ static void init_cusparse() {
}
}
std::tuple<at::Tensor, at::Tensor> spspmm_cuda(at::Tensor A, at::Tensor B) {
std::tuple<at::Tensor, at::Tensor>
spspmm_cuda(at::Tensor indexA, at::Tensor valueA, at::Tensor indexB,
at::Tensor valueB, int m, int k, int n) {
init_cusparse();
auto m = A.size(0);
auto k = A.size(1);
auto n = B.size(1);
auto nnzA = valueA.size(0);
auto nnzB = valueB.size(0);
auto nnzA = A._nnz();
auto nnzB = B._nnz();
indexA = indexA.toType(at::kInt);
indexB = indexB.toType(at::kInt);
auto valueA = A._values();
auto indexA = A._indices().toType(at::kInt);
auto row_ptrA = at::empty(indexA.type(), {m + 1});
// Convert A to CSR format.
auto row_ptrA = at::empty(m + 1, indexA.type());
cusparseXcoo2csr(cusparse_handle, indexA[0].data<int>(), nnzA, k,
row_ptrA.data<int>(), CUSPARSE_INDEX_BASE_ZERO);
auto colA = indexA[1];
cudaMemcpy(row_ptrA.data<int>() + m, &nnzA, sizeof(int),
cudaMemcpyHostToDevice);
auto valueB = B._values();
auto indexB = B._indices().toType(at::kInt);
auto row_ptrB = at::empty(indexB.type(), {k + 1});
// Convert B to CSR format.
auto row_ptrB = at::empty(k + 1, indexB.type());
cusparseXcoo2csr(cusparse_handle, indexB[0].data<int>(), nnzB, k,
row_ptrB.data<int>(), CUSPARSE_INDEX_BASE_ZERO);
auto colB = indexB[1];
......@@ -61,14 +60,14 @@ std::tuple<at::Tensor, at::Tensor> spspmm_cuda(at::Tensor A, at::Tensor B) {
cusparseSetMatIndexBase(descr, CUSPARSE_INDEX_BASE_ZERO);
int nnzC;
auto row_ptrC = at::empty(indexA.type(), {m + 1});
auto row_ptrC = at::empty(m + 1, indexB.type());
cusparseXcsrgemmNnz(cusparse_handle, CUSPARSE_OPERATION_NON_TRANSPOSE,
CUSPARSE_OPERATION_NON_TRANSPOSE, m, n, k, descr, nnzA,
row_ptrA.data<int>(), colA.data<int>(), descr, nnzB,
row_ptrB.data<int>(), colB.data<int>(), descr,
row_ptrC.data<int>(), &nnzC);
auto colC = at::empty(indexA.type(), {nnzC});
auto valueC = at::empty(valueA.type(), {nnzC});
auto colC = at::empty(nnzC, indexA.type());
auto valueC = at::empty(nnzC, valueA.type());
CSRGEMM(valueC.type(), cusparse_handle, CUSPARSE_OPERATION_NON_TRANSPOSE,
CUSPARSE_OPERATION_NON_TRANSPOSE, m, n, k, descr, nnzA,
......@@ -77,7 +76,7 @@ std::tuple<at::Tensor, at::Tensor> spspmm_cuda(at::Tensor A, at::Tensor B) {
colB.data<int>(), descr, valueC.data<scalar_t>(),
row_ptrC.data<int>(), colC.data<int>());
auto rowC = at::empty(indexA.type(), {nnzC});
auto rowC = at::empty(nnzC, indexA.type());
cusparseXcsr2coo(cusparse_handle, row_ptrC.data<int>(), nnzC, m,
rowC.data<int>(), CUSPARSE_INDEX_BASE_ZERO);
......
......@@ -8,6 +8,6 @@ def test_coalesce():
index = torch.stack([row, col], dim=0)
value = torch.tensor([[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7]])
index, value = coalesce(index, value, torch.Size([4, 2]))
index, value = coalesce(index, value, m=3, n=2)
assert index.tolist() == [[0, 1, 1, 2], [1, 0, 1, 0]]
assert value.tolist() == [[6, 8], [7, 9], [3, 4], [5, 6]]
from itertools import product
import pytest
import torch
from torch_sparse import sparse_coo_tensor, spspmm, to_value
from .utils import dtypes, devices, tensor
tests = [{
'name': 'Test coalesced input',
'indexA': [[0, 0, 1, 2, 2], [1, 2, 0, 0, 1]],
'valueA': [1, 2, 3, 4, 5],
'sizeA': [3, 3],
'indexB': [[0, 2], [1, 0]],
'valueB': [2, 4],
'sizeB': [3, 2],
}, {
'name': 'Test uncoalesced input',
'indexA': [[2, 2, 1, 0, 2, 0], [1, 1, 0, 2, 0, 1]],
'valueA': [3, 2, 3, 2, 4, 1],
'sizeA': [3, 3],
'indexB': [[2, 0, 2], [0, 1, 0]],
'valueB': [2, 2, 2],
'sizeB': [3, 2],
}]
@pytest.mark.parametrize('test,dtype,device', product(tests, dtypes, devices))
def test_spspmm(test, dtype, device):
indexA = torch.tensor(test['indexA'], device=device)
valueA = tensor(test['valueA'], dtype, device, requires_grad=True)
sizeA = torch.Size(test['sizeA'])
A = sparse_coo_tensor(indexA, valueA, sizeA)
denseA = A.detach().to_dense().requires_grad_()
indexB = torch.tensor(test['indexB'], device=device)
valueB = tensor(test['valueB'], dtype, device, requires_grad=True)
sizeB = torch.Size(test['sizeB'])
B = sparse_coo_tensor(indexB, valueB, sizeB)
denseB = B.detach().to_dense().requires_grad_()
C = spspmm(A, B)
denseC = torch.matmul(denseA, denseB)
assert C.detach().to_dense().tolist() == denseC.tolist()
to_value(C).sum().backward()
denseC.sum().backward()
assert valueA.grad.tolist() == denseA.grad[indexA[0], indexA[1]].tolist()
# from itertools import product
# import pytest
# import torch
# from torch_sparse import sparse_coo_tensor, spspmm, to_value
# from .utils import dtypes, devices, tensor
# tests = [{
# 'name': 'Test coalesced input',
# 'indexA': [[0, 0, 1, 2, 2], [1, 2, 0, 0, 1]],
# 'valueA': [1, 2, 3, 4, 5],
# 'sizeA': [3, 3],
# 'indexB': [[0, 2], [1, 0]],
# 'valueB': [2, 4],
# 'sizeB': [3, 2],
# }, {
# 'name': 'Test uncoalesced input',
# 'indexA': [[2, 2, 1, 0, 2, 0], [1, 1, 0, 2, 0, 1]],
# 'valueA': [3, 2, 3, 2, 4, 1],
# 'sizeA': [3, 3],
# 'indexB': [[2, 0, 2], [0, 1, 0]],
# 'valueB': [2, 2, 2],
# 'sizeB': [3, 2],
# }]
# @pytest.mark.parametrize('test,dtype,device', product(tests, dtypes, devices))
# def test_spspmm(test, dtype, device):
# indexA = torch.tensor(test['indexA'], device=device)
# valueA = tensor(test['valueA'], dtype, device, requires_grad=True)
# sizeA = torch.Size(test['sizeA'])
# A = sparse_coo_tensor(indexA, valueA, sizeA)
# denseA = A.detach().to_dense().requires_grad_()
# indexB = torch.tensor(test['indexB'], device=device)
# valueB = tensor(test['valueB'], dtype, device, requires_grad=True)
# sizeB = torch.Size(test['sizeB'])
# B = sparse_coo_tensor(indexB, valueB, sizeB)
# denseB = B.detach().to_dense().requires_grad_()
# C = spspmm(A, B)
# denseC = torch.matmul(denseA, denseB)
# assert C.detach().to_dense().tolist() == denseC.tolist()
# to_value(C).sum().backward()
# denseC.sum().backward()
# assert valueA.grad.tolist() == denseA.grad[indexA[0], indexA[1]].tolist()
import torch
from torch_sparse import transpose
def test_transpose():
row = torch.tensor([1, 0, 1, 0, 2, 1])
col = torch.tensor([0, 1, 1, 1, 0, 0])
index = torch.stack([row, col], dim=0)
value = torch.tensor([[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7]])
index, value = transpose(index, value, m=3, n=2)
assert index.tolist() == [[0, 0, 1, 1], [1, 2, 0, 1]]
assert value.tolist() == [[7, 9], [5, 6], [6, 8], [3, 4]]
......@@ -3,10 +3,9 @@ import torch
dtypes = [torch.float, torch.double]
devices = [torch.device('cpu')]
if torch.cuda.is_available(): # pragma: no cover
if torch.cuda.is_available():
devices += [torch.device('cuda:{}'.format(torch.cuda.current_device()))]
def tensor(x, dtype, device, requires_grad=False):
return torch.tensor(
x, dtype=dtype, device=device, requires_grad=requires_grad)
def tensor(x, dtype, device):
return torch.tensor(x, dtype=dtype, device=device)
import torch
class SparseCooTensor(torch.autograd.Function):
"""Constructs Sparse matrix with autograd capabilities w.r.t. to value."""
@staticmethod
def forward(ctx, index, value, size):
ctx.size = size
ctx.save_for_backward(index)
return torch.sparse_coo_tensor(index, value, size, device=value.device)
@staticmethod
def backward(ctx, grad_out):
index = ctx.saved_variables[0]
grad_in = None
if ctx.needs_input_grad[1]:
value = grad_out._values()
id1 = index[0] * ctx.size[1] + index[1]
index = grad_out._indices()
id2 = index[0] * ctx.size[1] + index[1]
grad_in = value.new_zeros(id1.max().item() + 1)
grad_in[id2] = value
grad_in = grad_in[id1]
return None, grad_in, None
sparse_coo_tensor = SparseCooTensor.apply
class ToValue(torch.autograd.Function):
"""Extract values of sparse tensors with autograd support."""
@staticmethod
def forward(ctx, A):
ctx.save_for_backward(A)
return A._values()
@staticmethod
def backward(ctx, grad_out):
A = ctx.saved_variables[0]
grad_in = None
if ctx.needs_input_grad[0]:
grad_in = torch.sparse_coo_tensor(
A._indices(), grad_out, A.size(), device=grad_out.device)
return grad_in
to_value = ToValue.apply
......@@ -6,6 +6,6 @@ def transpose(index, value, m, n):
row, col = index
index = torch.stack([col, row], dim=0)
index, value = coalesce(index, value, m, n)
index, value = coalesce(index, value, n, m)
return index, value
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment