SparseTensor#
- class iskra.sparse.SparseTensor(tensor: Tensor, *, layout: Literal['coo', 'csr'] = 'coo', dtype: dtype | None = None, device: device | None = None, requires_grad: bool = False)[SOURCE]#
Sane PyTorch sparse tensor.
A subclass of
torch.Tensorwith a sparse layout, eithertorch.sparse_cooortorch.sparse_csr. Unlike the default PyTorch sparse tensors, it offers many quality-of-life utilities which make working with sparse tensors a bareable experience in PyTorch, such as scalar/vector/matrix multiplications which just work and ensure your gradients remain sparse (which is somehow not the default in PyTorch), indexing, slicing, as well as small helpers here and there to patch missing functionality.Primarily, this class supports COO tensors; CSR tensor experience might be mixed.
Warning
Constructing
SparseTensorwith COO values will automaticall callcoalesce()for you, as this is required foralias()to work.Note
SparseTensoris not necessary to use the free-standing functions in this module. You should be able to use most free-standing functions with a normal tensor created viatorch.sparse_coo_tensor. You should be careful with things like multiplications in that case (@will create dense gradients even if the operands are both sparse matrices).Important
You almost always want to use
sp.coo_tensor()orsp.csr_tensor()to construct aSparseTensorobject.Builds upon and extends existing PyTorch sparse tensors, and offers a (somewhat) sane interface and defaults.
Implementation Details
Alright, I’ve spent a bunch of time digging through PyTorch’s subclassing mess, so I am sharing what I learned here, hoping it is useful in the future. So, our goal is to create a subclass of
torch.Tensorwhich has sparse storage under the hood (i.e.,torch.layout == torch.sparse_cooortorch.layout == torch.sparse_csr).PyTorch’s
_make_subclass()is not what we need because it terminates autograd history, meaning, e.g., it won’t propagate gradients to the values of a COO tensor if the values are autograd leafs. In essence,_make_subclass()makes a new leaf tensor with the same data as the input. This is not what we want!PyTorch’s
as_subclass()should be the fix, but fails on sparse tensors. Under the hood, it does a couple of things, see pytorch/pytorch First, it creates a view into the tensor, thus sharing memory but having a new tensor object. Second, it sets__class__to equal the subclass class. Lastly, it enables__torch_dispatch__on the subclass viaset_python_dispatch(), but only if the subclass has a custom__torch_dispatch__defined.Sadly, the PyTorch does not implement aliasing for sparse tensors, but we can simply implement a Python alternative; see
alias(). Likewiese, we settensor.__class__ = SparseTensormanually from Python. The only thing we cannot do from Python is__torch_dispatch__. However,__torch_dispatch__is only needed if you wish to override low-level tensor behavior, such as behavior during autograd or CUDA kernel calls. In our specific scenario, we are not overriding__torch_dispatch__, so we do not really need to worry aboutset_python_dispatch(). However, if we were, we would probably need to do somehting akin to this: albanD/subclass_zoo This implementation seems to be more complex, so we will only migrate when this is absolutely necessary.- classmethod from_coo(indices: Tensor | tuple[Tensor, ...] | list[Tensor], values: Tensor, size: Size | list[int] | tuple[int, ...] | None = None, *, dtype: dtype | None = None, device: device | None = None, requires_grad: bool = False, check_invariants: bool = False, is_coalesced: bool = True) SparseTensor[SOURCE]#
Constructs a COO
SparseTensorfrom indices and values.This method wraps
torch.sparse_coo_tensor()and does plumbing to convert it to aSparseTensor. The argument and return descriptions reuse the original torch descriptions. See that function for more details.See also
torch.sparse_coo_tensor(),coo_tensor().- Parameters:
indices (
Tensor[Int64, [Dim, nnz]] | tuple[Tensor[Int64, [nnz]], …] | list[Tensor[Int64, [nnz]]]) – Initial data for the tensor. Will be cast to a Tensor[Int64, [Dim, nzz]] internally. Ifindicesis a sequence of 1D tensors, we stack them into a index tensor. The indices are the coordinates of the non-zero values in the matrix, and thus should be two-dimensional where the first dimension is the number of tensor dimensions and the second dimension is the number of non-zero values. A sequence of per-dimension index tensors is also accepted and stacked along dimension 0.values (
Tensor[DType, [nnz]]) – Initial values for the tensor.size (list[int] | tuple[int, ...] | None) – Size of the sparse tensor. If not provided the size will be inferred as the minimum size big enough to hold all non-zero elements.
dtype (torch.dtype) – the desired data type of returned tensor. Default: if None, infers data type from
values.device (torch.device) – the desired device of returned tensor. Default: if None, uses the device of the input tensors.
requires_grad (bool) – If
True, the returnedSparseTensoris an autograd leaf. Default:False.check_invariants (bool) – If sparse tensor invariants are checked. Default:
False.is_coalesced (bool) – When
True, the caller is responsible for providing tensor indices that correspond to a coalesced tensor. If thecheck_invariantsflag is False, no error will be raised if the prerequisites are not met and this will lead to silently incorrect results. To force coalescion please usecoalesce()on the resulting Tensor. Default:True.
- Returns:
(
SparseTensor[Float, [*Bs, N, M, *Ds]]) – Sparse tensor in COO layout.
- classmethod from_csr(crow_indices: Tensor, col_indices: Tensor, values: Tensor, size: Size | list[int] | tuple[int, ...] | None = None, *, dtype: dtype | None = None, device: device | None = None, requires_grad: bool = False) SparseTensor[SOURCE]#
Constructs a CSR
SparseTensorfrom compressed-row data.This method wraps
torch.sparse_csr_tensor()and does plumbing to convert it to aSparseTensor. The argument and return descriptions reuse the original torch descriptions. See that function for more details.See also
torch.sparse_csr_tensor(),csr_tensor().- Parameters:
crow_indices (
Tensor[Int64, [*Bs, N + 1]]) – (B+1)-dimensional array of size(*batchsize, nrows + 1). The last element of each batch is the number of non-zeros. This tensor encodes the index in values and col_indices depending on where the given row starts. Each successive number in the tensor subtracted by the number before it denotes the number of elements in a given row.col_indices (
Tensor[Int64, [*Bs, nnz]]) – Column co-ordinates of each element in values. (B+1)-dimensional tensor with the same length as values.values (
Tensor[Float, [*Bs, nnz, *Ds]]) – Initial values for the tensor. Represents a (1+K)-dimensional tensor whereKis the number of dense dimensions.size (list[int] | tuple[int, ...] | None) – Size of the sparse tensor:
(*batchsize, nrows, ncols, *densesize). If not provided, the size will be inferred as the minimum size big enough to hold all non-zero elements.dtype (torch.dtype) – the desired data type of returned tensor. Default: if None, infers data type from
values.device (torch.device) – the desired device of returned tensor. Default: if None, uses the device of the input tensors.
requires_grad (bool) – If
True, the returnedSparseTensoris an autograd leaf. Default:False.
- Returns:
(
SparseTensor[Float, [*Bs, N, M, *Ds]]) – Sparse tensor in CSR layout.
- __matmul__(other: SparseTensor) SparseTensor[SOURCE]#
- __matmul__(other: Tensor) SparseTensor
Matrix multiplication which works with sparse tensors.
Allows the user to write
a @ bwith sparse tensors and still get the expected behavior (like sparse gradients to sparse tensors).Important
Wrapper around
matmul().- Parameters:
other (
SparseTensor|Tensor) – Left-multiply with this tensor.- Returns:
(
SparseTensor|Tensor) – Multiplied result tensor.
- __rmatmul__(other: SparseTensor) SparseTensor[SOURCE]#
- __rmatmul__(other: Tensor) SparseTensor
Matrix multiplication which works with sparse tensors.
Allows the user to write
a @ bwith sparse tensors and still get the expected behavior (like sparse gradients to sparse tensors).Important
Wrapper around
matmul().- Parameters:
other (
SparseTensor|Tensor) – Right-multiply with this tensor.- Returns:
(
SparseTensor|Tensor) – Multiplied result tensor.
- __mul__(other: SparseTensor) SparseTensor[SOURCE]#
- __mul__(other: Tensor) SparseTensor
- __mul__(other: Number) SparseTensor
Elementwise multiplication which works with sparse tensors.
Important
Wrapper around
mul().- Parameters:
other (
SparseTensor|Tensor) – Multiply with this tensor.- Returns:
(
SparseTensor|Tensor) – Multiplied result tensor.
- __rmul__(other: SparseTensor) SparseTensor[SOURCE]#
- __rmul__(other: Tensor) SparseTensor
- __rmul__(other: Number) SparseTensor
Elementwise multiplication which works with sparse tensors.
Important
Wrapper around
mul().- Parameters:
other (
SparseTensor|Tensor) – Multiply with this tensor.- Returns:
(
SparseTensor|Tensor) – Multiplied result tensor.
- reshape(*shape: int) SparseTensor[SOURCE]#
Reshapes the tensor into a specified shape.
Important
Wrapper around
reshape().- Parameters:
*shape (int) – Dimensions for the resulting tensor
- Returns:
(
SparseTensor) – Reshaped tensor with a view into the same data.
- scipy() sparray[SOURCE]#
Constructs a SciPy tensor with the same data (detaches autodiff graph).
Important
Wrapper around
to_scipy().- Returns:
(scipy.sparse.coo_array) – SciPy tensor with the same data.
- __getitem__(index: Any) SparseTensor[SOURCE]#
Slices the sparse tensor.
Important
Wrapper around
get_slice().- Parameters:
index (Any) – Slicing indices. See
get_slice()for more information.- Returns:
(
SparseTensor) – Sliced sparse tensor.
- square() SparseTensor[SOURCE]#
Elementwise square of the matrix.
Important
Wrapper around
iskra.sparse.square().- Returns:
(
SparseTensor) – Matrix with squared entries.