Skip to content

fix(linalg,mps): scale-safe truncation-error accumulation - #488

Merged
ultimatile merged 1 commit into
mainfrom
fix/480-trunc-error-scale-safe
Jul 18, 2026
Merged

fix(linalg,mps): scale-safe truncation-error accumulation#488
ultimatile merged 1 commit into
mainfrom
fix/480-trunc-error-scale-safe

Conversation

@ultimatile

Copy link
Copy Markdown
Owner

Summary

The truncation-error paths accumulated sums of squares naively, so the reported error saturated to inf once a singular value's square exceeded the real type's range and underflowed to zero for the smallest discarded tails — the failure the shared scale_safe_norm / combine_norms kernel exists to avoid. This routes the truncation-error sites through that kernel.

Closes #480

Changes

  • crates/ariadnetor-linalg/src/decomposition/mod.rstrunc_svd_with_policy_dense: the final Frobenius-norm error via scale_safe_norm, and the target_trunc_err rank-selection scan folds the discarded norm with combine_norms, widening each value to f64 before it is squared.
  • crates/ariadnetor-linalg/src/block_sparse_decomp.rscross_sector_truncate: the same two changes for the block-sparse twin.
  • crates/ariadnetor-mps/src/truncate.rs — the four private step functions return the unsquared per-step error; the truncate_dense / truncate_bsp sweep drivers fold those errors with combine_norms.

Impact

The step functions are private and every call site is updated in the same diff. Public error semantics are unchanged — TruncResult.error and trunc_err remain the Frobenius norm of the discarded singular values — now finite and nonzero across the real type's range. The combine_norms-folded sqrt(sum of err^2) equals the previous accumulation mathematically.

Test plan

New tests pin the two failure modes and the scan fix:

  • dense trunc_svd: f64 overflow stays finite (1e200), f32 underflow stays nonzero (1e-23), and the f32 rank-selection scan discards a value whose si * si overflows in the real type.
  • block-sparse cross_sector_truncate: f64 overflow stays finite.
  • dense sweep: an overflow-scale swept error stays finite.

Existing boundary tests (..._exact_boundary, ..._strict_boundary) and the reconstruction-identity pins are unchanged and still pass. cargo test (linalg + mps + core): 934 passed, 0 failed; clippy and fmt clean.

Notes

A direct block-sparse sweep overflow test is not included: the faer SVD backend fails to converge on the general block matrices a real chain presents at these magnitudes, so the per-step error the sweep would accumulate is never produced. That path's overflow safety is covered by composition — the block-sparse SVD-site overflow test supplies a scale-safe per-step error, and the dense sweep test exercises the identical combine_norms fold.

Truncation-error paths accumulated sums of squares naively, so the
reported error saturated to inf once a singular value neared
sqrt(real::MAX) and underflowed to zero for tiny tails. Route the
sites through the shared scale_safe_norm / combine_norms kernel:

- trunc_svd (dense) and cross_sector_truncate (block-sparse): the
  final Frobenius-norm error via scale_safe_norm, and the
  target_trunc_err rank-selection scan via a combine_norms fold that
  widens each value to f64 before squaring (an f32 singular value near
  sqrt(f32::MAX) previously overflowed the scan's si*si and wrongly
  kept the value).
- truncate sweep drivers: fold per-step errors with combine_norms;
  the four step functions now return the unsquared error.

Public error semantics are unchanged (Frobenius norm of the discarded
singular values), now finite and nonzero across the real type's range.

Closes #480

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

Routes truncation-error accumulation in both the linalg SVD truncation paths and the MPS sweep truncation paths through the shared scale-safe norm kernel (scale_safe_norm / combine_norms) to prevent overflow-to-inf and underflow-to-0 in extreme singular-value regimes, while preserving the public meaning of truncation error as the Frobenius norm of discarded singular values.

Changes:

  • Replace naive sum-of-squares truncation-error accumulation with scale_safe_norm (tail norms) and combine_norms (incremental fold) in dense + block-sparse truncation code paths.
  • Change MPS private per-step truncation helpers to return unsquared per-step error and fold sweep totals via combine_norms.
  • Add regression tests covering f64 overflow scale, f32 underflow scale, and the f32 target-error scan overflow case; adjust existing test documentation accordingly.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.

Show a summary per file
File Description
crates/ariadnetor-linalg/src/decomposition/mod.rs Uses combine_norms for target-error scan and scale_safe_norm for final truncation error in dense truncated SVD.
crates/ariadnetor-linalg/src/block_sparse_decomp.rs Mirrors the dense fix for cross-sector block-sparse truncation (scan + final error).
crates/ariadnetor-mps/src/truncate.rs Switches sweep error aggregation to combine_norms and returns unsquared per-step errors from private truncation steps.
crates/ariadnetor-linalg/tests/decomposition.rs Adds dense trunc-SVD regression tests for overflow/underflow and scan correctness.
crates/ariadnetor-linalg/src/block_sparse_decomp/tests/mutant.rs Adds block-sparse trunc-SVD overflow-scale regression test.
crates/ariadnetor-mps/tests/mps/truncate.rs Updates test commentary to match scale-safe sweep aggregation behavior.
crates/ariadnetor-mps/tests/mps/truncate_mutant.rs Adds dense sweep overflow-scale regression test.
crates/ariadnetor-mps/tests/mps/truncate_block_sparse.rs Updates documentation/comments to reflect combine_norms folding and adds note about infeasible bsp sweep overflow fixture.
.cargo/mutants.toml Removes fragile line-number-based mutant excludes now obsoleted by the accumulation changes.

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

@ultimatile
ultimatile merged commit 1b73559 into main Jul 18, 2026
1 check passed
@ultimatile
ultimatile deleted the fix/480-trunc-error-scale-safe branch July 18, 2026 20:44
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.

Migrate truncation-error accumulations onto the scale-safe kernel

2 participants