algebraeon_groups/composition_table/
subgroup.rs1use std::collections::BTreeSet;
2use std::collections::HashSet;
3
4use algebraeon_sets::combinatorics::Partition;
5
6use super::group::*;
7use super::normal_subgroup::*;
8use super::partition::*;
9use super::subset::*;
10use std::hash::Hash;
11
12#[derive(Clone)]
13pub struct Subgroup<'a> {
14 pub subset: Subset<'a>,
15}
16
17impl<'a> PartialEq for Subgroup<'a> {
18 fn eq(&self, other: &Self) -> bool {
19 self.subset == other.subset
20 }
21}
22
23impl<'a> Eq for Subgroup<'a> {}
24
25impl<'a> Hash for Subgroup<'a> {
26 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
27 self.subset
28 .elems()
29 .iter()
30 .collect::<BTreeSet<_>>()
31 .hash(state);
32 }
33}
34
35impl<'a> Subgroup<'a> {
36 pub fn check_state(&self) -> Result<(), &'static str> {
37 match self.subset.check_state() {
38 Ok(_) => {}
39 Err(msg) => {
40 return Err(msg);
41 }
42 }
43
44 if !self.subset.is_subgroup() {
45 return Err("subgroup is not closed under composition");
46 }
47
48 Ok(())
49 }
50
51 pub fn size(&self) -> usize {
52 self.subset.size()
53 }
54
55 pub fn to_group(&self) -> FiniteGroupMultiplicationTable {
56 let sg_elems: Vec<usize> = self.subset.elems().clone().into_iter().collect();
57 let k = sg_elems.len();
58 let mut group_to_subgroup: Vec<Option<usize>> = vec![None; self.subset.group().size()];
59 for (i, x) in sg_elems.iter().enumerate() {
60 group_to_subgroup[*x] = Some(i);
61 }
62 FiniteGroupMultiplicationTable::new_unchecked(
64 self.size(),
65 group_to_subgroup[self.subset.group().ident()].unwrap(),
66 (0..k)
67 .map(|x| group_to_subgroup[self.subset.group().inv(sg_elems[x])].unwrap())
68 .collect(),
69 (0..k)
70 .map(|x| {
71 (0..k)
72 .map(|y| {
73 group_to_subgroup[self.subset.group().mul(sg_elems[x], sg_elems[y])]
74 .unwrap()
75 })
76 .collect()
77 })
78 .collect(),
79 None,
80 None,
81 )
82 }
83
84 pub fn left_cosets(&self) -> GroupPartition {
85 let mut cosets: Vec<HashSet<usize>> = vec![];
86 let mut coset_lookup = vec![0; self.subset.group().size()];
87 let mut missing: HashSet<usize> = self.subset.group().elems().collect();
88 while missing.len() > 0 {
89 let g = missing.iter().next().unwrap();
90 let coset = self.subset.left_mul(*g).unwrap().elems().clone();
91 for h in &coset {
92 missing.remove(h);
93 coset_lookup[*h] = cosets.len();
94 }
95 cosets.push(coset);
96 }
97 GroupPartition {
98 group: self.subset.group(),
99 partition: Partition::new_unchecked(cosets, coset_lookup),
100 }
101 }
102
103 pub fn right_cosets(&self) -> GroupPartition {
104 let mut cosets: Vec<HashSet<usize>> = vec![];
105 let mut coset_lookup = vec![0; self.subset.group().size()];
106 let mut missing: HashSet<usize> = self.subset.group().elems().collect();
107 while missing.len() > 0 {
108 let g = missing.iter().next().unwrap();
109 let coset = self.subset.right_mul(*g).unwrap().elems().clone();
110 for h in &coset {
111 missing.remove(h);
112 coset_lookup[*h] = cosets.len();
113 }
114 cosets.push(coset);
115 }
116 GroupPartition {
117 group: self.subset.group(),
118 partition: Partition::new_unchecked(cosets, coset_lookup),
119 }
120 }
121
122 pub fn is_normal_subgroup(&self) -> bool {
123 for x in self.subset.elems() {
124 for g in self.subset.group().elems() {
125 if !self.subset.elems().contains(
126 &self
127 .subset
128 .group()
129 .mul(self.subset.group().mul(g, *x), self.subset.group().inv(g)),
130 )
131 {
133 return false;
134 }
135 }
136 }
137 true
138 }
139
140 pub fn to_normal_subgroup(&self) -> Option<NormalSubgroup> {
141 match self.is_normal_subgroup() {
142 true => Some(NormalSubgroup::new_unchecked(self.clone())),
143 false => None,
144 }
145 }
146}
147
148#[cfg(test)]
149mod subgroup_tests {
150 use super::*;
151
152 #[test]
153 fn subgroup_counts() {
154 for (grp, num_sgs) in vec![
155 (examples::cyclic_group_structure(12), 6),
156 (examples::cyclic_group_structure(13), 2),
157 (examples::dihedral_group_structure(1), 2),
158 (examples::dihedral_group_structure(12), 34),
159 (examples::dihedral_group_structure(13), 16),
160 (examples::symmetric_group_structure(1), 1),
161 (examples::symmetric_group_structure(2), 2),
162 (examples::symmetric_group_structure(3), 6),
163 (examples::symmetric_group_structure(4), 30),
164 (examples::symmetric_group_structure(5), 156),
165 ] {
166 assert_eq!(grp.subgroups().len(), num_sgs);
167 }
168 }
169
170 #[test]
171 fn subgroup_state() {
172 for grp in vec![
174 examples::symmetric_group_structure(4),
175 examples::dihedral_group_structure(12),
176 examples::cyclic_group_structure(8),
177 ] {
178 let sgs = grp.subgroups();
179 for (sg, gens) in sgs {
180 sg.check_state().unwrap();
182 gens.check_state().unwrap();
183 }
186 }
187 }
188
189 #[test]
190 fn subgroup_left_cosets() {
191 use crate::examples::symmetric::Permutation;
192
193 let (grp, _perms, elems) = Permutation::<3>::symmetric_composition_table();
194 let sg = Subgroup {
195 subset: Subset::new_unchecked(
196 &grp,
197 vec![
198 elems[&Permutation::new([0, 1, 2]).unwrap()],
199 elems[&Permutation::new([1, 0, 2]).unwrap()],
200 ]
201 .into_iter()
202 .collect(),
203 ),
204 };
205 sg.check_state().unwrap();
206 let cosets = sg.left_cosets();
207 cosets.check_state().unwrap();
208 assert_eq!(cosets.size(), 3);
209 assert!(
210 cosets
211 == GroupPartition::from_subsets(
212 &grp,
213 vec![
214 vec![
215 elems[&Permutation::new([0, 1, 2]).unwrap()],
216 elems[&Permutation::new([1, 0, 2]).unwrap()],
217 ]
218 .into_iter()
219 .collect(),
220 vec![
221 elems[&Permutation::new([1, 2, 0]).unwrap()],
222 elems[&Permutation::new([2, 1, 0]).unwrap()],
223 ]
224 .into_iter()
225 .collect(),
226 vec![
227 elems[&Permutation::new([2, 0, 1]).unwrap()],
228 elems[&Permutation::new([0, 2, 1]).unwrap()],
229 ]
230 .into_iter()
231 .collect()
232 ]
233 )
234 .unwrap()
235 );
236 }
237
238 #[test]
239 fn subgroup_right_cosets() {
240 use crate::examples::symmetric::Permutation;
241
242 let (grp, _perms, elems) = Permutation::<3>::symmetric_composition_table();
243 let sg = Subgroup {
244 subset: Subset::new_unchecked(
245 &grp,
246 vec![
247 elems[&Permutation::new([0, 1, 2]).unwrap()],
248 elems[&Permutation::new([1, 0, 2]).unwrap()],
249 ]
250 .into_iter()
251 .collect(),
252 ),
253 };
254 sg.check_state().unwrap();
255 let cosets = sg.right_cosets();
256 println!("{:#?}", cosets);
257 cosets.check_state().unwrap();
258 assert_eq!(cosets.size(), 3);
259 assert!(
260 cosets
261 == GroupPartition::from_subsets(
262 &grp,
263 vec![
264 vec![
265 elems[&Permutation::new([0, 1, 2]).unwrap()],
266 elems[&Permutation::new([1, 0, 2]).unwrap()],
267 ]
268 .into_iter()
269 .collect(),
270 vec![
271 elems[&Permutation::new([1, 2, 0]).unwrap()],
272 elems[&Permutation::new([0, 2, 1]).unwrap()],
273 ]
274 .into_iter()
275 .collect(),
276 vec![
277 elems[&Permutation::new([2, 0, 1]).unwrap()],
278 elems[&Permutation::new([2, 1, 0]).unwrap()],
279 ]
280 .into_iter()
281 .collect()
282 ]
283 )
284 .unwrap()
285 );
286 }
287}