Skip to content

fix: clamp negative eigenvalues before sqrt in LRD predict#21

Open
jamgochiana wants to merge 2 commits into
mainfrom
arec/fix-predict-sqrt
Open

fix: clamp negative eigenvalues before sqrt in LRD predict#21
jamgochiana wants to merge 2 commits into
mainfrom
arec/fix-predict-sqrt

Conversation

@jamgochiana

Copy link
Copy Markdown
Collaborator

Closes #19

Root cause

predict on an LRDMvNormal computes the conditional low-rank factor via an
eigendecomposition of I - I_plus_FF_inv followed by a matrix square root
(src/predict.jl:110-111). When the rank nearly saturates the dimension
(e.g. d=3, rank=2), the eigenvalues of I - I_plus_FF_inv are
mathematically nonnegative but can tip slightly negative in floating point
(e.g. -2.4e-14). The unclamped sqrt then throws
DomainError: sqrt was called with a negative real argument.

Fix

Clamp the eigenvalues at zero before the square root:
sqrt.(max.(λ, 0)). This is a numerical-safety change only — it does not
alter results for well-conditioned inputs, since a value like -2.4e-14
becomes 0, which is the correct mathematical value.

Regression test

Added a deterministic regression test in test/test_prediction.jl (Edge
Cases testset) that constructs a d=3, rank=2 LRDMvNormal with seed 2
and conditions the 3D joint on coordinate 1 (input_indices=[1],
output_indices=[2,3]). This reproduced the DomainError on the unmodified
code and asserts the returned conditional has length 2 after the fix. Full
suite passes (Prediction: 25/25, LRDMvNormal: 53/53, Fitting: 84/84).

🤖 Generated with Claude Code

jamgochiana and others added 2 commits July 9, 2026 18:23
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When conditioning an LRDMvNormal whose rank nearly saturates the dimension
(e.g. d=3, rank=2), the eigenvalues of I - I_plus_FF_inv are mathematically
nonnegative but can tip slightly negative in floating point (e.g. -2.4e-14).
The unclamped sqrt then throws DomainError.

Clamp eigenvalues at zero with sqrt.(max.(λ, 0)) before taking the matrix
square root. This does not change results for well-conditioned inputs: a
value like -2.4e-14 becomes 0, which is the correct mathematical value.

Adds a deterministic regression test (seed 2) that reproduced the
DomainError prior to this fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

predict() throws DomainError from sqrt of numerically-negative eigenvalue (LRD conditional)

1 participant