#![allow(dead_code, unreachable_pub)]
use nuvec_core::{Array, Element, SliceArg, slice};
pub struct Rng(u64);
impl Rng {
pub fn new(seed: u64) -> Self {
Self(seed.max(1))
}
pub fn next(&mut self) -> u64 {
self.0 ^= self.0 >> 12;
self.0 ^= self.0 << 25;
self.0 ^= self.0 >> 27;
self.0.wrapping_mul(0x2545_F491_4F6C_DD1D)
}
pub fn below(&mut self, n: usize) -> usize {
(self.next() % n as u64) as usize
}
pub fn range(&mut self, lo: isize, hi: isize) -> isize {
lo + self.below((hi - lo + 1) as usize) as isize
}
pub fn coin(&mut self) -> bool {
self.next() & 1 == 1
}
pub fn f64(&mut self) -> f64 {
(self.next() >> 11) as f64 / (1u64 << 53) as f64 * 20.0 - 10.0
}
pub fn shape(&mut self) -> Vec<usize> {
(0..self.below(5)).map(|_| 1 + self.below(5)).collect()
}
pub fn broadcastable(&mut self, shape: &[usize]) -> Vec<usize> {
let drop = self.below(shape.len() + 1);
shape[drop..]
.iter()
.map(|&d| if self.below(3) == 0 { 1 } else { d })
.collect()
}
}
pub fn size(shape: &[usize]) -> usize {
shape.iter().product()
}
fn unravel(mut i: usize, shape: &[usize]) -> Vec<usize> {
let mut idx = vec![0; shape.len()];
for d in (0..shape.len()).rev() {
idx[d] = i % shape[d];
i /= shape[d];
}
idx
}
fn ravel(idx: &[usize], shape: &[usize]) -> usize {
idx.iter().zip(shape).fold(0, |acc, (&i, &d)| acc * d + i)
}
pub fn slice_indices(n: usize, arg: SliceArg) -> Vec<usize> {
let n = n as isize;
let wrap = |i: isize| if i < 0 { i + n } else { i };
let mut out = Vec::new();
if arg.step > 0 {
let mut i = arg.start.map_or(0, wrap).clamp(0, n);
let stop = arg.stop.map_or(n, wrap).clamp(0, n);
while i < stop {
out.push(i as usize);
i += arg.step;
}
} else {
let mut i = arg.start.map_or(n - 1, wrap).clamp(-1, n - 1);
let stop = arg.stop.map_or(-1, wrap).clamp(-1, n - 1);
while i > stop {
out.push(i as usize);
i += arg.step;
}
}
out
}
pub fn broadcast_shape(a: &[usize], b: &[usize]) -> Option<Vec<usize>> {
let n = a.len().max(b.len());
let dim = |s: &[usize], i: usize| {
if i + s.len() < n {
1
} else {
s[i + s.len() - n]
}
};
(0..n)
.map(|i| match (dim(a, i), dim(b, i)) {
(x, y) if x == y => Some(x),
(1, y) => Some(y),
(x, 1) => Some(x),
_ => None,
})
.collect()
}
#[derive(Debug, Clone, PartialEq)]
pub struct Ref<T> {
pub shape: Vec<usize>,
pub data: Vec<T>,
}
impl<T: Copy> Ref<T> {
pub fn new(shape: Vec<usize>, data: Vec<T>) -> Self {
assert_eq!(size(&shape), data.len());
Self { shape, data }
}
fn build(shape: Vec<usize>, f: impl Fn(&[usize]) -> T) -> Self {
let data = (0..size(&shape)).map(|i| f(&unravel(i, &shape))).collect();
Self { shape, data }
}
fn at(&self, idx: &[usize]) -> T {
self.data[ravel(idx, &self.shape)]
}
pub fn transpose(&self, axes: &[usize]) -> Self {
let shape = axes.iter().map(|&a| self.shape[a]).collect();
Self::build(shape, |out| {
let mut idx = vec![0; axes.len()];
for (o, &a) in axes.iter().enumerate() {
idx[a] = out[o];
}
self.at(&idx)
})
}
pub fn slice(&self, args: &[SliceArg]) -> Self {
let picks: Vec<Vec<usize>> = self
.shape
.iter()
.zip(args)
.map(|(&n, &a)| slice_indices(n, a))
.collect();
let shape = picks.iter().map(Vec::len).collect();
Self::build(shape, |out| {
let idx: Vec<usize> = out.iter().zip(&picks).map(|(&o, p)| p[o]).collect();
self.at(&idx)
})
}
pub fn broadcast(&self, shape: &[usize]) -> Self {
let lead = shape.len() - self.shape.len();
Self::build(shape.to_vec(), |out| {
let idx: Vec<usize> = self
.shape
.iter()
.enumerate()
.map(|(d, &n)| if n == 1 { 0 } else { out[d + lead] })
.collect();
self.at(&idx)
})
}
pub fn reshape(&self, shape: Vec<usize>) -> Self {
Self::new(shape, self.data.clone())
}
pub fn map<U: Copy>(&self, f: impl Fn(T) -> U) -> Ref<U> {
Ref {
shape: self.shape.clone(),
data: self.data.iter().map(|&x| f(x)).collect(),
}
}
pub fn reduce<A: Copy>(
&self,
axes: &[usize],
keepdims: bool,
init: A,
f: impl Fn(A, T, usize) -> A,
) -> Ref<A> {
let kept: Vec<usize> = (0..self.shape.len())
.filter(|d| !axes.contains(d))
.collect();
let out_shape: Vec<usize> = kept.iter().map(|&d| self.shape[d]).collect();
let mut acc = vec![init; size(&out_shape)];
let mut seen = vec![0usize; acc.len()];
for (i, &x) in self.data.iter().enumerate() {
let idx = unravel(i, &self.shape);
let key: Vec<usize> = kept.iter().map(|&d| idx[d]).collect();
let o = ravel(&key, &out_shape);
acc[o] = f(acc[o], x, seen[o]);
seen[o] += 1;
}
let shape = if keepdims {
(0..self.shape.len())
.map(|d| if axes.contains(&d) { 1 } else { self.shape[d] })
.collect()
} else {
out_shape
};
Ref { shape, data: acc }
}
pub fn zip<U: Copy, V: Copy>(&self, other: &Ref<U>, f: impl Fn(T, U) -> V) -> Ref<V> {
let shape = broadcast_shape(&self.shape, &other.shape).expect("broadcastable");
let (a, b) = (self.broadcast(&shape), other.broadcast(&shape));
Ref {
shape,
data: a.data.iter().zip(&b.data).map(|(&x, &y)| f(x, y)).collect(),
}
}
}
pub fn random_layout<T: Element>(
rng: &mut Rng,
shape: &[usize],
values: Vec<T>,
) -> (Array, Ref<T>) {
let reference = Ref::new(shape.to_vec(), values);
let layout = if reference.data.is_empty() {
0
} else {
rng.below(3)
};
let array = match layout {
0 => Array::from_slice(&reference.data, shape).unwrap(),
1 => {
let axes: Vec<usize> = (0..shape.len()).rev().collect();
let stored = reference.transpose(&axes);
Array::from_slice(&stored.data, &stored.shape)
.unwrap()
.t()
.unwrap()
}
_ => {
let reversed: Vec<bool> = shape.iter().map(|_| rng.coin()).collect();
let big: Vec<usize> = shape.iter().map(|&d| 2 * d).collect();
let filler = reference.data.first().copied();
let mut stored = vec![filler.expect("non-empty"); size(&big)];
for i in 0..reference.data.len() {
let idx = unravel(i, shape);
let pos: Vec<usize> = idx
.iter()
.zip(shape)
.zip(&reversed)
.map(|((&j, &d), &r)| if r { 2 * d - 1 - 2 * j } else { 2 * j })
.collect();
stored[ravel(&pos, &big)] = reference.data[i];
}
let args: Vec<SliceArg> = reversed
.iter()
.map(|&r| SliceArg::new(None, None, if r { -2 } else { 2 }))
.collect();
slice(&Array::from_slice(&stored, &big).unwrap(), &args).unwrap()
}
};
(array, reference)
}
pub trait Same: Copy + std::fmt::Debug {
fn same(self, other: Self) -> bool;
}
macro_rules! same_eq {
($($t:ty),*) => {$( impl Same for $t { fn same(self, o: Self) -> bool { self == o } } )*};
}
same_eq!(bool, u8, u16, u32, u64, i8, i16, i32, i64);
macro_rules! same_float {
($($t:ty),*) => {$( impl Same for $t {
fn same(self, o: Self) -> bool { self.to_bits() == o.to_bits() || (self.is_nan() && o.is_nan()) }
} )*};
}
same_float!(f32, f64, nuvec_core::f16, nuvec_core::bf16);
impl Same for nuvec_core::Complex64 {
fn same(self, o: Self) -> bool {
self.re.same(o.re) && self.im.same(o.im)
}
}
pub fn assert_matches<T: Element + Same>(array: &Array, expected: &Ref<T>, context: &str) {
assert_eq!(array.shape(), expected.shape.as_slice(), "{context}: shape");
let got = array.to_vec::<T>().unwrap();
for (i, (g, e)) in got.iter().zip(&expected.data).enumerate() {
assert!(
g.same(*e),
"{context}: element {i}: got {g:?}, expected {e:?}"
);
}
}
pub fn matmul_ref(a: &Ref<f64>, b: &Ref<f64>) -> Ref<f64> {
let (na, nb) = (a.shape.len(), b.shape.len());
let (m, k, n) = (a.shape[na - 2], a.shape[na - 1], b.shape[nb - 1]);
let batch = broadcast_shape(&a.shape[..na - 2], &b.shape[..nb - 2]).expect("broadcastable");
let full = |r: &Ref<f64>, rows: usize, cols: usize| {
let mut s = batch.clone();
s.extend([rows, cols]);
r.broadcast(&s)
};
let (a, b) = (full(a, m, k), full(b, k, n));
let mut shape = batch.clone();
shape.extend([m, n]);
let data = (0..size(&shape))
.map(|i| {
let idx = unravel(i, &shape);
let (bi, r, c) = (
&idx[..idx.len() - 2],
idx[idx.len() - 2],
idx[idx.len() - 1],
);
(0..k)
.map(|p| {
let at = |x: &Ref<f64>, row: usize, col: usize| {
let mut j = bi.to_vec();
j.extend([row, col]);
x.data[ravel(&j, &x.shape)]
};
at(&a, r, p) * at(&b, p, c)
})
.sum()
})
.collect();
Ref { shape, data }
}