stack.rs

4.9 kB · rust · 175 lines

1use super::field::Field;2use super::layer::{layer, Layer};3use super::{Combine, Lattice, Spec};4use mrlycore::errors::Result;56/// Folds one boolean layer into the accumulator under the combine, the first layer seeding a meet.7pub fn merge(acc: &mut [f32], mask: &[bool], combine: Combine, first: bool) {8    match combine {9        Combine::Sum => {10            for (a, &b) in acc.iter_mut().zip(mask.iter()) {11                *a += b as u8 as f32;12            }13        }14        Combine::And => {15            for (a, &b) in acc.iter_mut().zip(mask.iter()) {16                *a = if first {17                    b as u8 as f3218                } else {19                    *a * b as u8 as f3220                };21            }22        }23        Combine::Xor => {24            for (a, &b) in acc.iter_mut().zip(mask.iter()) {25                *a = ((*a != 0.0) ^ b) as u8 as f32;26            }27        }28    }29}3031/// Layers one design at several side numbers into a field under the chosen combine.32pub fn stack(33    spec: Spec,34    numbers: &[usize],35    combine: Combine,36    level: usize,37    lattice: Lattice,38    size: usize,39    slices: &[f64],40) -> Result<Field> {41    let mut acc = vec![0.0f32; size * size];42    let mut first = true;43    for &n in numbers {44        let params = Layer {45            spec,46            number: n,47            level,48            lattice,49            size,50            slices: slices.to_vec(),51        };52        merge(&mut acc, &layer(&params)?, combine, first);53        first = false;54    }55    Ok(Field::from_data(acc, size))56}5758/// Sums layers of several designs at one side number into a field.59pub fn stack_codes(60    specs: &[Spec],61    number: usize,62    level: usize,63    lattice: Lattice,64    size: usize,65    slices: &[f64],66) -> Result<Field> {67    let mut acc = vec![0.0f32; size * size];68    for &spec in specs {69        let params = Layer {70            spec,71            number,72            level,73            lattice,74            size,75            slices: slices.to_vec(),76        };77        let m = layer(&params)?;78        for (a, &b) in acc.iter_mut().zip(m.iter()) {79            *a += b as u8 as f32;80        }81    }82    Ok(Field::from_data(acc, size))83}8485#[cfg(test)]86mod tests {87    use super::*;88    fn spec(code: u128) -> Spec {89        Spec::new(code, 2, 2)90    }91    #[test]92    fn sum_stack_counts_layers() {93        let numbers = [1, 3, 5];94        let f = stack(spec(7), &numbers, Combine::Sum, 1, Lattice::Square, 64, &[]).unwrap();95        assert!(f.max() <= numbers.len() as f32);96        assert!(f.min() >= 0.0);97    }98    #[test]99    fn complement_identity() {100        let numbers: Vec<usize> = (1..52).step_by(2).collect();101        let size = 48;102        for code in [1u128, 2, 3, 6, 7] {103            let h = stack(104                spec(code),105                &numbers,106                Combine::Sum,107                1,108                Lattice::Square,109                size,110                &[],111            )112            .unwrap();113            let hc = stack(114                spec(15 - code),115                &numbers,116                Combine::Sum,117                1,118                Lattice::Square,119                size,120                &[],121            )122            .unwrap();123            let n = numbers.len() as f32;124            let max_err = h125                .data126                .iter()127                .zip(hc.data.iter())128                .map(|(&a, &b)| (a + b - n).abs())129                .fold(0.0f32, f32::max);130            assert!(max_err < 1e-3, "code {code} max_err {max_err}");131        }132    }133    #[test]134    fn sponge_slice0_equals_carpet_stack() {135        use mrlymath::bang::corners_to_code;136        let carpet_corners: Vec<Vec<u8>> = (0..3)137            .flat_map(|a| (0..3).map(move |b| vec![a as u8, b as u8]))138            .filter(|c| !(c[0] == 1 && c[1] == 1))139            .collect();140        let carpet_code = corners_to_code(&carpet_corners, 2, 3);141        let sponge_corners: Vec<Vec<u8>> = (0..3)142            .flat_map(|a| {143                (0..3).flat_map(move |b| (0..3).map(move |c| vec![a as u8, b as u8, c as u8]))144            })145            .filter(|c| {146                let centers = (c[0] == 1) as u8 + (c[1] == 1) as u8 + (c[2] == 1) as u8;147                centers <= 1148            })149            .collect();150        let sponge_code = corners_to_code(&sponge_corners, 3, 3);151        let numbers: Vec<usize> = (1..20).step_by(2).collect();152        let size = 48;153        let carpet = stack(154            Spec::new(carpet_code, 3, 2),155            &numbers,156            Combine::Sum,157            1,158            Lattice::Square,159            size,160            &[],161        )162        .unwrap();163        let sponge = stack(164            Spec::new(sponge_code, 3, 3),165            &numbers,166            Combine::Sum,167            1,168            Lattice::Square,169            size,170            &[0.0],171        )172        .unwrap();173        assert_eq!(carpet.data, sponge.data);174    }175}