algebraeon_groups/composition_table/
partition.rs1use algebraeon_sets::combinatorics::Partition;
2
3use super::group::*;
4use super::subset::*;
5use std::collections::HashSet;
6
7#[derive(Debug)]
8pub struct GroupPartition<'a> {
9 pub group: &'a FiniteGroupMultiplicationTable,
10 pub partition: Partition,
11 }
14
15impl<'a> PartialEq for GroupPartition<'a> {
16 fn eq(&self, other: &Self) -> bool {
17 let grp = self.group;
18 if !std::ptr::eq(grp, other.group) {
19 return false;
20 }
21 let n = self.size();
22 if n != other.size() {
23 return false;
24 }
25 let mut f: Vec<Option<usize>> = vec![None; n];
29 for i in 0..grp.size() {
30 match f[self.partition.project(i)] {
31 Some(expected_other_lookup_i) => {
32 if expected_other_lookup_i != other.partition.project(i) {
33 return false;
34 }
35 }
36 None => {
37 f[self.partition.project(i)] = Some(other.partition.project(i));
38 }
39 }
40 }
41 true
42 }
43}
44
45impl<'a> Eq for GroupPartition<'a> {}
46
47impl<'a> GroupPartition<'a> {
48 pub fn check_state(&self) -> Result<(), &'static str> {
49 if self.partition.num_elements() != self.group.size() {
50 return Err("Partition is of set of the wrong size");
51 }
52 Ok(())
53 }
54
55 pub fn size(&self) -> usize {
56 self.partition.size()
57 }
58
59 pub fn is_left_cosets(&self) -> bool {
60 let ident_class = Subset::new_unchecked(
61 self.group,
62 self.partition.class_containing(self.group.ident()).clone(),
63 );
64 match ident_class.to_subgroup() {
65 Some(ident_subgroup) => &ident_subgroup.left_cosets() == self,
66 None => false,
67 }
68 }
69
70 pub fn is_right_cosets(&self) -> bool {
71 let ident_class = Subset::new_unchecked(
72 self.group,
73 self.partition.class_containing(self.group.ident()).clone(),
74 );
75 match ident_class.to_subgroup() {
76 Some(ident_subgroup) => &ident_subgroup.right_cosets() == self,
77 None => false,
78 }
79 }
80
81 pub fn is_congruence(&self) -> bool {
82 self.is_left_cosets() && self.is_right_cosets()
83 }
84
85 pub fn from_subsets(
86 group: &'a FiniteGroupMultiplicationTable,
87 subsets: Vec<HashSet<usize>>,
88 ) -> Result<Self, ()> {
89 let classes = subsets;
90 let mut lookup = vec![0; group.size()];
91 for (class_idx, class) in classes.iter().enumerate() {
92 for x in class {
93 if !(*x < group.size()) {
94 return Err(());
95 }
96 lookup[*x] = class_idx;
97 }
98 }
99 let partition = Self {
100 group,
101 partition: Partition::new_unchecked(classes, lookup),
102 };
103 match partition.check_state() {
104 Ok(_) => {}
105 Err(_) => {
106 return Err(());
107 }
108 }
109 Ok(partition)
110 }
111}
112
113pub struct Congruence<'a> {
114 pub partition: GroupPartition<'a>,
115}
116
117impl<'a> Congruence<'a> {
118 pub fn check_state(&self) -> Result<(), &'static str> {
119 match self.partition.check_state() {
120 Ok(()) => {}
121 Err(msg) => {
122 return Err(msg);
123 }
124 }
125
126 match self.partition.is_congruence() {
127 false => return Err("congruence is not a congruence"),
128 true => {}
129 }
130
131 Ok(())
132 }
133
134 pub fn size(&self) -> usize {
135 self.partition.size()
136 }
137
138 pub fn quotient_group(&self) -> FiniteGroupMultiplicationTable {
139 let n = self.size();
140 self.partition.group.check_state().unwrap();
141 FiniteGroupMultiplicationTable::new_unchecked(
142 n,
143 self.partition
144 .partition
145 .project(self.partition.group.ident()),
146 {
147 let mut inv = vec![0; n];
148 for i in 0..n {
149 inv[i] = self.partition.partition.project(
150 self.partition
151 .group
152 .inv(*self.partition.partition.get_class(i).iter().next().unwrap()),
153 );
154 }
155 inv
156 },
157 {
158 let mut mul = vec![vec![0; n]; n];
159 for i in 0..n {
160 for j in 0..n {
161 mul[i][j] = self.partition.partition.project(self.partition.group.mul(
162 *self.partition.partition.get_class(i).iter().next().unwrap(),
163 *self.partition.partition.get_class(j).iter().next().unwrap(),
164 ));
165 }
166 }
167 mul
168 },
169 None,
170 None,
171 )
172 }
173}
174
175#[cfg(test)]
176mod partition_tests {
177 use super::*;
178
179 #[test]
180 fn congruence_check_state() {
181 let grp = examples::symmetric_group_structure(4);
182 for (sg, _gens) in grp.subgroups() {
183 match sg.to_normal_subgroup() {
184 None => {
185 let cong: Congruence<'_> = Congruence {
186 partition: sg.left_cosets(),
187 };
188 match cong.check_state() {
189 Ok(_) => panic!(),
190 Err(_) => {}
191 }
192 let cong: Congruence<'_> = Congruence {
193 partition: sg.right_cosets(),
194 };
195 match cong.check_state() {
196 Ok(_) => panic!(),
197 Err(_) => {}
198 }
199 }
200 Some(nsg) => {
201 let cong = nsg.cosets();
202 match cong.check_state() {
203 Ok(_) => {}
204 Err(_) => panic!(),
205 }
206 }
207 }
208 }
209 }
210}