Crates.io | strobe |
lib.rs | strobe |
version | 0.2.0 |
source | src |
created_at | 2023-09-17 22:46:06.861463 |
updated_at | 2023-12-10 14:09:45.821415 |
description | Fast, low-memory, elementwise array expressions on the stack. Compatible with no-std (and no-alloc) environments. |
homepage | https://github.com/jlogan03/strobe/strobe |
repository | https://github.com/jlogan03/strobe/ |
max_upload_size | |
id | 975386 |
size | 81,491 |
Fast, low-memory, elementwise array expressions on the stack. Compatible with no-std (and no-alloc) environments.
This crate provides array expressions of arbitrary depth and executes without allocation (if an output array is provided) or with a single allocation only for the output array.
Vectorization over segments of the data is achieved whenever the inner function is compatible with the compiler's loop vectorizer and matches the available (and enabled) SIMD instruction sets on the target CPU.
If you're
then this may be helpful.
If, however, you're
ndarray
,then this may not be helpful at all.
use strobe::{array, add, sub, mul};
// Generate some arrays to operate on
const NT: usize = 10_000;
let a = vec![1.25_f64; NT];
let b = vec![-5.32; NT];
let c = vec![1e-3; NT];
let d = vec![3.14; NT];
// Associate those arrays with inputs to the expression
let an = &mut array(&a);
let bn = &mut array(&b);
let cn = &mut array(&c);
let dn = &mut array(&d);
// Build the expression tree, then evaluate,
// allocating once for the output array purely for convenience
let y = mul(&mut sub(an, bn), &mut add(cn, dn)).eval();
// Check results for consistency
(0..NT).for_each(|i| { assert_eq!(y[i], (a[i] - b[i]) * (c[i] + d[i]) ) });
While we use a simple example here, any strobe expression can be evaluated into existing storage in this way.
use strobe::{array, mul};
// Generate some arrays to operate on
const NT: usize = 10_000;
let a = vec![1.25_f64; NT];
let an0 = &mut array(&a); // Two input nodes from `a`, for brevity
let an1 = &mut array(&a);
// Pre-allocate storage
let mut y = vec![0.0; NT];
// Build the expression tree, then evaluate into preallocated storage.
mul(an0, an1).eval_into(&mut y);
// Check results for consistency
(0..NT).for_each(|i| { assert_eq!(y[i], a[i] * a[i] ) });
Many common functions are already implemented. Ones that are not
can be assembled using the unary
, binary
, ternary
, and
accumulator
functions along with a matching function pointer
or closure.
use strobe::{array, unary};
let x = [0.0_f64, 1.0, 2.0];
let mut xn = array(&x);
let sq_func = |a: &[f64], out: &mut [f64]| { (0..a.len()).for_each(|i| {out[i] = x[i].powi(2)}) };
let xsq = unary(&mut xn, &sq_func).eval();
(0..x.len()).for_each(|i| {assert_eq!(x[i] * x[i], xsq[i])});
Cool! Have fun with that.
Licensed under either of
at your option.