algebraeon_groups/composition_table/
generating_set.rs1use 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>, elems: Vec<Vec<usize>>, }
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 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 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 let Some(max_size_val) = max_size
109 && gens.len() == max_size_val
110 {
111 return Err(());
112 }
113
114 let new_g = *missing.iter().next().unwrap(); gens.push(new_g);
117
118 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}