#![allow(clippy::disallowed_methods, clippy::float_cmp)]
use trueno::{Matrix, SymmetricEigen, Vector};
#[test]
fn f092_perturbation_stability() {
let a = Vector::from_slice(&[1.0f32, 2.0, 3.0, 4.0, 5.0]);
let b = Vector::from_slice(&[5.0f32, 4.0, 3.0, 2.0, 1.0]);
let baseline_dot = a.dot(&b).unwrap();
let baseline_add = a.add(&b).unwrap();
let eps = 1e-6f32;
let a_perturbed: Vec<f32> = a.as_slice().iter().map(|x| x * (1.0 + eps)).collect();
let a_perturbed = Vector::from_slice(&a_perturbed);
let perturbed_dot = a_perturbed.dot(&b).unwrap();
let perturbed_add = a_perturbed.add(&b).unwrap();
let dot_relative_change = (perturbed_dot - baseline_dot).abs() / baseline_dot.abs();
assert!(
dot_relative_change < 10.0 * eps,
"F092 FALSIFIED: dot product not stable under perturbation (change: {})",
dot_relative_change
);
for (i, (orig, pert)) in
baseline_add.as_slice().iter().zip(perturbed_add.as_slice().iter()).enumerate()
{
let relative_change = (pert - orig).abs() / orig.abs().max(1e-10);
assert!(
relative_change < 2.0 * eps,
"F092 FALSIFIED: add[{}] not stable (change: {})",
i,
relative_change
);
}
println!("F092 PASSED: Operations stable under {:.0e} perturbation", eps);
}
#[test]
fn f093_matmul_stability() {
#[rustfmt::skip]
let a = Matrix::from_vec(3, 3, vec![
2.0, 0.1, 0.1,
0.1, 2.0, 0.1,
0.1, 0.1, 2.0,
]).unwrap();
#[rustfmt::skip]
let b = Matrix::from_vec(3, 3, vec![
1.0, 0.5, 0.25,
0.5, 1.0, 0.5,
0.25, 0.5, 1.0,
]).unwrap();
let c = a.matmul(&b).unwrap();
for i in 0..3 {
for j in 0..3 {
let val = c.get(i, j).unwrap();
assert!(val.is_finite(), "F093 FALSIFIED: matmul[{},{}] is not finite: {}", i, j, val);
}
}
let expected_00 = 2.0 * 1.0 + 0.1 * 0.5 + 0.1 * 0.25;
let actual_00 = c.get(0, 0).unwrap();
assert!(
(actual_00 - expected_00).abs() < 1e-5,
"F093 FALSIFIED: matmul[0,0] incorrect (expected {}, got {})",
expected_00,
actual_00
);
println!("F093 PASSED: Matrix multiplication numerically stable");
}
#[test]
fn f094_eigen_well_conditioned() {
#[rustfmt::skip]
let a = Matrix::from_vec(3, 3, vec![
3.0, 1.0, 0.5,
1.0, 3.0, 1.0,
0.5, 1.0, 3.0,
]).unwrap();
let eigen = SymmetricEigen::new(&a).expect("eigendecomposition should succeed");
let values = eigen.eigenvalues();
for (i, val) in values.iter().enumerate() {
assert!(*val > 0.0, "F094 FALSIFIED: eigenvalue[{}] not positive: {}", i, val);
}
let cond = values[0] / values[values.len() - 1];
assert!(cond < 10.0, "F094 FALSIFIED: condition number too high: {}", cond);
let reconstructed = eigen.reconstruct().unwrap();
let mut max_error = 0.0f32;
for i in 0..3 {
for j in 0..3 {
let orig = a.get(i, j).unwrap();
let recon = reconstructed.get(i, j).unwrap();
max_error = max_error.max((orig - recon).abs());
}
}
assert!(max_error < 1e-5, "F094 FALSIFIED: reconstruction error too large: {}", max_error);
println!(
"F094 PASSED: Eigen decomposition stable (cond: {:.2}, error: {:.2e})",
cond, max_error
);
}
#[test]
fn f095_ill_conditioned_warning() {
#[rustfmt::skip]
let a = Matrix::from_vec(3, 3, vec![
1e6, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1e-6,
]).unwrap();
let result = SymmetricEigen::new(&a);
match result {
Ok(eigen) => {
let values = eigen.eigenvalues();
assert!((values[0] - 1e6).abs() / 1e6 < 1e-3, "F095: largest eigenvalue wrong");
println!(
"F095 PASSED: Ill-conditioned matrix handled (cond ~{:.0e})",
values[0] / values[values.len() - 1]
);
}
Err(e) => {
println!("F095 PASSED: Ill-conditioned matrix rejected with error: {}", e);
}
}
}
#[test]
fn f096_dot_product_order() {
let n = 10000;
let data: Vec<f32> = (1..=n).map(|i| 1.0 / (i as f32)).collect();
let v = Vector::from_slice(&data);
let ones: Vec<f32> = vec![1.0; n];
let ones_v = Vector::from_slice(&ones);
let forward_sum = ones_v.dot(&v).unwrap();
let data_rev: Vec<f32> = data.iter().rev().copied().collect();
let v_rev = Vector::from_slice(&data_rev);
let reverse_sum = ones_v.dot(&v_rev).unwrap();
let relative_diff = (forward_sum - reverse_sum).abs() / forward_sum.abs();
assert!(
relative_diff < 1e-5,
"F096 FALSIFIED: dot product order dependent (diff: {})",
relative_diff
);
println!("F096 PASSED: Dot product order stable (diff: {:.2e})", relative_diff);
}
#[test]
fn f097_norm_stability() {
let large_val = 1e18f32;
let data = vec![large_val; 4];
let v = Vector::from_slice(&data);
let dot = v.dot(&v).unwrap();
let norm_sq = dot;
assert!(norm_sq.is_finite(), "F097 FALSIFIED: norm squared overflowed");
let expected = 4.0 * large_val * large_val;
let relative_error = (norm_sq - expected).abs() / expected;
assert!(
relative_error < 1e-5,
"F097 FALSIFIED: norm squared error too large: {} (expected ~{:.2e}, got {:.2e})",
relative_error,
expected,
norm_sq
);
let small_val = 1e-30f32;
let data_small = vec![small_val; 4];
let v_small = Vector::from_slice(&data_small);
let dot_small = v_small.dot(&v_small).unwrap();
assert!(dot_small >= 0.0, "F097 FALSIFIED: small norm squared negative");
println!("F097 PASSED: Norm computation stable");
}
#[test]
fn f098_matvec_stability() {
#[rustfmt::skip]
let a = Matrix::from_vec(3, 3, vec![
1.0, 2.0, 3.0,
4.0, 5.0, 6.0,
7.0, 8.0, 9.0,
]).unwrap();
let x = Vector::from_slice(&[1.0f32, 1.0, 1.0]);
let y = a.matvec(&x).unwrap();
let expected = [6.0f32, 15.0, 24.0];
for (i, (actual, exp)) in y.as_slice().iter().zip(expected.iter()).enumerate() {
assert!(
(actual - exp).abs() < 1e-5,
"F098 FALSIFIED: matvec[{}] incorrect (expected {}, got {})",
i,
exp,
actual
);
}
println!("F098 PASSED: Matrix-vector multiply stable");
}
#[test]
fn f099_higham_stability_suite() {
let a = Vector::from_slice(&[1.0f32 + 1e-5, 1.0]);
let b = Vector::from_slice(&[1.0f32, 1.0 + 1e-5]);
let diff = a.sub(&b).unwrap();
let expected = [1e-5f32, -1e-5f32];
for (i, (actual, exp)) in diff.as_slice().iter().zip(expected.iter()).enumerate() {
let relative_error = (actual - exp).abs() / exp.abs().max(1e-10);
assert!(
relative_error < 0.2,
"F099.1 FALSIFIED: cancellation error at [{}]: {} vs {} (rel error: {})",
i,
actual,
exp,
relative_error
);
}
let n = 100000;
let ones: Vec<f32> = vec![1.0; n];
let ones_v = Vector::from_slice(&ones);
let sum = ones_v.dot(&ones_v).unwrap();
let expected_sum = n as f32;
let relative_error = (sum - expected_sum).abs() / expected_sum;
assert!(
relative_error < 1e-5,
"F099.2 FALSIFIED: large sum error: {} (expected {}, got {})",
relative_error,
expected_sum,
sum
);
let alternating: Vec<f32> = (0..1000).map(|i| if i % 2 == 0 { 1.0 } else { -1.0 }).collect();
let alt_v = Vector::from_slice(&alternating);
let sum_sq = alt_v.dot(&alt_v).unwrap();
assert!(
(sum_sq - 1000.0).abs() < 1e-3,
"F099.3 FALSIFIED: alternating series error: {}",
sum_sq - 1000.0
);
println!("F099 PASSED: Higham stability suite complete");
}
fn _estimate_condition_number_2x2(a: f32, b: f32, c: f32, d: f32) -> f32 {
let norm_a = (a * a + b * b + c * c + d * d).sqrt();
let det = a * d - b * c;
if det.abs() < 1e-10 {
return f32::INFINITY;
}
let norm_ainv = (d * d + b * b + c * c + a * a).sqrt() / det.abs();
norm_a * norm_ainv
}