use scirs2_core::ndarray::{Array1, Array2};
use scirs2_linalg::compat::{ArrayLinalgExt, UPLO};
use sklears_core::error::{Result as SklResult, SklearsError};
pub struct IterativeRefinement {
max_iterations: usize,
tolerance: f64,
residual_threshold: f64,
condition_number_threshold: f64,
}
impl Default for IterativeRefinement {
fn default() -> Self {
Self::new()
}
}
impl IterativeRefinement {
pub fn new() -> Self {
Self {
max_iterations: 10,
tolerance: 1e-12,
residual_threshold: 1e-10,
condition_number_threshold: 1e12,
}
}
pub fn max_iterations(mut self, max_iterations: usize) -> Self {
self.max_iterations = max_iterations;
self
}
pub fn tolerance(mut self, tolerance: f64) -> Self {
self.tolerance = tolerance;
self
}
pub fn residual_threshold(mut self, residual_threshold: f64) -> Self {
self.residual_threshold = residual_threshold;
self
}
pub fn condition_number_threshold(mut self, condition_number_threshold: f64) -> Self {
self.condition_number_threshold = condition_number_threshold;
self
}
pub fn solve(&self, a: &Array2<f64>, b: &Array1<f64>) -> SklResult<RefinementResult> {
if a.nrows() != a.ncols() {
return Err(SklearsError::InvalidInput(
"Matrix A must be square".to_string(),
));
}
if a.nrows() != b.len() {
return Err(SklearsError::InvalidInput(
"Matrix A and vector b dimensions must match".to_string(),
));
}
let condition_number = self.estimate_condition_number(a)?;
let is_ill_conditioned = condition_number > self.condition_number_threshold;
let mut x = match a.solve(b) {
Ok(solution) => solution,
Err(_) => {
let regularized_a = self.add_regularization(a, 1e-12);
regularized_a.solve(b).map_err(|_| {
SklearsError::InvalidInput("Matrix is singular or nearly singular".to_string())
})?
}
};
let mut residuals = Vec::new();
let mut corrections = Vec::new();
let mut converged = false;
for iteration in 0..self.max_iterations {
let residual = b - &a.dot(&x);
let residual_norm = self.vector_norm(&residual);
residuals.push(residual_norm);
if residual_norm < self.residual_threshold {
converged = true;
break;
}
let delta_x = match a.solve(&residual) {
Ok(correction) => correction,
Err(_) => {
let regularized_a = self.add_regularization(a, 1e-12);
regularized_a.solve(&residual).map_err(|_| {
SklearsError::InvalidInput("Cannot compute correction".to_string())
})?
}
};
let correction_norm = self.vector_norm(&delta_x);
corrections.push(correction_norm);
x = &x + &delta_x;
if correction_norm < self.tolerance {
converged = true;
break;
}
if iteration > 0 && correction_norm > corrections[iteration - 1] * 2.0 {
break;
}
}
let final_residual = b - &a.dot(&x);
let final_residual_norm = self.vector_norm(&final_residual);
Ok(RefinementResult {
solution: x,
converged,
iterations: residuals.len(),
final_residual_norm,
condition_number,
is_ill_conditioned,
residual_history: residuals,
correction_history: corrections,
})
}
pub fn solve_matrix(
&self,
a: &Array2<f64>,
b: &Array2<f64>,
) -> SklResult<MatrixRefinementResult> {
if a.nrows() != a.ncols() {
return Err(SklearsError::InvalidInput(
"Matrix A must be square".to_string(),
));
}
if a.nrows() != b.nrows() {
return Err(SklearsError::InvalidInput(
"Matrix A and B row dimensions must match".to_string(),
));
}
let n_rhs = b.ncols();
let mut solutions = Vec::new();
let mut all_converged = true;
let mut max_iterations = 0;
let mut max_residual: f64 = 0.0;
for j in 0..n_rhs {
let b_col = b.column(j).to_owned();
let result = self.solve(a, &b_col)?;
if !result.converged {
all_converged = false;
}
max_iterations = max_iterations.max(result.iterations);
max_residual = max_residual.max(result.final_residual_norm);
solutions.push(result.solution);
}
let mut solution_matrix = Array2::zeros((a.nrows(), n_rhs));
for (j, solution) in solutions.iter().enumerate() {
solution_matrix.column_mut(j).assign(solution);
}
Ok(MatrixRefinementResult {
solution: solution_matrix,
converged: all_converged,
max_iterations,
max_residual_norm: max_residual,
})
}
fn estimate_condition_number(&self, a: &Array2<f64>) -> SklResult<f64> {
let (_, singular_values, _) = a
.svd(false)
.map_err(|_| SklearsError::InvalidInput("SVD computation failed".to_string()))?;
if let Some(max_sv) = singular_values.iter().fold(None, |max, &x| {
if x.is_finite() && x > 0.0 {
Some(match max {
None => x,
Some(m) => {
if x > m {
x
} else {
m
}
}
})
} else {
max
}
}) {
if let Some(min_sv) = singular_values.iter().fold(None, |min, &x| {
if x.is_finite() && x > 0.0 {
Some(match min {
None => x,
Some(m) => {
if x < m {
x
} else {
m
}
}
})
} else {
min
}
}) {
Ok(max_sv / min_sv)
} else {
Ok(f64::INFINITY)
}
} else {
Ok(f64::INFINITY)
}
}
fn add_regularization(&self, a: &Array2<f64>, reg: f64) -> Array2<f64> {
let mut regularized = a.clone();
let n = a.nrows();
for i in 0..n {
regularized[[i, i]] += reg;
}
regularized
}
fn vector_norm(&self, v: &Array1<f64>) -> f64 {
v.dot(v).sqrt()
}
}
#[derive(Debug, Clone)]
pub struct RefinementResult {
pub solution: Array1<f64>,
pub converged: bool,
pub iterations: usize,
pub final_residual_norm: f64,
pub condition_number: f64,
pub is_ill_conditioned: bool,
pub residual_history: Vec<f64>,
pub correction_history: Vec<f64>,
}
#[derive(Debug, Clone)]
pub struct MatrixRefinementResult {
pub solution: Array2<f64>,
pub converged: bool,
pub max_iterations: usize,
pub max_residual_norm: f64,
}
pub struct AdaptivePrecision {
base_precision: f64,
precision_increase_factor: f64,
max_precision_level: usize,
convergence_threshold: f64,
}
impl Default for AdaptivePrecision {
fn default() -> Self {
Self::new()
}
}
impl AdaptivePrecision {
pub fn new() -> Self {
Self {
base_precision: 1e-12,
precision_increase_factor: 100.0,
max_precision_level: 5,
convergence_threshold: 1e-10,
}
}
pub fn base_precision(mut self, precision: f64) -> Self {
self.base_precision = precision;
self
}
pub fn precision_increase_factor(mut self, factor: f64) -> Self {
self.precision_increase_factor = factor;
self
}
pub fn max_precision_level(mut self, level: usize) -> Self {
self.max_precision_level = level;
self
}
pub fn convergence_threshold(mut self, threshold: f64) -> Self {
self.convergence_threshold = threshold;
self
}
pub fn adaptive_eigendecomposition(
&self,
matrix: &Array2<f64>,
) -> SklResult<AdaptiveEigenResult> {
let mut current_precision = self.base_precision;
let mut best_result = None;
let mut precision_levels = Vec::new();
for level in 0..self.max_precision_level {
let regularized_matrix = self.regularize_matrix(matrix, current_precision);
match regularized_matrix.eigh(UPLO::Lower) {
Ok((eigenvalues, eigenvectors)) => {
let quality = self.assess_eigen_quality(
®ularized_matrix,
&eigenvalues,
&eigenvectors,
)?;
precision_levels.push(AdaptivePrecisionLevel {
level,
precision: current_precision,
quality,
converged: quality.reconstruction_error < self.convergence_threshold,
});
if quality.reconstruction_error < self.convergence_threshold {
best_result = Some((eigenvalues, eigenvectors, quality));
break;
}
if best_result.is_none()
|| quality.reconstruction_error
< best_result
.as_ref()
.expect("operation should succeed")
.2
.reconstruction_error
{
best_result = Some((eigenvalues, eigenvectors, quality));
}
}
Err(_) => {
precision_levels.push(AdaptivePrecisionLevel {
level,
precision: current_precision,
quality: EigenQuality {
reconstruction_error: f64::INFINITY,
orthogonality_error: f64::INFINITY,
numerical_rank: 0,
},
converged: false,
});
}
}
current_precision /= self.precision_increase_factor;
}
match best_result {
Some((eigenvalues, eigenvectors, quality)) => Ok(AdaptiveEigenResult {
eigenvalues,
eigenvectors,
quality,
precision_levels,
converged: quality.reconstruction_error < self.convergence_threshold,
}),
None => Err(SklearsError::InvalidInput(
"Eigendecomposition failed at all precision levels".to_string(),
)),
}
}
fn regularize_matrix(&self, matrix: &Array2<f64>, precision: f64) -> Array2<f64> {
let mut regularized = matrix.clone();
let n = matrix.nrows();
for i in 0..n {
regularized[[i, i]] += precision;
}
regularized
}
fn assess_eigen_quality(
&self,
original_matrix: &Array2<f64>,
eigenvalues: &Array1<f64>,
eigenvectors: &Array2<f64>,
) -> SklResult<EigenQuality> {
let n = original_matrix.nrows();
let lambda_diag = Array2::from_diag(eigenvalues);
let reconstructed = eigenvectors.dot(&lambda_diag).dot(&eigenvectors.t());
let diff = original_matrix - &reconstructed;
let reconstruction_error = diff.mapv(|x| x * x).sum().sqrt();
let vtv = eigenvectors.t().dot(eigenvectors);
let identity: Array2<f64> = Array2::eye(n);
let orth_diff = &vtv - &identity;
let orthogonality_error = orth_diff.mapv(|x| x * x).sum().sqrt();
let max_eigenvalue = eigenvalues
.iter()
.filter(|&&x| x.is_finite())
.fold(0.0f64, |max, &x| max.max(x.abs()));
let rank_threshold = max_eigenvalue * 1e-12;
let numerical_rank = eigenvalues
.iter()
.filter(|&&x| x.abs() > rank_threshold)
.count();
Ok(EigenQuality {
reconstruction_error,
orthogonality_error,
numerical_rank,
})
}
}
#[derive(Debug, Clone, Copy)]
pub struct EigenQuality {
pub reconstruction_error: f64,
pub orthogonality_error: f64,
pub numerical_rank: usize,
}
#[derive(Debug, Clone)]
pub struct AdaptivePrecisionLevel {
pub level: usize,
pub precision: f64,
pub quality: EigenQuality,
pub converged: bool,
}
#[derive(Debug, Clone)]
pub struct AdaptiveEigenResult {
pub eigenvalues: Array1<f64>,
pub eigenvectors: Array2<f64>,
pub quality: EigenQuality,
pub precision_levels: Vec<AdaptivePrecisionLevel>,
pub converged: bool,
}
pub struct MultiLevelPreconditioning {
levels: usize,
smoothing_iterations: usize,
coarsening_factor: f64,
tolerance: f64,
}
impl Default for MultiLevelPreconditioning {
fn default() -> Self {
Self::new()
}
}
impl MultiLevelPreconditioning {
pub fn new() -> Self {
Self {
levels: 3,
smoothing_iterations: 2,
coarsening_factor: 0.5,
tolerance: 1e-8,
}
}
pub fn levels(mut self, levels: usize) -> Self {
self.levels = levels;
self
}
pub fn smoothing_iterations(mut self, iterations: usize) -> Self {
self.smoothing_iterations = iterations;
self
}
pub fn coarsening_factor(mut self, factor: f64) -> Self {
self.coarsening_factor = factor;
self
}
pub fn tolerance(mut self, tolerance: f64) -> Self {
self.tolerance = tolerance;
self
}
pub fn solve(&self, a: &Array2<f64>, b: &Array1<f64>) -> SklResult<Array1<f64>> {
let mut hierarchy = self.build_hierarchy(a)?;
let mut x = Array1::zeros(b.len());
for _cycle in 0..10 {
x = self.v_cycle(&mut hierarchy, &x, b, 0)?;
let residual = b - &a.dot(&x);
let residual_norm = residual.dot(&residual).sqrt();
if residual_norm < self.tolerance {
break;
}
}
Ok(x)
}
fn build_hierarchy(&self, matrix: &Array2<f64>) -> SklResult<Vec<Array2<f64>>> {
let mut hierarchy = vec![matrix.clone()];
let mut current_matrix = matrix.clone();
for _level in 1..self.levels {
let coarse_size =
((current_matrix.nrows() as f64) * self.coarsening_factor).max(2.0) as usize;
if coarse_size >= current_matrix.nrows() {
break;
}
let k = current_matrix.nrows() / coarse_size;
let indices: Vec<usize> = (0..coarse_size).map(|i| i * k).collect();
let mut coarse_matrix = Array2::zeros((coarse_size, coarse_size));
for (i, &idx_i) in indices.iter().enumerate() {
for (j, &idx_j) in indices.iter().enumerate() {
coarse_matrix[[i, j]] = current_matrix[[idx_i, idx_j]];
}
}
hierarchy.push(coarse_matrix.clone());
current_matrix = coarse_matrix;
}
Ok(hierarchy)
}
fn v_cycle(
&self,
hierarchy: &mut [Array2<f64>],
x: &Array1<f64>,
b: &Array1<f64>,
level: usize,
) -> SklResult<Array1<f64>> {
if level >= hierarchy.len() - 1 {
return hierarchy[level].solve(b).map_err(|_| {
SklearsError::InvalidInput("Direct solve failed at coarsest level".to_string())
});
}
let mut x_smooth = x.clone();
for _ in 0..self.smoothing_iterations {
x_smooth = self.smooth(&hierarchy[level], &x_smooth, b)?;
}
let residual = b - &hierarchy[level].dot(&x_smooth);
let coarse_residual = self.restrict(&residual, hierarchy[level + 1].nrows());
let coarse_correction = self.v_cycle(
hierarchy,
&Array1::zeros(hierarchy[level + 1].nrows()),
&coarse_residual,
level + 1,
)?;
let fine_correction = self.prolongate(&coarse_correction, x.len());
x_smooth = &x_smooth + &fine_correction;
for _ in 0..self.smoothing_iterations {
x_smooth = self.smooth(&hierarchy[level], &x_smooth, b)?;
}
Ok(x_smooth)
}
fn smooth(&self, a: &Array2<f64>, x: &Array1<f64>, b: &Array1<f64>) -> SklResult<Array1<f64>> {
let n = a.nrows();
let mut x_new = Array1::zeros(n);
for i in 0..n {
let mut sum = 0.0;
for j in 0..n {
if i != j {
sum += a[[i, j]] * x[j];
}
}
if a[[i, i]].abs() > 1e-15 {
x_new[i] = (b[i] - sum) / a[[i, i]];
} else {
x_new[i] = x[i]; }
}
Ok(x_new)
}
fn restrict(&self, fine_vector: &Array1<f64>, coarse_size: usize) -> Array1<f64> {
let fine_size = fine_vector.len();
let mut coarse_vector = Array1::zeros(coarse_size);
let ratio = fine_size as f64 / coarse_size as f64;
for i in 0..coarse_size {
let fine_idx = (i as f64 * ratio) as usize;
if fine_idx < fine_size {
coarse_vector[i] = fine_vector[fine_idx];
}
}
coarse_vector
}
fn prolongate(&self, coarse_vector: &Array1<f64>, fine_size: usize) -> Array1<f64> {
let coarse_size = coarse_vector.len();
let mut fine_vector = Array1::zeros(fine_size);
let ratio = fine_size as f64 / coarse_size as f64;
for i in 0..fine_size {
let coarse_idx = (i as f64 / ratio) as usize;
if coarse_idx < coarse_size {
fine_vector[i] = coarse_vector[coarse_idx];
}
}
fine_vector
}
}
pub struct AdaptivePrecisionArithmetic {
base_precision: f64,
max_precision_level: usize,
convergence_threshold: f64,
error_scaling_factor: f64,
}
impl Default for AdaptivePrecisionArithmetic {
fn default() -> Self {
Self::new()
}
}
impl AdaptivePrecisionArithmetic {
pub fn new() -> Self {
Self {
base_precision: 1e-12,
max_precision_level: 5,
convergence_threshold: 1e-15,
error_scaling_factor: 10.0,
}
}
pub fn base_precision(mut self, precision: f64) -> Self {
self.base_precision = precision;
self
}
pub fn max_precision_level(mut self, level: usize) -> Self {
self.max_precision_level = level;
self
}
pub fn convergence_threshold(mut self, threshold: f64) -> Self {
self.convergence_threshold = threshold;
self
}
pub fn error_scaling_factor(mut self, factor: f64) -> Self {
self.error_scaling_factor = factor;
self
}
pub fn adaptive_eigendecomposition(
&self,
matrix: &Array2<f64>,
) -> SklResult<(Array1<f64>, Array2<f64>)> {
let mut current_precision = self.base_precision;
let mut previous_eigenvalues: Option<Array1<f64>> = None;
for level in 0..self.max_precision_level {
let result = self.eigendecomposition_at_precision(matrix, current_precision)?;
if let Some(ref prev_eigenvals) = previous_eigenvalues {
let error = self.compute_eigenvalue_error(&result.0, prev_eigenvals);
if error < self.convergence_threshold {
return Ok(result);
}
}
previous_eigenvalues = Some(result.0.clone());
current_precision /= self.error_scaling_factor;
if level == self.max_precision_level - 1 {
return Ok(result);
}
}
self.eigendecomposition_at_precision(matrix, self.base_precision)
}
fn eigendecomposition_at_precision(
&self,
matrix: &Array2<f64>,
precision: f64,
) -> SklResult<(Array1<f64>, Array2<f64>)> {
let stabilized_matrix = self.stabilize_matrix(matrix, precision)?;
let (eigenvalues, eigenvectors) = stabilized_matrix.eigh(UPLO::Lower).map_err(|e| {
SklearsError::InvalidInput(format!("Eigendecomposition failed: {:?}", e))
})?;
Ok((eigenvalues, eigenvectors))
}
fn stabilize_matrix(&self, matrix: &Array2<f64>, precision: f64) -> SklResult<Array2<f64>> {
let mut stabilized = matrix.clone();
let n = matrix.nrows();
let regularization = precision.sqrt();
for i in 0..n {
stabilized[[i, i]] += regularization;
}
if !self.is_symmetric(&stabilized, precision) {
stabilized = self.symmetrize_matrix(&stabilized);
}
Ok(stabilized)
}
fn is_symmetric(&self, matrix: &Array2<f64>, tolerance: f64) -> bool {
let n = matrix.nrows();
if n != matrix.ncols() {
return false;
}
for i in 0..n {
for j in 0..n {
if (matrix[[i, j]] - matrix[[j, i]]).abs() > tolerance {
return false;
}
}
}
true
}
fn symmetrize_matrix(&self, matrix: &Array2<f64>) -> Array2<f64> {
let transposed = matrix.t();
(matrix + &transposed) / 2.0
}
fn compute_eigenvalue_error(&self, current: &Array1<f64>, previous: &Array1<f64>) -> f64 {
if current.len() != previous.len() {
return f64::INFINITY;
}
let mut total_error = 0.0;
for i in 0..current.len() {
let relative_error = (current[i] - previous[i]).abs() / (previous[i].abs() + 1e-15);
total_error += relative_error * relative_error;
}
(total_error / current.len() as f64).sqrt()
}
pub fn adaptive_svd(
&self,
matrix: &Array2<f64>,
) -> SklResult<(Array2<f64>, Array1<f64>, Array2<f64>)> {
let mut current_precision = self.base_precision;
let mut previous_singular_values: Option<Array1<f64>> = None;
for level in 0..self.max_precision_level {
let stabilized_matrix = self.stabilize_matrix_for_svd(matrix, current_precision)?;
let (u, s, vt) = stabilized_matrix
.svd(true)
.map_err(|e| SklearsError::InvalidInput(format!("SVD failed: {:?}", e)))?;
if let Some(ref prev_s) = previous_singular_values {
let error = self.compute_eigenvalue_error(&s, prev_s);
if error < self.convergence_threshold {
return Ok((u, s, vt));
}
}
previous_singular_values = Some(s.clone());
current_precision /= self.error_scaling_factor;
if level == self.max_precision_level - 1 {
return Ok((u, s, vt));
}
}
let (u, s, vt) = matrix
.svd(true)
.map_err(|e| SklearsError::InvalidInput(format!("SVD failed: {:?}", e)))?;
Ok((u, s, vt))
}
fn stabilize_matrix_for_svd(
&self,
matrix: &Array2<f64>,
precision: f64,
) -> SklResult<Array2<f64>> {
let mut stabilized = matrix.clone();
let (m, n) = matrix.dim();
let noise_level = precision.sqrt();
for i in 0..m {
for j in 0..n {
if stabilized[[i, j]].abs() < precision {
stabilized[[i, j]] += noise_level * (if (i + j) % 2 == 0 { 1.0 } else { -1.0 });
}
}
}
Ok(stabilized)
}
pub fn adaptive_matrix_inverse(&self, matrix: &Array2<f64>) -> SklResult<Array2<f64>> {
let mut current_precision = self.base_precision;
for _level in 0..self.max_precision_level {
if let Ok(inverse) = self.matrix_inverse_at_precision(matrix, current_precision) {
let identity_check = matrix.dot(&inverse);
let identity_error = self.compute_identity_error(&identity_check);
if identity_error < self.convergence_threshold {
return Ok(inverse);
}
}
current_precision /= self.error_scaling_factor;
}
self.compute_pseudoinverse(matrix)
}
fn matrix_inverse_at_precision(
&self,
matrix: &Array2<f64>,
precision: f64,
) -> SklResult<Array2<f64>> {
let stabilized = self.stabilize_matrix(matrix, precision)?;
let (u, s, vt) = stabilized.svd(true).map_err(|e| {
SklearsError::InvalidInput(format!("Matrix inversion SVD failed: {:?}", e))
})?;
let threshold = s.iter().fold(0.0f64, |acc, &x| acc.max(x)) * precision;
let mut s_inv = Array1::zeros(s.len());
for (i, &sigma) in s.iter().enumerate() {
if sigma > threshold {
s_inv[i] = 1.0 / sigma;
}
}
let s_inv_diag = Array2::from_diag(&s_inv);
let result = vt.t().dot(&s_inv_diag).dot(&u.t());
Ok(result)
}
fn compute_pseudoinverse(&self, matrix: &Array2<f64>) -> SklResult<Array2<f64>> {
let (u, s, vt) = self.adaptive_svd(matrix)?;
let threshold = s.iter().fold(0.0f64, |acc, &x| acc.max(x)) * self.base_precision;
let mut s_inv = Array1::zeros(s.len());
for (i, &sigma) in s.iter().enumerate() {
if sigma > threshold {
s_inv[i] = 1.0 / sigma;
}
}
let s_inv_diag = Array2::from_diag(&s_inv);
let result = vt.t().dot(&s_inv_diag).dot(&u.t());
Ok(result)
}
fn compute_identity_error(&self, matrix: &Array2<f64>) -> f64 {
if matrix.nrows() != matrix.ncols() {
return f64::INFINITY;
}
let n = matrix.nrows();
let mut error = 0.0;
for i in 0..n {
for j in 0..n {
let expected = if i == j { 1.0 } else { 0.0 };
let diff = matrix[[i, j]] - expected;
error += diff * diff;
}
}
(error / (n * n) as f64).sqrt()
}
}
#[allow(non_snake_case)]
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_abs_diff_eq;
#[test]
fn test_iterative_refinement() {
let a = Array2::from_shape_vec((3, 3), vec![4.0, 1.0, 0.0, 1.0, 4.0, 1.0, 0.0, 1.0, 4.0])
.expect("operation should succeed");
let b = Array1::from_vec(vec![1.0, 2.0, 3.0]);
let refinement = IterativeRefinement::new().tolerance(1e-10);
let result = refinement.solve(&a, &b).expect("operation should succeed");
assert!(result.converged);
assert!(result.final_residual_norm < 1e-10);
let residual = &b - &a.dot(&result.solution);
let residual_norm = residual.dot(&residual).sqrt();
assert!(residual_norm < 1e-10);
}
#[test]
fn test_multi_level_preconditioning() {
let a = Array2::from_shape_vec(
(4, 4),
vec![
4.0, 1.0, 0.0, 0.0, 1.0, 4.0, 1.0, 0.0, 0.0, 1.0, 4.0, 1.0, 0.0, 0.0, 1.0, 4.0,
],
)
.expect("operation should succeed");
let b = Array1::from_vec(vec![1.0, 2.0, 3.0, 4.0]);
let mlp = MultiLevelPreconditioning::new().levels(2).tolerance(1e-6);
let solution = mlp.solve(&a, &b).expect("operation should succeed");
let residual = &b - &a.dot(&solution);
let residual_norm = residual.dot(&residual).sqrt();
assert!(residual_norm < 1e-6);
}
#[test]
fn test_condition_number_estimation() {
let refinement = IterativeRefinement::new();
let well_conditioned = Array2::eye(3);
let cond_num = refinement
.estimate_condition_number(&well_conditioned)
.expect("operation should succeed");
assert_abs_diff_eq!(cond_num, 1.0, epsilon = 1e-10);
let ill_conditioned = Array2::from_shape_vec((2, 2), vec![1.0, 1.0, 1.0, 1.0 + 1e-15])
.expect("operation should succeed");
let cond_num_ill = refinement
.estimate_condition_number(&ill_conditioned)
.expect("operation should succeed");
assert!(
cond_num_ill > 1e7,
"Expected condition number > 1e7, got {}",
cond_num_ill
);
}
#[test]
fn test_matrix_refinement() {
let a = Array2::from_shape_vec((2, 2), vec![2.0, 1.0, 1.0, 2.0])
.expect("operation should succeed");
let b = Array2::from_shape_vec((2, 2), vec![1.0, 0.0, 0.0, 1.0])
.expect("operation should succeed");
let refinement = IterativeRefinement::new();
let result = refinement
.solve_matrix(&a, &b)
.expect("operation should succeed");
assert!(result.converged);
assert!(result.max_residual_norm < 1e-10);
}
#[test]
fn test_adaptive_precision_eigendecomposition() {
let adaptive = AdaptivePrecisionArithmetic::new();
let matrix =
Array2::from_shape_vec((3, 3), vec![2.0, 1.0, 0.0, 1.0, 2.0, 1.0, 0.0, 1.0, 2.0])
.expect("operation should succeed");
let result = adaptive.adaptive_eigendecomposition(&matrix);
assert!(result.is_ok());
let (eigenvalues, _) = result.expect("operation should succeed");
assert_eq!(eigenvalues.len(), 3);
for &val in eigenvalues.iter() {
assert!(val > 0.0 && val < 5.0);
}
}
#[test]
fn test_adaptive_precision_svd() {
let adaptive = AdaptivePrecisionArithmetic::new();
let matrix = Array2::from_shape_vec((3, 2), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
.expect("operation should succeed");
let result = adaptive.adaptive_svd(&matrix);
assert!(result.is_ok());
let (u, s, vt) = result.expect("operation should succeed");
assert_eq!(u.shape(), &[3, 3]);
assert_eq!(s.len(), 2);
assert_eq!(vt.shape(), &[2, 2]);
assert!(s[0] >= s[1]);
assert!(s[1] >= 0.0);
}
#[test]
fn test_adaptive_matrix_inverse() {
let adaptive = AdaptivePrecisionArithmetic::new();
let matrix = Array2::from_shape_vec((2, 2), vec![4.0, 2.0, 2.0, 2.0])
.expect("operation should succeed");
let result = adaptive.adaptive_matrix_inverse(&matrix);
assert!(result.is_ok());
let inverse = result.expect("operation should succeed");
assert_eq!(inverse.shape(), &[2, 2]);
let identity_check = matrix.dot(&inverse);
let error = adaptive.compute_identity_error(&identity_check);
assert!(error < 1e-10);
}
#[test]
fn test_matrix_stabilization() {
let adaptive = AdaptivePrecisionArithmetic::new();
let matrix = Array2::from_shape_vec((2, 2), vec![1.0, 0.5, 0.5, 1.0])
.expect("operation should succeed");
let result = adaptive.stabilize_matrix(&matrix, 1e-12);
assert!(result.is_ok());
let stabilized = result.expect("operation should succeed");
assert!(stabilized[[0, 0]] > matrix[[0, 0]]);
assert!(stabilized[[1, 1]] > matrix[[1, 1]]);
assert!(adaptive.is_symmetric(&stabilized, 1e-10));
}
#[test]
fn test_symmetry_enforcement() {
let adaptive = AdaptivePrecisionArithmetic::new();
let asymmetric = Array2::from_shape_vec((2, 2), vec![1.0, 2.0, 3.0, 4.0])
.expect("operation should succeed");
let symmetric = adaptive.symmetrize_matrix(&asymmetric);
assert!(adaptive.is_symmetric(&symmetric, 1e-15));
assert_eq!(symmetric[[0, 0]], 1.0);
assert_eq!(symmetric[[1, 1]], 4.0);
assert_eq!(symmetric[[0, 1]], 2.5);
assert_eq!(symmetric[[1, 0]], 2.5);
}
#[test]
fn test_eigenvalue_error_computation() {
let adaptive = AdaptivePrecisionArithmetic::new();
let current = Array1::from_vec(vec![1.0, 2.0, 3.0]);
let previous = Array1::from_vec(vec![1.1, 2.1, 3.1]);
let error = adaptive.compute_eigenvalue_error(¤t, &previous);
assert!(error > 0.0);
assert!(error < 1.0); }
}