use cubecl::prelude::*;
#[derive(CubeType)]
pub(crate) struct PrngState<N: Size> {
taus_0: Vector<u32, N>,
taus_1: Vector<u32, N>,
taus_2: Vector<u32, N>,
lcg: Vector<u32, N>,
}
#[derive(CubeLaunch, CubeType)]
pub(crate) struct Seeds {
taus_0: u32,
taus_1: u32,
taus_2: u32,
lcg: u32,
}
#[cube]
impl<N: Size> PrngState<N> {
pub fn seeded(unit: usize, seeds: Seeds) -> PrngState<N> {
#[allow(arithmetic_overflow)]
let stream = Vector::new(1000000007u32)
* (Vector::new(unit as u32 * N::value() as u32) + component_indices::<N>());
PrngState::<N> {
taus_0: stream + Vector::new(seeds.taus_0),
taus_1: stream + Vector::new(seeds.taus_1),
taus_2: stream + Vector::new(seeds.taus_2),
lcg: stream + Vector::new(seeds.lcg),
}
}
pub fn next(&mut self) -> Vector<u32, N> {
self.taus_0 = taus_step(self.taus_0, 13u32, 19u32, 12u32, 4294967294u32);
self.taus_1 = taus_step(self.taus_1, 2u32, 25u32, 4u32, 4294967288u32);
self.taus_2 = taus_step(self.taus_2, 3u32, 11u32, 17u32, 4294967280u32);
self.lcg = lcg_step(self.lcg);
self.taus_0 ^ self.taus_1 ^ self.taus_2 ^ self.lcg
}
}
#[cube]
fn component_indices<N: Size>() -> Vector<u32, N> {
let mut indices = Vector::empty();
#[unroll]
for component in 0..N::value() {
indices.insert(component, comptime!(component as u32));
}
indices
}
#[cube]
fn taus_step<N: Size>(
z: Vector<u32, N>,
#[comptime] s1: u32,
#[comptime] s2: u32,
#[comptime] s3: u32,
#[comptime] m: u32,
) -> Vector<u32, N> {
let b = ((z << Vector::new(s1)) ^ z) >> Vector::new(s2);
((z & Vector::new(m)) << Vector::new(s3)) ^ b
}
#[cube]
fn lcg_step<N: Size>(z: Vector<u32, N>) -> Vector<u32, N> {
z * Vector::new(1664525u32) + Vector::new(1013904223u32)
}