@@ -166,11 +166,15 @@ impl<const D: usize> Matrix<D> {
166166 /// offending pair when this returns `false`.
167167 ///
168168 /// # NaN / infinity handling
169- /// Any NaN off-diagonal entry causes this predicate to return `false`
170- /// (because `NaN <= eps` is `false`). A matrix whose [`inf_norm`](Self::inf_norm)
171- /// is `+∞` can produce an `eps` of `+∞`, under which any finite asymmetry
172- /// is tolerated — callers who need strict equality on infinite entries
173- /// should validate finiteness separately.
169+ /// Any non-finite `|self[r][c] - self[c][r]|` (NaN or ±∞) causes this
170+ /// predicate to return `false`. This catches both NaN off-diagonals and
171+ /// asymmetric pairs where one side is infinite and the other is finite
172+ /// (which would otherwise slip through when `inf_norm()` blows `eps` up
173+ /// to `+∞` and makes `diff > eps` trivially false). A matrix whose
174+ /// [`inf_norm`](Self::inf_norm) is `+∞` can still tolerate *finite*
175+ /// asymmetries under an infinite `eps` — callers who need strict equality
176+ /// on large-magnitude finite entries should validate finiteness
177+ /// separately.
174178 ///
175179 /// # Panics
176180 /// In debug builds, panics if `rel_tol` is negative or NaN; in release
@@ -228,10 +232,15 @@ impl<const D: usize> Matrix<D> {
228232 for r in 0 ..D {
229233 for c in ( r + 1 ) ..D {
230234 let diff = ( self . rows [ r] [ c] - self . rows [ c] [ r] ) . abs ( ) ;
231- // NaN is reported as asymmetric: a NaN entry contaminates `diff`,
232- // and `diff > eps` alone would silently skip it because ordered
233- // comparisons against NaN are always `false`.
234- if diff. is_nan ( ) || diff > eps {
235+ // Any non-finite `diff` is reported as asymmetric:
236+ // * NaN contaminates one side only, and `diff > eps` would
237+ // silently skip it because ordered comparisons against NaN
238+ // are always `false`.
239+ // * ±∞ arises when exactly one of `self[r][c]` / `self[c][r]`
240+ // is infinite; a naive `diff > eps` misses this when the
241+ // matrix's `inf_norm()` pushes `eps` to `+∞` (because
242+ // `∞ > ∞` is `false`).
243+ if !diff. is_finite ( ) || diff > eps {
235244 cold_path ( ) ;
236245 return Some ( ( r, c) ) ;
237246 }
@@ -1027,6 +1036,18 @@ mod tests {
10271036 assert_eq ! ( a. first_asymmetry( 1e-12 ) , Some ( ( 0 , 2 ) ) ) ;
10281037 }
10291038
1039+ /// Regression: a single infinite off-diagonal paired with a finite entry
1040+ /// used to slip through as "symmetric" because `inf_norm()` blew `eps` up
1041+ /// to `+∞` and `∞ > ∞` evaluates to `false`. After the fix, any
1042+ /// non-finite `|a[r][c] - a[c][r]|` is reported as an asymmetry regardless
1043+ /// of `eps`.
1044+ #[ test]
1045+ fn first_asymmetry_flags_infinite_offdiagonal_against_finite ( ) {
1046+ let a = Matrix :: < 2 > :: from_rows ( [ [ 1.0 , f64:: INFINITY ] , [ 0.0 , 1.0 ] ] ) ;
1047+ assert_eq ! ( a. first_asymmetry( 1e-12 ) , Some ( ( 0 , 1 ) ) ) ;
1048+ assert ! ( !a. is_symmetric( 1e-12 ) ) ;
1049+ }
1050+
10301051 #[ cfg( debug_assertions) ]
10311052 #[ test]
10321053 #[ should_panic( expected = "rel_tol must be non-negative" ) ]
0 commit comments