Skip to content

Commit eb61d4f

Browse files
authored
Merge pull request #90 from developing-human/features/rref-stability
FIX: compare to epsilon during rref
2 parents adf71be + f987e34 commit eb61d4f

File tree

2 files changed

+56
-2
lines changed

2 files changed

+56
-2
lines changed

src/structure/matrix.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3293,14 +3293,17 @@ impl LinearAlgebra<Matrix> for Matrix {
32933293
///
32943294
/// Implementation of [RosettaCode](https://rosettacode.org/wiki/Reduced_row_echelon_form)
32953295
fn rref(&self) -> Matrix {
3296+
let max_abs = self.data.iter().fold(0f64, |acc, &x| acc.max(x.abs()));
3297+
let epsilon = (max_abs * 1e-12).max(1e-15);
3298+
32963299
let mut lead = 0usize;
32973300
let mut result = self.clone();
32983301
'outer: for r in 0..self.row {
32993302
if self.col <= lead {
33003303
break;
33013304
}
33023305
let mut i = r;
3303-
while result[(i, lead)] == 0f64 {
3306+
while result[(i, lead)].abs() < epsilon {
33043307
i += 1;
33053308
if self.row == i {
33063309
i = r;
@@ -3314,7 +3317,7 @@ impl LinearAlgebra<Matrix> for Matrix {
33143317
result.swap(i, r, Row);
33153318
}
33163319
let tmp = result[(r, lead)];
3317-
if tmp != 0f64 {
3320+
if tmp.abs() > epsilon {
33183321
unsafe {
33193322
result.row_mut(r).iter_mut().for_each(|t| *(*t) /= tmp);
33203323
}

tests/matrix.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,54 @@ fn test_kronecker() {
111111
let c1 = a1.kronecker(&b1);
112112
assert_eq!(c1, ml_matrix("0 5 0 10;6 7 12 14;0 15 0 20;18 21 24 28"));
113113
}
114+
115+
#[test]
116+
fn test_rref() {
117+
let a = ml_matrix(
118+
r#"
119+
-3 2 -1 -1;
120+
6 -6 7 -7;
121+
3 -4 4 -6"#,
122+
);
123+
let b = a.rref();
124+
125+
assert_eq!(
126+
b,
127+
ml_matrix(
128+
r#"
129+
1 0 0 2;
130+
0 1 0 2;
131+
0 0 1 -1"#
132+
)
133+
);
134+
}
135+
136+
#[test]
137+
fn test_rref_unstable() {
138+
let epsilon = 1e-10;
139+
140+
// this matrix used to become unstable during rref
141+
let a = ml_matrix(
142+
r#"
143+
1 1 0 0 0 1 0 1 31;
144+
1 1 1 1 0 0 1 1 185;
145+
0 0 1 0 0 1 1 1 165;
146+
1 0 1 0 1 1 0 1 32;
147+
1 0 1 0 0 0 1 1 174;
148+
0 0 1 0 1 1 1 1 171;
149+
0 1 1 0 1 1 0 1 27;
150+
1 0 0 1 0 1 0 0 20;
151+
1 0 1 1 0 1 0 0 23"#,
152+
);
153+
154+
let b = a.rref();
155+
156+
// creating a row like "0 0 0 0 0 0 0 0 1" will "prove" 0 == 1
157+
// which is a tell of numeric instability
158+
for row in 0..b.row {
159+
let ends_in_1 = (b[(row, b.col - 1)] - 1.0).abs() < epsilon;
160+
let rest_zeroes = (0..b.col - 1).all(|col| b[(row, col)].abs() < epsilon);
161+
162+
assert!(!(ends_in_1 && rest_zeroes));
163+
}
164+
}

0 commit comments

Comments
 (0)