Skip to content

LazyMatrixGive your matrix a break.

Normalized design matrices for Rust. Keep sparse matrices sparse, reuse fitted parameters, and choose when to materialize.

A matrix relaxing in a beach chair under a palm tree.A matrix relaxing in a beach chair under a palm tree.

A normalized matrix, used as an operator ​

LazyMatrix represents

X~=(X−1c⊤)S−1 \widetilde{X} = (X - \mathbf{1}c^\top)S^{-1}

without storing the centered matrix. Here, cc holds the column centers, and SS is diagonal with the column scales. Products use the original storage and apply normalization algebraically.

Here is what each one represents, with C=1c⊤C = \mathbf{1}c^\top:

XX
[360060009]\begin{bmatrix}3&6&\phantom{0}\\\phantom{0}&6&\phantom{0}\\\phantom{0}&\phantom{0}&9\end{bmatrix}
Original matrix
Only four stored values
−-
C=1c⊤C = \mathbf{1}c^\top
[143143143]\begin{bmatrix}1&4&3\\1&4&3\\1&4&3\end{bmatrix}
Column centers, repeated
Represented by c = [1, 4, 3]
×\times
S−1S^{-1}
[120001400016]\begin{bmatrix}\frac12&\phantom{0}&\phantom{0}\\\phantom{0}&\frac14&\phantom{0}\\\phantom{0}&\phantom{0}&\frac16\end{bmatrix}
Inverse column scales
Represented by s = [2, 4, 6]

Without LazyMatrix, you either have to forget about normalizing the matrix, or realize the normalized matrix as a dense one:

X~=[3−126−440−360−126−440−360−120−449−36]=[112−12−1212−12−12−11]\widetilde{X} = \begin{bmatrix} \dfrac{3-1}{2} & \dfrac{6-4}{4} & \dfrac{0-3}{6}\\[1em] \dfrac{0-1}{2} & \dfrac{6-4}{4} & \dfrac{0-3}{6}\\[1em] \dfrac{0-1}{2} & \dfrac{0-4}{4} & \dfrac{9-3}{6} \end{bmatrix} = \begin{bmatrix} 1 & \dfrac12 & \color{#3b82f6}{-\dfrac12}\\[1em] \color{#3b82f6}{-\dfrac12} & \dfrac12 & \color{#3b82f6}{-\dfrac12}\\[1em] \color{#3b82f6}{-\dfrac12} & \color{#3b82f6}{-1} & 1 \end{bmatrix}

The blue values were implicit zeros. LazyMatrix keeps X and the two short vectors c and s. The repeated centers, diagonal matrix, and normalized entries above describe the operation; they never need to be stored as matrices.

Example ​

First, add the crate to your project with the ndarray feature:

sh
cargo add lazymatrix --features ndarray
cargo add ndarray@0.17

Then, use the LazyMatrix type to wrap an existing matrix and apply normalization. Here, we center by the mean and scale by the maximum absolute value of each column. Then, we multiply the normalized matrix by a vector.

rust
use lazymatrix::{Centering, LazyMatrix, MatVec, ColumnNormalization, Scaling};
use ndarray::array;

let x = array![[3.0, 6.0, 0.0], [0.0, 6.0, 0.0], [0.0, 0.0, 9.0]];
let lazy = LazyMatrix::new(
    x.view(),
    ColumnNormalization::new(Centering::Mean, Scaling::MaxAbs),
).unwrap();

let y = lazy.matvec(&array![1.0, 1.0, 1.0]).unwrap();
assert_eq!(y, array![1.0, -0.5, -0.5]);

Citation ​

See Citing LazyMatrix for a text citation and BibLaTeX and BibTeX entries.

Where to go next ​

Read the guide for installation and eager conversion, or see how it works for the normalization identities. The benchmark comparisons explain the costs of construction, repeated operations, and additional memory.

LazyMatrix is in early development, and breaking changes are expected. Find releases on crates.io and source code on GitHub.

Released under the MIT and Apache 2.0 licenses.