algebraeon_groups/composition_table/
group.rs

1use super::normal_subgroup::*;
2use super::partition::*;
3use super::subgroup::*;
4use super::subset::*;
5use rayon::prelude::*;
6use std::collections::BTreeSet;
7use std::collections::HashMap;
8use std::collections::HashSet;
9use std::fmt::Debug;
10use std::hash::Hash;
11
12pub struct Group {
13    n: usize,
14    ident: usize,
15    inv: Vec<usize>,
16    mul: Vec<Vec<usize>>,
17    conjugacy_classes: Option<PartitionState>,
18    is_abelian: Option<bool>,
19    is_simple: Option<bool>,
20}
21
22impl Group {
23    pub fn check_state(&self) -> Result<(), &'static str> {
24        //check ident
25        if !(self.ident < self.n) {
26            return Err("bad ident elem");
27        }
28        //check inv
29        if !(self.inv.len() == self.n) {
30            return Err("bad inv len");
31        }
32        for x in &self.inv {
33            if !(*x < self.n) {
34                return Err("bad inv elem");
35            }
36        }
37        //check mul
38        if !(self.mul.len() == self.n) {
39            return Err("bad mul left len");
40        }
41        for m in &self.mul {
42            if !(m.len() == self.n) {
43                return Err("bad mul right len");
44            }
45            for x in m {
46                if !(*x < self.n) {
47                    return Err("bad mul elem");
48                }
49            }
50        }
51        //identity axiom
52        for x in 0..self.n {
53            if !(x == self.mul[x][self.ident] && x == self.mul[self.ident][x]) {
54                return Err("identity axiom failed");
55            }
56        }
57        //inv axiom
58        for x in 0..self.n {
59            if !(self.ident == self.mul[self.inv[x]][x] && self.ident == self.mul[x][self.inv[x]]) {
60                return Err("inverse axiom failed");
61            }
62        }
63        //assoc axiom
64        for x in 0..self.n {
65            for y in 0..self.n {
66                for z in 0..self.n {
67                    if !(self.mul[x][self.mul[y][z]] == self.mul[self.mul[x][y]][z]) {
68                        return Err("assoc axiom failed");
69                    }
70                }
71            }
72        }
73
74        //cached is_abelian if present
75        match self.is_abelian {
76            Some(claimed_is_abelian) => {
77                if claimed_is_abelian != self.compute_is_abelian() {
78                    return Err("incorrect is_abelian flag");
79                }
80            }
81            None => {}
82        };
83
84        //cached conjugacy classes (if present)
85        match &self.conjugacy_classes {
86            Some(conj_state) => {
87                conj_state.check_state(self).unwrap();
88            }
89            None => {}
90        };
91
92        //check is_simple
93        match &self.is_simple {
94            Some(is_simple) => {
95                if (self.normal_subgroups().len() == 2) != *is_simple {
96                    return Err("is_simple flag is incorrect");
97                }
98            }
99            None => {}
100        }
101
102        Ok(())
103    }
104
105    pub fn new(
106        n: usize,
107        ident: usize,
108        inv: Vec<usize>,
109        mul: Vec<Vec<usize>>,
110    ) -> Result<Self, &'static str> {
111        let grp = Group {
112            n,
113            ident,
114            inv,
115            mul,
116            is_abelian: None,
117            conjugacy_classes: None,
118            is_simple: None,
119        };
120        match grp.check_state() {
121            Ok(()) => Ok(grp),
122            Err(msg) => Err(msg),
123        }
124    }
125
126    pub fn new_unchecked(
127        n: usize,
128        ident: usize,
129        inv: Vec<usize>,
130        mul: Vec<Vec<usize>>,
131        is_abelian: Option<bool>,
132        is_simple: Option<bool>,
133    ) -> Self {
134        Self {
135            n,
136            ident,
137            inv,
138            mul,
139            is_abelian,
140            conjugacy_classes: None,
141            is_simple,
142        }
143    }
144
145    pub fn mul(&self, x: usize, y: usize) -> usize {
146        self.mul[x][y]
147    }
148
149    pub fn inv(&self, x: usize) -> usize {
150        self.inv[x]
151    }
152
153    pub fn ident(&self) -> usize {
154        self.ident
155    }
156
157    pub fn from_raw_model<T: PartialEq + Eq + Hash + Clone + Debug>(
158        elems: Vec<T>,
159        ident: impl Fn() -> T,
160        inv: impl Fn(T) -> T,
161        mul: impl Fn(T, T) -> T,
162    ) -> Result<Self, &'static str> {
163        let grp = Self::from_raw_model_unchecked(elems, ident, inv, mul, None, None);
164        match grp.check_state() {
165            Ok(()) => Ok(grp),
166            Err(msg) => Err(msg),
167        }
168    }
169
170    pub fn from_raw_model_unchecked<T: PartialEq + Eq + Hash + Clone + Debug>(
171        elems: Vec<T>,
172        ident: impl Fn() -> T,
173        inv: impl Fn(T) -> T,
174        mul: impl Fn(T, T) -> T,
175        is_abelian: Option<bool>,
176        is_simple: Option<bool>,
177    ) -> Self {
178        let n = elems.len();
179        let mut idx_map: HashMap<T, usize> = HashMap::new();
180        for (i, x) in elems.iter().enumerate() {
181            idx_map.insert(x.clone(), i);
182        }
183
184        Self::new_unchecked(
185            n,
186            idx_map[&ident()],
187            {
188                let mut inv_lookup = vec![];
189                for x in &elems {
190                    inv_lookup.push(idx_map[&inv(x.clone())]);
191                }
192                inv_lookup
193            },
194            {
195                let mut mul_lookup = vec![];
196                for (i, x) in elems.iter().enumerate() {
197                    mul_lookup.push(vec![]);
198                    for y in &elems {
199                        mul_lookup[i].push(idx_map[&mul(x.clone(), y.clone())]);
200                    }
201                }
202                mul_lookup
203            },
204            is_abelian,
205            is_simple,
206        )
207    }
208
209    pub fn size(&self) -> usize {
210        self.n
211    }
212
213    pub fn elems(&self) -> std::ops::Range<usize> {
214        0..self.n
215    }
216
217    pub fn mul_many(&self, elems: &Vec<usize>) -> usize {
218        if elems.len() == 0 {
219            return self.ident;
220        }
221        let mut prod = elems[0];
222        for i in 1..elems.len() {
223            prod = self.mul[prod][elems[i]];
224        }
225        prod
226    }
227
228    pub fn order(&self, x: usize) -> Result<usize, ()> {
229        if !(x < self.n) {
230            return Err(());
231        }
232        let mut y = x;
233        let mut ord = 1;
234        while y != self.ident {
235            y = self.mul[x][y];
236            ord += 1;
237            debug_assert!(ord <= self.n);
238        }
239        return Ok(ord);
240    }
241
242    fn compute_is_abelian(&self) -> bool {
243        for x in 0..self.n {
244            for y in 0..x {
245                if self.mul[x][y] != self.mul[y][x] {
246                    return false;
247                }
248            }
249        }
250        true
251    }
252
253    pub fn is_abelian(&self) -> bool {
254        match &self.is_abelian {
255            Some(flag) => {
256                return *flag;
257            }
258            None => {
259                return self.compute_is_abelian();
260            }
261        }
262    }
263
264    fn compute_conjugacy_classes(&self) -> PartitionState {
265        let mut unclassified_elems = HashSet::<_>::from_iter(self.elems());
266        let mut classes = vec![];
267        let mut lookup = vec![0; self.n];
268        while unclassified_elems.len() > 0 {
269            let x = unclassified_elems.iter().next().unwrap();
270            let mut class = BTreeSet::new();
271            for g in self.elems() {
272                class.insert(self.mul[self.mul[g][*x]][self.inv[g]]);
273            }
274            for y in &class {
275                unclassified_elems.remove(y);
276                lookup[*y] = classes.len();
277            }
278            classes.push(class);
279        }
280        PartitionState::new_unchecked(classes, lookup)
281    }
282
283    pub fn cache_conjugacy_classes(&mut self) {
284        self.conjugacy_classes = Some(self.compute_conjugacy_classes())
285    }
286
287    pub fn conjugacy_class(&mut self, x: usize) -> Result<Subset, ()> {
288        if !(x < self.n) {
289            return Err(());
290        }
291        self.cache_conjugacy_classes();
292        match &self.conjugacy_classes {
293            Some(conj_state) => Ok(Subset::new_unchecked(self, conj_state.project(x).clone())),
294            None => panic!(),
295        }
296    }
297
298    pub fn conjugacy_classes(&self) -> Partition {
299        Partition {
300            group: self,
301            state: match &self.conjugacy_classes {
302                Some(state) => state.clone(),
303                None => self.compute_conjugacy_classes(),
304            },
305        }
306    }
307
308    //returns a hashmap of subgroups and a minimal generating set
309    fn subgroups_impl(&self, only_normal: bool) -> Vec<(Subgroup, Subset)> {
310        //choose one generator of each cyclic subgroup
311        //The partition of elements where x~y iff <x>=<y> is such that an equivelence class is either a subset of a subgroup or doesnot intersect the subgroup
312        let mut distinguished_gens = vec![];
313        let mut subgroups: HashMap<Subgroup, Subset> = HashMap::new();
314        for x in self.elems() {
315            let singleton_x_subset = Subset::new_unchecked(&self, BTreeSet::from_iter(vec![x]));
316            let cyclic_sg = match only_normal {
317                false => singleton_x_subset.generated_subgroup().unwrap(),
318                true => singleton_x_subset.normal_closure().unwrap().to_subgroup(),
319            };
320            if !subgroups.contains_key(&cyclic_sg) {
321                subgroups.insert(cyclic_sg, singleton_x_subset.clone());
322                distinguished_gens.push(x);
323            }
324        }
325
326        //generate all subgroups by itteratively adding generators
327        let mut boundary = HashMap::new();
328        for (sg, gens) in subgroups.clone().into_iter() {
329            boundary.insert(sg, gens);
330        }
331        let mut next_boundary = HashMap::new();
332        while boundary.len() > 0 {
333            for (_sg, sg_gens) in &boundary {
334                //compute and sort the next subgroups in parallel threads
335                for (new_sg, new_gens) in distinguished_gens
336                    .par_iter()
337                    .map(|dgen| {
338                        let mut new_gens = sg_gens.clone();
339                        new_gens.add_elem(*dgen).unwrap();
340                        let new_sg = match only_normal {
341                            false => new_gens.generated_subgroup().unwrap(),
342                            true => new_gens.normal_closure().unwrap().to_subgroup(),
343                        };
344                        return (new_sg, new_gens);
345                    })
346                    .collect::<Vec<(Subgroup, Subset)>>()
347                {
348                    //collect the computed subgroups in the main thread
349                    if !subgroups.contains_key(&new_sg) {
350                        next_boundary.insert(new_sg.clone(), new_gens.clone());
351                        subgroups.insert(new_sg, new_gens);
352                    }
353                }
354            }
355            boundary = next_boundary;
356            next_boundary = HashMap::new();
357        }
358        return subgroups
359            .into_iter()
360            .map(|(elems, gens)| (elems, gens))
361            .collect();
362    }
363
364    pub fn subgroups(&self) -> Vec<(Subgroup, Subset)> {
365        self.subgroups_impl(false)
366    }
367
368    pub fn normal_subgroups(&self) -> Vec<(NormalSubgroup, Subset)> {
369        self.subgroups_impl(true)
370            .into_iter()
371            .map(|(subgroup, gens)| (NormalSubgroup::new_unchecked(subgroup), gens))
372            .collect()
373    }
374}
375
376pub fn direct_product_structure(group_one: &Group, group_two: &Group) -> Group {
377    let m = group_one.n;
378    let n = group_two.n;
379
380    let single_to_pair = |i: usize| -> (usize, usize) { (i % m, i / m) };
381    let pair_to_single = |i: usize, j: usize| -> usize { i + j * m };
382
383    Group {
384        n: m * n,
385        ident: pair_to_single(group_one.ident, group_two.ident),
386        inv: (0..m * n)
387            .map(|x| {
388                let (i, j) = single_to_pair(x);
389                pair_to_single(group_one.inv[i], group_two.inv[j])
390            })
391            .collect(),
392        mul: (0..m * n)
393            .map(|x| {
394                (0..m * n)
395                    .map(|y| {
396                        let (ix, jx) = single_to_pair(x);
397                        let (iy, jy) = single_to_pair(y);
398                        pair_to_single(group_one.mul[ix][iy], group_two.mul[jx][jy])
399                    })
400                    .collect()
401            })
402            .collect(),
403        conjugacy_classes: None,
404        is_abelian: None,
405        is_simple: None,
406    }
407}
408
409pub mod examples {
410    use crate::free_group::todd_coxeter::FinitelyGeneratedGroupPresentation;
411
412    use super::*;
413
414    pub fn trivial_group_structure() -> Group {
415        cyclic_group_structure(1)
416    }
417
418    pub fn cyclic_group_structure(n: usize) -> Group {
419        Group::from_raw_model_unchecked(
420            (0..n).collect(),
421            || 0,
422            |x: usize| (n - x) % n,
423            |x: usize, y: usize| (x + y) % n,
424            Some(true),
425            None,
426        )
427    }
428
429    pub fn klein_four_structure() -> Group {
430        direct_product_structure(&cyclic_group_structure(2), &cyclic_group_structure(2))
431    }
432
433    pub fn dihedral_group_structure(n: usize) -> Group {
434        // dihedral group using the presentation
435        // <a b : a^2 = b^2 = (ab)^n = e>
436        assert!(1 <= n);
437
438        let mut grp = FinitelyGeneratedGroupPresentation::new();
439        let a = grp.add_generator();
440        let b = grp.add_generator();
441        grp.add_relation(a.pow(2));
442        grp.add_relation(b.pow(2));
443        grp.add_relation((&a * &b).pow(n as isize));
444        let mut grp = grp.into_finite_group();
445        grp.is_abelian = Some(n <= 2);
446        grp.is_simple = Some(n <= 1);
447        grp
448    }
449
450    pub fn quaternion_group_structure() -> Group {
451        // quaternion group using the presentation
452        // <-1 i j k : (-1)^2 = 1  i^2 = j^2 = k^2 = ijk = -1>
453        let mut grp = FinitelyGeneratedGroupPresentation::new();
454        let a = grp.add_generator();
455        let i = grp.add_generator();
456        let j = grp.add_generator();
457        let k = grp.add_generator();
458        grp.add_relation(a.pow(2));
459        grp.add_two_sided_relation(i.pow(2), a.clone());
460        grp.add_two_sided_relation(j.pow(2), a.clone());
461        grp.add_two_sided_relation(k.pow(2), a.clone());
462        grp.add_two_sided_relation(&i * &j * &k, a.clone());
463        let mut grp = grp.into_finite_group();
464        grp.is_abelian = Some(false);
465        grp.is_simple = Some(false);
466        grp
467    }
468
469    pub fn symmetric_group_structure(n: usize) -> Group {
470        super::super::super::permutation::Permutation::symmetric_composition_table(n).0
471    }
472
473    pub fn alternating_group_structure(n: usize) -> Group {
474        super::super::super::permutation::Permutation::alternating_composition_table(n).0
475    }
476}
477
478#[cfg(test)]
479mod group_tests {
480    use super::*;
481
482    #[test]
483    fn test_cyclic() {
484        for k in vec![1, 2, 3, 81, 91, 97, 100, 128] {
485            let mut grp = examples::cyclic_group_structure(k);
486            grp.cache_conjugacy_classes();
487            grp.check_state().unwrap();
488            assert_eq!(grp.elems().len(), k);
489        }
490    }
491
492    #[test]
493    fn test_dihedral() {
494        for k in vec![1, 2, 3, 16, 17, 18, 50] {
495            let mut grp = examples::dihedral_group_structure(k);
496            grp.cache_conjugacy_classes();
497            grp.check_state().unwrap();
498            assert_eq!(grp.elems().len(), 2 * k);
499        }
500    }
501
502    #[test]
503    fn test_quaternion() {
504        let mut grp = examples::quaternion_group_structure();
505        assert_eq!(grp.size(), 8);
506        grp.cache_conjugacy_classes();
507        grp.check_state().unwrap();
508    }
509
510    #[test]
511    fn test_direct_product() {
512        let grp1 = examples::dihedral_group_structure(5);
513        let grp2 = examples::dihedral_group_structure(4);
514        let grp3 = direct_product_structure(&grp1, &grp2);
515        grp3.check_state().unwrap();
516    }
517
518    #[test]
519    fn test_conjugacy_class_count() {
520        for (grp, num_ccls) in vec![
521            (examples::cyclic_group_structure(12), 12),
522            (examples::cyclic_group_structure(13), 13),
523            (examples::dihedral_group_structure(1), 2),
524            (examples::dihedral_group_structure(12), 9),
525            (examples::dihedral_group_structure(13), 8),
526            (examples::symmetric_group_structure(1), 1), //only the identity
527            (examples::symmetric_group_structure(2), 2), //ident, 2-cycle
528            (examples::symmetric_group_structure(3), 3), //ident, 2-cycle, 3-cycle
529            (examples::symmetric_group_structure(4), 5), //ident, 2-cycle, 3-cycle, 4-cycle, (2, 2)-cycle
530            (examples::symmetric_group_structure(5), 7), //ident, 2-cycle, 3-cycle, 4-cycle, 5-cycle, (2, 2)-cycle, (2, 3)-cycle
531                                                         // (examples::symmetric_group_structure(6), 11), //ident, 2, 3, 4, 5, 6, (2, 2), (2, 3), (2, 4), (3, 3), (2, 2, 2)
532        ] {
533            assert_eq!(grp.conjugacy_classes().size(), num_ccls);
534        }
535    }
536}