use scirs2_core::ndarray::{array, Array1, Array2, ArrayView1};
use scirs2_linalg::matrixfree::{conjugate_gradient, gmres, LinearOperator};
use scirs2_linalg::quantization::{
quantized_matrixfree::QuantizedMatrixFreeOp,
solvers::{
quantized_conjugate_gradient, quantized_gmres, quantized_jacobi_preconditioner,
quantized_preconditioned_conjugate_gradient,
},
QuantizationMethod,
};
#[allow(dead_code)]
fn main() {
println!("=== Quantized Matrix Solvers Example ===");
println!("\n--- Example 1: Conjugate Gradient with Quantized Matrix ---");
example_conjugate_gradient();
println!("\n--- Example 2: GMRES with Quantized Matrix ---");
example_gmres();
println!("\n--- Example 3: Preconditioned Conjugate Gradient ---");
example_preconditioned_cg();
println!("\n--- Example 4: Banded Matrix Example ---");
example_bandedmatrix();
println!("\n--- Example 5: Adaptive Precision for Ill-conditioned Matrix ---");
example_adaptive_precision();
}
#[allow(dead_code)]
fn example_conjugate_gradient() {
let matrix = array![[4.0f32, 1.0, 0.0], [1.0, 3.0, 1.0], [0.0, 1.0, 5.0]];
println!("Matrix A:\n{:?}", matrix);
let b = array![1.0f32, 2.0, 3.0];
println!("Right-hand side b: {:?}", b);
let matrix_clone = matrix.clone();
let standard_op = LinearOperator::new(3, move |v: &ArrayView1<f32>| matrix_clone.dot(v))
.symmetric()
.positive_definite();
let x_standard = conjugate_gradient(&standard_op, &b, 10, 1e-6).expect("Operation failed");
println!("Solution using standard CG: {:?}", x_standard);
let quantized_op =
QuantizedMatrixFreeOp::frommatrix(&matrix.view(), 8, QuantizationMethod::Symmetric)
.expect("Operation failed")
.symmetric()
.positive_definite();
let x_quantized =
quantized_conjugate_gradient(&quantized_op, &b, 10, 1e-6, false).expect("Operation failed");
println!("Solution using quantized CG (8-bit): {:?}", x_quantized);
let quantized_op_4bit =
QuantizedMatrixFreeOp::frommatrix(&matrix.view(), 4, QuantizationMethod::Symmetric)
.expect("Operation failed")
.symmetric()
.positive_definite();
let x_quantized_4bit = quantized_conjugate_gradient(&quantized_op_4bit, &b, 10, 1e-6, false)
.expect("Operation failed");
println!(
"Solution using quantized CG (4-bit): {:?}",
x_quantized_4bit
);
let residual_standard = &matrix.dot(&x_standard) - &b;
let residual_quantized = &matrix.dot(&x_quantized) - &b;
let residual_quantized_4bit = &matrix.dot(&x_quantized_4bit) - &b;
println!("Residual norm (standard): {}", l2_norm(&residual_standard));
println!("Residual norm (8-bit): {}", l2_norm(&residual_quantized));
println!(
"Residual norm (4-bit): {}",
l2_norm(&residual_quantized_4bit)
);
}
#[allow(dead_code)]
fn example_gmres() {
let matrix = array![[3.0f32, 1.0, 0.5], [1.0, 4.0, 2.0], [0.5, 1.0, 3.0]];
println!("Matrix A:\n{:?}", matrix);
let b = array![1.0f32, 2.0, 3.0];
println!("Right-hand side b: {:?}", b);
let matrix_clone = matrix.clone();
let standard_op = LinearOperator::new(3, move |v: &ArrayView1<f32>| matrix_clone.dot(v));
let x_standard = gmres(&standard_op, &b, 10, 1e-6, None).expect("Operation failed");
println!("Solution using standard GMRES: {:?}", x_standard);
let quantized_op =
QuantizedMatrixFreeOp::frommatrix(&matrix.view(), 8, QuantizationMethod::Symmetric)
.expect("Operation failed");
let x_quantized =
quantized_gmres(&quantized_op, &b, 10, 1e-6, None, false).expect("Operation failed");
println!("Solution using quantized GMRES (8-bit): {:?}", x_quantized);
let quantized_op_4bit =
QuantizedMatrixFreeOp::frommatrix(&matrix.view(), 4, QuantizationMethod::Symmetric)
.expect("Operation failed");
let x_quantized_4bit =
quantized_gmres(&quantized_op_4bit, &b, 10, 1e-6, None, false).expect("Operation failed");
println!(
"Solution using quantized GMRES (4-bit): {:?}",
x_quantized_4bit
);
let residual_standard = &matrix.dot(&x_standard) - &b;
let residual_quantized = &matrix.dot(&x_quantized) - &b;
let residual_quantized_4bit = &matrix.dot(&x_quantized_4bit) - &b;
println!("Residual norm (standard): {}", l2_norm(&residual_standard));
println!("Residual norm (8-bit): {}", l2_norm(&residual_quantized));
println!(
"Residual norm (4-bit): {}",
l2_norm(&residual_quantized_4bit)
);
}
#[allow(dead_code)]
fn example_preconditioned_cg() {
let matrix = array![[10.0f32, 1.0, 0.0], [1.0, 8.0, 3.0], [0.0, 3.0, 15.0]];
println!("Matrix A:\n{:?}", matrix);
let b = array![1.0f32, 2.0, 3.0];
println!("Right-hand side b: {:?}", b);
let quantized_op =
QuantizedMatrixFreeOp::frommatrix(&matrix.view(), 8, QuantizationMethod::Symmetric)
.expect("Operation failed")
.symmetric()
.positive_definite();
let precond = quantized_jacobi_preconditioner(&quantized_op).expect("Operation failed");
let x_unpreconditioned =
quantized_conjugate_gradient(&quantized_op, &b, 10, 1e-6, false).expect("Operation failed");
println!(
"Solution using unpreconditioned CG: {:?}",
x_unpreconditioned
);
let x_preconditioned =
quantized_preconditioned_conjugate_gradient(&quantized_op, &precond, &b, 10, 1e-6, false)
.expect("Operation failed");
println!("Solution using preconditioned CG: {:?}", x_preconditioned);
let residual_unpreconditioned = &matrix.dot(&x_unpreconditioned) - &b;
let residual_preconditioned = &matrix.dot(&x_preconditioned) - &b;
println!(
"Residual norm (unpreconditioned): {}",
l2_norm(&residual_unpreconditioned)
);
println!(
"Residual norm (preconditioned): {}",
l2_norm(&residual_preconditioned)
);
}
#[allow(dead_code)]
fn example_bandedmatrix() {
let n = 10;
let main_diag = Array1::from_vec(vec![2.0f32; n]);
let upper_diag = Array1::from_vec(vec![-1.0f32; n - 1]);
let lower_diag = Array1::from_vec(vec![-1.0f32; n - 1]);
let b = Array1::from_vec(vec![1.0f32; n]);
println!("Right-hand side b: {:?}", b);
let bands = vec![
(0, main_diag.view()),
(1, upper_diag.view()),
(-1, lower_diag.view()),
];
let banded_op = QuantizedMatrixFreeOp::banded(n, bands, 8, QuantizationMethod::Symmetric)
.expect("Operation failed")
.symmetric()
.positive_definite();
let x_banded =
quantized_conjugate_gradient(&banded_op, &b, 20, 1e-6, false).expect("Operation failed");
println!("Solution using banded matrix CG: {:?}", x_banded);
let mut densematrix = Array2::zeros((n, n));
for i in 0..n {
densematrix[[i, i]] = 2.0;
}
for i in 0..n - 1 {
densematrix[[i, i + 1]] = -1.0;
densematrix[[i + 1, i]] = -1.0;
}
let residual = &densematrix.dot(&x_banded) - &b;
println!("Residual norm: {}", l2_norm(&residual));
}
#[allow(dead_code)]
fn example_adaptive_precision() {
let matrix = array![[100.0f32, 99.0, 0.0], [99.0, 100.0, 0.01], [0.0, 0.01, 1.0]];
println!("Ill-conditioned matrix A:\n{:?}", matrix);
let b = array![1.0f32, 1.0, 1.0];
println!("Right-hand side b: {:?}", b);
let quantized_op =
QuantizedMatrixFreeOp::frommatrix(&matrix.view(), 4, QuantizationMethod::Symmetric)
.expect("Operation failed")
.symmetric()
.positive_definite();
let x_standard =
quantized_conjugate_gradient(&quantized_op, &b, 50, 1e-5, false).expect("Operation failed");
println!("Solution using standard quantized CG: {:?}", x_standard);
let x_adaptive =
quantized_conjugate_gradient(&quantized_op, &b, 50, 1e-5, true).expect("Operation failed");
println!("Solution using adaptive precision CG: {:?}", x_adaptive);
let residual_standard = &matrix.dot(&x_standard) - &b;
let residual_adaptive = &matrix.dot(&x_adaptive) - &b;
println!("Residual norm (standard): {}", l2_norm(&residual_standard));
println!("Residual norm (adaptive): {}", l2_norm(&residual_adaptive));
}
#[allow(dead_code)]
fn l2_norm(v: &Array1<f32>) -> f32 {
v.dot(v).sqrt()
}