spin.rs
2.2 kB · rust · 71 lines
1use mrlyrs::math::bang::corners_to_code;2use mrlyrs::math::moire::{stack, Combine, Field, Lattice, Spec};3use mrlyrs::math::spin::{profile, reach};45const RINGS: [(usize, usize, f64); 3] = [(3, 5, 0.38), (5, 7, -0.33), (9, 13, 0.38)];67const SIZE: usize = 135;89const STEPS: usize = 400;1011// RING PROFILES1213pub fn the_coprime_law_survives_the_spin() -> Result<(), String> {14 let far = reach(SIZE);15 let disc: Vec<f64> = (0..STEPS)16 .map(|k| {17 let radius = k as f64 * far / (STEPS - 1) as f64;18 if radius <= SIZE as f64 / 2.0 {19 radius20 } else {21 0.022 }23 })24 .collect();25 let ones = vec![1.0; SIZE * SIZE];26 for (m, n, ring) in RINGS {27 let (a, b) = (layer(m)?, layer(n)?);28 let flat = pearson(&a.as_f64(), &b.as_f64(), &ones);29 if flat.abs() >= 0.03 {30 return Err(format!("the flat layers {m} and {n} correlate at {flat}"));31 }32 let spun = pearson(&profiled(&a), &profiled(&b), &disc);33 if (spun - ring).abs() >= 0.02 {34 return Err(format!("the ring profiles {m} and {n} correlate at {spun}"));35 }36 }37 Ok(())38}3940fn layer(scale: usize) -> Result<Field, String> {41 let spec = Spec::new(corners_to_code(&[vec![0, 0]], 2, 2).get(), 2, 2);42 stack(spec, &[scale], Combine::Sum, 1, Lattice::Square, SIZE, &[])43 .map_err(|_| format!("the layer at {scale} does not build"))44}4546fn profiled(field: &Field) -> Vec<f64> {47 profile(&field.data, SIZE, STEPS)48 .unwrap_or_default()49 .iter()50 .map(|&value| value as f64)51 .collect()52}5354fn pearson(a: &[f64], b: &[f64], weight: &[f64]) -> f64 {55 let total: f64 = weight.iter().sum();56 let mean = |x: &[f64]| x.iter().zip(weight).map(|(v, w)| v * w).sum::<f64>() / total;57 let (ma, mb) = (mean(a), mean(b));58 let covariance: f64 = a59 .iter()60 .zip(b)61 .zip(weight)62 .map(|((x, y), w)| w * (x - ma) * (y - mb))63 .sum();64 let spread = |x: &[f64], m: f64| {65 x.iter()66 .zip(weight)67 .map(|(v, w)| w * (v - m) * (v - m))68 .sum::<f64>()69 };70 covariance / (spread(a, ma) * spread(b, mb)).sqrt()71}