A Rust library built on top of ndarray that provides a domain-specific language (DSL) for expressing tensor computations using named indexes.
- Make multi-index array expressions explicit, readable, and compositional
- Remain compatible with the
ndarrayecosystem - Provide compile-time index checking via Ricci indexes
use ndarray::Array2;
use tensorism::new_ndarray;
// Generate a 2D array from indexed expressions
let x: Array2<f64> = new_ndarray! { for i j => a[i, j] + b[j] };Index i ranges over the first dimension of a, while j ranges over the second dimension of a and the only dimension of b (which must be compatible).
A top-level for ⟨indexes⟩ => ⟨body⟩ inside new_ndarray! generates a new ndarray::Array whose number of dimensions matches the number of indexes:
let y = new_ndarray! {
for i j k =>
if p[i, j] - 0.3 < 0.4 * q[j, k] {
r[j] * q[j, k] + 0.2
} else {
0.5 * s[i, j, k]
}
};When used as a sub-expression, for ⟨indexes⟩ => ⟨body⟩ evaluates to a Rust iterator, enabling aggregations:
let x: i64 = new_ndarray! {
Iterator::sum(for i => Iterator::min(for j => a[i, j]).unwrap())
};Tensorism supports special indexers inside bracket syntax:
rev: i— iterates over the axis in reverse orderplain: ⟨expr⟩— uses a fixedusizeexpression independent of iteration- User-defined indexers via
Reindexing1,Reindexing2, etc.
let array = new_ndarray! {
for i j => a[sorting_reindexing[i], rev: j] + b[j, plain: 3 * SIZE + 1]
};Fresh index names can be bound using let inside for constructs:
let array = new_ndarray! {
for i j let k = reindexing2[rev: i, j] => x[k, j] + y[k]
};Nested for constructs support conditional guards with if:
let conditional_sum = new_ndarray! {
Iterator::sum(for i j if u[i] < v[i, j] => w[i, j] * u[i])
};Tensorism is currently experimental. The evaluation strategy is primarily iterator-based, and the DSL may evolve in non-backward-compatible ways in future versions.
MIT