pairs.rs

6.0 kB · rust · 183 lines

1use crate::lattice::{cell, Family, Rule};2use crate::sums::pairwise;3use mrlynum::factor::gcd;4use mrlynum::series::chi4;56fn marks(first: i64, second: i64, span: i64) -> Vec<i64> {7    let mut out = Vec::new();8    let (mut a, mut b) = (0i64, 0i64);9    while a <= span || b <= span {10        let next = a.min(b);11        out.push(next);12        if a == next {13            a += first;14        }15        if b == next {16            b += second;17        }18    }19    out20}2122pub fn pearson(m: usize, n: usize, rule: &Rule) -> (f64, f64) {23    let g = gcd(m, n) as i64;24    let (unit_m, unit_n) = (n as i64 / g, m as i64 / g);25    let (m, n) = (m as i64, n as i64);26    let (wide, tall) = (4 * m * n / g, 2 * m * n / g);27    let across = marks(unit_m, unit_n, wide);28    let down = marks(unit_m, unit_n, tall);29    let mut strips: Vec<[f64; 4]> = Vec::new();30    for pair in across.windows(2) {31        let width = (pair[1] - pair[0]) as f64 / wide as f64;32        let (xm, xn) = (pair[0] / unit_m, pair[0] / unit_n);33        let mut total = [0.0; 4];34        for strip in down.windows(2) {35            let height = (strip[1] - strip[0]) as f64 / tall as f64;36            let (zm, zn) = (2 * (strip[0] / unit_m), 2 * (strip[0] / unit_n));37            if let (Some(a), Some(b)) = (cell(rule, m, xm, zm), cell(rule, n, xn, zn)) {38                let weight = width * height;39                total[0] += weight;40                total[1] += weight * f64::from(a);41                total[2] += weight * f64::from(b);42                total[3] += weight * f64::from(a && b);43            }44        }45        strips.push(total);46    }47    let sum = |k: usize| pairwise(&strips.iter().map(|row| row[k]).collect::<Vec<f64>>());48    let area = sum(0);49    let (ea, eb, eab) = (sum(1) / area, sum(2) / area, sum(3) / area);50    let covariance = eab - ea * eb;51    (52        covariance / (ea * (1.0 - ea) * eb * (1.0 - eb)).sqrt(),53        covariance,54    )55}5657fn det(a: [[f64; 3]; 3]) -> f64 {58    a[0][0] * (a[1][1] * a[2][2] - a[1][2] * a[2][1])59        - a[0][1] * (a[1][0] * a[2][2] - a[1][2] * a[2][0])60        + a[0][2] * (a[1][0] * a[2][1] - a[1][1] * a[2][0])61}6263fn richardson(triple: &[(usize, f64)]) -> f64 {64    let row = |m: usize| [1.0, 1.0 / m as f64, 1.0 / (m * m) as f64];65    let a = [row(triple[0].0), row(triple[1].0), row(triple[2].0)];66    let mut b = a;67    for (slot, (_, value)) in triple.iter().enumerate() {68        b[slot][0] = *value;69    }70    det(b) / det(a)71}7273fn branch(first: (usize, f64), second: (usize, f64)) -> f64 {74    (second.0 as f64 * second.1 - first.0 as f64 * first.1) / (second.0 - first.0) as f6475}7677const SMALL: [(usize, usize); 7] = [(5, 9), (5, 7), (9, 17), (13, 25), (17, 51), (5, 15), (3, 9)];7879const DOUBLING: [(usize, usize); 18] = [80    (3, 5),81    (7, 13),82    (7, 15),83    (11, 21),84    (11, 23),85    (19, 39),86    (23, 45),87    (27, 53),88    (103, 205),89    (149, 297),90    (151, 301),91    (157, 313),92    (201, 401),93    (301, 601),94    (401, 801),95    (403, 805),96    (501, 1001),97    (601, 1201),98];99100const FITTED: [usize; 6] = [157, 201, 301, 401, 501, 601];101102type Row = (&'static str, &'static [(usize, usize)], &'static [usize]);103104pub fn small_pairs(rule: &Rule) {105    println!("full-hexagon exact Pearson, carpet, the seven blind pairs");106    for (m, n) in SMALL {107        let (r, covariance) = pearson(m, n, rule);108        println!("  ({m},{n}) r = {r:+.8}  cov = {covariance:+.8}");109    }110}111112pub fn doubling(rule: &Rule) {113    println!("doubling pairs (m, 2m+-1), carpet: sign law sign r = -chi4(m) chi4(n)");114    let mut hits = 0;115    let mut fitted: Vec<(usize, f64)> = Vec::new();116    let mut fitted_cov: Vec<(usize, f64)> = Vec::new();117    let mut branches: Vec<(usize, f64)> = Vec::new();118    for (m, n) in DOUBLING {119        let (r, covariance) = pearson(m, n, rule);120        let predicted = -f64::from(chi4(m) * chi4(n));121        let agreed = r.signum() == predicted;122        hits += usize::from(agreed);123        println!(124            "  ({m},{n}) r = {r:+.8}  cov = {covariance:+.8}  predicted sign {predicted:+}  {}",125            if agreed { "ok" } else { "MISS" }126        );127        if FITTED.contains(&m) {128            fitted.push((m, r));129            fitted_cov.push((m, covariance));130        }131        if [103, 403, 501, 601].contains(&m) {132            branches.push((m, r.abs()));133        }134    }135    println!("  sign law: {hits}/{} pairs", DOUBLING.len());136    println!("Richardson r = r_inf + a/m + b/m^2 on sliding triples of m = {FITTED:?}");137    for start in 0..FITTED.len() - 2 {138        let r = richardson(&fitted[start..start + 3]);139        let c = richardson(&fitted_cov[start..start + 3]);140        println!(141            "  m = {:?}: r_inf = {r:+.8}  cov_inf = {c:+.8}",142            &FITTED[start..start + 3]143        );144    }145    let low = branch(branches[2], branches[3]);146    let high = branch(branches[0], branches[1]);147    println!("  branch 1/m extrapolation |r|: (501,601) -> {low:.7}  (103,403) -> {high:.7}");148    println!(149        "  candidates: 253/2160 = {:.8}  19/162 = {:.8}",150        253.0 / 2160.0,151        19.0 / 162.0152    );153}154155pub fn persistence() {156    println!("other pairs and families");157    let rules: Vec<(Family, Rule)> = [Family::Carpet, Family::Tree, Family::Void]158        .into_iter()159        .map(|family| (family, Rule::new(family)))160        .collect();161    let table: [Row; 4] = [162        ("doubling", &[(201, 401)], &[1, 2]),163        ("adjacent", &[(199, 201), (249, 251)], &[0, 1, 2]),164        ("gcd echo (m,3m)", &[(67, 201), (99, 297)], &[0, 1, 2]),165        ("coprime control", &[(101, 173), (97, 251)], &[0, 1, 2]),166    ];167    for (label, pairs, chosen) in table {168        for (m, n) in pairs.iter().copied() {169            let mut line = format!("  {label} ({m},{n}):");170            for slot in chosen {171                let (family, rule) = &rules[*slot];172                let (r, _) = pearson(m, n, rule);173                line.push_str(&format!("  {} {r:+.8}", family.name()));174            }175            println!("{line}");176        }177    }178    println!(179        "  candidates: adjacent -11/135 = {:+.7}  echo 29/135 = {:+.7}",180        -11.0 / 135.0,181        29.0 / 135.0182    );183}