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}