1use std::borrow::Borrow;
2use std::collections::BTreeSet;
3use std::collections::HashMap;
4
5use super::generating_set::GeneratingSet;
6use super::group::FiniteGroupMultiplicationTable;
7
8#[derive(Clone)]
9pub struct Homomorphism<
10 DomainT: Borrow<FiniteGroupMultiplicationTable> + Clone,
11 RangeT: Borrow<FiniteGroupMultiplicationTable> + Clone,
12> {
13 domain: DomainT,
14 range: RangeT,
15 func: Vec<usize>, }
17
18impl<
19 DomainT: Borrow<FiniteGroupMultiplicationTable> + Clone,
20 RangeT: Borrow<FiniteGroupMultiplicationTable> + Clone,
21> Homomorphism<DomainT, RangeT>
22{
23 pub fn check_state(&self) -> Result<(), &'static str> {
24 if self.func.len() != self.domain.borrow().size() {
26 return Err("func size does not match domain size");
27 }
28
29 for x in self.domain.borrow().elems() {
30 if self.func[x] >= self.range.borrow().size() {
31 return Err("func image is too big for an element of the range");
32 }
33 }
34
35 for x in self.domain.borrow().elems() {
37 for y in self.domain.borrow().elems() {
38 if self.func[self.domain.borrow().mul(x, y)]
39 != self.range.borrow().mul(self.func[x], self.func[y])
40 {
41 return Err("homomorphism does not respect composition");
42 }
43 }
44 }
45
46 Ok(())
47 }
48
49 pub fn new_unchecked(domain: DomainT, range: RangeT, func: Vec<usize>) -> Self {
50 Self {
51 domain,
52 range,
53 func,
54 }
55 }
56
57 pub fn to_isomorphism(self) -> Option<Isomorphism<DomainT, RangeT>> {
58 let n = self.domain.borrow().size();
59 if n != self.range.borrow().size() {
60 return None;
61 }
62
63 let mut inv = vec![None; n];
64 #[allow(clippy::indexing_slicing)]
65 for x in 0..n {
66 match inv[self.func[x]] {
67 Some(_y) => {
68 return None;
70 }
71 None => inv[self.func[x]] = Some(x),
72 }
73 }
74
75 let inv = inv
76 .into_iter()
77 .map(|x| if let Some(x_val) = x { x_val } else { panic!() })
78 .collect::<Vec<usize>>();
79
80 Some(Isomorphism {
81 left_group: self.domain,
82 right_group: self.range,
83 left_func: self.func.clone(),
84 right_func: inv,
85 })
86 }
87}
88
89#[derive(Clone)]
90pub struct Isomorphism<
91 LeftGrpT: Borrow<FiniteGroupMultiplicationTable> + Clone,
92 RightGrpT: Borrow<FiniteGroupMultiplicationTable> + Clone,
93> {
94 left_group: LeftGrpT,
95 right_group: RightGrpT,
96 left_func: Vec<usize>,
97 right_func: Vec<usize>,
98}
99
100impl<
101 LeftGrpT: Borrow<FiniteGroupMultiplicationTable> + Clone,
102 RightGrpT: Borrow<FiniteGroupMultiplicationTable> + Clone,
103> Isomorphism<LeftGrpT, RightGrpT>
104{
105 pub fn check_state(&self) -> Result<(), &'static str> {
106 let left_hom = Homomorphism {
107 domain: self.left_group.borrow(),
108 range: self.right_group.borrow(),
109 func: self.left_func.clone(),
110 };
111 match left_hom.check_state() {
112 Ok(()) => {}
113 Err(msg) => {
114 return Err(msg);
115 }
116 }
117
118 let right_hom = Homomorphism {
119 domain: self.right_group.borrow(),
120 range: self.left_group.borrow(),
121 func: self.right_func.clone(),
122 };
123 match right_hom.check_state() {
124 Ok(()) => {}
125 Err(msg) => {
126 return Err(msg);
127 }
128 }
129
130 if self.left_group.borrow().size() != self.right_group.borrow().size() {
131 return Err("isomorphism group sizes dont match");
132 }
133
134 for x in self.left_group.borrow().elems() {
137 if x != self.right_func[self.left_func[x]] {
138 return Err("isomorphism not inv");
139 }
140 }
141
142 Ok(())
143 }
144}
145
146#[allow(clippy::too_many_lines)]
149pub fn find_isomorphism<'a, 'b>(
150 domain: &'a FiniteGroupMultiplicationTable,
151 range: &'b FiniteGroupMultiplicationTable,
152) -> Option<Isomorphism<&'a FiniteGroupMultiplicationTable, &'b FiniteGroupMultiplicationTable>> {
153 let group_size = domain.size();
154 if group_size != range.size() {
155 return None;
156 }
157
158 #[allow(clippy::items_after_statements)]
162 #[derive(Debug, PartialEq, Eq, Hash)]
163 struct ElementProfile {
164 order: usize,
165 mul_order: BTreeSet<usize>, }
167
168 impl ElementProfile {
169 fn new(x: usize, group: &FiniteGroupMultiplicationTable) -> Self {
170 debug_assert!(x < group.size());
171 ElementProfile {
172 order: group.order(x).unwrap(),
173 mul_order: group
174 .elems()
175 .map(|y| group.order(group.mul(x, y)).unwrap())
176 .collect(),
177 }
178 }
179 }
180
181 let domain_elem_profiles: Vec<ElementProfile> = domain
183 .elems()
184 .map(|x| ElementProfile::new(x, domain))
185 .collect();
186
187 let mut range_elem_profiles: HashMap<ElementProfile, Vec<usize>> = HashMap::new();
191 for x in range.elems() {
192 let p = ElementProfile::new(x, range);
193 match range_elem_profiles.get_mut(&p) {
194 Some(elems) => {
195 elems.push(x);
196 }
197 None => {
198 range_elem_profiles.insert(p, vec![x]);
199 }
200 }
201 }
202
203 struct GenInfo<'c> {
206 gen_list: Vec<usize>,
207 gen_set: GeneratingSet<'c>,
208 image_options: Vec<Vec<usize>>,
209 quant_to_check: usize,
210 }
211
212 fn find_new_gen_info<'c>(
213 domain: &'c FiniteGroupMultiplicationTable,
214 domain_elem_profiles: &[ElementProfile],
215 range_elem_profiles: &HashMap<ElementProfile, Vec<usize>>,
216 ) -> Result<GenInfo<'c>, ()> {
217 let domain_gen_set = domain.generating_set();
218 let domain_gens = domain_gen_set.gens();
219
220 let mut gen_image_options: Vec<Vec<usize>> = vec![];
223 for g in domain_gens {
224 match range_elem_profiles.get(&domain_elem_profiles[*g]) {
225 Some(r_elems) => gen_image_options.push(r_elems.clone()),
226 None => {
227 return Err(()); }
229 }
230 }
231
232 let mut num_to_check: usize = 1;
233 for gen_image_option in &gen_image_options {
234 num_to_check = match num_to_check.checked_mul(gen_image_option.len()) {
235 Some(prod) => prod,
236 None => usize::MAX,
237 };
238 }
239
240 Ok(GenInfo {
241 gen_list: domain_gens.clone(),
242 gen_set: domain_gen_set,
243 image_options: gen_image_options,
244 quant_to_check: num_to_check,
245 })
246 }
247
248 let mut current_gen_info;
249 match find_new_gen_info(domain, &domain_elem_profiles, &range_elem_profiles) {
250 Ok(gen_info) => current_gen_info = gen_info,
251 Err(()) => {
252 return None;
253 }
254 }
255
256 'outer_loop: loop {
260 let mut image_option_counter = vec![0; current_gen_info.gen_list.len()];
262 let mut already_checked: usize = 0;
263 'loop_label: loop {
264 if already_checked % 128 == 127 {
267 match find_new_gen_info(domain, &domain_elem_profiles, &range_elem_profiles) {
269 Ok(gen_info) => {
270 if gen_info.quant_to_check
271 < current_gen_info.quant_to_check - already_checked
272 {
273 current_gen_info = gen_info;
275 continue 'outer_loop;
276 }
277 }
278 Err(()) => {
279 return None;
280 }
281 }
282 }
283
284 if let Some(f) = current_gen_info
286 .gen_set
287 .generated_homomorphism(
288 &image_option_counter
289 .iter()
290 .enumerate()
291 .map(|(i, j)| current_gen_info.image_options[i][*j])
292 .collect(),
293 range,
294 )
295 .unwrap()
296 && let Some(f_iso) = f.to_isomorphism()
297 {
298 return Some(f_iso);
299 }
300 already_checked += 1;
301
302 'for_label: {
304 for i in 0..current_gen_info.gen_list.len() {
305 image_option_counter[i] += 1;
306 if image_option_counter[i] == current_gen_info.image_options[i].len() {
307 image_option_counter[i] = 0;
308 } else {
309 break 'for_label;
310 }
311 }
312 break 'loop_label;
313 }
314 }
315 return None;
316 }
317}
318
319#[cfg(test)]
320mod homomorphism_tests {
321 use crate::{
322 composition_table::group::examples,
323 free_group::todd_coxeter::FinitelyGeneratedGroupPresentation,
324 };
325
326 use super::*;
327
328 #[test]
329 fn homomorphism_state() {
330 {
331 let grp_g = examples::cyclic_group_structure(6);
333 let grp_h = examples::cyclic_group_structure(6);
334 let f = Homomorphism {
335 domain: &grp_g,
336 range: &grp_h,
337 func: vec![0, 1, 2, 3, 4, 5],
338 };
339 if f.check_state().is_err() {
340 panic!()
341 }
342 }
343
344 {
345 let grp_g = examples::cyclic_group_structure(3);
347 let grp_h = examples::cyclic_group_structure(6);
348 let f = Homomorphism {
349 domain: &grp_g,
350 range: &grp_h,
351 func: vec![0, 2, 4],
352 };
353 if f.check_state().is_err() {
354 panic!()
355 }
356 }
357
358 {
359 let grp_g = examples::cyclic_group_structure(6);
361 let grp_h = examples::cyclic_group_structure(3);
362 let f = Homomorphism {
363 domain: &grp_g,
364 range: &grp_h,
365 func: vec![0, 1, 2, 0, 1, 2],
366 };
367 if f.check_state().is_err() {
368 panic!()
369 }
370 }
371
372 {
373 let grp_g = examples::cyclic_group_structure(3);
375 let grp_h = examples::cyclic_group_structure(6);
376 let f = Homomorphism {
377 domain: &grp_g,
378 range: &grp_h,
379 func: vec![0, 1, 2, 3],
380 };
381 if let Ok(()) = f.check_state() {
382 panic!()
383 }
384 }
385
386 {
387 let grp_g = examples::cyclic_group_structure(6);
389 let grp_h = examples::cyclic_group_structure(3);
390 let f = Homomorphism {
391 domain: &grp_g,
392 range: &grp_h,
393 func: vec![0, 1, 2, 3, 4, 5, 6],
394 };
395 if let Ok(()) = f.check_state() {
396 panic!()
397 }
398 }
399
400 {
401 let grp_g = examples::cyclic_group_structure(3);
403 let grp_h = examples::cyclic_group_structure(6);
404 let f = Homomorphism {
405 domain: &grp_g,
406 range: &grp_h,
407 func: vec![0, 1, 2],
408 };
409 if let Ok(()) = f.check_state() {
410 panic!()
411 }
412 }
413 }
414
415 #[test]
416 fn isomorphism_state() {
417 {
418 let grp_g = examples::cyclic_group_structure(6);
420 let grp_h = examples::cyclic_group_structure(6);
421 let f = Isomorphism {
422 left_group: &grp_g,
423 right_group: &grp_h,
424 left_func: vec![0, 5, 4, 3, 2, 1],
425 right_func: vec![0, 5, 4, 3, 2, 1],
426 };
427 if f.check_state().is_err() {
428 panic!()
429 }
430 }
431
432 {
433 let grp_g = examples::cyclic_group_structure(3);
435 let grp_h = examples::cyclic_group_structure(6);
436 let f = Isomorphism {
437 left_group: &grp_g,
438 right_group: &grp_h,
439 left_func: vec![0, 2, 4],
440 right_func: vec![0, 1, 2, 0, 1, 2],
441 };
442 if let Ok(()) = f.check_state() {
443 panic!()
444 }
445 }
446
447 {
448 let grp_g = examples::cyclic_group_structure(6);
450 let grp_h = examples::cyclic_group_structure(6);
451 let f = Isomorphism {
452 left_group: &grp_g,
453 right_group: &grp_h,
454 left_func: vec![0, 2, 4, 0, 2, 4],
455 right_func: vec![0, 5, 4, 3, 2, 1],
456 };
457 if let Ok(()) = f.check_state() {
458 panic!()
459 }
460 }
461 }
462
463 #[test]
464 fn homomorphism_to_isomorphism() {
465 {
466 let grp_g = examples::cyclic_group_structure(7);
468 let grp_h = examples::cyclic_group_structure(7);
469 let f = Homomorphism {
470 domain: &grp_g,
471 range: &grp_h,
472 func: vec![0, 3, 6, 2, 5, 1, 4],
473 };
474 f.check_state().unwrap();
475 if let Some(_f_iso) = f.to_isomorphism() {
476 } else {
477 panic!()
478 }
479 }
480
481 {
482 let grp_g = examples::cyclic_group_structure(3);
484 let grp_h = examples::cyclic_group_structure(6);
485 let f = Homomorphism {
486 domain: &grp_g,
487 range: &grp_h,
488 func: vec![0, 2, 4],
489 };
490 f.check_state().unwrap();
491 if let Some(_f_iso) = f.to_isomorphism() {
492 panic!()
493 }
494 }
495
496 {
497 let grp_g = examples::cyclic_group_structure(6);
499 let grp_h = examples::cyclic_group_structure(6);
500 let f = Homomorphism {
501 domain: &grp_g,
502 range: &grp_h,
503 func: vec![0, 2, 4, 0, 2, 4],
504 };
505 f.check_state().unwrap();
506 if let Some(__iso) = f.to_isomorphism() {
507 panic!()
508 }
509 }
510 }
511
512 #[test]
513 fn test_find_isomorphism() {
514 {
515 let grp_g = examples::symmetric_group_structure(3);
516 let grp_h = examples::dihedral_group_structure(3);
517
518 if let Some(f) = find_isomorphism(&grp_g, &grp_h) {
519 assert!(std::ptr::eq(&grp_g, f.left_group));
520 assert!(std::ptr::eq(&grp_h, f.right_group));
521 } else {
522 panic!()
523 }
524 }
525
526 {
527 let grp_g = examples::symmetric_group_structure(3);
528 let grp_h = examples::cyclic_group_structure(6);
529
530 if let Some(_f) = find_isomorphism(&grp_g, &grp_h) {
531 panic!();
532 }
533 }
534
535 {
536 let grp_g = examples::symmetric_group_structure(5);
537 let mut grp_h = FinitelyGeneratedGroupPresentation::new();
538 let a = grp_h.add_generator();
539 let b = grp_h.add_generator();
540 let c = grp_h.add_generator();
541 grp_h.add_relation(a.pow(2));
542 grp_h.add_relation(b.pow(2));
543 grp_h.add_relation(c.pow(2));
544 grp_h.add_relation((&a * &c).pow(2));
545 grp_h.add_relation((&a * &b).pow(3));
546 grp_h.add_relation((&b * &c).pow(5));
547 let grp_h = grp_h.into_finite_group(); if let Some(_f) = find_isomorphism(&grp_g, &grp_h) {
550 panic!()
551 }
552 }
553 }
554}