algebraeon_groups/composition_table/
subset.rs

1use super::group::FiniteGroupMultiplicationTable;
2use super::normal_subgroup::NormalSubgroup;
3use super::subgroup::Subgroup;
4use std::collections::HashSet;
5
6pub struct Subset<'a> {
7    group: &'a FiniteGroupMultiplicationTable,
8    elems: HashSet<usize>,
9}
10
11impl<'a> Subset<'a> {
12    pub fn check_state(&self) -> Result<(), &'static str> {
13        for x in &self.elems {
14            if *x >= self.group.size() {
15                return Err("invalid subset element");
16            }
17        }
18
19        Ok(())
20    }
21
22    pub fn new_unchecked(group: &'a FiniteGroupMultiplicationTable, elems: HashSet<usize>) -> Self {
23        Self { group, elems }
24    }
25
26    pub fn group(&self) -> &FiniteGroupMultiplicationTable {
27        self.group
28    }
29
30    pub fn elems(&self) -> &HashSet<usize> {
31        &self.elems
32    }
33
34    pub fn size(&self) -> usize {
35        self.elems.len()
36    }
37
38    pub fn add_elem(&mut self, x: usize) -> Result<(), ()> {
39        if self.elems.contains(&x) {
40            return Ok(());
41        } else if x >= self.group.size() {
42            return Err(());
43        }
44        self.elems.insert(x);
45        Ok(())
46    }
47
48    pub fn left_mul(&'_ self, g: usize) -> Result<Subset<'_>, ()> {
49        if g >= self.group.size() {
50            return Err(());
51        }
52        Ok(Subset {
53            group: self.group,
54            elems: self.elems.iter().map(|h| self.group.mul(g, *h)).collect(),
55        })
56    }
57
58    pub fn right_mul(&'_ self, g: usize) -> Result<Subset<'_>, ()> {
59        if g >= self.group.size() {
60            return Err(());
61        }
62        Ok(Subset {
63            group: self.group,
64            elems: self.elems.iter().map(|h| self.group.mul(*h, g)).collect(),
65        })
66    }
67
68    pub fn is_subgroup(&self) -> bool {
69        for x in &self.elems {
70            for y in &self.elems {
71                if !self.elems.contains(&self.group.mul(*x, *y)) {
72                    return false;
73                }
74            }
75        }
76        true
77    }
78
79    pub fn to_subgroup(&'_ self) -> Option<Subgroup<'_>> {
80        if self.is_subgroup() {
81            Some(Subgroup {
82                subset: self.clone(),
83            })
84        } else {
85            None
86        }
87    }
88
89    pub fn generated_subgroup(&self) -> Result<Subgroup<'a>, ()> {
90        for g in &self.elems {
91            if *g >= self.group.size() {
92                return Err(());
93            }
94        }
95
96        //generate subgroup by adding all generated elements
97        let mut sg = HashSet::new();
98        sg.insert(self.group.ident());
99
100        let mut boundary = vec![self.group.ident()];
101        let mut next_boundary = vec![];
102        let mut y;
103        #[allow(clippy::assigning_clones)]
104        while !boundary.is_empty() {
105            for x in &boundary {
106                for g in &self.elems {
107                    y = self.group.mul(*x, *g);
108                    if !sg.contains(&y) {
109                        sg.insert(y);
110                        next_boundary.push(y);
111                    }
112                }
113            }
114            boundary = next_boundary.clone();
115            next_boundary = vec![];
116        }
117        Ok(Subgroup {
118            subset: Subset {
119                group: self.group,
120                elems: sg,
121            },
122        })
123    }
124
125    pub fn normal_closure(&self) -> Result<NormalSubgroup<'a>, &'static str> {
126        for g in &self.elems {
127            if *g >= self.group.size() {
128                return Err("gen out of range");
129            }
130        }
131
132        let conj_info = self.group.conjugacy_classes().partition;
133
134        //generate subgroup by adding all generated elements
135        let mut sg = HashSet::new();
136        sg.insert(self.group.ident());
137
138        let mut boundary = vec![self.group.ident()];
139        let mut next_boundary = vec![];
140        #[allow(clippy::assigning_clones)]
141        while !boundary.is_empty() {
142            for x in &boundary {
143                for g in &self.elems {
144                    for y in conj_info.class_containing(self.group.mul(*x, *g)) {
145                        if !sg.contains(y) {
146                            sg.insert(*y);
147                            next_boundary.push(*y);
148                        }
149                    }
150                }
151            }
152            boundary = next_boundary.clone();
153            next_boundary = vec![];
154        }
155        Ok(NormalSubgroup::new_unchecked(Subgroup {
156            subset: Subset {
157                group: self.group,
158                elems: sg,
159            },
160        }))
161    }
162}
163
164impl<'a> IntoIterator for Subset<'a> {
165    type Item = usize;
166    type IntoIter = <HashSet<usize> as IntoIterator>::IntoIter;
167
168    fn into_iter(self) -> Self::IntoIter {
169        self.elems.into_iter()
170    }
171}
172
173impl<'a> PartialEq for Subset<'a> {
174    fn eq(&self, other: &Self) -> bool {
175        self.elems == other.elems
176    }
177}
178
179impl<'a> Eq for Subset<'a> {}
180
181// impl<'a> Hash for Subset<'a> {
182//     fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
183//         self.elems.hash(state);
184//     }
185// }
186
187impl<'a> Clone for Subset<'a> {
188    fn clone(&self) -> Self {
189        Self {
190            group: self.group,
191            elems: self.elems.clone(),
192        }
193    }
194}
195
196#[cfg(test)]
197mod subset_tests {
198    use super::*;
199
200    #[test]
201    fn subset_left_mul() {
202        use crate::examples::symmetric::Permutation;
203
204        let (grp, _perms, elems) = Permutation::<4>::symmetric_composition_table();
205
206        let subset = Subset {
207            group: &grp,
208            elems: vec![
209                elems[&Permutation::new([1, 2, 3, 0]).unwrap()],
210                elems[&Permutation::new([0, 1, 3, 2]).unwrap()],
211                elems[&Permutation::new([3, 2, 1, 0]).unwrap()],
212            ]
213            .into_iter()
214            .collect(),
215        };
216        let left_mul_subset = subset
217            .left_mul(elems[&Permutation::new([1, 2, 0, 3]).unwrap()])
218            .unwrap();
219        assert_eq!(
220            left_mul_subset.elems,
221            Subset {
222                group: &grp,
223                elems: vec![
224                    elems[&Permutation::new([2, 0, 3, 1]).unwrap()],
225                    elems[&Permutation::new([1, 2, 3, 0]).unwrap()],
226                    elems[&Permutation::new([3, 0, 2, 1]).unwrap()]
227                ]
228                .into_iter()
229                .collect()
230            }
231            .elems
232        );
233    }
234
235    #[test]
236    fn subset_right_mul() {
237        use crate::examples::symmetric::Permutation;
238
239        let (grp, _perms, elems) = Permutation::<4>::symmetric_composition_table();
240
241        let subset = Subset {
242            group: &grp,
243            elems: vec![
244                elems[&Permutation::new([1, 2, 3, 0]).unwrap()],
245                elems[&Permutation::new([0, 1, 3, 2]).unwrap()],
246                elems[&Permutation::new([3, 2, 1, 0]).unwrap()],
247            ]
248            .into_iter()
249            .collect(),
250        };
251        let left_mul_subset = subset
252            .right_mul(elems[&Permutation::new([1, 2, 0, 3]).unwrap()])
253            .unwrap();
254        assert_eq!(
255            left_mul_subset.elems,
256            Subset {
257                group: &grp,
258                elems: vec![
259                    elems[&Permutation::new([2, 3, 1, 0]).unwrap()],
260                    elems[&Permutation::new([1, 3, 0, 2]).unwrap()],
261                    elems[&Permutation::new([2, 1, 3, 0]).unwrap()]
262                ]
263                .into_iter()
264                .collect()
265            }
266            .elems
267        );
268    }
269}