atoms.rs

7.0 kB · rust · 248 lines

1use super::tensor::Tensor;23fn build(n: usize, rank: usize, rule: impl Fn(&[usize]) -> bool) -> Tensor {4    let mut out = Tensor::new(vec![n; rank]);5    let mut at = vec![0usize; rank];6    for flat in 0..out.size() {7        let mut rest = flat;8        for axis in (0..rank).rev() {9            at[axis] = rest % n;10            rest /= n;11        }12        out.bytes_mut()[flat] = rule(&at) as u8;13    }14    out15}1617fn odd(at: &[usize]) -> usize {18    at.iter().filter(|c| *c % 2 == 1).count()19}2021/// Builds a carpet of the given side at any rank, on where at most one coordinate is odd.22pub fn carpet_nd(n: usize, rank: usize) -> Tensor {23    build(n, rank, |at| odd(at) <= 1)24}2526/// Builds a net of the given side at any rank, on where the odd coordinates plus one reach the rank.27pub fn net_nd(n: usize, rank: usize) -> Tensor {28    build(n, rank, |at| odd(at) + 1 >= rank)29}3031/// 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.32pub fn tree_nd(n: usize, rank: usize, axis: usize) -> Tensor {33    build(n, rank, |at| {34        at.iter().enumerate().all(|(i, c)| i == axis || c % 2 == 0)35    })36}3738/// Builds a void of the given side at any rank, on where every coordinate shares one parity.39pub fn void_nd(n: usize, rank: usize) -> Tensor {40    build(n, rank, |at| at.iter().all(|c| c % 2 == at[0] % 2))41}4243/// Builds a point of the given side at any rank, on where every coordinate is odd.44pub fn point_nd(n: usize, rank: usize) -> Tensor {45    build(n, rank, |at| odd(at) == rank)46}4748/// Builds a dust of the given side at any rank, on where every coordinate is even.49pub fn dust_nd(n: usize, rank: usize) -> Tensor {50    build(n, rank, |at| odd(at) == 0)51}5253/// 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.54pub fn line_nd(n: usize, rank: usize, axis: usize) -> Tensor {55    build(n, rank, |at| {56        at.iter().enumerate().all(|(i, c)| i == axis || c % 2 == 1)57    })58}5960/// Builds a star of the given side at any rank, on where exactly one coordinate is odd.61pub fn star_nd(n: usize, rank: usize) -> Tensor {62    build(n, rank, |at| odd(at) == 1)63}6465/// Builds an n by n tensor of zeros.66pub fn zeros_2d(n: usize) -> Tensor {67    Tensor::new(vec![n, n])68}6970/// Builds an n by n by n tensor of zeros.71pub fn zeros_3d(n: usize) -> Tensor {72    Tensor::new(vec![n, n, n])73}7475/// Builds an n by n tensor of ones.76pub fn ones_2d(n: usize) -> Tensor {77    Tensor::full(vec![n, n], 1)78}7980/// Builds an n by n by n tensor of ones.81pub fn ones_3d(n: usize) -> Tensor {82    Tensor::full(vec![n, n, n], 1)83}8485/// Builds an n by n tensor where each cell turns on with probability density.86pub fn noise_2d(n: usize, density: f64) -> Tensor {87    build(n, 2, |_| super::state::random() < density)88}8990/// Builds an n by n by n tensor where each cell turns on with probability density.91pub fn noise_3d(n: usize, density: f64) -> Tensor {92    build(n, 3, |_| super::state::random() < density)93}9495/// Builds an n by n carpet, on where at most one coordinate is odd.96pub fn carpet_2d(n: usize) -> Tensor {97    carpet_nd(n, 2)98}99100/// Builds an n by n by n carpet, on where at most one coordinate is odd.101pub fn carpet_3d(n: usize) -> Tensor {102    carpet_nd(n, 3)103}104105/// Builds an n by n net, on where at least one coordinate is odd.106pub fn net_2d(n: usize) -> Tensor {107    net_nd(n, 2)108}109110/// Builds an n by n by n net, on where at least two coordinates are odd.111pub fn net_3d(n: usize) -> Tensor {112    net_nd(n, 3)113}114115/// Builds an n by n tree, free on axis 1, on along the even rows.116pub fn htree_2d(n: usize) -> Tensor {117    tree_nd(n, 2, 1)118}119120/// Builds an n by n tree, free on axis 0, on along the even columns.121pub fn vtree_2d(n: usize) -> Tensor {122    tree_nd(n, 2, 0)123}124125/// Builds an n by n by n tree, free on axis 0, its beams running along x.126pub fn xtree_3d(n: usize) -> Tensor {127    tree_nd(n, 3, 0)128}129130/// Builds an n by n by n tree, free on axis 1, its beams running along y.131pub fn ytree_3d(n: usize) -> Tensor {132    tree_nd(n, 3, 1)133}134135/// Builds an n by n by n tree, free on axis 2, its beams running along z.136pub fn ztree_3d(n: usize) -> Tensor {137    tree_nd(n, 3, 2)138}139140/// Builds an n by n void, on where both coordinates share one parity.141pub fn void_2d(n: usize) -> Tensor {142    void_nd(n, 2)143}144145/// Builds an n by n by n void, on where all three coordinates share one parity.146pub fn void_3d(n: usize) -> Tensor {147    void_nd(n, 3)148}149150/// Builds an n by n point, on where both coordinates are odd.151pub fn point_2d(n: usize) -> Tensor {152    point_nd(n, 2)153}154155/// Builds an n by n by n point, on where all three coordinates are odd.156pub fn point_3d(n: usize) -> Tensor {157    point_nd(n, 3)158}159160/// Builds an n by n dust, on where both coordinates are even.161pub fn dust_2d(n: usize) -> Tensor {162    dust_nd(n, 2)163}164165/// Builds an n by n by n dust, on where all three coordinates are even.166pub fn dust_3d(n: usize) -> Tensor {167    dust_nd(n, 3)168}169170/// Builds an n by n line, free on axis 1, on along the odd rows.171pub fn hline_2d(n: usize) -> Tensor {172    line_nd(n, 2, 1)173}174175/// Builds an n by n line, free on axis 0, on along the odd columns.176pub fn vline_2d(n: usize) -> Tensor {177    line_nd(n, 2, 0)178}179180/// Builds an n by n by n line, free on axis 0, its rods running along x.181pub fn xline_3d(n: usize) -> Tensor {182    line_nd(n, 3, 0)183}184185/// Builds an n by n by n line, free on axis 1, its rods running along y.186pub fn yline_3d(n: usize) -> Tensor {187    line_nd(n, 3, 1)188}189190/// Builds an n by n by n line, free on axis 2, its rods running along z.191pub fn zline_3d(n: usize) -> Tensor {192    line_nd(n, 3, 2)193}194195/// Builds an n by n star, on where exactly one coordinate is odd.196pub fn star_2d(n: usize) -> Tensor {197    star_nd(n, 2)198}199200/// Builds an n by n by n star, on where exactly one coordinate is odd.201pub fn star_3d(n: usize) -> Tensor {202    star_nd(n, 3)203}204205#[cfg(test)]206mod tests {207    use super::*;208    use crate::state;209    #[test]210    fn noise_draws_in_flat_order() {211        let _g = state::guard();212        state::seed(11);213        let drawn = noise_3d(3, 0.5);214        state::seed(11);215        let mut want = Tensor::new(vec![3, 3, 3]);216        for flat in 0..27 {217            want.bytes_mut()[flat] = (state::random() < 0.5) as u8;218        }219        assert_eq!(drawn, want);220    }221    #[test]222    fn the_antis_complement_the_classics() {223        for n in [3, 5] {224            for (classic, anti) in [225                (carpet_2d(n), point_2d(n)),226                (net_2d(n), dust_2d(n)),227                (htree_2d(n), hline_2d(n)),228                (vtree_2d(n), vline_2d(n)),229                (void_2d(n), star_2d(n)),230            ] {231                let both = classic.bytes().iter().zip(anti.bytes());232                assert!(both.map(|(a, b)| a + b).all(|v| v == 1));233            }234        }235    }236    #[test]237    fn the_sponge_is_dust_plus_star() {238        for n in [3, 5, 7] {239            let sum: Vec<u8> = dust_3d(n)240                .bytes()241                .iter()242                .zip(star_3d(n).bytes())243                .map(|(a, b)| a + b)244                .collect();245            assert_eq!(sum, carpet_3d(n).bytes());246        }247    }248}