groups.rs

4.5 kB · rust · 140 lines

1use crate::rules::RULES;2use mrlymath::bang::symmetries;3use mrlymath::bang::universe::{apply, corner_index, corners, permutations};4use std::collections::BTreeSet;56pub type Elem = (Vec<usize>, Vec<u8>, bool);78pub fn act(code: usize, element: &Elem) -> usize {9    let cells = corners(3);10    let mut image = 0usize;11    for (i, cell) in cells.iter().enumerate() {12        if (code >> i) & 1 == 1 {13            image |= 1 << corner_index(&apply(&(element.0.clone(), element.1.clone()), cell));14        }15    }16    if element.2 {17        image ^= 0xff;18    }19    image20}2122fn flip(bits: usize) -> Vec<u8> {23    (0..3).map(|j| ((bits >> (2 - j)) & 1) as u8).collect()24}2526pub fn group(name: &str) -> Vec<Elem> {27    match name {28        "R" => vec![29            (vec![0, 1, 2], vec![0, 0, 0], false),30            (vec![2, 1, 0], vec![0, 0, 0], false),31        ],32        "H" => vec![33            (vec![0, 1, 2], vec![0, 0, 0], false),34            (vec![2, 1, 0], vec![0, 0, 0], false),35            (vec![0, 1, 2], vec![1, 1, 1], true),36            (vec![2, 1, 0], vec![1, 1, 1], true),37        ],38        "flips" => (0..8).map(|f| (vec![0, 1, 2], flip(f), false)).collect(),39        "perms" => permutations(3)40            .into_iter()41            .map(|p| (p, vec![0, 0, 0], false))42            .collect(),43        "B3" => symmetries(3)44            .into_iter()45            .map(|(p, f)| (p, f, false))46            .collect(),47        "B3xZ2" => symmetries(3)48            .into_iter()49            .flat_map(|(p, f)| [(p.clone(), f.clone(), false), (p.clone(), f.clone(), true)])50            .collect(),51        _ => unreachable!(),52    }53}5455pub fn orbit(code: usize, elements: &[Elem]) -> BTreeSet<usize> {56    elements.iter().map(|e| act(code, e)).collect()57}5859pub fn representatives(elements: &[Elem]) -> Vec<usize> {60    (0..RULES)61        .map(|code| {62            *orbit(code, elements)63                .iter()64                .next()65                .expect("an orbit is nonempty")66        })67        .collect()68}6970fn walk_count(elements: &[Elem]) -> usize {71    representatives(elements)72        .into_iter()73        .collect::<BTreeSet<_>>()74        .len()75}7677fn burnside(elements: &[Elem]) -> usize {78    let fixed: usize = elements79        .iter()80        .map(|e| (0..RULES).filter(|&c| act(c, e) == c).count())81        .sum();82    assert_eq!(fixed % elements.len(), 0, "Burnside sum is not divisible");83    fixed / elements.len()84}8586fn action(elements: &[Elem]) -> BTreeSet<Vec<usize>> {87    elements88        .iter()89        .map(|e| (0..RULES).map(|c| act(c, e)).collect())90        .collect()91}9293pub const NAMES: [&str; 6] = ["R", "H", "flips", "perms", "B3", "B3xZ2"];9495pub fn report() {96    println!("GROUP LATTICE");97    println!("group order classes burnside");98    let mut counts = Vec::new();99    for name in NAMES {100        let elements = group(name);101        let walk = walk_count(&elements);102        let burn = burnside(&elements);103        assert_eq!(walk, burn, "{name} disagrees with its Burnside average");104        println!("{name} {} {walk} {burn}", elements.len());105        counts.push((name, walk));106    }107    for (name, want) in [("H", 88), ("B3", 22), ("B3xZ2", 14)] {108        let got = counts109            .iter()110            .find(|(n, _)| *n == name)111            .expect("the group is listed")112            .1;113        assert_eq!(got, want, "{name} class count is not {want}");114    }115    let actions: Vec<(&str, BTreeSet<Vec<usize>>)> =116        NAMES.iter().map(|n| (*n, action(&group(n)))).collect();117    println!("containments");118    for (a, sa) in &actions {119        let inside: Vec<&str> = actions120            .iter()121            .filter(|(b, sb)| b != a && sa.is_subset(sb))122            .map(|(b, _)| *b)123            .collect();124        let inside = if inside.is_empty() {125            "nothing".to_string()126        } else {127            inside.join(" ")128        };129        println!("{a} sits inside {inside}");130    }131    let h = &actions.iter().find(|(n, _)| *n == "H").expect("H").1;132    let b3 = &actions.iter().find(|(n, _)| *n == "B3").expect("B3").1;133    let r = &actions.iter().find(|(n, _)| *n == "R").expect("R").1;134    let meet: BTreeSet<Vec<usize>> = h.intersection(b3).cloned().collect();135    assert_eq!(&meet, r, "H meets B3 outside the reflection group");136    assert!(!h.is_subset(b3), "H is a subgroup of B3");137    assert!(!b3.is_subset(h), "B3 is a subgroup of H");138    println!("H and B3 are incomparable and meet in R of order 2");139    println!("the walk is 256 -> 88 under H and 256 -> 22 under B3, two branches, joined at 14 under B3xZ2");140}