atoms.rs

7.2 kB · rust · 253 lines

1use crate::core::rng::Rng;2use crate::core::tensor::Tensor;34fn build(n: usize, rank: usize, mut rule: impl FnMut(&[usize]) -> bool) -> Tensor {5    let mut out = Tensor::new(vec![n; rank]);6    let mut at = vec![0usize; rank];7    for flat in 0..out.size() {8        let mut rest = flat;9        for axis in (0..rank).rev() {10            at[axis] = rest % n;11            rest /= n;12        }13        out.put(flat, i64::from(rule(&at)));14    }15    out16}1718fn odd(at: &[usize]) -> usize {19    at.iter().filter(|c| *c % 2 == 1).count()20}2122/// Builds a carpet of the given side at any rank, on where at most one coordinate is odd.23pub fn carpet_nd(n: usize, rank: usize) -> Tensor {24    build(n, rank, |at| odd(at) <= 1)25}2627/// Builds a net of the given side at any rank, on where the odd coordinates plus one reach the rank.28pub fn net_nd(n: usize, rank: usize) -> Tensor {29    build(n, rank, |at| odd(at) + 1 >= rank)30}3132/// Builds a tree of the given side at any rank, even on every axis but the free one; an axis past the rank frees none.33pub fn tree_nd(n: usize, rank: usize, axis: usize) -> Tensor {34    build(n, rank, |at| {35        at.iter().enumerate().all(|(i, c)| i == axis || c % 2 == 0)36    })37}3839/// Builds a void of the given side at any rank, on where every coordinate shares one parity.40pub fn void_nd(n: usize, rank: usize) -> Tensor {41    build(n, rank, |at| at.iter().all(|c| c % 2 == at[0] % 2))42}4344/// Builds a point of the given side at any rank, on where every coordinate is odd.45pub fn point_nd(n: usize, rank: usize) -> Tensor {46    build(n, rank, |at| odd(at) == rank)47}4849/// Builds a dust of the given side at any rank, on where every coordinate is even.50pub fn dust_nd(n: usize, rank: usize) -> Tensor {51    build(n, rank, |at| odd(at) == 0)52}5354/// Builds a line of the given side at any rank, odd on every axis but the free one; an axis past the rank frees none.55pub fn line_nd(n: usize, rank: usize, axis: usize) -> Tensor {56    build(n, rank, |at| {57        at.iter().enumerate().all(|(i, c)| i == axis || c % 2 == 1)58    })59}6061/// Builds a star of the given side at any rank, on where exactly one coordinate is odd.62pub fn star_nd(n: usize, rank: usize) -> Tensor {63    build(n, rank, |at| odd(at) == 1)64}6566/// Builds an n by n tensor of zeros.67pub fn zeros_2d(n: usize) -> Tensor {68    Tensor::new(vec![n, n])69}7071/// Builds an n by n by n tensor of zeros.72pub fn zeros_3d(n: usize) -> Tensor {73    Tensor::new(vec![n, n, n])74}7576/// Builds an n by n tensor of ones.77pub fn ones_2d(n: usize) -> Tensor {78    Tensor::full(vec![n, n], 1)79}8081/// Builds an n by n by n tensor of ones.82pub fn ones_3d(n: usize) -> Tensor {83    Tensor::full(vec![n, n, n], 1)84}8586/// Builds an n by n tensor where each cell turns on with probability density, drawn from the stream.87pub fn noise_2d(n: usize, density: f64, rng: &mut Rng) -> Tensor {88    build(n, 2, |_| rng.chance(density))89}9091/// Builds an n by n by n tensor where each cell turns on with probability density, drawn from the stream.92pub fn noise_3d(n: usize, density: f64, rng: &mut Rng) -> Tensor {93    build(n, 3, |_| rng.chance(density))94}9596/// Builds an n by n carpet, on where at most one coordinate is odd.97///98/// ```99/// let seed = mrlyrs::math::atoms::carpet_2d(3);100/// assert_eq!(seed.sum(), 8);101/// ```102pub fn carpet_2d(n: usize) -> Tensor {103    carpet_nd(n, 2)104}105106/// Builds an n by n by n carpet, on where at most one coordinate is odd.107pub fn carpet_3d(n: usize) -> Tensor {108    carpet_nd(n, 3)109}110111/// Builds an n by n net, on where at least one coordinate is odd.112pub fn net_2d(n: usize) -> Tensor {113    net_nd(n, 2)114}115116/// Builds an n by n by n net, on where at least two coordinates are odd.117pub fn net_3d(n: usize) -> Tensor {118    net_nd(n, 3)119}120121/// Builds an n by n tree, free on axis 1, on along the even rows.122pub fn htree_2d(n: usize) -> Tensor {123    tree_nd(n, 2, 1)124}125126/// Builds an n by n tree, free on axis 0, on along the even columns.127pub fn vtree_2d(n: usize) -> Tensor {128    tree_nd(n, 2, 0)129}130131/// Builds an n by n by n tree, free on axis 0, its beams running along x.132pub fn xtree_3d(n: usize) -> Tensor {133    tree_nd(n, 3, 0)134}135136/// Builds an n by n by n tree, free on axis 1, its beams running along y.137pub fn ytree_3d(n: usize) -> Tensor {138    tree_nd(n, 3, 1)139}140141/// Builds an n by n by n tree, free on axis 2, its beams running along z.142pub fn ztree_3d(n: usize) -> Tensor {143    tree_nd(n, 3, 2)144}145146/// Builds an n by n void, on where both coordinates share one parity.147pub fn void_2d(n: usize) -> Tensor {148    void_nd(n, 2)149}150151/// Builds an n by n by n void, on where all three coordinates share one parity.152pub fn void_3d(n: usize) -> Tensor {153    void_nd(n, 3)154}155156/// Builds an n by n point, on where both coordinates are odd.157pub fn point_2d(n: usize) -> Tensor {158    point_nd(n, 2)159}160161/// Builds an n by n by n point, on where all three coordinates are odd.162pub fn point_3d(n: usize) -> Tensor {163    point_nd(n, 3)164}165166/// Builds an n by n dust, on where both coordinates are even.167pub fn dust_2d(n: usize) -> Tensor {168    dust_nd(n, 2)169}170171/// Builds an n by n by n dust, on where all three coordinates are even.172pub fn dust_3d(n: usize) -> Tensor {173    dust_nd(n, 3)174}175176/// Builds an n by n line, free on axis 1, on along the odd rows.177pub fn hline_2d(n: usize) -> Tensor {178    line_nd(n, 2, 1)179}180181/// Builds an n by n line, free on axis 0, on along the odd columns.182pub fn vline_2d(n: usize) -> Tensor {183    line_nd(n, 2, 0)184}185186/// Builds an n by n by n line, free on axis 0, its rods running along x.187pub fn xline_3d(n: usize) -> Tensor {188    line_nd(n, 3, 0)189}190191/// Builds an n by n by n line, free on axis 1, its rods running along y.192pub fn yline_3d(n: usize) -> Tensor {193    line_nd(n, 3, 1)194}195196/// Builds an n by n by n line, free on axis 2, its rods running along z.197pub fn zline_3d(n: usize) -> Tensor {198    line_nd(n, 3, 2)199}200201/// Builds an n by n star, on where exactly one coordinate is odd.202pub fn star_2d(n: usize) -> Tensor {203    star_nd(n, 2)204}205206/// Builds an n by n by n star, on where exactly one coordinate is odd.207pub fn star_3d(n: usize) -> Tensor {208    star_nd(n, 3)209}210211#[cfg(test)]212mod tests {213    use super::*;214    #[test]215    fn noise_replays_from_its_seed_and_parts_on_another() {216        assert_eq!(217            noise_3d(3, 0.5, &mut Rng::new(11)),218            noise_3d(3, 0.5, &mut Rng::new(11))219        );220        assert_ne!(221            noise_2d(4, 0.5, &mut Rng::new(1)),222            noise_2d(4, 0.5, &mut Rng::new(2))223        );224    }225    #[test]226    fn the_antis_complement_the_classics() {227        for n in [3, 5] {228            for (classic, anti) in [229                (carpet_2d(n), point_2d(n)),230                (net_2d(n), dust_2d(n)),231                (htree_2d(n), hline_2d(n)),232                (vtree_2d(n), vline_2d(n)),233                (void_2d(n), star_2d(n)),234            ] {235                let both = classic.bytes().unwrap().iter().zip(anti.bytes().unwrap());236                assert!(both.map(|(a, b)| a + b).all(|v| v == 1));237            }238        }239    }240    #[test]241    fn the_sponge_is_dust_plus_star() {242        for n in [3, 5, 7] {243            let sum: Vec<u8> = dust_3d(n)244                .bytes()245                .unwrap()246                .iter()247                .zip(star_3d(n).bytes().unwrap())248                .map(|(a, b)| a + b)249                .collect();250            assert_eq!(sum, carpet_3d(n).bytes().unwrap());251        }252    }253}