Skip to content

refactor!: 🔥 drop quimb as a runtime dependency - #24

Merged
Panadestein merged 4 commits into
mainfrom
refactor/drop-quimb-runtime-dependency
Sep 2, 2026
Merged

refactor!: 🔥 drop quimb as a runtime dependency#24
Panadestein merged 4 commits into
mainfrom
refactor/drop-quimb-runtime-dependency

Conversation

@Panadestein

Copy link
Copy Markdown
Member

Motivation

quimb was a hard runtime dependency, but it contributed nothing to the SRC algorithm itself. Every hot path in apply.py and compress.py is opt_einsum.contract over plain numpy/cupy arrays. quimb was used only as:

  1. Type hints
  2. isinstance dispatch (MPS vs MPO)
  3. Attribute access — .nsites, [i].data, .arrays, .site_ind(), .ind_size()
  4. Re-wrapping the resulting array list into qtn.MatrixProductState / MatrixProductOperator
  5. The n_sites < 3 fallback (.apply(compress=True, method="svd") / .compress(method="svd"))

Only item 5 was real functionality, and it is replaced here by ~40 lines of exact numpy SVD. In exchange, every install of src_method pulled in cotengra, cytoolz, psutil, scipy, tqdm and autoray — a 14 MB sdist and a large transitive tree — for a library whose core is einsum + LAPACK.

Changes

  • apply / compress now take and return plain list[NDArray], one array per site.
  • Array layout is unchanged. It still follows the default quimb index ordering, so callers round-trip with no permutation:
    result = qtn.MatrixProductOperator(apply(H1.arrays, H2.arrays, chi_out=64))
  • MPS vs MPO is inferred from the rank of the first site tensor (rank-2 boundary → MPS, rank-3 → MPO), replacing the isinstance dispatch. No wrapper type needed.
  • New src_method/_tensor_train.py documenting the array conventions and holding the exact two-site path (exact_apply, exact_compress). At two sites the whole network fits in one dense matrix, so a single exact SVD is both cheaper and more accurate than a randomized sketch.
  • quimb moved to the test dependency group, where it is still legitimately used to build reference networks and to measure .distance().
  • Docs, README, CONTRIBUTING, SECURITY and the Leonardo benchmark updated accordingly.

Breaking change

Callers passing quimb objects must now pass .arrays and re-wrap the result. This is the only source-level change required; numerics and index ordering are identical.

Single-site trains are now rejected with a ValueError — they are degenerate (a bare vector or matrix, no bond to compress).

Validation

  • 25 tests pass; the two former "dispatch to quimb" tests became four exact-fallback tests covering both MPS and MPO for apply and compress.
  • Verified quimb is not in sys.modules after a full compress call.
  • uv run pre-commit run --all-files passes.
  • GPU tests are updated but remain skipped here (no cupy in the dev container) — worth a run on a GPU runner before merge.

quimb contributed nothing to the SRC algorithm itself: every hot path is
opt_einsum over plain numpy/cupy arrays. It was used only as a container
type (isinstance dispatch, attribute access, re-wrapping results) and for
the sub-3-site SVD fallback, yet it pulled cotengra, cytoolz, psutil,
scipy and tqdm into every install.

BREAKING CHANGE: `apply` and `compress` now take and return plain lists of
per-site arrays instead of quimb MPS/MPO objects. The array layout is
unchanged (default quimb index ordering), so callers can round-trip with
`qtn.MatrixProductOperator(result)` / `qtn.MatrixProductState(result)`.

- infer MPS vs MPO from the rank of the first site tensor
- replace the quimb sub-3-site fallback with an exact two-site SVD
- move quimb to the `test` dependency group, where it is still used to
  build reference networks and measure distances
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

Test Results

  4 files  ± 0    4 suites  ±0   2m 1s ⏱️ +10s
 37 tests + 5   36 ✅ + 5  1 💤 ±0  0 ❌ ±0 
128 runs  +20  126 ✅ +20  2 💤 ±0  0 ❌ ±0 

Results for commit dcd3b47. ± Comparison against base commit 3d1b553.

This pull request removes 2 and adds 7 tests. Note that renamed tests count towards both.
tests.test_package ‑ test_apply_quimb_dispatch
tests.test_package ‑ test_compress_quimb_dispatch
tests.test_package ‑ test_apply_small_system_dispatch
tests.test_package ‑ test_apply_small_system_mpo_mpo
tests.test_package ‑ test_compress_small_system_dispatch
tests.test_package ‑ test_compress_small_system_mpo
tests.test_package ‑ test_mismatched_site_counts_raise
tests.test_package ‑ test_single_site_train_raises
tests.test_package ‑ test_single_site_train_raises_without_warning

♻️ This comment has been updated with latest results.

With quimb gone, the remaining runtime dependency list was mostly dead
weight. `numba` and `llvmlite` are imported nowhere in `src/` — they were
transitively required by quimb, not by us. `cyclopts` is only used by the
benchmark scripts under `benches/`, which are not packaged.

`cmaes` is not dead: cotengra (via quimb) discovers it by name as a
hyper-optimization backend and warns when it is missing, which
`filterwarnings = ["error"]` promotes to a test failure. It moves to the
`test` group alongside quimb rather than being removed.

A runtime install is now numpy + opt_einsum + structlog.

- move `cyclopts` to the `dev` group (benchmarks)
- move `cmaes` to the `test` group (cotengra path optimizer)
- drop the now-dead `@jit(` / `@njit(` coverage excludes

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR removes quimb as a runtime dependency by refactoring the core SRC primitives (apply, compress) to operate purely on list[NDArray] tensor trains (quimb-compatible array layout) and by adding an exact two-site fallback implemented via dense contraction + SVD.

Changes:

  • Drop quimb from runtime dependencies; keep it in test/dev groups for reference construction and distance checks.
  • Update apply / compress to accept and return plain lists of per-site arrays, inferring MPS vs MPO from boundary tensor rank.
  • Add src_method/_tensor_train.py documenting conventions and implementing exact two-site exact_apply / exact_compress, with tests/docs/benchmarks updated accordingly.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
uv.lock Removes quimb from runtime resolution and adds it to dev/test groups.
pyproject.toml Drops runtime quimb dependency and documents quimb as test-only.
src/src_method/apply.py Refactors public API to accept/return site-array lists and adds exact fallback dispatch.
src/src_method/compress.py Refactors public API to accept/return site-array lists and adds exact fallback dispatch.
src/src_method/_tensor_train.py New module defining tensor-train conventions and exact two-site primitives.
tests/test_package.py Updates tests to unwrap .arrays and re-wrap outputs; adds exact-fallback coverage.
tests/test_gpu_backend.py Updates GPU tests to use array-list API and wrap outputs for quimb distance checks.
README.md Updates public-facing docs to describe array-list API and quimb round-tripping.
docs/index.md Mirrors README updates for documentation site.
SECURITY.md Removes quimb from dependency/security wording.
CONTRIBUTING.md Removes quimb from “version info to include” guidance.
benches/primitives/leonardo/bench_mpo_mpo.py Updates benchmark to call array-list API and wrap output back into quimb for comparison.
Suppressed comments (2)

src/src_method/apply.py:112

  • The public cutoff parameter is silently ignored for the small-system exact fallback (exact_apply). This changes behavior depending on n_sites and can surprise callers using adaptive truncation. Consider plumbing cutoff into the exact path and applying the same relative singular-value threshold when choosing the truncated rank.
    if len(left_tensor) < MIN_SRC_SITES:
        logger.warning(LOG_WARN_SMALL)
        return exact_apply(left_tensor, right_tensor, chi_out, right_kind)
    if right_kind == "mps":

src/src_method/compress.py:108

  • The public cutoff parameter is silently ignored for the small-system exact fallback (exact_compress). This makes truncation behavior depend on the number of sites. Consider applying the same cutoff * sigma_max rule in the exact SVD path when selecting the kept singular values.
    if len(tensor) < MIN_SRC_SITES:
        logger.warning(LOG_WARN_SMALL)
        return exact_compress(tensor, chi_out, kind)
    if kind == "mps":

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/src_method/_tensor_train.py
Comment thread src/src_method/apply.py
Comment thread src/src_method/compress.py
Addresses the Copilot review on #24.

The exact two-site path called `np.linalg.svd` on whatever the caller
passed, so it broke on device arrays while the SRC sweep handled them via
`xp.asarray`. It now brings inputs onto the host with `to_numpy`, matching
the sweep and always returning numpy arrays.

`apply` also accepted trains of different lengths: the sweep sizes itself
from the left train, so a 4-site MPO applied to a 6-site MPS silently
returned a 4-site result. quimb used to reject this; the list-based API
has to do it itself.

- reject mismatched site counts in `apply`
- move the two-site guard to the public boundary as `check_exact_supported`,
  so degenerate trains raise instead of first logging a fallback warning
- cover both guards, and assert the warning is not emitted when raising
@sonarqubecloud

sonarqubecloud Bot commented Sep 2, 2026

Copy link
Copy Markdown

@Panadestein
Panadestein merged commit d457d86 into main Sep 2, 2026
16 of 17 checks passed
@Panadestein
Panadestein deleted the refactor/drop-quimb-runtime-dependency branch September 3, 2026 07:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants