@@ -518,16 +518,12 @@ impl<const D: usize> Matrix<D> {
518518 let mut c = 0 ;
519519 while c < D {
520520 row_sum += row[ c] . abs ( ) ;
521- if !row_sum. is_finite ( ) {
522- cold_path ( ) ;
523- return Err ( LaError :: non_finite_computation_matrix (
524- ArithmeticOperation :: MatrixInfinityNorm ,
525- r,
526- c,
527- ) ) ;
528- }
529521 c += 1 ;
530522 }
523+ if !row_sum. is_finite ( ) {
524+ cold_path ( ) ;
525+ return Err ( Self :: inf_norm_overflow_error ( row, r) ) ;
526+ }
531527 if row_sum > max_row_sum {
532528 max_row_sum = row_sum;
533529 }
@@ -537,6 +533,37 @@ impl<const D: usize> Matrix<D> {
537533 Ok ( max_row_sum)
538534 }
539535
536+ /// Replay an overflowed infinity-norm row to locate the first non-finite sum.
537+ ///
538+ /// This runs only after the success-path traversal has found a non-finite
539+ /// completed row sum. Because stored entries are finite and their absolute
540+ /// values are non-negative, replaying the same additions must find the
541+ /// first column whose addition overflowed; if every earlier prefix is
542+ /// finite, the final column is that first failure.
543+ #[ cold]
544+ const fn inf_norm_overflow_error ( row : & [ f64 ; D ] , row_index : usize ) -> LaError {
545+ let mut row_sum = 0.0 ;
546+ let mut col = 0 ;
547+ let last_col = D . saturating_sub ( 1 ) ;
548+ while col < last_col {
549+ row_sum += row[ col] . abs ( ) ;
550+ if !row_sum. is_finite ( ) {
551+ return LaError :: non_finite_computation_matrix (
552+ ArithmeticOperation :: MatrixInfinityNorm ,
553+ row_index,
554+ col,
555+ ) ;
556+ }
557+ col += 1 ;
558+ }
559+
560+ LaError :: non_finite_computation_matrix (
561+ ArithmeticOperation :: MatrixInfinityNorm ,
562+ row_index,
563+ last_col,
564+ )
565+ }
566+
540567 /// Returns `true` if the matrix is approximately symmetric within a relative tolerance.
541568 ///
542569 /// Two entries `self[r][c]` and `self[c][r]` are considered equal (for the
@@ -1758,20 +1785,56 @@ mod tests {
17581785 fn [ <matrix_inf_norm_max_row_sum_ $d d>] ( ) {
17591786 let mut rows = [ [ 0.0f64 ; $d] ; $d] ;
17601787
1761- // Row 0 has absolute row sum = D .
1788+ // Row 0 has a smaller absolute row sum.
17621789 for c in 0 ..$d {
1763- rows[ 0 ] [ c] = - 1.0 ;
1790+ rows[ 0 ] [ c] = 0.5 ;
17641791 }
17651792
1766- // Row 1 has smaller absolute row sum.
1793+ // The last row has absolute row sum = D .
17671794 for c in 0 ..$d {
1768- rows[ 1 ] [ c] = 0.5 ;
1795+ rows[ $d - 1 ] [ c] = - 1.0 ;
17691796 }
17701797
17711798 let m = Matrix :: <$d>:: try_from_rows( rows) . unwrap( ) ;
17721799 assert_abs_diff_eq!( m. inf_norm( ) . unwrap( ) , f64 :: from( $d) , epsilon = 0.0 ) ;
17731800 }
17741801
1802+ #[ test]
1803+ fn [ <matrix_inf_norm_reports_first_overflowing_column_ $d d>] ( ) {
1804+ let mut rows = [ [ 0.0f64 ; $d] ; $d] ;
1805+ rows[ $d - 1 ] [ 0 ] = f64 :: MAX ;
1806+ rows[ $d - 1 ] [ 1 ] = f64 :: MAX ;
1807+
1808+ let m = Matrix :: <$d>:: try_from_rows( rows) . unwrap( ) ;
1809+ assert_eq!(
1810+ m. inf_norm( ) ,
1811+ Err ( LaError :: non_finite_computation_matrix(
1812+ ArithmeticOperation :: MatrixInfinityNorm ,
1813+ $d - 1 ,
1814+ 1 ,
1815+ ) )
1816+ ) ;
1817+ }
1818+
1819+ #[ test]
1820+ fn [ <matrix_inf_norm_reports_first_overflowing_row_ $d d>] ( ) {
1821+ let mut rows = [ [ 0.0f64 ; $d] ; $d] ;
1822+ rows[ 0 ] [ 0 ] = f64 :: MAX ;
1823+ rows[ 0 ] [ $d - 1 ] = f64 :: MAX ;
1824+ rows[ $d - 1 ] [ 0 ] = f64 :: MAX ;
1825+ rows[ $d - 1 ] [ 1 ] = f64 :: MAX ;
1826+
1827+ let m = Matrix :: <$d>:: try_from_rows( rows) . unwrap( ) ;
1828+ assert_eq!(
1829+ m. inf_norm( ) ,
1830+ Err ( LaError :: non_finite_computation_matrix(
1831+ ArithmeticOperation :: MatrixInfinityNorm ,
1832+ 0 ,
1833+ $d - 1 ,
1834+ ) )
1835+ ) ;
1836+ }
1837+
17751838 #[ test]
17761839 fn [ <matrix_identity_lu_det_solve_ $d d>] ( ) {
17771840 let m = Matrix :: <$d>:: identity( ) ;
@@ -1818,6 +1881,16 @@ mod tests {
18181881 gen_matrix_tests ! ( 4 ) ;
18191882 gen_matrix_tests ! ( 5 ) ;
18201883
1884+ #[ test]
1885+ fn matrix_inf_norm_preserves_left_to_right_row_sum_order ( ) {
1886+ let large = 9_007_199_254_740_992.0 ;
1887+ let matrix =
1888+ Matrix :: < 4 > :: try_from_rows ( [ [ large, 1.0 , 1.0 , 1.0 ] , [ 0.0 ; 4 ] , [ 0.0 ; 4 ] , [ 0.0 ; 4 ] ] )
1889+ . unwrap ( ) ;
1890+
1891+ assert_eq ! ( matrix. inf_norm( ) , Ok ( large) ) ;
1892+ }
1893+
18211894 // === det_direct tests ===
18221895
18231896 #[ test]
0 commit comments