use scirs2_core::ndarray::{array, Array2};
use scirs2_linalg::{
cholesky, det, inv, lu, matrix_norm, qr, solve, svd, LinalgError, LinalgResult,
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== SciRS2 Linear Algebra Tutorial ===\n");
section_creating_matrices()?;
section_basic_operations()?;
section_solving_linear_systems()?;
section_decompositions()?;
section_norms()?;
println!("\n=== Tutorial Complete ===");
Ok(())
}
fn section_creating_matrices() -> Result<(), Box<dyn std::error::Error>> {
println!("--- 1. Creating Matrices ---\n");
let a = array![[1.0_f64, 2.0], [3.0, 4.0]];
println!("2x2 matrix from macro:\n{}\n", a);
let b = Array2::from_shape_vec((2, 3), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
.map_err(|e| format!("Shape error: {e}"))?;
println!("2x3 matrix from vec:\n{}\n", b);
let eye: Array2<f64> = Array2::eye(3);
println!("3x3 identity:\n{}\n", eye);
let zeros: Array2<f64> = Array2::zeros((2, 4));
println!("2x4 zeros:\n{}\n", zeros);
let ones: Array2<f64> = Array2::ones((3, 2));
println!("3x2 ones:\n{}\n", ones);
let diag = Array2::from_diag(&array![1.0, 2.0, 3.0]);
println!("Diagonal matrix:\n{}\n", diag);
Ok(())
}
fn section_basic_operations() -> LinalgResult<()> {
println!("--- 2. Basic Matrix Operations ---\n");
let a = array![[1.0_f64, 2.0], [3.0, 4.0]];
let d = det(&a.view(), None)?;
println!("Matrix A:\n{}", a);
println!("det(A) = {:.6}", d);
let a_inv = inv(&a.view(), None)?;
println!("inv(A):\n{}\n", a_inv);
let product = a.dot(&a_inv);
println!("A * inv(A) (should be ~identity):\n{}\n", product);
let a_sq = a.dot(&a);
println!("A^2 = A * A:\n{}\n", a_sq);
Ok(())
}
fn section_solving_linear_systems() -> LinalgResult<()> {
println!("--- 3. Solving Linear Systems ---\n");
let a = array![[2.0_f64, 1.0], [1.0, 3.0]];
let b = array![5.0_f64, 7.0];
let x = solve(&a.view(), &b.view(), None)?;
println!("System: 2x + y = 5, x + 3y = 7");
println!("Solution: x = {:.6}, y = {:.6}", x[0], x[1]);
let ax = a.dot(&x);
println!("Verification A*x = {:?}", ax.to_vec());
println!("Original b = {:?}\n", b.to_vec());
let a3 = array![[1.0_f64, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 10.0]];
let b3 = array![1.0_f64, 2.0, 3.0];
let x3 = solve(&a3.view(), &b3.view(), None)?;
println!("3x3 system solution: {:?}", x3.to_vec());
let a_over = array![[1.0_f64, 1.0], [1.0, 2.0], [1.0, 3.0], [1.0, 4.0]];
let b_over = array![2.1_f64, 3.9, 6.1, 7.9];
let result = scirs2_linalg::lstsq(&a_over.view(), &b_over.view(), None)?;
println!("Least squares solution: {:?}", result.x.to_vec());
println!("Residual sum of squares: {:.6}\n", result.residuals);
Ok(())
}
fn section_decompositions() -> LinalgResult<()> {
println!("--- 4. Matrix Decompositions ---\n");
let a = array![[2.0_f64, 1.0, 1.0], [4.0, 3.0, 3.0], [8.0, 7.0, 9.0]];
let (p, l, u) = lu(&a.view(), None)?;
println!("LU Decomposition of:\n{}", a);
println!("P (permutation):\n{}", p);
println!("L (lower triangular):\n{}", l);
println!("U (upper triangular):\n{}\n", u);
let a = array![[1.0_f64, 2.0], [3.0, 4.0], [5.0, 6.0]];
let (q, r) = qr(&a.view(), None)?;
println!("QR Decomposition of:\n{}", a);
println!("Q (orthogonal):\n{}", q);
println!("R (upper triangular):\n{}\n", r);
let a = array![[1.0_f64, 2.0], [3.0, 4.0], [5.0, 6.0]];
let (u_mat, s, vt) = svd(&a.view(), true, None)?;
println!("SVD of:\n{}", a);
println!("Singular values: {:?}", s.to_vec());
println!("U shape: {:?}", u_mat.dim());
println!("Vt shape: {:?}\n", vt.dim());
let spd = array![[4.0_f64, 2.0], [2.0, 5.0]];
let l = cholesky(&spd.view(), None)?;
println!("Cholesky of SPD matrix:\n{}", spd);
println!("L (lower triangular):\n{}", l);
let reconstructed = l.dot(&l.t());
println!("L * L^T (should match original):\n{}\n", reconstructed);
let non_square = array![[1.0_f64, 2.0, 3.0], [4.0, 5.0, 6.0]];
match cholesky(&non_square.view(), None) {
Err(LinalgError::ShapeError(msg)) => {
println!("Expected error for non-square Cholesky: {}", msg);
}
other => {
println!("Unexpected result: {:?}", other.is_ok());
}
}
Ok(())
}
fn section_norms() -> LinalgResult<()> {
println!("\n--- 5. Matrix Norms ---\n");
let a = array![[1.0_f64, 2.0], [3.0, 4.0]];
let fro = matrix_norm(&a.view(), "fro", None)?;
println!("Frobenius norm of A: {:.6}", fro);
let one_norm = matrix_norm(&a.view(), "1", None)?;
println!("1-norm of A: {:.6}", one_norm);
let inf_norm = matrix_norm(&a.view(), "inf", None)?;
println!("Inf-norm of A: {:.6}", inf_norm);
println!();
Ok(())
}