algebraeon_groups/composition_table/
subset.rs

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