Skip to content

feat(mps): drive SRC adaptive loop with incremental QR and batched sketching - #477

Merged
ultimatile merged 11 commits into
mainfrom
feat/469-src-p2-incremental-qr
Jul 15, 2026
Merged

feat(mps): drive SRC adaptive loop with incremental QR and batched sketching#477
ultimatile merged 11 commits into
mainfrom
feat/469-src-p2-incremental-qr

Conversation

@ultimatile

@ultimatile ultimatile commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Summary

The successive-randomized-compression sweep compresses each bond by factorizing a sketch panel — the matrix that the sketch columns sample the partially compressed product into at that cut — and growing the sketch until an error estimate is satisfied. Every growth round restacked all panels sketched so far, refactorized them, and inverted the resulting triangular factor, so a round cost as much as the whole sketch rather than as much as the columns it added. Columns were also recursed through the chain one at a time, one tensordot chain per column per site.

This drives the per-site loop with a new incremental QR instead — a round appends a column block and updates the error estimate in place — and creates and recurses columns in blocks, so each site's environments come out of one batched contraction.

Closes #473 Closes #474

Changes

  • crates/ariadnetor-linalg/src/incremental_qr.rs (new): IncrementalQr grows a thin QR one column block at a time via block Gram-Schmidt with a reorthogonalization pass, taking the backend per method like the rest of the crate's dense entry points. It optionally maintains the squared row norms of R^-1 — the quantity SRC's leave-one-out stopping rule reads — through the identity [[R, C], [0, R22]]^-1 = [[G, -G C G22], [0, G22]], so a round updates them instead of inverting from scratch. Rank deficiency is detected from the maintained diagonal and terminates the factorization before the singular factor is inverted.
  • The accumulated basis is not unconditionally orthonormal: a rank-deficient append can overlap the existing span, and even full-rank appends can lose orthogonality gradually when their projected parts are ill-conditioned — a regime the diagonal test cannot see, because the offending entries sit legitimately above its tolerance. Rather than document that as a caveat every caller must remember, the terminal accessor into_orthonormal_q re-runs one plain QR whenever more than a single block was appended.
  • crates/ariadnetor-linalg/src/einsum.rs: fix einsum with three or more operands returning its result in an internal index order rather than the requested one. The pairwise recursion used its own first-appearance labeling for the last step too and no final permutation followed, so whenever the two orders differed the returned tensor carried the wrong shape and data. Two-operand calls were unaffected, and every pre-existing multi-operand call site and test requests an order that coincides with the first-appearance one (rg over the workspace), which is why the gap stayed invisible until the new sketch contractions needed a differing order.
  • crates/ariadnetor-mps/src/apply/successive_randomized.rs: feed the per-site panels to an IncrementalQr; draw and recurse Gaussians per site for a block of columns; hold environments in per-site buffers that grow geometrically and are released once the sweep passes their last reader, bounding peak memory by the live prefix rather than the whole chain.
  • crates/ariadnetor-algorithms/benches/mpo_mps_apply.rs (new): criterion bench over a Heisenberg MPO times a random MPS, with adaptive and fixed-rank arms. Its setup runs one untimed apply and asserts the adaptive arm grows past the initial sketch, so the timed loop provably exercises the multi-append path.

Impact

  • No public MPS API change: the rewrite stays behind apply_with_method, and ApplyMethod / SuccessiveRandomizedParams are untouched.
  • IncrementalQr and QrAppendOutcome are new pub items in ariadnetor-linalg, consumed directly by ariadnetor-mps and not re-exported through the ariadnetor umbrella — the Mid-layer placement of CONTRIBUTING.md's public-API taxonomy.
  • The einsum fix changes the returned axis order for three-or-more-operand contractions requesting an order that differs from the first-appearance one. Per the sweep above, no pre-existing caller does.

Test plan

Each of the four behaviors below was checked to fail on the corresponding broken implementation, by hand-reverting the fix and re-running:

  • A failed append leaves the factorization unchanged. A test backend serves one linear solve and then fails every later one, so an append succeeds at its orthogonalization and fails at its inversion; the test compares the basis, the maintained inverse, the diagonal, the append count, and the termination flag across the failure. Reverting to a commit-then-compute order grows the column count and fails it.
  • Growth stops on rank deficiency. A product whose true rank the sketch crosses mid-growth, with a per-site cap above the resulting size and a zero cutoff, so the rank-deficient outcome is the only thing that can end the growth there; the test asserts exactness, right-canonicality, and the selected bond. Ignoring the outcome fails it.
  • Adaptive rank selection does not depend on the state's scale. Scaling one site by 1e200 puts every panel norm where its square overflows f64 while the norm itself stays representable; the selected bonds must match the unit-scale run's. Accumulating squared norms instead of chaining hypot collapses the first bond from 3 to 1 and fails it.
  • The rank test weighs every diagonal entry against the largest one, so a later block 18 orders larger demotes an earlier entry. Testing only the new block's diagonal fails it.

Other coverage:

  • IncrementalQr unit tests (f64 and Complex64): multi-append equivalence against one full QR of the stacked blocks (diagonal magnitudes, orthonormality, span, and inverse row norms against a direct inversion), first-append reduction to a plain QR, rank deficiency in a first block, the guard rejecting a backend whose memory order differs from the factorization's, post-termination panic, inverse tracking off, and every input-validation path.
  • Einsum: a four-operand contraction whose requested output order differs from the first-appearance order, verified against a brute-force element sum.
  • The existing SRC suite and the backend-authority test are unchanged and pass; none of them pins the RNG stream.
  • cargo make gate (format, clippy over all targets with warnings denied, and unit / integration / doc tests) passes.

Notes

  • Performance, cargo bench -p ariadnetor-algorithms --bench mpo_mps_apply, 30 samples per arm: 12 sites at bond dimension 16 improves ~20% adaptive and ~15% fixed-rank, while 8 sites at bond dimension 8 regresses ~15% adaptive — the per-round bookkeeping is not amortized at that size, so the win arrives as the sketch grows. The baseline is that same bench file copied onto a main worktree, since it only calls API that already exists there. Eliminate the per-round copies in the SRC sweep #475 tracks eliminating the per-round copies of the basis and the maintained inverse, which should move the crossover down.
  • The adaptive stopping rule is not scale-free downward: the estimator squares row norms of R^-1, which overflow for states scaled below roughly 1e-154, collapsing the estimate to zero so the sweep stops at the initial sketch. The limitation predates this PR — the estimator it replaces squared the same quantities — and Leave-one-out estimate collapses to zero for extreme state scales #476 tracks the fix.
  • Gaussians are now drawn per site for a whole block rather than per column across sites, so a given seed produces a different sketch than it did before this change. The sketch's distribution is unchanged, and run-to-run determinism for equal seeds — all the seed field promises — still holds.

Summary by CodeRabbit

  • New Features

    • Added incremental QR factorization capabilities for building orthonormal bases from successive matrix blocks.
    • Improved successive randomized MPS application with adaptive sketch growth, rank-deficiency handling, and reduced memory retention.
  • Bug Fixes

    • Improved numerical stability across scaling and rank-deficient scenarios.
    • Ensured tensor contractions preserve the requested output index order.
  • Benchmarks & Tests

    • Added coverage for incremental QR behavior and randomized application edge cases.
    • Added a benchmark for MPO–MPS application performance.

@coderabbitai ignore

Plan-vs-actual delta

Against #473's Scope and Acceptance:

einsum with three or more operands returned its result in the internal
first-appearance order of the surviving indices instead of the order the
notation requested: the pairwise recursion used that internal labeling for
the last step too, and no final permutation followed. Whenever the two
orders differed the returned tensor carried the wrong shape and data,
silently breaking downstream axis bookkeeping.

Derive the last step's output notation from the caller's requested order
instead. The index sets coincide either way -- every index surviving the
last step is a final-output index and vice versa -- and einsum_pair
already reorders its GEMM output to an arbitrary requested order, so the
reorder fuses into the step that was already running.

Two-operand calls were never affected, and the existing N-operand tests
only requested orders that happened to coincide with the first-appearance
order, which is why the gap stayed invisible.

Closes #474
IncrementalQr grows a thin QR factorization one column block at a time
without refactorizing the columns already absorbed, and optionally
maintains the inverse triangular factor's squared row norms alongside it --
the quantity randomized leave-one-out error estimators consume. Each
append costs a block update instead of the full recompute plus inversion a
from-scratch scheme pays every round.

Each append runs block classical Gram-Schmidt with one reorthogonalization
pass; the inverse rides the block-triangular inverse identity. Rank
deficiency is detected from the maintained diagonal and terminates the
factorization before the singular factor can be inverted.

The accumulated basis is not unconditionally orthonormal: beyond the overt
rank-deficient case, orthogonality can degrade gradually across appends
whose projected parts are ill-conditioned, a regime the diagonal test
cannot see because the offending entries sit legitimately above its
tolerance. into_orthonormal_q therefore owns the repair, running one plain
QR whenever more than a single block was appended, so callers cannot
inherit the caveat by omission.

Mid-layer: dense-only, consumed by ariadnetor-mps, not re-exported through
the umbrella.
…etching

Each growth round of the successive-randomized-compression sweep restacked
every panel, refactorized the whole sketch, and inverted the resulting
triangular factor -- work that grows with everything sketched so far rather
than with the columns just added. Sketch columns were also recursed through
the chain one at a time, so each round paid a separate tensordot chain per
column per site.

Feed the per-site panels to an IncrementalQr instead, so a round costs a
column-block append and an incremental error-estimate update, and create
and recurse columns in blocks, so each site's environments come out of one
batched contraction. Environments live in per-site buffers that grow
geometrically and are released once the sweep passes their last reader,
which also bounds peak memory by the live prefix instead of the whole
chain.

Measured on a Heisenberg MPO times a random MPS (12 sites, chi = 16): the
adaptive sweep runs ~20% faster, and the fixed-rank sweep ~15% faster from
the batched recursion alone. Small problems (8 sites, chi = 8) are ~15%
slower -- the per-round bookkeeping is not amortized there -- so the win
arrives as the sketch grows.

The Gaussians are now drawn per site for a whole block rather than per
column across sites, so a given seed produces a different sketch than
before. The sketch's distribution is unchanged and run-to-run determinism
for equal seeds -- all the seed field promises -- still holds.

Closes #473
@ultimatile
ultimatile requested a review from Copilot July 15, 2026 08:32
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: bc4a5925-8119-4bbc-b8ae-6b39ad52570b

📥 Commits

Reviewing files that changed from the base of the PR and between ded910e and 6c81a31.

📒 Files selected for processing (10)
  • crates/ariadnetor-algorithms/Cargo.toml
  • crates/ariadnetor-algorithms/benches/mpo_mps_apply.rs
  • crates/ariadnetor-linalg/src/einsum.rs
  • crates/ariadnetor-linalg/src/incremental_qr.rs
  • crates/ariadnetor-linalg/src/incremental_qr/tests.rs
  • crates/ariadnetor-linalg/src/lib.rs
  • crates/ariadnetor-linalg/tests/einsum.rs
  • crates/ariadnetor-mps/src/apply/successive_randomized.rs
  • crates/ariadnetor-mps/src/types.rs
  • crates/ariadnetor-mps/tests/mps/apply_successive_randomized.rs

📝 Walkthrough

Walkthrough

SuccessiveRandomized gains batched environment sketching and incremental QR updates, with rank-deficiency handling and new regression coverage. Multi-operand einsum output ordering is corrected and tested. A Criterion benchmark compares adaptive and fixed application modes.

Changes

Incremental QR foundation

Layer / File(s) Summary
Einsum output ordering
crates/ariadnetor-linalg/src/einsum.rs, crates/ariadnetor-linalg/tests/einsum.rs
The final multi-operand contraction preserves the requested output index order, with a regression test covering permuted output axes.
Incremental QR implementation and exports
crates/ariadnetor-linalg/src/incremental_qr.rs, crates/ariadnetor-linalg/src/lib.rs
Adds block-wise BCGS2 QR, optional inverse row-norm tracking, rank-deficiency termination, orthonormalization repair, validation, and public exports.
Incremental QR validation
crates/ariadnetor-linalg/src/incremental_qr/tests.rs
Tests full-QR equivalence, complex and real inputs, rank deficiency, memory order, invalid inputs, disabled tracking, and failure-safe state rollback.

SuccessiveRandomized integration

Layer / File(s) Summary
Batched environment sketching and adaptive QR loop
crates/ariadnetor-mps/src/apply/successive_randomized.rs, crates/ariadnetor-mps/src/types.rs
Replaces per-column environments with batched buffers, uses IncrementalQr for sketch growth, updates adaptive stopping, and releases consumed buffers.
SRC regression coverage
crates/ariadnetor-mps/tests/mps/apply_successive_randomized.rs
Adds scale-invariance coverage and a rank-deficient growth test checking exact output, canonical form, isometry, and termination dimensions.
SRC benchmark
crates/ariadnetor-algorithms/Cargo.toml, crates/ariadnetor-algorithms/benches/mpo_mps_apply.rs
Adds Criterion cases measuring adaptive and fixed SuccessiveRandomized MPO-MPS application, including an adaptive growth probe.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

  • Issue 475 — The benchmark and incremental QR/environment changes directly cover the referenced copy-elimination and performance work.
  • Issue 469 — The PR implements the incremental QR and batched environment portion of the broader SuccessiveRandomized phase.

Poem

A rabbit found blocks in a row,
And watched little QR columns grow.
Environments danced,
Rank defects were glanced,
While fast benchmark numbers now glow.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: incremental QR and batched sketching for SRC.
Linked Issues check ✅ Passed The changes implement incremental QR/batched SRC plus the N-operand einsum fix, with matching tests and benchmark coverage for #473 and #474.
Out of Scope Changes check ✅ Passed All edits support the SRC rewrite, einsum ordering fix, or their tests/benchmark; no unrelated scope creep is evident.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/469-src-p2-incremental-qr

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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 improves performance and correctness of the successive randomized compression (SRC) MPO–MPS apply path by (1) driving adaptive sketch growth with an incremental QR that updates state per appended column block, (2) batching sketch-column environment recursions per site, and (3) fixing einsum_with_backend for 3+ operands to honor the requested output index order.

Changes:

  • Added ariadnetor-linalg::IncrementalQr (+ tests) to support block-appended thin QR with optional R^{-1} row-norm tracking for SRC’s leave-one-out estimator.
  • Reworked SRC internals to generate/recurse sketch columns in blocks and feed per-site sketch panels into IncrementalQr, reducing per-round recomputation.
  • Fixed multi-operand einsum output ordering and added regression tests + a Criterion bench covering multi-append growth behavior.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
crates/ariadnetor-mps/tests/mps/apply_successive_randomized.rs Adds SRC regression tests for scale invariance and late rank-deficiency re-orthonormalization.
crates/ariadnetor-mps/src/types.rs Updates ApplyMethod::SuccessiveRandomized docs to reflect batched environment recursions.
crates/ariadnetor-mps/src/apply/successive_randomized.rs Rewrites SRC sweep internals to use batched sketching + IncrementalQr-driven adaptive loop.
crates/ariadnetor-linalg/tests/einsum.rs Adds regression test ensuring multi-operand einsum honors requested output order.
crates/ariadnetor-linalg/src/lib.rs Wires new incremental_qr module and re-exports IncrementalQr/QrAppendOutcome.
crates/ariadnetor-linalg/src/incremental_qr/tests.rs New unit test suite for incremental QR behavior, rank-deficiency, and error paths.
crates/ariadnetor-linalg/src/incremental_qr.rs New incremental thin-QR implementation with optional inverse tracking and rank test.
crates/ariadnetor-linalg/src/einsum.rs Fixes multi-operand einsum recursion to emit final output in caller-requested index order.
crates/ariadnetor-algorithms/Cargo.toml Registers new mpo_mps_apply benchmark target.
crates/ariadnetor-algorithms/benches/mpo_mps_apply.rs Adds end-to-end Criterion benchmark for SRC adaptive vs fixed rank.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/ariadnetor-algorithms/benches/mpo_mps_apply.rs
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.

einsum: N-operand contraction ignores the requested output index order Phase 2: incremental QR and batched environment sketching for SRC

2 participants