COOLJAPAN
← All posts

NumRS2 0.4.1 Released — O(1) Copy-on-Write Arrays, Real Distributed Collectives, and a Production-Hardening Pass

NumRS2 0.4.1 is a production-hardening pass across the whole crate: Arc-backed copy-on-write arrays with O(1) clone, a shared kernels dispatch layer, expression fusion via .expr()/.eval(), real TCP-backed distributed collectives replacing fabricated results, lapack on by default, and a long, independently verified list of correctness fixes — on pure-Rust SciRS2 0.6.5 with 5,635+ passing tests.

release numrs2 numpy numerical-computing scirs2 simd distributed-computing pure-rust linear-algebra

The N-dimensional array core just got harder to break — O(1) clones, a real distributed transport, and a long list of bugs that were quietly returning wrong numbers, all fixed and independently verified against the working tree.

Today we released NumRS2 0.4.1 — a production-hardening pass across the whole crate: Array<T> is now Arc-backed copy-on-write, a shared dispatch layer backs the hot paths, the distributed feature’s collectives run over a real network transport instead of returning fabricated local data, lapack is on by default, and a long list of correctness bugs — some of them silently wrong numbers, not crashes — got found and fixed.

No C. No Fortran. No system BLAS/LAPACK. No hand-rolled, per-call-site SIMD dispatch to get subtly wrong under maintenance. Just clean, blazing-fast N-dimensional arrays — broadcasting, fancy indexing, SVD, FFT, autodiff — that compile to a single static binary (or WASM) and run everywhere, backed by a compute core that’s now been through a real correctness audit.

Why NumRS2 0.4.1 is a game changer

Fast array libraries are common. Array libraries that are honest about where they used to be wrong are not. NumPy is the bedrock of scientific Python, but its C/Fortran core makes this kind of audit nearly impossible for an outside contributor to perform — you can’t easily prove an upstream BLAS kernel silently mishandles a shape mismatch. NumRS2 0.4.1 spent this cycle doing exactly that kind of audit on its own codebase, in Rust, with regression tests as the receipts:

Technical Deep Dive: From Shared Kernels to a Real Wire Transport

Layer 1 — The kernels dispatch layer. src/kernels/ centralizes what used to be scattered, hand-rolled SIMD/parallel thresholds across the hot paths into one dtype-dispatched module: elementwise (unary/binary dispatch), gemm (2D matmul dispatch table), reduce (sum/mean/var/min/max with a deterministic accumulation order), cast (sound TypeId-guarded reinterpretation, replacing ad hoc raw-pointer type-punning), and borrow (contiguous-vs-owned operand bridging). Every ad hoc mem::transmute_copy and x as *const T as *const f64 cast in the hot paths now goes through kernels::cast, which proves soundness via TypeId::of::<T>() before reinterpreting.

Layer 2 — Expression fusion. IntoExpr::expr() builds a lazy ExprNode tree from an Array/scalar/expression; .eval() walks it in one fused pass instead of materializing every intermediate array. It’s honest about scope — eager syntax (&a + &b) doesn’t fuse on its own, only the explicit .expr()...eval() builder does (a compile-time fused! macro is deferred to 0.6.0) — and it’s honest about precision too: fused and eager evaluation are guaranteed bit-for-bit equal for every finite, infinite, and signed-zero element, but not for a NaN’s payload/sign bits when a single operation combines two distinct NaNs. That’s measured, not assumed — there’s a regression test that pins the exact case where the one-pass fused loop and the two-pass eager spelling disagree under rustc -O.

Layer 3 — A real distributed transport. src/distributed/net/ implements Endpoint, a real point-to-point TCP transport with a fixed 56-byte frame header and LZ4 payload compression, and src/distributed/collective.rs now runs all eight collectives (plus v-variants and reduce_scatter) over it for real. TSQR (Tall-Skinny QR) and block-cyclic-column Cholesky land alongside it in src/distributed/linalg/, and a LocalCluster test harness drives real loopback-TCP multi-rank tests without needing a process launcher.

Layer 4 — NumPy-parity breadth. Ufunc reduce/accumulate/outer/reduceat/.at() (in-place scatter), fftn/ifftn/rfftn/irfftn with NumPy’s exact s=/axes=/norm= conventions, all 9 NumPy≥1.22 quantile/percentile methods, histogramdd with density=, a full masked-array completion pass (std/var/prod/median/argmin/argmax/sort/cumsum/dot/concatenate), the Chebyshev/Legendre/Hermite/HermiteE/Laguerre polynomial classes, and new SeedSequence + Generator::spawn, Philox4x64, and SFC64 random generators.

Layer 5 — The correctness audit itself. Beyond variance: min/max returning a wrong finite value for certain NaN placements (a live upstream scirs2_core bug, now bypassed and pinned as a tripwire test), min_along_axis/max_along_axis panicking on every axis reduction, tile()’s N-D shape/ordering, moveaxis’s permutation for arbitrary axis pairs, einsum panicking on a HashMap miss, two confirmed scirs2_fft::fftn normalization bugs worked around at the wrapper level, and more — each with its own regression test and its own line in CHANGELOG.md pointing at the fix.

Getting Started

cargo add numrs2
use numrs2::prelude::*;

fn main() -> Result<()> {
    // N-dimensional arrays with NumPy-style broadcasting
    let a = Array::from_vec(vec![1.0, 2.0, 3.0, 4.0]).reshape(&[2, 2]);
    let b = Array::from_vec(vec![5.0, 6.0, 7.0, 8.0]).reshape(&[2, 2]);

    // Clone is O(1) now — an Arc bump, not a deep copy
    let a_clone = a.clone();
    let e = a.matmul(&b)?;             // matrix multiply, now up to 18.82x faster at 512^3
    println!("a @ b = {}", e);

    // Expression fusion: builds a lazy tree, evaluates in one fused pass
    let fused = a.expr() * 2.0 + b.expr();
    println!("fused = {}", fused.eval()?);

    // Linear algebra — lapack is a default feature now, no opt-in needed
    let (u, s, vt) = a.svd_compute()?;
    println!("singular values = {}", s);
    let _ = (u, vt, a_clone);

    Ok(())
}

The array, matmul, fusion, and linear-algebra calls above are the safe, copy-pasteable core. The new distributed collectives, the masked-array completion, and the polynomial classes are reached through their respective modules — see the Tips below and CHANGELOG.md for the full per-module list.

What’s New in 0.4.1

Added

Changed

Fixed

Tips

This is the foundation

NumRS2 is the NumPy-class N-dimensional array core at the base of the COOLJAPAN scientific stack, and 0.4.1 is the release where the compute paths underneath every higher layer got audited and hardened rather than extended. It sits directly beneath SciRS2 (now at v0.6.5 across the full scirs2-* family), with linear algebra on OxiBLAS, serialization on OxiCode, and compression on OxiArc — and it’s a real, in-production dependency: QuantRS2 and VoiRS both pull numrs2 directly today. Around it, OptiRS and PandRS sit alongside for optimization and dataframes; the ML and applied stack spans ToRSh, sklears, and trustformers; and the acceleration tier reaches from OxiCUDA for GPU compute to OxiMedia and OxiGDAL for media and geospatial.

Repository: https://github.com/cool-japan/numrs

Star the repo if you want a NumPy-class array core that’s not just fast, but auditable — O(1) clones, a real distributed transport, and a changelog that tells you exactly which numbers used to be wrong and why.

The era of trusting an opaque C/Fortran core is over. Pure Rust numerical computing — hardened, not just extended — is here.

KitaSan at COOLJAPAN OÜ August 29, 2026

↑ Back to all posts