Skip to content

Nicole 0.4.1

Latest

Choose a tag to compare

@changkai-zhang changkai-zhang released this 19 Sep 17:27

Nicole 0.4.1 — Relative Squared Discarded Weight in SVD

Release Date: September 20, 2026

Version 0.4.1 redefines the "discarded_weight" diagnostic reported by svd(..., requires_info=True). It was previously the raw sum of truncated singular values; it is now the relative squared weight ‖T − T_trunc‖²/‖T‖², the fractional squared 2-norm lost by truncation, which is the quantity conventionally used to monitor truncation error in DMRG and related algorithms. For SU(2) tensors, each reduced singular value is weighted by the dimension of its irrep so that the reported value is the physical norm loss in the full state space. Alongside this, the package metadata is updated with new author and maintainer contact information. The call signature and return structure of svd are unchanged.

🔧 API Changes

"discarded_weight" is now a relative squared weight

svd(T, axis, trunc=..., requires_info=True) returns an info dict whose "discarded_weight" entry previously accumulated Σ_dropped s — the plain sum of singular values removed by "thresh" and "nkeep" truncation. This quantity has no fixed scale (it grows with ‖T‖) and is not the norm-loss measure that truncation error analyses actually use. It is now defined as

discarded_weight = (Σ_q d_q Σ_dropped s²) / (Σ_q d_q Σ_all s²)

where the outer sum runs over left charge sectors q and d_q = irrep_dim(q) is the multiplicity of that sector in the full state space. The denominator equals T.norm()**2, so the result lies in [0, 1) and is exactly 1 − ‖T_trunc‖²/‖T‖². A value of 1e-8 therefore means the truncated tensor differs from the original by a relative 2-norm error of 1e-4.

Implementation details:

  • The untruncated spectrum of each sector is accumulated into the denominator before any truncation mask is applied, so the normalization is always against the full tensor.
  • Under "thresh" truncation, the squared weight of masked-out singular values is accumulated per sector; under global "nkeep" truncation, the per-sector multiplicity is recovered from the charge stored alongside each singular value in the global sort.
  • For Abelian groups d_q = 1 and the formula reduces to the plain relative squared sum. For SU(2), each reduced singular value stands for d_q degenerate Schmidt values, and the factor is required for the weighted spectrum to reproduce T.norm()**2.
  • The multiplicity factor weights the reported value only. It does not enter the "thresh" or "nkeep" truncation criteria, since all d_q Schmidt vectors of a multiplet share the same singular value and are kept or dropped together.
  • An identically-zero tensor has no spectrum to lose and reports 0.0 instead of dividing by zero.

Both the extra accumulation and the multiplicity lookup are gated on requires_info=True, so calls without it pay no additional cost.

Package metadata

The author email in pyproject.toml is updated to c.zhang@ideogenesis.ai, and a maintainers entry for Ideogenesis AI <developer@ideogenesis.ai> is added.

📖 Documentation

  • docs/api/decomposition/svd.md now states the [0, 1) range and the ‖T − T_trunc‖²/‖T‖² definition of "discarded_weight", and adds a note on the irrep-dimension factor for generic symmetry groups.
  • docs/examples/operations/decomposition-examples.md explains that the reported weight is relative and squared, with a worked interpretation of what 1e-8 means in terms of 2-norm error. The example output is printed in scientific notation to suit the new scale.
  • The svd docstring gains a Notes paragraph giving the formula and the role of d_q.
  • The documentation hero image is refreshed.

🧪 Test Suite (1635 tests)

  • 1625 tests pass, 10 skipped (CUDA-only tests on a CUDA-less CI runner)
  • 5 existing info tests in tests/operations/test_factorize.py (nkeep, thresh, combined, multi-block) are updated to assert the relative squared weight against the full and truncated S_dict spectra
  • 3 new tests in tests/operations/test_factorize.py:
    • test_svd_info_discarded_weight_bounded checks that the reported value lies in [0, 1)
    • test_svd_info_matches_reconstruction_norm rebuilds T_trunc = U · S · Vh via decomp and asserts discarded_weight == 1 − ‖T_trunc‖²/‖T‖² directly from the definition
    • test_svd_su2_discarded_weight_multiplicity verifies for an SU(2) tensor that the irrep_dim-weighted full spectrum reproduces T.norm()**2, and that the reported value matches the reconstruction norm loss under nkeep truncation

📊 Statistics

Code Changes

  • 8 commits since v0.4.0
  • 6 files changed: 111 insertions, 26 deletions
  • Source module touched: src/nicole/decomp.py
  • Test module touched: tests/operations/test_factorize.py
  • Documentation touched: docs/api/decomposition/svd.md, docs/examples/operations/decomposition-examples.md, docs/images/hero.png
  • Metadata touched: pyproject.toml

✅ Compatibility

Breaking Changes: The numerical meaning of info["discarded_weight"] returned by svd(..., requires_info=True) has changed. Code that compared it against an absolute threshold calibrated to the old sum-of-singular-values definition must be recalibrated to the relative squared scale in [0, 1). The svd signature, the shape of the return tuple, the info dict keys, and all truncation behavior are unchanged; U, S_dict, and Vh are bit-for-bit identical to v0.4.0. Calls without requires_info=True are unaffected.

Requirements:

  • Python ≥ 3.11
  • PyTorch ≥ 2.5
  • Yuzuha ≥ 0.1.5

📝 Notes

The relative squared definition is the standard truncation-error measure in tensor-network algorithms: the sum of discarded squared Schmidt values is the squared distance between the original and truncated states, and normalizing by ‖T‖² makes it independent of the overall scale of the tensor. For SU(2) tensors, the singular values obtained from the block-wise SVD are reduced: each one stands for a full multiplet of 2j + 1 degenerate Schmidt values, so the irrep-dimension factor is needed for the squared spectrum to add up to ‖T‖². Omitting it would systematically undercount the weight carried by higher-spin sectors.