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 {
297 if let Some(f_iso) = f.to_isomorphism() {
298 return Some(f_iso);
299 }
300 }
301 already_checked += 1;
302
303 'for_label: {
305 for i in 0..current_gen_info.gen_list.len() {
306 image_option_counter[i] += 1;
307 if image_option_counter[i] == current_gen_info.image_options[i].len() {
308 image_option_counter[i] = 0;
309 } else {
310 break 'for_label;
311 }
312 }
313 break 'loop_label;
314 }
315 }
316 return None;
317 }
318}
319
320#[cfg(test)]
321mod homomorphism_tests {
322 use crate::{
323 composition_table::group::examples,
324 free_group::todd_coxeter::FinitelyGeneratedGroupPresentation,
325 };
326
327 use super::*;
328
329 #[test]
330 fn homomorphism_state() {
331 {
332 let grp_g = examples::cyclic_group_structure(6);
334 let grp_h = examples::cyclic_group_structure(6);
335 let f = Homomorphism {
336 domain: &grp_g,
337 range: &grp_h,
338 func: vec![0, 1, 2, 3, 4, 5],
339 };
340 if f.check_state().is_err() {
341 panic!()
342 }
343 }
344
345 {
346 let grp_g = examples::cyclic_group_structure(3);
348 let grp_h = examples::cyclic_group_structure(6);
349 let f = Homomorphism {
350 domain: &grp_g,
351 range: &grp_h,
352 func: vec![0, 2, 4],
353 };
354 if f.check_state().is_err() {
355 panic!()
356 }
357 }
358
359 {
360 let grp_g = examples::cyclic_group_structure(6);
362 let grp_h = examples::cyclic_group_structure(3);
363 let f = Homomorphism {
364 domain: &grp_g,
365 range: &grp_h,
366 func: vec![0, 1, 2, 0, 1, 2],
367 };
368 if f.check_state().is_err() {
369 panic!()
370 }
371 }
372
373 {
374 let grp_g = examples::cyclic_group_structure(3);
376 let grp_h = examples::cyclic_group_structure(6);
377 let f = Homomorphism {
378 domain: &grp_g,
379 range: &grp_h,
380 func: vec![0, 1, 2, 3],
381 };
382 if let Ok(()) = f.check_state() {
383 panic!()
384 }
385 }
386
387 {
388 let grp_g = examples::cyclic_group_structure(6);
390 let grp_h = examples::cyclic_group_structure(3);
391 let f = Homomorphism {
392 domain: &grp_g,
393 range: &grp_h,
394 func: vec![0, 1, 2, 3, 4, 5, 6],
395 };
396 if let Ok(()) = f.check_state() {
397 panic!()
398 }
399 }
400
401 {
402 let grp_g = examples::cyclic_group_structure(3);
404 let grp_h = examples::cyclic_group_structure(6);
405 let f = Homomorphism {
406 domain: &grp_g,
407 range: &grp_h,
408 func: vec![0, 1, 2],
409 };
410 if let Ok(()) = f.check_state() {
411 panic!()
412 }
413 }
414 }
415
416 #[test]
417 fn isomorphism_state() {
418 {
419 let grp_g = examples::cyclic_group_structure(6);
421 let grp_h = examples::cyclic_group_structure(6);
422 let f = Isomorphism {
423 left_group: &grp_g,
424 right_group: &grp_h,
425 left_func: vec![0, 5, 4, 3, 2, 1],
426 right_func: vec![0, 5, 4, 3, 2, 1],
427 };
428 if f.check_state().is_err() {
429 panic!()
430 }
431 }
432
433 {
434 let grp_g = examples::cyclic_group_structure(3);
436 let grp_h = examples::cyclic_group_structure(6);
437 let f = Isomorphism {
438 left_group: &grp_g,
439 right_group: &grp_h,
440 left_func: vec![0, 2, 4],
441 right_func: vec![0, 1, 2, 0, 1, 2],
442 };
443 if let Ok(()) = f.check_state() {
444 panic!()
445 }
446 }
447
448 {
449 let grp_g = examples::cyclic_group_structure(6);
451 let grp_h = examples::cyclic_group_structure(6);
452 let f = Isomorphism {
453 left_group: &grp_g,
454 right_group: &grp_h,
455 left_func: vec![0, 2, 4, 0, 2, 4],
456 right_func: vec![0, 5, 4, 3, 2, 1],
457 };
458 if let Ok(()) = f.check_state() {
459 panic!()
460 }
461 }
462 }
463
464 #[test]
465 fn homomorphism_to_isomorphism() {
466 {
467 let grp_g = examples::cyclic_group_structure(7);
469 let grp_h = examples::cyclic_group_structure(7);
470 let f = Homomorphism {
471 domain: &grp_g,
472 range: &grp_h,
473 func: vec![0, 3, 6, 2, 5, 1, 4],
474 };
475 f.check_state().unwrap();
476 if let Some(_f_iso) = f.to_isomorphism() {
477 } else {
478 panic!()
479 }
480 }
481
482 {
483 let grp_g = examples::cyclic_group_structure(3);
485 let grp_h = examples::cyclic_group_structure(6);
486 let f = Homomorphism {
487 domain: &grp_g,
488 range: &grp_h,
489 func: vec![0, 2, 4],
490 };
491 f.check_state().unwrap();
492 if let Some(_f_iso) = f.to_isomorphism() {
493 panic!()
494 }
495 }
496
497 {
498 let grp_g = examples::cyclic_group_structure(6);
500 let grp_h = examples::cyclic_group_structure(6);
501 let f = Homomorphism {
502 domain: &grp_g,
503 range: &grp_h,
504 func: vec![0, 2, 4, 0, 2, 4],
505 };
506 f.check_state().unwrap();
507 if let Some(__iso) = f.to_isomorphism() {
508 panic!()
509 }
510 }
511 }
512
513 #[test]
514 fn test_find_isomorphism() {
515 {
516 let grp_g = examples::symmetric_group_structure(3);
517 let grp_h = examples::dihedral_group_structure(3);
518
519 if let Some(f) = find_isomorphism(&grp_g, &grp_h) {
520 assert!(std::ptr::eq(&grp_g, f.left_group));
521 assert!(std::ptr::eq(&grp_h, f.right_group));
522 } else {
523 panic!()
524 }
525 }
526
527 {
528 let grp_g = examples::symmetric_group_structure(3);
529 let grp_h = examples::cyclic_group_structure(6);
530
531 if let Some(_f) = find_isomorphism(&grp_g, &grp_h) {
532 panic!();
533 }
534 }
535
536 {
537 let grp_g = examples::symmetric_group_structure(5);
538 let mut grp_h = FinitelyGeneratedGroupPresentation::new();
539 let a = grp_h.add_generator();
540 let b = grp_h.add_generator();
541 let c = grp_h.add_generator();
542 grp_h.add_relation(a.pow(2));
543 grp_h.add_relation(b.pow(2));
544 grp_h.add_relation(c.pow(2));
545 grp_h.add_relation((&a * &c).pow(2));
546 grp_h.add_relation((&a * &b).pow(3));
547 grp_h.add_relation((&b * &c).pow(5));
548 let grp_h = grp_h.into_finite_group(); if let Some(_f) = find_isomorphism(&grp_g, &grp_h) {
551 panic!()
552 }
553 }
554 }
555}