6. Math Utilities
The math module is the numeric base the rest of RustyML stands on. It holds the pairwise distance kernels, the gemm-backed matrix products, and the deterministic parallel reductions that every estimator, neural-network layer, and metric calls underneath. Most of the time you use these primitives without naming them. KNN reaches for a distance kernel. A Dense layer reaches for a GEMM. A variance computation reaches for a blocked reduction. RustyML exports the callable primitives, so you can use them directly when you build something the higher-level API does not cover. The tunable primitives expose knobs you can fit to your hardware. The module compiles under the math feature. Any of machine_learning, neural_network, utils, or metrics turns math on transitively, and full includes it too. If you use RustyML at all, these primitives are already built. See Installation and Feature Flags.
One theme runs through all 3 sections. These primitives run in parallel, but stay reproducible. Each one decides serial-versus-rayon by comparing a work estimate against a calibrated threshold. The reductions never let that choice change the result. A reduction is bit-identical either way. The matrix products reproduce run to run, though a flipped strategy can move the last few bits (see 6.2.4). That property is what makes the knobs in Performance Tuning and Parallelism safe to move. It also backs the guarantees in Reproducibility and Random Seeds. Be comfortable with ndarray’s Array1/Array2 and views before you read on. Working with ndarray covers what you need. Read the sections in order. 6.1 and 6.3 give you functions you can call today. 6.2 is mostly context for a backend you invoke indirectly.
6.1. Distance Metrics
Distance Metrics covers 3 allocation-free per-row kernels: squared_euclidean_distance_row, manhattan_distance_row, and minkowski_distance_row. It also covers the DistanceCalculationMetric enum layered on top of them. DistanceCalculationMetric is the single dispatcher that KNN, DBSCAN, and the silhouette score all share. It turns the choice of metric into a runtime value, instead of a hard-coded match. The kernels are the fast path, and the Euclidean one deliberately skips the square root. The enum is the convenient path. Read this section first. It is the most directly usable part of the chapter.
use ndarray::array;
use rustyml::math::{DistanceCalculationMetric, squared_euclidean_distance_row};
fn main() {
let a = array![0.0_f64, 0.0];
let b = array![3.0_f64, 4.0];
// The raw kernel returns the *squared* distance. It never takes the root.
println!("squared: {}", squared_euclidean_distance_row(&a, &b)); // 25.0
// The dispatcher takes the root and lets the metric vary at runtime.
let metric = DistanceCalculationMetric::Euclidean;
println!("euclidean: {}", metric.distance(a.view(), b.view())); // 5.0
}
6.2. Matrix Multiplication
Matrix Multiplication explains the gemmkit backend behind every dense product in the library. It covers how gemmkit picks between shape-specialized routes. It covers how gemmkit decides, on its own, whether a product is worth threading and how wide to thread it. It also covers why a matrix-vector product gets its own cost class. It explains what it means for a result to stay bit-for-bit identical, no matter how many workers ran it. You rarely call this layer by name. It sits under the linear models and the dense and convolution layers. This section explains the strategy more than an API to call. It also names the knobs that are yours to turn. One is the caller-side tiling policy in rustyml::tuning::matmul. The other is the backend’s own GEMMKIT_* knobs, re-exported through it. Read this section when a model’s throughput matters to you.
6.3. Parallel Reductions
Parallel Reductions covers det_reduce and det_reduce_range. These are blocked folds. They give a sum, a dot product, or a per-bucket accumulator the same bits, whether they run on one thread or on all of them. A bare par_iter().sum() reorders its float additions however rayon happens to steal work. These helpers instead cut the input into fixed DET_REDUCE_BLOCK-sized chunks. The grouping, and therefore the rounding, never depends on scheduling. Reach for these helpers whenever you write your own parallel numeric loop, and want a result that does not drift between runs or thread counts.