fit: adaptive low-rank initialization for variational (MPO·MPO) contraction - #656
Draft
SamuelBadr wants to merge 1 commit into
Draft
fit: adaptive low-rank initialization for variational (MPO·MPO) contraction#656SamuelBadr wants to merge 1 commit into
SamuelBadr wants to merge 1 commit into
Conversation
Repository rules reviewRepository rules review (9e9aedaebe0d3918b34dd399ff0981e337f3835b...06c0ea86544f01d3c286e73190432d123420b28f) Verdict: pass Findings: - [warn] llm-skipped (External LLM Review) <unknown>: External LLM review was skipped External LLM review is permanently disabled in this repository; deterministic rule checks run instead. |
SamuelBadr
force-pushed
the
fit/adaptive-contraction-initialization
branch
from
August 19, 2026 15:26
24da4dd to
3d8d8f7
Compare
Fit contraction previously always started from a topology-preserving zip-up
contraction of A·B. Without a truncation policy that materializes the exact
product bond (up to χ_A·χ_B per edge) before any sweep runs, this is wasteful
for large QTT/MPO bonds and defeats the purpose of the variational fit.
Split initialization from the sweep engine and make an adaptive low-rank start
the default for the public TensorTrain API:
- treetn: add `contract_fit_from_initial` (generic sweep engine over a provided
C0) and `low_rank_initializer_tree_tn` (deterministic small-rank random C0
carrying the surviving output site indices; never forms χ_A·χ_B).
- itensorlike: add `FitInitializer` ({ZipUp, LowRankRandom{bond_dim, seed}}) to
`ContractOptions`; `ContractOptions::fit()` now defaults to a rank-1
deterministic random initializer. With `max_bond_dim = None` and an SVD
truncation tolerance, fit grows ranks adaptively from the small start — no
user-supplied rank cap is required.
- keep the zip-up initializer available explicitly for the exact-start +
refine workflow.
Rank growth semantics (unchanged in the sweeps):
- max_bond_dim = Some(cap) caps every sweep bond;
- max_bond_dim = None + SVD truncation policy -> unconstrained adaptive growth;
- no cap and no policy -> preserve the existing (initializer) rank.
Tests: structural regression that the low-rank initializer keeps every bond at
the requested small dimension (would fail if a χ_A·χ_B bond were created);
adaptive growth from rank 1 with only a tolerance; tolerance->rank/error;
matching exact products for chains and a branched tree, f64 and Complex64;
existing zip-up-based fit tests pinned to ZipUp init to preserve their intent.
Benchmark (benchmarks/results/2026-08-19-adaptive-fit-lowrank-initializer.md):
chi_a = chi_b = 24-48 with a compressible product; low-rank-initialized fit is
~1.6-1.8x faster than zip-up-initialized fit at the same accuracy, with no
chi^2 intermediate.
SamuelBadr
force-pushed
the
fit/adaptive-contraction-initialization
branch
from
August 20, 2026 09:43
3d8d8f7 to
06c0ea8
Compare
This was referenced Sep 6, 2026
Member
|
Is this still alive? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
ContractOptions::fit()(and the TreeTNcontract_fit) always started thevariational state with a topology-preserving zip-up contraction of A·B.
Without an explicit truncation policy that initializer materializes the exact
product — output bonds up to χ_A·χ_B per edge, plus intermediates of the
same scale — before any sweep ever runs. For large QTT/MPO bonds this
max_bond_dimon the user just to keep initialization fromblowing up, and
is constructed first and only then refined.
The requested semantics are
C₀ small → two-site FIT sweeps + tolerance → C ≈ ABwith adaptive rank growth and no required rank cap, and never an
explicit χ_A·χ_B product bond as an intermediate.
What I found in the current implementation
Environments
env[(from,to)]carry the A-, B- and C-link indices separately(χ_A × χ_B × χ_C)(explicitly allowed). In the two-site update every A/Bbond is contracted away into the environments before factorization, so the
local SVD matrix sides are built purely from C-bonds and output sites — no
χ_A·χ_B term appears in the factorization.
max_bond_dim = Noneand ansvd_policy, the bond cap isNone, so ranksgrow freely per the tolerance; with a cap the cap wins; with neither, existing
ranks are preserved.
Rintermediates whose element count scales as
rank · χ_A · χ_Band, with notruncation, emits an output bond of dimension up to
χ_A·χ_B; the root nodematerializes the full exact contraction. So the product bond is constructed
a priori exactly when the user leaves
max_bond_dimunset.This matches upstream behavior: FastMPOContractions.jl defaults to zip-up init
(
init === nothing), while ITensorMPS.jl's fit starts from a small randomoutput-site MPS and grows with
maxdim = typemax(Int)+cutoff.Design chosen
tensor4all-treetn:contract_fit_from_initial(tn_a, tn_b, center, options, tn_c)— thegeneric two-site sweep engine over a caller-provided
C₀(theinitialization, zip-up or otherwise, is now just how you get
C₀).low_rank_initializer_tree_tn(tn_a, tn_b, center, bond_dim, seed)—builds a deterministic small-rank random
C₀carrying the survivingoutput site indices, with every bond at
bond_dim(default 1). It nevercontracts A and B, so no χ_A·χ_B-sized tensor is ever formed. Concrete on
IdxTensor(f64/Complex64), which is where seeded random constructionlives (the generic tensor layer is scalar-erased).
tensor4all-itensorlikelayer:FitInitializer { ZipUp, LowRankRandom { bond_dim, seed } }added toContractOptions(with_initializer/initializer()).ContractOptions::fit()defaults toLowRankRandom { bond_dim: 1, seed: None }(deterministic seed). Sofit()+with_svd_policy(tol)andmax_bond_dim = Nonenow genuinelymeans unconstrained adaptive rank growth from a small start, with no
product-bond intermediate.
FitInitializer::ZipUprestores the previous exact-start behavior.treetn::contraction::contract(…, Fit)dispatcher isunchanged (still zip-up init), because a generic random C₀ cannot be built
for an arbitrary
TensorLike; callers who need a custom start usecontract_fit_from_initial.Alternatives considered (and why rejected)
task's core requirement is that adaptive FIT never constructs the product
bond; leaving zip-up as the default would keep violating it for every default
call. The repo is early-stage with no back-compat burden.
layer is scalar-erased (
TensorLikehas no associated scalar type), so ameaningful random tensor for arbitrary
Tis not expressible;IdxTensoris the right seam.
degenerate for products (symmetry traps); seeded random is the standard,
reproducible choice (ITensorMPS-style) and is what we ship.
max_bond_dim = None= "grow freely even without a tolerance"(one-site/two-site enrichment with no criterion). Rejected: there is no
sensible growth criterion without a tolerance or cap; the exact-product
behavior only survives under explicit
ZipUpinit.Exact rank-growth semantics
max_bond_dim = Some(cap)→ every sweep bond capped atcap.max_bond_dim = None+svd_policy→ unconstrained adaptive growth per thetolerance (no user rank cap required).
max_bond_dim = Noneand no policy → existing (initializer-provided) ranksare preserved.
nfullsweeps = 0→ returns the initializer unchanged.Does any χ_A·χ_B-sized intermediate remain?
low-rank-initialized path: not in initialization (bonds start at 1) and not
in the sweeps (A/B bonds only appear inside environments as separate
(χ_A, χ_B, χ_C)indices, which is explicitly allowed).construction (env of shape
χ_A×χ_B×χ_Cis contracted with the localtensors) — this is intrinsic to any two-site tree fit and is cost, not a
materialized product bond.
FitInitializer::ZipUp, zip-up still forms the exact productbond — by explicit opt-in only.
Tests
test_low_rank_initializer_stays_small_no_product_bond— structuralregression: with χ_A = 6, χ_B = 7 the initializer must keep every bond at
bond_dim(1/3); would fail if any χ_A·χ_B bond were created.test_low_rank_initializer_carries_surviving_sites/_is_deterministic/_requires_bond_dim_at_least_one.test_contract_fit_from_rank1_grows_adaptively— rank-1 C₀ + tolerance,max_bond_dim = None→ bond grows beyond 1 and matches the exact product.adaptive_fit_grows_rank_without_cap,adaptive_fit_tolerance_controls_rank_and_error,adaptive_fit_avoids_product_bond_intermediate(χ_A·χ_B = 144, compressibleto rank 12),
adaptive_fit_respects_explicit_cap.tree, and Complex64, all matching the exact product to ≤ 1e-6.
fit_bond_capping,bug_fit_elementwise,test_contract_fit_nhalfsweeps_zero_ok) were pinned toFitInitializer::ZipUpto preserve their original intent under the newdefault.
Benchmark
benchmarks/results/2026-08-19-adaptive-fit-lowrank-initializer.md:χ_A = χ_B = 24–48,
nsweeps = 3, tolerance 1e-8, compressible product (B isthe identity padded to bond χ_B):
Low-rank-initialized fit is ~1.6–1.8× faster at equal accuracy and never
constructs the χ² intermediate.
API compatibility
treetn::{contract_fit_from_initial, low_rank_initializer_tree_tn}(+ re-exports),
itensorlike::FitInitializer,ContractOptions::with_initializer/
initializer(),DEFAULT_FIT_INITIALIZER_SEED.ContractOptions::fit()with no explicit initializer nowstarts from a deterministic rank-1 random state instead of the exact zip-up
state.
FitInitializer::ZipUprestores the old behavior. The repo's"early development, no backward compatibility" policy applies; the affected
tests were updated deliberately.
tensor4all-itensorlike'scontract()drives Fit directly; the treetngeneric
contraction::contract(…, Fit)default is unchanged (zip-up init).path through the same
ContractOptionssurface once exposed if desired).Validation
cargo fmt --all -- --check✓cargo test --release --workspace(excludingtensor4all-hdf5anddocs/book-tests, which need a system HDF5 that is absent in thisenvironment — a pre-existing limitation) → 3789 passed, 0 failed.
cargo test --release --docfor changed crates ✓ (treetn 121, itensorlike 28).cargo clippyon the changed crates: no warnings in changed files. Note:cargo clippy --workspace -- -D warningscurrently fails on pristinemaintoo (45 pre-existingnonminimal_boollints intensor4all-coreunder rustc 1.96.1), unrelated to this PR.
python3 scripts/repository-rules-review.py --base origin/main --worktree --dry-run→ pass, no findings.Remaining limitations / future work
IdxTensor(f64/Complex64); otherdtypes fall back to the zip-up/
Providedpaths. A generic randomconstruction would require an associated scalar type on
TensorLike.max_bond_dim = None+ no tolerance preserves the initial rank; a caller whowants compression must supply an
svd_policy. This is documented.remain future work (see Feature request: successive randomized compression (SRC) for MPO-MPS / MPO-MPO contraction #563).