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

Solve systems and factor matrices

std.linalg gives you dense matrices and column vectors of f64 values. It uses faer’s numerical algorithms through an optional native engine. Install the linear algebra engine before running these examples. Matrix construction and element access work without it.

Solve a linear system

Pass matrix entries in row order. Use solve to find x in A x = b:

import std.linalg as la

def main() -> Result[(), AllocError | IoError | la.LinalgError]:
    a = la.matrix(2, 2, &[3.0, 1.0, 1.0, 2.0]?)?
    b = la.vector(&[9.0, 8.0]?)?
    x = a.solve(&b)?
    print(x.get(0, 0)?, x.get(1, 0)?)?
    predicted = (a * x)?
    residual = (predicted - b)?
    print(residual.to_list()?)?
    Ok(())
2.0 3.0
[0.0, 0.0]

Both inputs are preserved. The result is a two-row, one-column matrix. For several right-hand sides with the same a, compute factor = a.lu()? once, then call factor.solve(&b)? for each. This reuses the expensive factorization.

a * x is a matrix product, so it also applies a matrix to a column vector. Addition and subtraction combine corresponding entries of equal-shaped matrices. The parentheses in (predicted - b)? propagate the operation’s error. A small residual is useful evidence about a solve; for general floating-point inputs, compare it with a tolerance appropriate to your data.

Scale with (2.0 * x)? or x.scale(2.0)?, and negate with (-x)?. Scalar multiplication currently puts the scalar on the left: x * 2.0 is unsupported. These operations preserve their inputs and allocate new results. There is no automatic fusion of a chain or fallible +=; use an explicit assignment such as x = (x + adjustment)? with a mutable binding.

Inspect a low-rank approximation

SVD separates a matrix into two orthogonal factors and a list of singular values. Small singular values indicate directions that contribute little:

import std.linalg as la

def main() -> Result[(), AllocError | IoError | la.LinalgError]:
    a = la.diagonal(&[4.0, 2.0, 0.01]?)?
    (u, values, v) = a.svd()?
    print(values)?
    print(u.rows(), u.cols(), v.rows(), v.cols())?
    Ok(())
[4.0, 2.0, 0.01]
3 3 3 3

To reconstruct, create d = la.diagonal(&values)? and vt = v.transpose()?, then compute scaled = (u * d)? and reconstructed = (scaled * vt)?. For rectangular inputs the factors have only min(rows, cols) columns. qr, cholesky, eigh, and eig provide other decompositions; their contracts are in the linear algebra reference.

All matrix-building operations are fallible. Check shapes and handle errors normally with ?. Floating-point calculations are approximate: compare residuals with a tolerance appropriate to your data, not exact factor entries. A successful solve does not guarantee accuracy for a nearly singular matrix.