#[path = "property/krylov/common.rs"]
#[allow(dead_code)]
mod common;
use rustebra::krylov::{ConvergenceError, inverse_power_iteration, power_iteration};
use rustebra::storage::StaticStorage;
use common::{
ALGORITHM_TOL, ASSERTION_TOL, SINGULAR_TOL, approx_eq_eigenvector, fixed_eigenvector_3,
fixed_similarity_3, max_iter_for,
};
#[test]
fn power_iteration_converges_at_the_gap_ratio_boundary_within_the_derived_budget() {
let a = StaticStorage::new([100.0, 0.0, 0.0, 98.0]);
let v0 = StaticStorage::new([1.0, 1.0]);
let mut eigenvector = [0.0; 2];
let mut scratch = [0.0; 2];
let eigenvalue = power_iteration(
&a,
2,
&v0,
max_iter_for(0.98, ALGORITHM_TOL),
ALGORITHM_TOL,
&mut eigenvector,
&mut scratch,
)
.unwrap();
assert!((eigenvalue - 100.0).abs() <= ASSERTION_TOL * 100.0);
assert!(approx_eq_eigenvector(
&eigenvector,
&[1.0, 0.0],
ASSERTION_TOL
));
}
#[test]
fn power_iteration_reports_exhaustion_when_the_budget_falls_short_of_the_gap() {
let a = StaticStorage::new([100.0, 0.0, 0.0, 98.0]);
let v0 = StaticStorage::new([1.0, 1.0]);
let mut eigenvector = [0.0; 2];
let mut scratch = [0.0; 2];
let result = power_iteration(
&a,
2,
&v0,
500,
ALGORITHM_TOL,
&mut eigenvector,
&mut scratch,
);
assert_eq!(result, Err(ConvergenceError::MaxIterationsExceeded));
}
#[test]
fn inverse_power_iteration_converges_at_the_rate_boundary_within_the_derived_budget() {
let a = StaticStorage::new([
1.0, 0.0, 0.0, 0.0, 1.02, 0.0, 0.0, 0.0, -3.0,
]);
let v0 = StaticStorage::new([1.0, 1.0, 1.0]);
let mut eigenvector = [0.0; 3];
let mut factor = [0.0; 9];
let mut pivots = [0_usize; 3];
let mut scratch = [0.0; 3];
let eigenvalue = inverse_power_iteration(
&a,
3,
&v0,
0.0,
max_iter_for(1.0 / 1.02, ALGORITHM_TOL),
ALGORITHM_TOL,
SINGULAR_TOL,
&mut eigenvector,
&mut factor,
&mut pivots,
&mut scratch,
)
.unwrap();
assert!((eigenvalue - 1.0).abs() <= ASSERTION_TOL);
assert!(approx_eq_eigenvector(
&eigenvector,
&[1.0, 0.0, 0.0],
ASSERTION_TOL
));
}
#[test]
fn inverse_power_iteration_reports_exhaustion_when_the_budget_falls_short_of_the_rate() {
let a = StaticStorage::new([
1.0, 0.0, 0.0, 0.0, 1.02, 0.0, 0.0, 0.0, -3.0,
]);
let v0 = StaticStorage::new([1.0, 1.0, 1.0]);
let mut eigenvector = [0.0; 3];
let mut factor = [0.0; 9];
let mut pivots = [0_usize; 3];
let mut scratch = [0.0; 3];
let result = inverse_power_iteration(
&a,
3,
&v0,
0.0,
500,
ALGORITHM_TOL,
SINGULAR_TOL,
&mut eigenvector,
&mut factor,
&mut pivots,
&mut scratch,
);
assert_eq!(result, Err(ConvergenceError::MaxIterationsExceeded));
}
#[test]
fn power_iteration_finds_the_dominant_eigenpair_of_an_ill_conditioned_matrix() {
let a = fixed_similarity_3([100.0, 1e-6, -50.0]);
let v0 = StaticStorage::new([1.0, 1.0, 1.0]);
let mut eigenvector = [0.0; 3];
let mut scratch = [0.0; 3];
let eigenvalue = power_iteration(
&StaticStorage::new(a),
3,
&v0,
max_iter_for(0.5, ALGORITHM_TOL),
ALGORITHM_TOL,
&mut eigenvector,
&mut scratch,
)
.unwrap();
assert!((eigenvalue - 100.0).abs() <= ASSERTION_TOL * 100.0);
assert!(approx_eq_eigenvector(
&eigenvector,
&fixed_eigenvector_3(0),
ASSERTION_TOL
));
}
#[test]
fn inverse_power_iteration_resolves_the_tiny_eigenvalue_of_an_ill_conditioned_matrix() {
let a = fixed_similarity_3([100.0, 1e-6, -50.0]);
let v0 = StaticStorage::new([1.0, 1.0, 1.0]);
let mut eigenvector = [0.0; 3];
let mut factor = [0.0; 9];
let mut pivots = [0_usize; 3];
let mut scratch = [0.0; 3];
let eigenvalue = inverse_power_iteration(
&StaticStorage::new(a),
3,
&v0,
0.0,
50,
ALGORITHM_TOL,
SINGULAR_TOL,
&mut eigenvector,
&mut factor,
&mut pivots,
&mut scratch,
)
.unwrap();
assert!(
(eigenvalue - 1e-6).abs() <= ASSERTION_TOL,
"got {eigenvalue}"
);
assert!(approx_eq_eigenvector(
&eigenvector,
&fixed_eigenvector_3(1),
ASSERTION_TOL
));
}
#[test]
fn power_iteration_handles_entries_at_the_positive_magnitude_boundary() {
let a = StaticStorage::new([100.0, -99.0, -99.0, 100.0]);
let v0 = StaticStorage::new([1.0, 0.0]);
let mut eigenvector = [0.0; 2];
let mut scratch = [0.0; 2];
let eigenvalue = power_iteration(
&a,
2,
&v0,
200,
ALGORITHM_TOL,
&mut eigenvector,
&mut scratch,
)
.unwrap();
assert!((eigenvalue - 199.0).abs() <= ASSERTION_TOL * 199.0);
assert!(approx_eq_eigenvector(
&eigenvector,
&[1.0, -1.0],
ASSERTION_TOL
));
}
#[test]
fn power_iteration_handles_entries_at_the_negative_magnitude_boundary() {
let a = StaticStorage::new([-100.0, 99.0, 99.0, -100.0]);
let v0 = StaticStorage::new([1.0, 0.0]);
let mut eigenvector = [0.0; 2];
let mut scratch = [0.0; 2];
let eigenvalue = power_iteration(
&a,
2,
&v0,
200,
ALGORITHM_TOL,
&mut eigenvector,
&mut scratch,
)
.unwrap();
assert!((eigenvalue + 199.0).abs() <= ASSERTION_TOL * 199.0);
assert!(approx_eq_eigenvector(
&eigenvector,
&[1.0, -1.0],
ASSERTION_TOL
));
}
#[test]
fn inverse_power_iteration_handles_entries_at_the_magnitude_boundary() {
let a = StaticStorage::new([100.0, -99.0, -99.0, 100.0]);
let v0 = StaticStorage::new([1.0, 0.0]);
let mut eigenvector = [0.0; 2];
let mut factor = [0.0; 4];
let mut pivots = [0_usize; 2];
let mut scratch = [0.0; 2];
let eigenvalue = inverse_power_iteration(
&a,
2,
&v0,
0.0,
200,
ALGORITHM_TOL,
SINGULAR_TOL,
&mut eigenvector,
&mut factor,
&mut pivots,
&mut scratch,
)
.unwrap();
assert!((eigenvalue - 1.0).abs() <= ASSERTION_TOL);
assert!(approx_eq_eigenvector(
&eigenvector,
&[1.0, 1.0],
ASSERTION_TOL
));
}