Skip to content

fix(nvf): recompute the floor's SVD with ?gesvd where ?gesdd fails, and say so - #128

Merged
yonghakim merged 3 commits into
mainfrom
claude/svd-gesvd-fallback
Oct 30, 2026
Merged

yonghakim merged 3 commits into
mainfrom
claude/svd-gesvd-fallback

Conversation

@yonghakim

Copy link
Copy Markdown
Member

Fixes #127.

The NVF inverse-rule floor takes singular vectors of <1/eps> where it binds. LAPACK's divide-and-conquer ?gesdd can report no convergence on a finite, well-conditioned matrix, through a DLASD2 deflation bug (Reference-LAPACK #1138, fixed by its PR #1140, in no reference release yet). The bug is in MKL up to 2026.1, which PyTorch's CPU wheels bundle (pytorch/pytorch#202596), in reference LAPACK up to 3.12.1, and in OpenBLAS up to 0.3.30. The previous retry used NumPy's SVD. That is the same driver, and it fails the same way wherever NumPy links an affected LAPACK, such as the default MKL build on Anaconda's defaults channel.

Changes

  • _svd (NumPy, PyTorch): a failure on a finite matrix is recomputed with SciPy's QR-based ?gesvd on the host. A non-finite matrix keeps its error, and so does a torch.func.vmap lane, which has no host values.
  • JAX: ?gesdd failures come back as NaN factors rather than an error. Eager JAX checks for that and recomputes with SvdAlgorithm.QR. Under jax.jit nothing can be recomputed. NaN singular values are below no floor, so the layer is left unfloored, and a jax.debug.callback warns. That is harmless on a passive dielectric and wrong by the floor on a metal.
  • New wavebit.rcwa.diagnostics.SvdConvergenceWarning: it announces each case without naming the cause, since ?gesdd can fail for other reasons too.
  • Docs:
    • the reference page and public-names page (EN/KO) list the new warning;
    • section 6 of docs/development/torch-upstream-defects.md records the measurement and the cause;
    • the NVF theory page says a failure is retried;
    • the code comments no longer imply NumPy's SVD is a safe substitute.
  • Tests:
    • the PyTorch retry now goes through gesvd, not NumPy, and gives the same floored matrix and gradient;
    • new tests cover the NumPy retry, a non-finite matrix keeping its error, the eager JAX NaN retry, the jit warning on an unfloored gold layer, and the callback's per-lane reading under vmap.

Validation

Linux x86-64 (AVX-512), Python 3.13.17, torch 2.14.1+cpu, jax 0.12.0.1, NumPy 2.5.4, SciPy 1.18.1.

  • Real failures: on this torch build, torch.linalg.svd actually fails on two matrices: the r = 161.48 nm <1/eps> from NVF: decide the inverse-rule floor from the materials, not a full SVD #121 and the bidiagonal matrix from CPU torch.linalg.svd raises LinAlgError on well-conditioned matrices: bundled MKL 2024.2 carries LAPACK DLASD2 deflation bug (Reference-LAPACK #1138, fixed upstream) pytorch/pytorch#202596. _svd recomputed both, with residual 2.5e-14 and 5.7e-14 and singular values within 5.3e-14 and 3.7e-14 of NumPy's. Each warned once. A NaN matrix kept its LinAlgError.
  • Related tests: tests/rcwa/test_nvf_metal_inverse_rule.py, tests/rcwa/test_torch_vmap_autograd.py, tests/api/test_public_api.py and tests/api/test_docs_contract.py, on this branch merged with current main: 592 passed, 1 skipped, 1 failed. The failure, test_a_port_named_tuple_constructor_cannot_name_its_replacement, fails on main too. Python 3.13's namedtuple _replace raises TypeError there rather than ValueError, which is unrelated to this change.
  • Wider NVF run: tests/rcwa -k "nvf or factoriz or floor or inverse_rule" gave 918 passed and 30 skipped. 41 failed only because the optional contourpy was missing; with it installed, those four files gave 204 passed. No SvdConvergenceWarning was raised anywhere outside the new tests.
  • Smoke check: python tools/run_verification.py --profile quick gave 17 passed.

Not verified

  • the Sphinx build of the new autoclass entry (only the docs-contract test ran);
  • CUDA and GPU JAX;
  • a NumPy or SciPy actually linked to an affected MKL (the NumPy and JAX failure tests use a monkeypatched driver).

Generated by Claude Code

…nd say so

The NVF inverse-rule floor takes singular vectors of <1/eps> where it binds.
LAPACK's divide-and-conquer ?gesdd can report no convergence on a finite,
well-conditioned matrix: a DLASD2 deflation bug (Reference-LAPACK #1138)
present in MKL up to 2026.1 (bundled by PyTorch's CPU wheels), reference
LAPACK up to 3.12.1 and OpenBLAS up to 0.3.30. The previous retry used
NumPy's SVD, which is the same driver and fails the same way wherever NumPy
links an affected LAPACK (the default MKL build on Anaconda's defaults).

- NumPy and PyTorch: a failure on a finite matrix is recomputed with SciPy's
  QR-based ?gesvd on the host; a non-finite matrix keeps its error.
- JAX returns NaN factors instead of raising, so eager JAX checks them and
  recomputes with SvdAlgorithm.QR. Under jax.jit nothing can be recomputed;
  the layer is then left unfloored, and a jax.debug.callback warns.
- New wavebit.rcwa.diagnostics.SvdConvergenceWarning announces each case
  without naming the cause, since ?gesdd can fail for other reasons too.

Fixes #127

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RKzHy8gt3tzmq4rXfS56Le
…ilure

Section 6 of the torch upstream defects page: what was measured
(pytorch/pytorch#202596), the LAPACK cause, how it reaches wavebit only
through the NVF floor's SVD, and when the retry can go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RKzHy8gt3tzmq4rXfS56Le
…lure is retried

main now decides the passive-dielectric case on the host, so under jax.jit
a material that is a constant of the trace skips the SVD; only a layer whose
permittivity is a jit argument or a vmap lane (or a metal) still takes it.
The warning's docstring and the defects page said every NVF layer under jit.
The NVF theory page now also says a ?gesdd failure is recomputed with ?gesvd.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RKzHy8gt3tzmq4rXfS56Le
@yonghakim
yonghakim marked this pull request as ready for review October 30, 2026 01:05
@yonghakim
yonghakim enabled auto-merge (rebase) October 30, 2026 01:05
@yonghakim
yonghakim merged commit 93cf7cb into main Oct 30, 2026
65 checks passed
@yonghakim
yonghakim deleted the claude/svd-gesvd-fallback branch October 30, 2026 01:23
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.

NVF inverse-rule floor: the vector SVD can still hit the LAPACK DLASD2 bug (pytorch/pytorch#202596)

2 participants