torchhd-sparsr makes Sparsr a PyTorch device. Importing it registers
"sparsr" the way CUDA registers "cuda", so standard
Torchhd code runs on
Sparsr with .to("sparsr") and nothing else changes:
import torch
import torchhd
import torchhd_sparsr # registers the "sparsr" device
a = torchhd.random(1, 4096, vsa="BSC", sparsity=0.998).squeeze().to("sparsr")
b = torchhd.random(1, 4096, vsa="BSC", sparsity=0.998).squeeze().to("sparsr")
bound = torchhd.bind(a, b) # one wide XOR on the device
similarity = torchhd.cosine_similarity(a, b) # a wide AND on the device, counted on the host
The Sparsr VM. The kernels underneath are RV32I, and
the VM runs RV32I. The package sets SPARSR_BACKEND=vm when it is
imported, if the variable is unset, so you set nothing. An explicit
SPARSR_BACKEND of your own still wins, which is how you point it at other
hardware.
pip install torchhd-sparsr
It is on PyPI as a wheel for Linux x86-64 and CPython 3.10 to 3.13. The runtime and the HDC library are bundled inside the wheel, so there is nothing else to install. It needs no hardware: it runs on the Sparsr VM, which ships in the same wheel.
Each release is built against one version of PyTorch and requires exactly that
version, the same way torchvision does, because the extension uses libtorch's C++
ABI. pip resolves it for you; if you already have a different torch installed,
pip tells you which version this package needs.
Every operation is a call into the HDC library,
which is bundled inside the package together with the runtime. The algorithms
and the device kernels are in the library. What is in this package is the
PyTorch side: registering the device, converting tensors, dispatching. So the
two can never disagree about what bind means, because there is one bind.
| Torchhd call | In the HDC library |
|---|---|
torchhd.bind() |
hdc_bind(), one wide XOR |
torchhd.bundle() |
hdc_bundle_majority() over the two operands and the tiebreak |
dot_similarity(), cosine_similarity() |
hdc_similarity(), one wide AND, then a count |
| Torchhd call | On Sparsr |
|---|---|
torchhd.bind() |
Yes. One wide XOR. |
torchhd.dot_similarity(), torchhd.cosine_similarity() |
Partly. The intersection is a wide AND on the device, and the host counts its bits. Single pairs only. |
torchhd.bundle() |
Refuses with RuntimeError for every real pair. Torchhd's BSC bundle needs a fair-coin tiebreak vector, which is dense across all 4,096 bits, and a dense full-width vector does not fit on the device (see below). |
torchhd.permute() |
Refuses with NotImplementedError. It needs a wide rotate, which Sparsr does not have yet. |
torchhd.multiset(), torchhd.multibundle() |
Refuses. The "sparsr" device holds single hypervectors, and these take a batch. |
Anything else on a "sparsr" tensor, arithmetic and printing included, is not
implemented. Move the tensor back with .to("cpu") first.
The device stores a hypervector through a compression codec that counts
non-zero 32-bit lanes, and a stored row holds 48 of the 128 lanes in a
4,096-bit register. Moving a tensor to "sparsr" checks this and raises a clear
RuntimeError when it does not fit, instead of letting data corrupt quietly.
torchhd.random(..., vsa="BSC", sparsity=0.998). That is the sparse
distributed representation the device is built for, and it is what the
package's own tests use.So a dense full-width 4,096-bit BSC hypervector does not fit, and that is the
whole reason bundle() refuses.