refactor!: 🔥 drop quimb as a runtime dependency - #24
Conversation
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
Test Results 4 files ± 0 4 suites ±0 2m 1s ⏱️ +10s 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.♻️ 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
There was a problem hiding this comment.
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
quimbfrom runtime dependencies; keep it intest/devgroups for reference construction and distance checks. - Update
apply/compressto accept and return plain lists of per-site arrays, inferring MPS vs MPO from boundary tensor rank. - Add
src_method/_tensor_train.pydocumenting conventions and implementing exact two-siteexact_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
cutoffparameter is silently ignored for the small-system exact fallback (exact_apply). This changes behavior depending onn_sitesand can surprise callers using adaptive truncation. Consider plumbingcutoffinto 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
cutoffparameter is silently ignored for the small-system exact fallback (exact_compress). This makes truncation behavior depend on the number of sites. Consider applying the samecutoff * sigma_maxrule 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.
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
|



Motivation
quimbwas a hard runtime dependency, but it contributed nothing to the SRC algorithm itself. Every hot path inapply.pyandcompress.pyisopt_einsum.contractover plain numpy/cupy arrays. quimb was used only as:isinstancedispatch (MPS vs MPO).nsites,[i].data,.arrays,.site_ind(),.ind_size()qtn.MatrixProductState/MatrixProductOperatorn_sites < 3fallback (.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_methodpulled incotengra,cytoolz,psutil,scipy,tqdmandautoray— a 14 MB sdist and a large transitive tree — for a library whose core is einsum + LAPACK.Changes
apply/compressnow take and return plainlist[NDArray], one array per site.isinstancedispatch. No wrapper type needed.src_method/_tensor_train.pydocumenting 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.quimbmoved to thetestdependency group, where it is still legitimately used to build reference networks and to measure.distance().Breaking change
Callers passing quimb objects must now pass
.arraysand 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
applyandcompress.quimbis not insys.modulesafter a fullcompresscall.uv run pre-commit run --all-filespasses.