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(¶ms)?, 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(¶ms)?;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}