algebraeon_groups/composition_table/
generating_set.rs

1use std::collections::{BTreeSet, HashSet};
2
3use super::group::FiniteGroupMultiplicationTable;
4use super::homomorphism::Homomorphism;
5
6pub struct GeneratingSet<'a> {
7    group: &'a FiniteGroupMultiplicationTable,
8    gens: Vec<usize>,       //subset of group which generates it
9    elems: Vec<Vec<usize>>, //each element of group written as a product of gens
10}
11
12impl<'a> GeneratingSet<'a> {
13    pub fn check_state(&self) -> Result<(), &'static str> {
14        for g in &self.gens {
15            if *g >= self.group.size() {
16                return Err("bad generator");
17            }
18        }
19
20        if self.elems.len() != self.group.size() {
21            return Err("bad elems len");
22        }
23
24        for x in self.group.elems() {
25            if x != self.group.mul_many(
26                &self.elems[x]
27                    .iter()
28                    .map(|gen_idx| self.gens[*gen_idx])
29                    .collect(),
30            ) {
31                return Err("incorrect word of gens for elem");
32            }
33        }
34
35        Ok(())
36    }
37
38    pub fn size(&self) -> usize {
39        self.gens.len()
40    }
41
42    pub fn gens(&self) -> &Vec<usize> {
43        &self.gens
44    }
45
46    pub fn generated_homomorphism<'b>(
47        &self,
48        partial_func: &Vec<usize>,
49        range_group: &'b FiniteGroupMultiplicationTable,
50    ) -> Result<
51        Option<
52            Homomorphism<&'a FiniteGroupMultiplicationTable, &'b FiniteGroupMultiplicationTable>,
53        >,
54        &'static str,
55    > {
56        if partial_func.len() != self.gens.len() {
57            return Err("partial func entries should correspond to images for each generator");
58        }
59        for x in partial_func {
60            if *x >= range_group.size() {
61                return Err("partial func has invalid element from range group");
62            }
63        }
64
65        //try to make a function from self.group -> range sending self.gens -> the images as in partial_func
66        let func: Vec<usize> = self
67            .group
68            .elems()
69            .map(|x| {
70                range_group.mul_many(&self.elems[x].iter().map(|g| partial_func[*g]).collect())
71            })
72            .collect();
73
74        //check if func is a homomorphism i.e. f(xy) = f(x)f(y) for all x, y in range group
75        //only need to check for all x and for all generators y
76        for x in self.group.elems() {
77            for g in &self.gens {
78                if func[self.group.mul(x, *g)] != range_group.mul(func[x], func[*g]) {
79                    return Ok(None);
80                }
81            }
82        }
83
84        Ok(Some(Homomorphism::new_unchecked(
85            self.group,
86            range_group,
87            func,
88        )))
89    }
90}
91
92impl FiniteGroupMultiplicationTable {
93    fn try_find_generating_set(&self, max_size: Option<usize>) -> Result<GeneratingSet, ()> {
94        let mut missing = HashSet::new();
95        for x in self.elems() {
96            missing.insert(x);
97        }
98        missing.remove(&self.ident());
99
100        let mut sg = BTreeSet::new();
101        sg.insert(self.ident());
102        let mut gen_words = vec![vec![]; self.size()];
103        gen_words[self.ident()] = vec![];
104        let mut gens = vec![];
105        #[allow(clippy::assigning_clones)]
106        while sg.len() < self.size() {
107            //if we are going to need more gens than max_size allows, then give up
108            if let Some(max_size_val) = max_size {
109                if gens.len() == max_size_val {
110                    return Err(());
111                }
112            }
113
114            //add a new generator
115            let new_g = *missing.iter().next().unwrap(); //random choice of new generator to try
116            gens.push(new_g);
117
118            //compute the subgroup generated by gens
119            let mut y;
120            let mut boundary: Vec<usize> = vec![];
121            for s in sg.clone() {
122                y = self.mul(s, new_g);
123                if !sg.contains(&y) {
124                    sg.insert(y);
125                    missing.remove(&y);
126                    gen_words[y] = gen_words[s].clone();
127                    gen_words[y].push(gens.len() - 1);
128                    boundary.push(y);
129                }
130            }
131            let mut next_boundary: Vec<usize> = vec![];
132            while !boundary.is_empty() {
133                for x in &boundary {
134                    for (g_idx, g) in gens.iter().enumerate() {
135                        y = self.mul(*x, *g);
136                        if !sg.contains(&y) {
137                            sg.insert(y);
138                            missing.remove(&y);
139                            gen_words[y] = gen_words[*x].clone();
140                            gen_words[y].push(g_idx);
141                            next_boundary.push(y);
142                        }
143                    }
144                }
145                boundary = next_boundary.clone();
146                next_boundary = vec![];
147            }
148        }
149
150        Ok(GeneratingSet {
151            group: self,
152            gens,
153            elems: gen_words,
154        })
155    }
156
157    pub fn generating_set(&self) -> GeneratingSet {
158        self.try_find_generating_set(None).unwrap()
159    }
160
161    pub fn small_generating_set(&self, attempts: Option<usize>) -> GeneratingSet {
162        let a = attempts.unwrap_or(12);
163        assert!(a > 0);
164
165        let mut smallest_gens = self.generating_set();
166        for _i in 0..a - 1 {
167            if let Ok(gens) = self.try_find_generating_set(Some(smallest_gens.size() - 1)) {
168                smallest_gens = gens;
169            }
170        }
171
172        smallest_gens
173    }
174}
175
176#[cfg(test)]
177mod generating_set_tests {
178    use crate::composition_table::group::examples;
179
180    #[test]
181    fn test_generating_set() {
182        let grp = examples::cyclic_group_structure(10);
183        let g_set = grp.generating_set();
184        g_set.check_state().unwrap();
185
186        let grp = examples::dihedral_group_structure(12);
187        let g_set = grp.generating_set();
188        g_set.check_state().unwrap();
189
190        let grp = examples::symmetric_group_structure(4);
191        let g_set = grp.generating_set();
192        g_set.check_state().unwrap();
193
194        let grp = examples::symmetric_group_structure(5);
195        let g_set = grp.generating_set();
196        g_set.check_state().unwrap();
197    }
198}