TensorLy: Backend-Agnostic Tensor Decomposition in Python
TensorLy: Tensor Learning in Python.
At a glance
- What is it?
- TensorLy is a Python library for tensor decomposition, tensor learning and tensor algebra that runs on NumPy, PyTorch, JAX, TensorFlow, CuPy or Paddle. The interesting part is not any single algorithm but the backend switch: the same decomposition call can execute on CPU NumPy or on a CUDA tensor through PyTorch.
- Who is it for?
- TensorLy fits teams that already have a numerical stack (NumPy, PyTorch, JAX, TensorFlow, CuPy or Paddle) and want decomposition routines without rewriting them per framework, and researchers who need CP, Tucker or TT factorizations with a consistent API.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 13 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem TensorLy solves: one tensor API across six numerical backends
Tensor decomposition code is usually written against one array library. A Tucker decomposition implemented with NumPy calls will not run on a GPU tensor without rewriting the array operations, and a PyTorch implementation cannot be reused by a colleague working in JAX. TensorLy addresses that by putting a backend layer between the user-facing functions and the array operations. The README states the goal directly: the backend system "allows to seamlessly perform computation with NumPy, PyTorch, JAX, TensorFlow, CuPy or Paddle, and run methods at scale on CPU or GPU." That sentence is the whole pitch, and it is also the constraint. You get portability across frameworks, but only for the operations TensorLy itself implements. The intended audience is people doing tensor factorization work (CP, Tucker, TT and the other methods listed in the decomposition module) who want to move between hardware and frameworks without maintaining parallel implementations.
How the backend switch works in practice
The mechanism is a global backend setting rather than a per-call argument. The README shows the pattern: call tl.set_backend('pytorch'), then create a tensor with tl.tensor(np.arange(24).reshape((3, 4, 2)), device='cuda:0'), and the resulting object has type torch.Tensor. Every subsequent TensorLy operation dispatches to PyTorch functions until the backend is changed again. The valid strings named in the README are 'numpy', 'tensorflow', 'cupy' and 'jax', with PyTorch shown as the example and Paddle mentioned in the prose. Because the backend is global state, the data flow is: user calls a decomposition, the decomposition calls backend-agnostic primitives, those primitives resolve to the currently selected framework, and the result comes back as a native tensor of that framework. The consequence worth noting is that backend selection is process-wide, so code that mixes two frameworks in the same process needs to set the backend before each block of TensorLy calls rather than relying on per-function dispatch.
Getting it installed and running a first decomposition
Installation is one command. The README gives pip install -U tensorly as the recommended route and conda install -c tensorly tensorly as the alternative. For development, it documents git clone https://github.com/tensorly/tensorly, cd tensorly, then pip install -e . for an editable install. The prerequisite is Python 3. A note in the README states that TensorLy depends on NumPy by default and that other backends require installing those packages separately, so a PyTorch backend means you install PyTorch yourself. The quickstart example creates a 3 x 4 x 2 tensor from np.arange(24).reshape((3, 4, 2)), then calls tl.unfold(tensor, mode=0) and tl.fold(unfolded, mode=0, shape=tensor.shape). Decomposition follows the same shape: from tensorly.decomposition import tucker, then tucker(tensor, rank=[2, 2, 2]), and reconstruction via tl.tucker_to_tensor(tucker_tensor). There is also a random module: random.random_tensor((3, 4, 2)) for a dense random tensor and random.random_tensor(shape=(3, 4, 2), rank='same') for a random CP tensor in factorized form.
Where the abstraction leaks: global state and separate backend installs
Two limitations follow from the design. First, the backend is a global setting, so library code that calls TensorLy internally cannot assume which framework is active unless it sets the backend itself. A function that quietly calls tl.set_backend will change behaviour for every other TensorLy call in the process. Second, the portability promise covers only what TensorLy implements. The README says other backends require installing those packages separately, which means the dependency footprint grows with each framework you want to support, and the CI matrix has to cover the combinations you claim. A third point is that the README does not document numerical equivalence across backends. Whether a Tucker decomposition converges to the same factors under JAX float32 and NumPy float64 is not addressed in the supplied material, so anyone relying on bit-for-bit or even tolerance-level agreement between backends should verify it on their own data rather than assume it.
A real alternative: quimb, and the difference in approach
quimb is a Python library for tensor networks that also provides tensor decomposition, including tensor-train and related factorizations. The difference is in what sits at the centre. TensorLy centres the backend abstraction: the API is framework-neutral and you choose NumPy, PyTorch, JAX, TensorFlow, CuPy or Paddle at runtime. quimb centres tensor networks as a data structure, with contraction and network manipulation as first-class operations, and it is built around NumPy and its own array handling rather than a swappable backend registry. If your work is decomposing a dense tensor and you want the option to move that computation to a GPU through PyTorch or CuPy without changing the decomposition call, TensorLy's model is the closer match. If your work is building and contracting tensor networks, quimb's model is the closer match. Choosing TensorLy for network contraction or quimb for backend portability means fighting the design either way.
Maintenance, releases and licence status
The release history supplied here shows 0.9.0 on 2024-11-12, 0.8.2 on 2024-06-09 and 0.8.0 on 2023-01-15. The gap between 0.8.0 and 0.8.2 is roughly five months, and between 0.8.2 and 0.9.0 roughly five months as well, which suggests patch and minor releases arrive on a loose cadence rather than a fixed schedule. The repository is not archived, and the last push date given is 2026-09-02, so development is ongoing. On licence: the repository metadata reports NOASSERTION, which means GitHub could not map the licence file to a recognised SPDX identifier. That is not the same as having no licence, but it does mean you cannot read the terms from the metadata alone. Anyone embedding TensorLy in a distributed product should open the licence file in the repository and read it, and route the question to their own legal review rather than treating the NOASSERTION label as a decision.
Who should adopt TensorLy, and what to check before committing
Adopt it if you need CP, Tucker, TT or the other decompositions listed in the tensorly.decomposition API, and you want the same call to run under NumPy in a notebook and under PyTorch or CuPy on a GPU. The backend switch is the reason to pick this library over writing the decomposition yourself against one framework. Do not adopt it if your tensor work is a handful of unfold and fold operations on small arrays, since you would be pulling in a framework abstraction for a few lines, or if the decomposition you need is not in the documented API. Before committing, check the decomposition module documentation for the specific method you need, confirm that your chosen backend package is installed separately as the README states, and read the licence file directly given the NOASSERTION metadata. Then run one decomposition under two backends on your own data and compare the reconstructed tensor, because the supplied material does not promise that the results will match across frameworks.
Editorial conclusion
TensorLy fits teams that already have a numerical stack (NumPy, PyTorch, JAX, TensorFlow, CuPy or Paddle) and want decomposition routines without rewriting them per framework, and researchers who need CP, Tucker or TT factorizations with a consistent API. It is a poor fit if you need a single tensor operation on a small array, since you would be installing a framework abstraction layer for one call, or if you need a decomposition that TensorLy does not list in its API and cannot implement yourself. Before adopting, verify three things: that the decomposition you need appears in the tensorly.decomposition module docs, that the backend you intend to use is installed as a separate dependency, and that the licence terms in the repository (GitHub reports NOASSERTION rather than a recognised SPDX identifier) match what your organisation accepts.
Community notes