Skip to content

Commit df7a358

Browse files
authored
Merge pull request #184 from acgetchell/perf/154-inf-norm
perf(matrix): improve inf_norm throughput
2 parents 9292e32 + dc28f98 commit df7a358

1 file changed

Lines changed: 85 additions & 12 deletions

File tree

src/matrix.rs

Lines changed: 85 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)