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