Skip to content

[linalg] Fix eigh JVP division-by-zero on degenerate spectra and add precision tests (follow-up to #40149) - #40257

Open
SakshamKapoor2911 wants to merge 1 commit into
jax-ml:mainfrom
SakshamKapoor2911:fix/eigh-jvp-nan-cliff
Open

[linalg] Fix eigh JVP division-by-zero on degenerate spectra and add precision tests (follow-up to #40149)#40257
SakshamKapoor2911 wants to merge 1 commit into
jax-ml:mainfrom
SakshamKapoor2911:fix/eigh-jvp-nan-cliff

Conversation

@SakshamKapoor2911

Copy link
Copy Markdown

#ai-generated

Summary

PR #40149 (5fb69a3f70) fixed the large-eigenvalue mantissa cliff ($|w| \ge 2^p$) by reordering the diagonal identity offset:

# Before #40149:
Fmat = lax.integer_pow(eye_n + w[..., np.newaxis, :] - w[..., np.newaxis], -1) - eye_n

# In #40149:
delta_w = w[..., np.newaxis, :] - w[..., np.newaxis]
Fmat = lax.integer_pow(delta_w + eye_n, -1) - eye_n

While this cleanly addresses strictly distinct spectra, for degenerate spectra (repeated eigenvalues $\lambda_i = \lambda_j$ with $i \neq j$), delta_w == 0 off-diagonal. Because eye_n is 0 off-diagonal, delta_w + eye_n has 0 at those positions, causing integer_pow(0, -1) to evaluate to inf ($1/0 \to \infty$) and silently corrupting the gradient pass with inf / NaN.

Prior to #40149, for normal-range eigenvalues, eye_n + delta_w evaluated to 1.0 on degenerate entries, keeping the reciprocal finite ($1/1 = 1$).

Fix

We mask all zero eigenvalue differences (delta_w == 0, which covers both the diagonal and all degenerate pairs) to 1.0 prior to taking the reciprocal:

with config.numpy_rank_promotion("allow"):
  delta_w = w[..., np.newaxis, :] - w[..., np.newaxis]
  delta_w = lax.select(delta_w == 0, lax.full_like(delta_w, 1), delta_w)
  Fmat = lax.integer_pow(delta_w, -1) - eye_n
  1. On the diagonal ($i = j$): delta_w == 0 is masked to 1.0 $\to$ 1/1 - 1 = 0.
  2. On degenerate off-diagonals ($i \neq j, \lambda_i = \lambda_j$): delta_w == 0 is masked to 1.0 $\to$ 1/1 - 0 = 1.0 (finite, arbitrary for the degenerate invariant subspace).
  3. On distinct off-diagonals: delta_w \neq 0 is inverted normally as $\frac{1}{\lambda_j - \lambda_i}$.

Tests Added

Added 5 comprehensive test methods in tests/linalg_test.py and tests/lax_test.py:

  1. testEighGradDegenerate: Verifies that matrices with exactly repeated eigenvalues (e.g. $[2^p, 2^p, 2 \cdot 2^p]$) produce finite tangents in jvp across float32, float64, complex64, complex128.
  2. testEighGradLargeEigenvalues: Tests the $2^p$ mantissa cliff for jsp.linalg.eigh in forward and reverse mode (jvp, jacfwd, jacrev, holomorphic jacrev).
  3. testEighGradIllConditioned: Tests wide eigenvalue spreads ($10^{10}$ and $10^{16}$) against the analytic perturbation-theory derivative $U (F \odot T)$.
  4. testEighJvpFiniteLargeEigenvalues: Direct primitive test on lax.linalg.eigh.
  5. testEighJvpLargeEigenvaluesAccuracy: Numeric accuracy verification against closed-form Givens perturbation reference.

Local Verification

  • 20/20 test cases pass with JAX_ENABLE_X64=1 and default precision.
  • Negative controls verified: all 18 cliff and degenerate tests fail without this fix.
  • Lint and format checks clean.

…precision tests

PR jax-ml#40149 resolved the large-eigenvalue mantissa cliff by computing eigenvalue
differences prior to reciprocal calculation (Fmat = (delta_w + eye_n)^(-1) - eye_n).
However, for degenerate spectra (repeated eigenvalues), off-diagonal entries have
delta_w == 0 and eye_n == 0, causing integer_pow(0, -1) to evaluate to inf.

This patch masks all zero eigenvalue differences (delta_w == 0, spanning both the
diagonal and degenerate off-diagonal pairs) to 1 before inverting:
delta_w = lax.select(delta_w == 0, lax.full_like(delta_w, 1), delta_w)
Fmat = lax.integer_pow(delta_w, -1) - eye_n

This preserves finite invariant subspace tangents for degenerate spectra, keeps
exact zero on the diagonal, and adds 5 comprehensive test methods in tests/lax_test.py
and tests/linalg_test.py covering cliffs, ill-conditioned matrices, and degenerate spectra.
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.

1 participant