spin.rs
2.1 kB · rust · 70 lines
1use mrlylab::moire::{stack, Combine, Field, Lattice, Spec};2use mrlymath::bang::corners_to_code;3use mrlynum::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), 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 .iter()49 .map(|&value| value as f64)50 .collect()51}5253fn pearson(a: &[f64], b: &[f64], weight: &[f64]) -> f64 {54 let total: f64 = weight.iter().sum();55 let mean = |x: &[f64]| x.iter().zip(weight).map(|(v, w)| v * w).sum::<f64>() / total;56 let (ma, mb) = (mean(a), mean(b));57 let covariance: f64 = a58 .iter()59 .zip(b)60 .zip(weight)61 .map(|((x, y), w)| w * (x - ma) * (y - mb))62 .sum();63 let spread = |x: &[f64], m: f64| {64 x.iter()65 .zip(weight)66 .map(|(v, w)| w * (v - m) * (v - m))67 .sum::<f64>()68 };69 covariance / (spread(a, ma) * spread(b, mb)).sqrt()70}