Skip to content

Commit f763b11

Browse files
committed
Added: regression tests for solver and determinant overflow handling
Add test cases to verify that LDLT and LU solvers, as well as determinant calculations, correctly detect and return `LaError::NonFinite` when intermediate calculations overflow to infinity despite having finite inputs. Refs: #67
1 parent 0ab3c33 commit f763b11

3 files changed

Lines changed: 44 additions & 0 deletions

File tree

src/ldlt.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,4 +418,19 @@ mod tests {
418418
let err = ldlt.solve_vec(b).unwrap_err();
419419
assert_eq!(err, LaError::NonFinite { row: None, col: 1 });
420420
}
421+
422+
#[test]
423+
fn nonfinite_solve_vec_diagonal_solve_overflow() {
424+
// Diagonal SPD matrix with a tiny diagonal entry just above the
425+
// singularity tolerance. Forward substitution passes through the
426+
// large RHS unchanged, then the diagonal solve z[1] = y[1] / D[1]
427+
// = 1e300 / 1e-11 = 1e311 overflows f64, exercising the
428+
// `!v.is_finite()` branch of the diagonal solve.
429+
let a = Matrix::<2>::from_rows([[1.0, 0.0], [0.0, 1.0e-11]]);
430+
let ldlt = a.ldlt(DEFAULT_SINGULAR_TOL).unwrap();
431+
432+
let b = Vector::<2>::new([0.0, 1.0e300]);
433+
let err = ldlt.solve_vec(b).unwrap_err();
434+
assert_eq!(err, LaError::NonFinite { row: None, col: 1 });
435+
}
421436
}

src/lu.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,4 +485,20 @@ mod tests {
485485
let err = lu.solve_vec(b).unwrap_err();
486486
assert_eq!(err, LaError::NonFinite { row: None, col: 1 });
487487
}
488+
489+
#[test]
490+
fn solve_vec_nonfinite_back_substitution_sum_overflow() {
491+
// Upper-triangular U with a very large off-diagonal in row 1 and a
492+
// very large x[2] produced by the RHS. The back-substitution
493+
// accumulator `sum = (-row[j]).mul_add(x[j], sum)` overflows while
494+
// reducing row 1, so the failure is detected via the `!sum.is_finite()`
495+
// branch of the combined diag/sum check (distinct from the
496+
// `q = sum / diag` overflow path covered above).
497+
let a = Matrix::<3>::from_rows([[1.0, 0.0, 0.0], [0.0, 1.0, 1.0e200], [0.0, 0.0, 1.0]]);
498+
let lu = a.lu(DEFAULT_PIVOT_TOL).unwrap();
499+
500+
let b = Vector::<3>::new([0.0, 0.0, 1.0e200]);
501+
let err = lu.solve_vec(b).unwrap_err();
502+
assert_eq!(err, LaError::NonFinite { row: None, col: 1 });
503+
}
488504
}

src/matrix.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,19 @@ mod tests {
710710
);
711711
}
712712

713+
#[test]
714+
fn det_returns_nonfinite_error_for_overflow_with_finite_entries() {
715+
// det_direct produces an overflowing f64 (1e300 * 1e300 = ∞) even
716+
// though every matrix entry is finite. The entry scan in `det`
717+
// falls through and returns NonFinite { row: None, col: 0 } to signal
718+
// a computed overflow rather than a NaN/∞ input.
719+
let m = Matrix::<2>::from_rows([[1e300, 0.0], [0.0, 1e300]]);
720+
assert_eq!(
721+
m.det(DEFAULT_PIVOT_TOL),
722+
Err(LaError::NonFinite { row: None, col: 0 })
723+
);
724+
}
725+
713726
#[test]
714727
fn det_direct_is_const_evaluable_d2() {
715728
// Const evaluation proves the function is truly const fn.

0 commit comments

Comments
 (0)