Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Operations

StaticMatrix and DynamicMatrix support the same core set of operations: element-wise addition and subtraction, scalar scaling, matrix-vector and matrix-matrix products, transpose, rank, and the LU, QR, Cholesky, and SVD decompositions, plus condition number. The example below runs each of them on a StaticMatrix.

#![allow(unused)]
fn main() {
use rustebra::matrix::StaticMatrix;
use rustebra::vector::StaticVector;

pub(crate) fn run() {
    println!("== StaticMatrix ==");
    let a = StaticMatrix::new([[1.0, 2.0], [3.0, 4.0]]);
    let b = StaticMatrix::new([[5.0, 6.0], [7.0, 8.0]]);
    let v = StaticVector::new([1.0, 1.0]);

    println!("a = {a:?}");
    println!("b = {b:?}");
    println!("a + b = {:?}", a.add(&b));
    println!("a - b = {:?}", a.sub(&b));
    println!("a scaled by 2 = {:?}", a.mul_scalar(2.0));
    println!("a * v = {:?}", a.mul_vector(&v));
    println!("a * b = {:?}", a.mul_matrix(&b));
    println!("a^T = {:?}", a.transpose());
    println!("det(a) = {:?}", a.determinant());
    println!("rank(a) = {:?}", a.rank());

    let (l, u, swap_count) = a.lu();
    println!("lu(a) = (l = {l:?}, u = {u:?}, swap_count = {swap_count})");

    let (q, r) = a.qr().expect("a has at least as many rows as columns");
    println!("qr(a) = (q = {q:?}, r = {r:?})");

    let spd = StaticMatrix::new([[4.0, 2.0], [2.0, 2.0]]);
    println!("cholesky(spd) = {:?}", spd.cholesky());

    let mut svd_scratch = [0.0; 5 * 2 * 2 + 2 + 2];
    let (svd_u, sigma, v) = a.svd(&mut svd_scratch).expect("scratch is correctly sized");
    println!("svd(a) = (u = {svd_u:?}, sigma = {sigma:?}, v = {v:?})");

    let mut condition_scratch = [0.0; 7 * 2 * 2 + 3 * 2];
    println!(
        "condition_number(a) = {:?}",
        a.condition_number(&mut condition_scratch)
    );
}
}

Gotchas

  • On StaticMatrix, operations between two matrices (add, sub, mul_matrix, mul_vector) return a plain value, not a Result — the const generics guarantee compatible shapes at compile time. The DynamicMatrix equivalents return Result<_, DimensionMismatch> instead, since two DynamicMatrixs aren’t guaranteed to match in shape.
  • qr returns Result<_, DimensionMismatch> on both types, because R >= C isn’t something the type system can enforce even for StaticMatrix — stable Rust has no way to bound one const generic against another.
  • svd and condition_number on StaticMatrix take a caller-provided scratch buffer sized by an explicit formula (5 * C * C + C + R for svd, 7 * N * N + 3 * N for condition_number) — get the size wrong and you get Err(DimensionMismatch) back rather than a panic. DynamicMatrix’s svd/condition_number allocate their own scratch space internally and don’t take this parameter.
  • determinant on StaticMatrix<T, N, N> returns Err(DeterminantError::MatrixTooLargeWithoutAlloc) if the alloc feature is disabled and N > 4 — for larger matrices without alloc, use rustebra::algorithm::matrix::determinant_lu directly with your own scratch buffer.
  • cholesky requires self to be symmetric positive-definite; it returns Err(CholeskyError::NotPositiveDefinite) rather than a garbage result or a panic when that doesn’t hold.