coprime.rs

10.7 kB · rust · 365 lines

1use mrlymath::bang::factory::{code_to_corners, corners_to_code, residue_corners};2use mrlymath::bang::Code;3use mrlynum::factor::{divisors, factorize, gcd, mobius, radical};4use mrlynum::series::zeta;5use std::collections::BTreeSet;67const BUDGET: usize = 200_000;8const TOLERANCE: f64 = 0.06;9const EXACT_LEVEL: u32 = 4;10const ZETA_TERMS: usize = 10_000;11const HEAD: usize = 6;1213pub struct Line {14    pub code: Code,15    pub k: usize,16    pub index: usize,17    pub spanning: bool,18    pub bracket: f64,19    pub predicted: f64,20    pub measured: f64,21    pub level: u32,22    pub exact: bool,23    pub within: bool,24    pub monotone: bool,25    pub head: Vec<u64>,26}2728pub struct Family {29    pub label: String,30    pub base: usize,31    pub lines: Vec<Line>,32}3334impl Family {35    fn spanning(&self) -> usize {36        self.lines.iter().filter(|line| line.spanning).count()37    }3839    fn distinct(&self, spanning_only: bool) -> usize {40        let heads: BTreeSet<&Vec<u64>> = self41            .lines42            .iter()43            .filter(|line| line.spanning || !spanning_only)44            .map(|line| &line.head)45            .collect();46        heads.len()47    }4849    fn flagged(&self) -> usize {50        self.lines51            .iter()52            .filter(|line| line.spanning && (!line.within || !line.exact))53            .count()54    }5556    fn exact_failures(&self) -> usize {57        self.lines.iter().filter(|line| !line.exact).count()58    }59}6061fn squarefree_divisors(base: usize) -> Vec<usize> {62    divisors(radical(base))63}6465fn hits(corners: &[Vec<u8>], divisor: usize) -> usize {66    corners67        .iter()68        .filter(|corner| corner.iter().all(|&r| (r as usize).is_multiple_of(divisor)))69        .count()70}7172fn bracket(corners: &[Vec<u8>], base: usize) -> f64 {73    let sum: f64 = squarefree_divisors(base)74        .iter()75        .map(|&e| f64::from(mobius(e)) * hits(corners, e) as f64)76        .sum();77    sum / corners.len() as f6478}7980fn foreign(base: usize, dimension: usize) -> f64 {81    let mut out = 1.0 / zeta(dimension as f64, ZETA_TERMS);82    for (prime, _) in factorize(base) {83        out /= 1.0 - (prime as f64).powi(-(dimension as i32));84    }85    out86}8788fn determinant(rows: &[Vec<i64>]) -> i64 {89    if rows.len() == 1 {90        return rows[0][0];91    }92    let mut total = 0;93    for (column, head) in rows[0].iter().enumerate() {94        let minor: Vec<Vec<i64>> = rows[1..]95            .iter()96            .map(|row| {97                row.iter()98                    .enumerate()99                    .filter(|(i, _)| *i != column)100                    .map(|(_, v)| *v)101                    .collect()102            })103            .collect();104        let sign = if column % 2 == 0 { 1 } else { -1 };105        total += sign * head * determinant(&minor);106    }107    total108}109110fn choose(start: usize, count: usize, size: usize) -> Vec<Vec<usize>> {111    if size == 0 {112        return vec![Vec::new()];113    }114    let mut out = Vec::new();115    for first in start..count {116        for mut rest in choose(first + 1, count, size - 1) {117            rest.insert(0, first);118            out.push(rest);119        }120    }121    out122}123124fn index(corners: &[Vec<u8>], dimension: usize) -> usize {125    let first = &corners[0];126    let diffs: Vec<Vec<i64>> = corners[1..]127        .iter()128        .map(|corner| {129            corner130                .iter()131                .zip(first)132                .map(|(a, b)| i64::from(*a) - i64::from(*b))133                .collect()134        })135        .collect();136    let mut out = 0usize;137    for rows in choose(0, diffs.len(), dimension) {138        let minor: Vec<Vec<i64>> = rows.iter().map(|&r| diffs[r].clone()).collect();139        out = gcd(out, determinant(&minor).unsigned_abs() as usize);140        if out == 1 {141            return out;142        }143    }144    out145}146147fn expand(columns: &mut [Vec<usize>], corners: &[Vec<u8>], base: usize) {148    for (axis, column) in columns.iter_mut().enumerate() {149        let mut next = Vec::with_capacity(column.len() * corners.len());150        for corner in corners {151            next.extend(column.iter().map(|&x| base * x + corner[axis] as usize));152        }153        *column = next;154    }155}156157fn common(columns: &[Vec<usize>]) -> Vec<usize> {158    (0..columns[0].len())159        .map(|i| columns.iter().fold(0, |g, column| gcd(g, column[i])))160        .collect()161}162163fn measure(corners: &[Vec<u8>], base: usize, dimension: usize) -> (Vec<u64>, bool) {164    let k = corners.len();165    let mut columns = vec![vec![0usize]; dimension];166    let mut terms = Vec::new();167    let mut exact = None;168    let mut level = 0u32;169    while k.pow(level + 1) <= BUDGET {170        level += 1;171        expand(&mut columns, corners, base);172        let shared = common(&columns);173        terms.push(shared.iter().filter(|&&g| g == 1).count() as u64);174        if level == EXACT_LEVEL {175            exact = Some(176                squarefree_divisors(base)177                    .iter()178                    .filter(|&&e| e > 1)179                    .all(|&e| {180                        let seen = shared.iter().filter(|&&g| g % e == 0).count();181                        seen == hits(corners, e) * k.pow(level - 1)182                    }),183            );184        }185    }186    (187        terms,188        exact.expect("the point budget reaches the exact level"),189    )190}191192pub fn survey(193    base: usize,194    dimension: usize,195    codes: impl Iterator<Item = Code>,196    label: &str,197) -> Family {198    let mut lines = Vec::new();199    for code in codes {200        let corners = code_to_corners(code, dimension, base).expect("the code fits its cells");201        let k = corners.len();202        if k < 2 {203            continue;204        }205        let (terms, exact) = measure(&corners, base, dimension);206        let level = terms.len() as u32;207        let measured = terms[terms.len() - 1] as f64 / k.pow(level) as f64;208        let bracket = bracket(&corners, base);209        let predicted = bracket * foreign(base, dimension);210        let gaps: Vec<f64> = terms211            .iter()212            .enumerate()213            .map(|(step, &count)| (count as f64 / k.pow(step as u32 + 1) as f64 - predicted).abs())214            .collect();215        let monotone = gaps.windows(2).all(|pair| pair[1] <= pair[0]);216        let index = index(&corners, dimension);217        lines.push(Line {218            code,219            k,220            index,221            spanning: index == 1,222            bracket,223            predicted,224            measured,225            level,226            exact,227            within: (measured - predicted).abs() < TOLERANCE,228            monotone,229            head: terms.iter().take(HEAD).copied().collect(),230        });231    }232    Family {233        label: label.to_string(),234        base,235        lines,236    }237}238239fn sponge() -> Code {240    let filled: Vec<Vec<u8>> = residue_corners(3, 3)241        .into_iter()242        .filter(|corner| corner.iter().filter(|&&r| r == 1).count() <= 1)243        .collect();244    corners_to_code(&filled, 3, 3)245}246247fn base6() -> Vec<Code> {248    let samples: [[[u8; 2]; 8]; 2] = [249        [250            [0, 0],251            [1, 1],252            [2, 3],253            [3, 2],254            [4, 5],255            [5, 4],256            [1, 0],257            [0, 1],258        ],259        [260            [0, 0],261            [2, 0],262            [4, 0],263            [0, 3],264            [1, 1],265            [5, 5],266            [1, 2],267            [2, 1],268        ],269    ];270    samples271        .iter()272        .map(|sample| {273            let filled: Vec<Vec<u8>> = sample.iter().map(|corner| corner.to_vec()).collect();274            corners_to_code(&filled, 2, 6)275        })276        .collect()277}278279pub fn report() {280    let families = [281        survey(2, 2, 1..16, "base 2 D 2"),282        survey(2, 3, 1..256, "base 2 D 3"),283        survey(3, 2, 1..512, "base 3 D 2"),284        survey(3, 3, [sponge()].into_iter(), "menger sponge"),285        survey(6, 2, base6().into_iter(), "base 6 samples"),286    ];287    println!("coprime census: designs with k >= 2, point budget {BUDGET}, tolerance {TOLERANCE}, exact identity at n = {EXACT_LEVEL}");288    println!(289        "{:<16}{:>9}{:>10}{:>10}{:>19}{:>9}{:>13}",290        "family", "designs", "spanning", "distinct", "distinct spanning", "flagged", "exact fails"291    );292    for family in &families {293        println!(294            "{:<16}{:>9}{:>10}{:>10}{:>19}{:>9}{:>13}",295            family.label,296            family.lines.len(),297            family.spanning(),298            family.distinct(false),299            family.distinct(true),300            family.flagged(),301            family.exact_failures()302        );303    }304    println!(305        "{:<16}{:>9}{:>10}{:>10}{:>19}{:>9}{:>13}",306        "total",307        families.iter().map(|f| f.lines.len()).sum::<usize>(),308        families.iter().map(Family::spanning).sum::<usize>(),309        "-",310        "-",311        families.iter().map(Family::flagged).sum::<usize>(),312        families.iter().map(Family::exact_failures).sum::<usize>()313    );314    let by = |keep: &dyn Fn(&Line, usize) -> bool| {315        families316            .iter()317            .flat_map(|f| f.lines.iter().map(move |line| (line, f.base)))318            .filter(|(line, base)| keep(line, *base))319            .count()320    };321    println!(322        "spanning by dimension log_q(k): above {}, exactly one {}, below {}; index 3 at k = q {}",323        by(&|line, base| line.spanning && line.k > base),324        by(&|line, base| line.spanning && line.k == base),325        by(&|line, base| line.spanning && line.k < base),326        by(&|line, base| line.index == 3 && line.k == base)327    );328    println!(329        "spanning lines whose gap to the predicted density widens at some level: {}",330        by(&|line, _| line.spanning && !line.monotone)331    );332    for family in &families[3..] {333        for line in &family.lines {334            let corners =335                code_to_corners(line.code, 2 + usize::from(family.base == 3), family.base)336                    .expect("the named code fits its cells");337            let counts: Vec<String> = squarefree_divisors(family.base)338                .iter()339                .filter(|&&e| e > 1)340                .map(|&e| format!("k_{e} {}", hits(&corners, e)))341                .collect();342            println!(343                "{}: code {} k {} {} bracket {:.4} pred {:.6} meas {:.6} n {} terms {:?}",344                family.label,345                line.code,346                line.k,347                counts.join(" "),348                line.bracket,349                line.predicted,350                line.measured,351                line.level,352                line.head353            );354        }355    }356    for k in [2usize, 3] {357        let level = families358            .iter()359            .flat_map(|f| f.lines.iter())360            .find(|line| line.k == k)361            .map(|line| line.level)362            .expect("a design with that k exists");363        println!("deepest level at k = {k}: n = {level}");364    }365}