dynamics.rs

7.6 kB · rust · 239 lines

1use crate::groups::{group, orbit};2use crate::rules::{output, RULES};3use std::collections::BTreeSet;45fn arrows(rule: usize) -> Vec<(usize, usize, u8)> {6    let mut out = Vec::new();7    for a in 0..2u8 {8        for b in 0..2u8 {9            for c in 0..2u8 {10                out.push((11                    2 * a as usize + b as usize,12                    2 * b as usize + c as usize,13                    output(rule, a, b, c),14                ));15            }16        }17    }18    out19}2021pub fn surjective(rule: usize) -> bool {22    let edges = arrows(rule);23    let start = 0b1111usize;24    let mut seen = BTreeSet::from([start]);25    let mut stack = vec![start];26    while let Some(set) = stack.pop() {27        for label in 0..2u8 {28            let mut next = 0usize;29            for &(u, v, l) in &edges {30                if l == label && (set >> u) & 1 == 1 {31                    next |= 1 << v;32                }33            }34            if next == 0 {35                return false;36            }37            if seen.insert(next) {38                stack.push(next);39            }40        }41    }42    true43}4445pub fn balanced_to(rule: usize, length: usize) -> bool {46    let mut counts = vec![0u32; 1 << length];47    for source in 0..1usize << (length + 2) {48        let mut word = 0usize;49        for i in 0..length {50            let a = ((source >> i) & 1) as u8;51            let b = ((source >> (i + 1)) & 1) as u8;52            let c = ((source >> (i + 2)) & 1) as u8;53            word |= (output(rule, a, b, c) as usize) << i;54        }55        counts[word] += 1;56    }57    counts.iter().all(|&c| c == 4)58}5960pub fn injective(rule: usize) -> bool {61    let edges = arrows(rule);62    let mut adjacency = vec![BTreeSet::new(); 16];63    for &(u1, v1, l1) in &edges {64        for &(u2, v2, l2) in &edges {65            if l1 == l2 {66                adjacency[4 * u1 + u2].insert(4 * v1 + v2);67            }68        }69    }70    let mut core: BTreeSet<usize> = (0..16).collect();71    loop {72        let outs: BTreeSet<usize> = core73            .iter()74            .filter(|p| adjacency[**p].iter().any(|q| core.contains(q)))75            .copied()76            .collect();77        let ins: BTreeSet<usize> = outs78            .iter()79            .flat_map(|p| adjacency[*p].iter().filter(|q| outs.contains(q)).copied())80            .collect();81        let next: BTreeSet<usize> = outs.intersection(&ins).copied().collect();82        if next == core {83            break;84        }85        core = next;86    }87    !core.iter().any(|p| p / 4 != p % 4)88}8990pub fn surjective_set() -> Vec<usize> {91    (0..RULES).filter(|&r| surjective(r)).collect()92}9394pub fn injective_set() -> Vec<usize> {95    (0..RULES).filter(|&r| injective(r)).collect()96}9798pub fn report() {99    println!("GEOMETRY IS NOT DYNAMICS");100    let surj = surjective_set();101    let inj = injective_set();102    println!(103        "de Bruijn subset walk: {} surjective rules {surj:?}",104        surj.len()105    );106    let mut previous: Vec<usize> = (0..RULES).collect();107    let mut settles = None;108    for length in 1..=12 {109        let balanced: Vec<usize> = (0..RULES).filter(|&r| balanced_to(r, length)).collect();110        assert!(111            balanced.iter().all(|r| previous.contains(r)),112            "the balanced set grows from length {} to {length}",113            length - 1114        );115        assert!(116            surj.iter().all(|r| balanced.contains(r)),117            "a surjective rule is not balanced at length {length}"118        );119        println!(120            "balanced on words of length {length}: {} rules",121            balanced.len()122        );123        if balanced == surj && settles.is_none() {124            settles = Some(length);125        }126        previous = balanced;127    }128    let settles = settles.expect("the balance test never reaches the de Bruijn set");129    println!("the exact balance test on all words of length n shrinks monotonically and equals the de Bruijn set from n = {settles} to n = 12");130    if surj.len() == 30 {131        println!("the count is 30, the published figure");132    } else {133        println!(134            "UNCLEAR the count is {} against the published 30",135            surj.len()136        );137    }138    println!("pair-graph core: {} injective rules {inj:?}", inj.len());139    assert_eq!(140        inj,141        vec![15, 51, 85, 170, 204, 240],142        "the reversible rules are not the expected six"143    );144    assert!(145        inj.iter().all(|r| surj.contains(r)),146        "an injective rule is not surjective"147    );148    let b3 = group("B3");149    let identity_class: Vec<usize> = orbit(204, &b3).into_iter().collect();150    assert_eq!(151        inj, identity_class,152        "the reversible rules are not the B3 orbit of 204"153    );154    println!(155        "the reversible six are exactly the B3 orbit of 204, the single-axis degree-1 designs"156    );157    let surjective_flags: Vec<bool> = (0..RULES).map(|r| surj.contains(&r)).collect();158    let mut mixed = Vec::new();159    let mut constant_yes = 0usize;160    let mut constant_no = 0usize;161    let mut reps: Vec<usize> = (0..RULES)162        .map(|c| *orbit(c, &b3).iter().next().expect("nonempty"))163        .collect();164    reps.sort();165    reps.dedup();166    for rep in &reps {167        let cls: Vec<usize> = orbit(*rep, &b3).into_iter().collect();168        let yes: Vec<usize> = cls169            .iter()170            .copied()171            .filter(|c| surjective_flags[*c])172            .collect();173        let no: Vec<usize> = cls174            .iter()175            .copied()176            .filter(|c| !surjective_flags[*c])177            .collect();178        if yes.is_empty() {179            constant_no += 1;180        } else if no.is_empty() {181            constant_yes += 1;182        } else {183            mixed.push((*rep, cls.len(), yes, no));184        }185    }186    println!(187        "B3 classes: {constant_yes} all surjective, {constant_no} none surjective, {} mixed",188        mixed.len()189    );190    for (rep, size, yes, no) in &mixed {191        println!("mixed class rep {rep} size {size} surjective {yes:?} not {no:?}");192    }193    assert!(194        !mixed.is_empty(),195        "surjectivity is constant on every B3 class"196    );197    let thirty = orbit(30, &b3);198    assert!(thirty.contains(&54), "30 and 54 do not share a B3 orbit");199    assert!(200        surjective_flags[30] && !surjective_flags[54],201        "30 and 54 do not split on surjectivity"202    );203    println!("witness: 30 and 54 share the B3 orbit of rep 30, 30 is surjective and 54 is not");204    println!("surjectivity is not a B3 invariant, witnessed by 30 and 54 in one orbit");205    let mut reps: Vec<usize> = (0..RULES)206        .map(|c| *orbit(c, &b3).iter().next().expect("nonempty"))207        .collect();208    reps.sort();209    reps.dedup();210    let mut mixed_rev = 0usize;211    for rep in &reps {212        let cls: Vec<usize> = orbit(*rep, &b3).into_iter().collect();213        if cls.iter().any(|c| injective(*c)) && cls.iter().any(|c| !injective(*c)) {214            mixed_rev += 1;215        }216    }217    assert_eq!(218        mixed_rev, 0,219        "reversibility is not constant on every B3 orbit"220    );221    println!("reversibility IS constant on every B3 orbit, 0 mixed of {}, the reversible six being exactly the orbit of 204", reps.len());222    let mut mixed_h = 0usize;223    let h = group("H");224    let mut hreps: Vec<usize> = (0..RULES)225        .map(|c| *orbit(c, &h).iter().next().expect("nonempty"))226        .collect();227    hreps.sort();228    hreps.dedup();229    for rep in &hreps {230        let cls: Vec<usize> = orbit(*rep, &h).into_iter().collect();231        if cls.iter().any(|c| surjective_flags[*c]) && cls.iter().any(|c| !surjective_flags[*c]) {232            mixed_h += 1;233        }234    }235    println!(236        "H classes with mixed surjectivity: {mixed_h} of {}",237        hreps.len()238    );239}