Numerical computing

NdArray construction, views, broadcasting, reductions, and matrix products.

NdArray<T> stores a dense numerical array with a shape. Use it for matrices, images, tensors, and other data where dimensions matter. Use an ordinary [T] array when you need a growable list or rows of different lengths.

Construct arrays

use std.ndarray

let zeros = ndarray.zeros<f32>([2, 3])
let ones = ndarray.ones<i16>([4])
let filled = ndarray.full<float>([2, 2], 1.5)
let sequence = ndarray.arange(0, 10, 2)
let points = ndarray.linspace(0.0, 1.0, 5)

let matrix = ndarray.from_array<i32>(
  [1, 2, 3, 4, 5, 6],
  [2, 3],
)?

from_array returns an error when the shape does not match the number of values. from_nested accepts rectangular nested arrays and rejects ragged rows.

Supported element types are bool, fixed-width signed and unsigned integers, f32, and float. Use astype<U>() for an explicit checked conversion.

Shape and indexing

println(matrix.rank())       // 2
println(matrix.shape())      // [2, 3]
println(matrix.strides())    // [3, 1]
println(matrix.get([1, -1])) // 6

matrix.set([0, 1], 20)

Negative indexes count from the end of a dimension. get_or_none is useful when an out-of-bounds coordinate should be handled as none.

Views

Transpose, slice, axis swapping, squeezing, expanding, and broadcasting create views of the same values:

let transposed = matrix.transpose([1, 0])?
let row = matrix.slice([0, 0], [1, 3], [1, 1])?

A write through the original array is visible through a writable view. A broadcast view is read-only because several coordinates may refer to the same value. Call copy() when you need independent writable data, or contiguous() when an API requires a compact row-major layout.

reshape reuses a contiguous array when possible and otherwise returns a compact copy.

Broadcasting and arithmetic

Binary operations align dimensions from the right. Equal dimensions match and a dimension of length one expands:

let row = ndarray.full<i32>([1, 3], 10)
let result = matrix + row
println(result.shape()) // [2, 3]

Mixed integer arrays use a wide enough integer type. Operations involving float produce float; f32 + f32 remains f32. Boolean arithmetic is not allowed.

Use eq_elements for element-by-element equality, array_equal for exact shape and content equality, and all_close for approximate numeric equality.

Reductions and linear algebra

println(matrix.sum())
println(matrix.mean())
println(matrix.sum_axis(1).to_array())

let right = ndarray.from_array<i32>(
  [1, 2, 3, 4, 5, 6],
  [3, 2],
)?
println(ndarray.matmul(matrix, right)?.shape()) // [2, 2]

sum, product, min, max, mean, any, and all reduce a whole array. The _axis forms preserve the other dimensions. dot accepts vectors; matmul handles vectors, matrices, and batched matrices.

Empty sums and products use their usual identities. Empty min, max, and mean return none; an invalid axis or incompatible matrix shape returns an error.

Raw bytes

Use to_bytes and from_bytes when exchanging a full array with an image, audio, or C API:

let data = ndarray.to_bytes(matrix)
let restored = ndarray.from_bytes<i32>(data, [2, 3])?

The bytes use the host's byte order and are intended for local bulk transfer, not as a portable file format. to_bytes returns values in logical row-major order even when the input is a transposed or sliced view.

Iterate and discover

iter() yields values in logical row-major order and works with ordinary iterator methods:

let positive = matrix.iter().filter(|value| value > 0).count()

Browse the complete surface with:

moon doc std ndarray
moon doc builtin NdArray

The same signatures are available in the NdArray API reference.