fix(mps): guard variational tol cast against overflow to infinity - #487
Merged
Conversation
The variational apply path cast its f64 tolerance into the scalar's real type with a bare NumCast::from(tol).expect(...) and no post-cast finiteness check. For f32-family scalars NumCast::from maps an out-of-range but finite f64 (e.g. 1e300) to Some(inf) rather than None, so the convergence comparison becomes trivially true and the fit reports convergence after one sweep with an arbitrarily bad result. Consolidate the three copies of this cast into one shared helper, try_real_from_f64, at the core layer where Scalar lives, migrate the successive-randomized apply and the algorithms tolerance casts onto it, and add the finiteness check to the variational path. A new f32 should_panic regression test mirrors the successive-randomized one. Closes #472
There was a problem hiding this comment.
Pull request overview
This PR hardens tolerance handling across MPO–MPS algorithms by centralizing the f64 -> T::Real cast behind a shared ariadnetor-core helper that rejects non-finite results (notably f64 values that overflow to ±inf when cast to f32). This prevents spurious “converged” reports in variational apply paths for f32-family scalars and removes duplicated cast logic across crates.
Changes:
- Add
ariadnetor_core::try_real_from_f64::<T: Scalar>(f64) -> Option<T::Real>and re-export it from the core crate root. - Migrate
ariadnetor-algorithms(DMRG + Krylov/Lanczos) andariadnetor-mps(successive-randomized + variational apply) to use the shared helper. - Add a variational Dense
f32regression test ensuring unrepresentable tolerances panic instead of silently becominginf.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/ariadnetor-core/src/numeric.rs | Introduces the shared, finiteness-checked try_real_from_f64 conversion + unit tests. |
| crates/ariadnetor-core/src/lib.rs | Wires the new numeric module and re-exports try_real_from_f64 at the crate root. |
| crates/ariadnetor-algorithms/src/lib.rs | Removes the crate-local numeric module entry point. |
| crates/ariadnetor-algorithms/src/krylov/lanczos.rs | Switches tolerance casting to ariadnetor_core::try_real_from_f64. |
| crates/ariadnetor-algorithms/src/krylov/lanczos_kernels.rs | Uses the core helper for RNG-derived real/imag conversions and test helpers. |
| crates/ariadnetor-algorithms/src/dmrg/sweep.rs | Imports try_real_from_f64 from core instead of the removed local helper. |
| crates/ariadnetor-algorithms/src/dmrg/heff.rs | Uses the core helper for eigensolver tolerance representability validation. |
| crates/ariadnetor-algorithms/src/dmrg/heff_block_sparse/validation.rs | Same validation change for the block-sparse DMRG heff path. |
| crates/ariadnetor-mps/src/apply/successive_randomized.rs | Delegates the SRC cutoff cast to the core helper (preserving panic semantics). |
| crates/ariadnetor-mps/src/apply/variational.rs | Fixes variational tolerance casting (Dense + BlockSparse) to reject overflow-to-inf. |
| crates/ariadnetor-mps/tests/mps/apply_variational.rs | Adds a Dense f32 #[should_panic] regression test for unrepresentable tolerances. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Move the tolerance cast ahead of canonicalize and BraketEnvs::build in both the Dense and BlockSparse variational paths. The cast is independent of the environments, so an unrepresentable tolerance now fails fast instead of panicking after that setup work, matching the up-front cutoff validation the successive-randomized path already does.
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.
Summary
The variational (fit) MPO-MPS apply path cast its
f64tolerance into the scalar's real type with a bareNumCast::from(tol).expect(...)and no post-cast finiteness check. Forf32-family scalars,NumCast::frommaps an out-of-range but finitef64(e.g.1e300) toSome(inf)rather thanNone, so the convergence comparison|current - prev| <= tol * currentbecomes trivially true and the fit reports convergence after one sweep with an arbitrarily bad result. Two hardened copies of the same cast already existed (successive-randomized apply and an algorithms-crate helper); this consolidates all three into one shared primitive and closes the gap on the variational path.Closes #472
Changes
ariadnetor-core: newtry_real_from_f64<T: Scalar>(f64) -> Option<T::Real>(NumCast::fromthen anis_finite()filter), exported at the crate root next to the existing scale-safe norm helpers.ariadnetor-algorithms: delete the crate-local copy and repoint every consumer (DMRGheff/heff_block_sparsevalidation /sweep, Krylovlanczosandlanczos_kernels, including a#[cfg(test)]helper) onto the core primitive.ariadnetor-mpssuccessive-randomized apply:real_from_f64now delegates to the core primitive, preserving its panic message.ariadnetor-mpsvariational apply: both the Dense and BlockSparse tolerance casts gain the finiteness check via the core primitive; the now-unusedNumCastimport is dropped.Impact
ariadnetor-coregains exactly one new public symbol,try_real_from_f64(confirmed viacargo public-api).f64but overflows the scalar's real type now panics at the cast instead of silently casting to infinity and mis-converging. Callers passing a representable tolerance are unaffected; the change only converts a previously-silent numerical fault into a fail-fast.Test plan
ariadnetor-core, coveringf32,f64, andComplexagainst overflow, NaN, and infinity inputs.f32#[should_panic]regression test on the variational path, mirroring the successive-randomized one, driving a 2-sitef32MPS with a1e300tolerance.cargo testacrossariadnetor-core,ariadnetor-algorithms, andariadnetor-mpspasses;cargo make clippy(warnings denied, all targets) is clean.Notes
The BlockSparse variational cast has no dedicated
f32regression test: both cast sites are the identical one-line construct andtry_real_from_f64monomorphizes independently of tensor storage, so the Dense test covers the shared finiteness-check-then-expectwiring, and the BlockSparse site's reachability is already exercised by the existingf64BlockSparse variational tests.@coderabbitai ignore