import rustymath as rmArrays and indexing
Python operations run eagerly. Basic slices, reversals and transposes share storage without writable aliases: assigning to an array never changes another array’s values. A small view keeps its whole base allocation alive. Use a.compact() to keep just its logical items, even for a contiguous slice of a larger base. Float display rounding depends on the dtype and does not change the values returned by .tolist(). For expression fusion and richer array notation, use BasedPL.
Construction and dtypes
array copies Python values or a buffer exporter into an owned array. dtype selects its item type. Fill constructors take a shape; their _like forms retain another array’s shape, keys and names:
temperatures = rm.array([[18., 20, 22], [16, 19, 23]], dtype='float32')
temperatures, rm.zeros_like(temperatures), rm.full((2, 3), 1)(Array([[18.0, 20.0, 22.0], [16.0, 19.0, 23.0]], dtype=float32),
Array([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]], dtype=float32),
Array([[1, 1, 1], [1, 1, 1]], dtype=int64))
arange generates values before its stop. linspace includes both endpoints by default. logspace generates evenly spaced exponents in the chosen base:
rm.arange(0, 6, 2), rm.linspace(0, 1, 5), rm.logspace(0, 3, 4)(Array([0, 2, 4], dtype=int64),
Array([0.0, 0.25, 0.5, 0.75, 1.0], dtype=float64),
Array([1.0, 10.0, 100.0, 1000.0], dtype=float64))
Dtype helpers use the same supported item types as arrays. finfo reports float limits and spacing at 1; iinfo reports integer limits. result_type gives the shared promotion rule:
rm.finfo('float32').eps, rm.iinfo('int16').max, rm.result_type('int16', 'float16'), rm.issubdtype('float32', 'floating')(1.1920928955078125e-07, 32767, 'float16', True)
Broadcasting and conversion
Each function takes numbers, nested lists, rustymath arrays, NumPy arrays, or any other object that exports a buffer. It returns a rustymath.Array, or a Python number when every argument is a Python number. Arguments of different shapes broadcast by aligning their leading axes, where NumPy aligns trailing axes. An axis of length 1 stretches to match. broadcast_to makes this explicit and returns a compact copy. Here a vector of two items adds one number to each row of a 2 by 3 matrix:
rm.add([[1, 2, 3], [4, 5, 6]], [10, 20])Array([[11, 12, 13], [24, 25, 26]], dtype=int64)
Arrays of different dtypes promote to the higher kind, wide enough to hold both. An integer array beside a float or complex array takes that dtype, as in JAX. So an int16 array beside a float16 array gives float16, where NumPy gives float32. Characters don’t mix with numbers. An integer sum, difference, product, magnitude or power that doesn’t fit its dtype wraps, as in NumPy. A Python number or list follows NEP 50, as in NumPy: it takes the dtype of the array beside it, unless its kind is higher, so a float beside an integer array gives float64. A function that computes only on floats, such as exp, converts Booleans and integers to the narrowest float that holds them exactly, but at least float32, which computes faster than float16. So Booleans, uint8 and int16 give float32, and wider integers give float64:
i16 = rm.array([1, 2]).astype('int16')
rm.add(i16, 3), rm.add(i16, 0.5), rm.add(i16, rm.array([0.5, 1]).astype('float16')), rm.exp(i16), rm.exp([1, 2])(Array([4, 5], dtype=int16),
Array([1.5, 2.5], dtype=float64),
Array([1.5, 3.0], dtype=float16),
Array([2.71828, 7.38906], dtype=float32),
Array([2.7182818, 7.3890561], dtype=float64))
A rustymath.Array has arithmetic, bitwise and comparison operators, @, and the methods .sum(), .prod(), .max(), .min(), .all(), .any(), .cumsum(), .cumprod(), .reshape(), .astype() and .tolist(). With no axis, a reduction reduces the whole array to a number, and a scan runs over the flattened array:
m = rm.array([[1, 2], [3, 4]])
(m * 10).sum(), m.sum(axis=0), m.cumsum()(100, Array([4, 6], dtype=int64), Array([1, 3, 6, 10], dtype=int64))
Selecting and reshaping
Brackets take one selector for each axis. Index arrays pair by broadcasting from the front, as function arguments do: grid[[1, 0], [2, 0]] reads two items. Length-1 axes expand, so index arrays shaped as an open grid read every combination, as np.ix_’s do. The paired axes take the first array’s place, where NumPy moves them to the front when a slice separates the arrays. Integers remove an axis; lists and one-dimensional Boolean masks keep it. Negative positions count from the end, and slices take Python’s start, stop and step syntax:
grid = rm.array([[10, 20, 30], [40, 50, 60]])
grid[[1, 0], [2, 0]], grid[[[1], [0]], [[2, 0]]], grid[:, ::-1], grid[:, [True, False, True]](Array([60, 10], dtype=int64),
Array([[60, 40], [30, 10]], dtype=int64),
Array([[30, 20, 10], [60, 50, 40]], dtype=int64),
Array([[10, 30], [40, 60]], dtype=int64))
... fills in omitted axes and None inserts an axis of length one. Transpose and reshape of compact inputs share storage. Joining arrays creates a compact result:
grid[None, ...].shape, grid.T, rm.concatenate([grid, grid], axis=0).shape((1, 2, 3), Array([[10, 40], [20, 50], [30, 60]], dtype=int64), (4, 3))
eye constructs an identity or rectangular diagonal matrix. diag constructs a matrix from a vector, or extracts a matrix’s diagonal. diagonal works on any two axes, moving the diagonal to the last axis. Its axes default to the last two, the axes that the matrix functions take, where NumPy defaults to the first two. tril and triu keep one triangle of each matrix. All return compact copies.
rm.eye(3), rm.diagonal(grid), rm.tril(grid)(Array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], dtype=float64),
Array([10, 50], dtype=int64),
Array([[10, 0, 0], [40, 50, 0]], dtype=int64))
A selection returns a Python scalar when every axis is removed, and an independent array otherwise. Assignment uses the same selectors and leading-axis broadcasting. It preserves the array’s dtype and shape, and leaves earlier selections unchanged. For repeated unkeyed targets, the last assigned value wins:
selected = grid[:, 1]
grid[:, 1] = [200, 500]
selected, grid(Array([20, 50], dtype=int64),
Array([[10, 200, 30], [40, 500, 60]], dtype=int64))
rm.at(rm.add, a, indices, b) adds b at the selection, as NumPy’s np.add.at does, but returns a new array. A target selected more than once adds every item that selects it. Here row 0’s last item is selected twice:
rm.at(rm.add, grid, ([0, 0, 1], [2, 2, 0]), [1, 2, 3])Array([[10, 200, 33], [43, 500, 60]], dtype=int64)
Names and keys
A dictionary becomes a vector whose axis has the dictionary’s keys. Functions align keyed axes by key:
rm.add({'a': 1.5, 'b': 2.5}, {'b': 10.0, 'a': 20.0})Array([21.5, 12.5], dtype=float64, keys=(('a', 'b'),))
Keys select positions too. An axis holds each key at most once. Selecting a key twice raises an error that names the key, such as duplicate key: a. Reordering a keyed axis keeps its keys:
keyed = rm.array({'a': [1, 2], 'b': [3, 4]})
keyed[['b', 'a']]Array([[3, 4], [1, 2]], dtype=int64, keys=(('b', 'a'), None))
Assigning a tuple to .names names the axes, with one name or None for each axis. axis then takes a name wherever it takes a position:
s = rm.array([[10, 20, 30], [40, 50, 60]])
s.names = ('city', 'month')
s.sum(axis='month')Array([60, 150], dtype=int64, names=('city',))
Index arrays with axis names follow arithmetic’s rule for names. Two with the same name read one position along both, and two with different names read every combination. A named index array selects along the axis with its name, wherever it stands in the brackets:
m = rm.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
i, j = rm.arange(3), rm.arange(3)
i.names, j.names = ('i',), ('j',)
month = rm.array([2, 0])
month.names = ('month',)
m[i, i], m[i, j].shape, s[month](Array([0, 4, 8], dtype=int64, names=('i',)),
(3, 3),
Array([[30, 10], [60, 40]], dtype=int64, names=('city', 'month')))
Interoperability and threads
Rustymath arrays export Python’s buffer protocol. NumPy can read them through np.asarray without copying. Rustymath accepts NumPy arrays and other buffer exporters too. Its Python names follow familiar NumPy or SciPy spellings where they fit the shared core. Broadcasting and indexing follow rustymath’s rules.
Here exp keeps its argument’s 32-bit width:
import numpy as npx = np.linspace(0, 2, 5, dtype=np.float32)
rm.exp(x)Array([1.0, 1.64872, 2.71828, 4.48169, 7.38906], dtype=float32)
Buffer exports are read-only snapshots. They retain their values after assignment to the Rustymath array. Transposes and slices export their actual strides without copying. Use ordinary Rustymath bracket assignment to change an array.
rm.set_threads(n) limits parallel calls on the calling thread and returns its previous setting. Use 0 for the default or 1 for serial calls. Results need not agree in their last bits across thread counts.