algebraeon_groups/composition_table/
homomorphism.rs

1use std::borrow::Borrow;
2use std::collections::BTreeSet;
3use std::collections::HashMap;
4
5use super::generating_set::*;
6use super::group::*;
7
8#[derive(Clone)]
9pub struct Homomorphism<DomainT: Borrow<Group> + Clone, RangeT: Borrow<Group> + Clone> {
10    domain: DomainT,
11    range: RangeT,
12    func: Vec<usize>, //func : domain -> range    y = funx[x]
13}
14
15impl<DomainT: Borrow<Group> + Clone, RangeT: Borrow<Group> + Clone> Homomorphism<DomainT, RangeT> {
16    pub fn check_state(&self) -> Result<(), &'static str> {
17        //is function
18        if self.func.len() != self.domain.borrow().size() {
19            return Err("func size does not match domain size");
20        }
21
22        for x in self.domain.borrow().elems() {
23            if !(self.func[x] < self.range.borrow().size()) {
24                return Err("func image is too big for an element of the range");
25            }
26        }
27
28        //is homomorphism
29        for x in self.domain.borrow().elems() {
30            for y in self.domain.borrow().elems() {
31                if self.func[self.domain.borrow().mul(x, y)]
32                    != self.range.borrow().mul(self.func[x], self.func[y])
33                {
34                    return Err("homomorphism does not respect composition");
35                }
36            }
37        }
38
39        Ok(())
40    }
41
42    pub fn new_unchecked(domain: DomainT, range: RangeT, func: Vec<usize>) -> Self {
43        Self {
44            domain,
45            range,
46            func,
47        }
48    }
49
50    pub fn to_isomorphism(self) -> Option<Isomorphism<DomainT, RangeT>> {
51        let n = self.domain.borrow().size();
52        if n != self.range.borrow().size() {
53            return None;
54        }
55
56        let mut inv = vec![None; n];
57        for x in 0..n {
58            match inv[self.func[x]] {
59                Some(_y) => {
60                    //self.func is not injective
61                    return None;
62                }
63                None => inv[self.func[x]] = Some(x),
64            }
65        }
66
67        let inv = inv
68            .into_iter()
69            .map(|x| match x {
70                Some(x_val) => x_val,
71                None => panic!(),
72            })
73            .collect::<Vec<usize>>();
74
75        Some(Isomorphism {
76            left_group: self.domain,
77            right_group: self.range,
78            left_func: self.func.clone(),
79            right_func: inv,
80        })
81    }
82}
83
84#[derive(Clone)]
85pub struct Isomorphism<LeftGrpT: Borrow<Group> + Clone, RightGrpT: Borrow<Group> + Clone> {
86    left_group: LeftGrpT,
87    right_group: RightGrpT,
88    left_func: Vec<usize>,
89    right_func: Vec<usize>,
90}
91
92impl<LeftGrpT: Borrow<Group> + Clone, RightGrpT: Borrow<Group> + Clone>
93    Isomorphism<LeftGrpT, RightGrpT>
94{
95    pub fn check_state(&self) -> Result<(), &'static str> {
96        let left_hom = Homomorphism {
97            domain: self.left_group.borrow(),
98            range: self.right_group.borrow(),
99            func: self.left_func.clone(),
100        };
101        match left_hom.check_state() {
102            Ok(()) => {}
103            Err(msg) => {
104                return Err(msg);
105            }
106        }
107
108        let right_hom = Homomorphism {
109            domain: self.right_group.borrow(),
110            range: self.left_group.borrow(),
111            func: self.right_func.clone(),
112        };
113        match right_hom.check_state() {
114            Ok(()) => {}
115            Err(msg) => {
116                return Err(msg);
117            }
118        }
119
120        if self.left_group.borrow().size() != self.right_group.borrow().size() {
121            return Err("isomorphism group sizes dont match");
122        }
123
124        //are mutually inverse
125        //only need to check one of left/right inverse becasue injective/surjective individually imply bijective once the sizes are the same
126        for x in self.left_group.borrow().elems() {
127            if x != self.right_func[self.left_func[x]] {
128                return Err("isomorphism not inv");
129            }
130        }
131
132        Ok(())
133    }
134}
135
136//return an isomorphism from domain to range if one exists
137//return None if no isomorphism exists
138pub fn find_isomorphism<'a, 'b>(
139    domain: &'a Group,
140    range: &'b Group,
141) -> Option<Isomorphism<&'a Group, &'b Group>> {
142    let group_size = domain.size();
143    if group_size != range.size() {
144        return None;
145    }
146
147    //an element profile can be generated for each element in the range and domain
148    //an isomorphism can only every map elements of the same profile to each other
149    //use this is restrict the number of things to check
150    #[derive(Debug, PartialEq, Eq, Hash)]
151    struct ElementProfile {
152        order: usize,
153        mul_order: BTreeSet<usize>, //helped a lot when comparing non-isomorphic groups of order 144
154    }
155
156    impl ElementProfile {
157        fn new(x: usize, group: &Group) -> Self {
158            debug_assert!(x < group.size());
159            ElementProfile {
160                order: group.order(x).unwrap(),
161                mul_order: group
162                    .elems()
163                    .map(|y| group.order(group.mul(x, y)).unwrap())
164                    .collect(),
165            }
166        }
167    }
168
169    //want to quickly lookup [element -> profile] on the domain side
170    let domain_elem_profiles: Vec<ElementProfile> = domain
171        .elems()
172        .map(|x| ElementProfile::new(x, domain))
173        .collect();
174
175    // println!("domain_elem_profiles {:?}", domain_elem_profiles);
176
177    //want to quickly lookup [profile -> elements] on the range side
178    let mut range_elem_profiles: HashMap<ElementProfile, Vec<usize>> = HashMap::new();
179    for x in range.elems() {
180        let p = ElementProfile::new(x, range);
181        match range_elem_profiles.get_mut(&p) {
182            Some(elems) => {
183                elems.push(x);
184            }
185            None => {
186                range_elem_profiles.insert(p, vec![x]);
187            }
188        };
189    }
190
191    // println!("range_elem_profiles {:?}", range_elem_profiles);
192
193    struct GenInfo<'c> {
194        gen_list: Vec<usize>,
195        gen_set: GeneratingSet<'c>,
196        image_options: Vec<Vec<usize>>,
197        quant_to_check: usize,
198    }
199
200    fn find_new_gen_info<'c>(
201        domain: &'c Group,
202        domain_elem_profiles: &Vec<ElementProfile>,
203        range_elem_profiles: &HashMap<ElementProfile, Vec<usize>>,
204    ) -> Result<GenInfo<'c>, ()> {
205        let domain_gen_set = domain.generating_set();
206        let domain_gens = domain_gen_set.gens();
207
208        //TODO: compute a smaller subset of possible image points
209        //e.g. each gen can only map to something of the same order
210        let mut gen_image_options: Vec<Vec<usize>> = vec![];
211        for gen in domain_gens {
212            match range_elem_profiles.get(&domain_elem_profiles[*gen]) {
213                Some(r_elems) => gen_image_options.push(r_elems.clone()),
214                None => {
215                    return Err(()); //there is no isomorphism
216                }
217            }
218        }
219
220        let mut num_to_check: usize = 1;
221        for gen_image_option in &gen_image_options {
222            num_to_check = match num_to_check.checked_mul(gen_image_option.len()) {
223                Some(prod) => prod,
224                None => usize::MAX,
225            };
226        }
227
228        Ok(GenInfo {
229            gen_list: domain_gens.clone(),
230            gen_set: domain_gen_set,
231            image_options: gen_image_options,
232            quant_to_check: num_to_check,
233        })
234    }
235
236    let mut current_gen_info;
237    match find_new_gen_info(domain, &domain_elem_profiles, &range_elem_profiles) {
238        Ok(gen_info) => current_gen_info = gen_info,
239        Err(_) => {
240            return None;
241        }
242    }
243
244    // println!("group size {}", group_size);
245    // println!("generating set {}", current_gen_info.quant_to_check);
246
247    'outer_loop: loop {
248        //loop over all possible images of the domain generators
249        let mut image_option_counter = vec![0; current_gen_info.gen_list.len()];
250        let mut already_checked: usize = 0;
251        'loop_label: loop {
252            //try to find a better generating set every few loops
253            //better meaning there are fewer possible images to check than we currently have left to go
254            if already_checked % 128 == 127 {
255                // println!("maybe better generating set {}", already_checked);
256                match find_new_gen_info(domain, &domain_elem_profiles, &range_elem_profiles) {
257                    Ok(gen_info) => {
258                        if gen_info.quant_to_check
259                            < current_gen_info.quant_to_check - already_checked
260                        {
261                            // println!("better generating set {} {}", gen_info.quant_to_check, gen_info.gen_list.len());
262                            current_gen_info = gen_info;
263                            continue 'outer_loop;
264                        }
265                    }
266                    Err(_) => {
267                        return None;
268                    }
269                }
270            }
271
272            //compute homomorphism sending domain_gens -> image_gens
273            match current_gen_info
274                .gen_set
275                .generated_homomorphism(
276                    &image_option_counter
277                        .iter()
278                        .enumerate()
279                        .map(|(i, j)| current_gen_info.image_options[i][*j])
280                        .collect(),
281                    range,
282                )
283                .unwrap()
284            {
285                Some(f) => match f.to_isomorphism() {
286                    Some(f_iso) => {
287                        return Some(f_iso);
288                    }
289                    None => {}
290                },
291                None => {}
292            }
293            already_checked += 1;
294
295            //compute next gen_img
296            'for_label: {
297                for i in 0..current_gen_info.gen_list.len() {
298                    image_option_counter[i] += 1;
299                    if image_option_counter[i] == current_gen_info.image_options[i].len() {
300                        image_option_counter[i] = 0;
301                    } else {
302                        break 'for_label;
303                    }
304                }
305                break 'loop_label;
306            }
307        }
308        return None;
309    }
310}
311
312#[cfg(test)]
313mod homomorphism_tests {
314    use crate::free_group::todd_coxeter::FinitelyGeneratedGroupPresentation;
315
316    use super::*;
317
318    #[test]
319    fn homomorphism_state() {
320        {
321            //identity map
322            let grp_g = examples::cyclic_group_structure(6);
323            let grp_h = examples::cyclic_group_structure(6);
324            let f = Homomorphism {
325                domain: &grp_g,
326                range: &grp_h,
327                func: vec![0, 1, 2, 3, 4, 5],
328            };
329            match f.check_state() {
330                Ok(()) => {}
331                Err(_) => panic!(),
332            }
333        }
334
335        {
336            //injective map
337            let grp_g = examples::cyclic_group_structure(3);
338            let grp_h = examples::cyclic_group_structure(6);
339            let f = Homomorphism {
340                domain: &grp_g,
341                range: &grp_h,
342                func: vec![0, 2, 4],
343            };
344            match f.check_state() {
345                Ok(()) => {}
346                Err(_) => panic!(),
347            }
348        }
349
350        {
351            //surjective map
352            let grp_g = examples::cyclic_group_structure(6);
353            let grp_h = examples::cyclic_group_structure(3);
354            let f = Homomorphism {
355                domain: &grp_g,
356                range: &grp_h,
357                func: vec![0, 1, 2, 0, 1, 2],
358            };
359            match f.check_state() {
360                Ok(()) => {}
361                Err(_) => panic!(),
362            }
363        }
364
365        {
366            //bad func
367            let grp_g = examples::cyclic_group_structure(3);
368            let grp_h = examples::cyclic_group_structure(6);
369            let f = Homomorphism {
370                domain: &grp_g,
371                range: &grp_h,
372                func: vec![0, 1, 2, 3],
373            };
374            match f.check_state() {
375                Ok(()) => panic!(),
376                Err(_) => {}
377            }
378        }
379
380        {
381            //bad func
382            let grp_g = examples::cyclic_group_structure(6);
383            let grp_h = examples::cyclic_group_structure(3);
384            let f = Homomorphism {
385                domain: &grp_g,
386                range: &grp_h,
387                func: vec![0, 1, 2, 3, 4, 5, 6],
388            };
389            match f.check_state() {
390                Ok(()) => panic!(),
391                Err(_) => {}
392            }
393        }
394
395        {
396            //bad hom
397            let grp_g = examples::cyclic_group_structure(3);
398            let grp_h = examples::cyclic_group_structure(6);
399            let f = Homomorphism {
400                domain: &grp_g,
401                range: &grp_h,
402                func: vec![0, 1, 2],
403            };
404            match f.check_state() {
405                Ok(()) => panic!(),
406                Err(_) => {}
407            }
408        }
409    }
410
411    #[test]
412    fn isomorphism_state() {
413        {
414            //happy
415            let grp_g = examples::cyclic_group_structure(6);
416            let grp_h = examples::cyclic_group_structure(6);
417            let f = Isomorphism {
418                left_group: &grp_g,
419                right_group: &grp_h,
420                left_func: vec![0, 5, 4, 3, 2, 1],
421                right_func: vec![0, 5, 4, 3, 2, 1],
422            };
423            match f.check_state() {
424                Ok(()) => {}
425                Err(_) => panic!(),
426            }
427        }
428
429        {
430            //size missmatch
431            let grp_g = examples::cyclic_group_structure(3);
432            let grp_h = examples::cyclic_group_structure(6);
433            let f = Isomorphism {
434                left_group: &grp_g,
435                right_group: &grp_h,
436                left_func: vec![0, 2, 4],
437                right_func: vec![0, 1, 2, 0, 1, 2],
438            };
439            match f.check_state() {
440                Ok(()) => panic!(),
441                Err(_) => {}
442            }
443        }
444
445        {
446            //not inv
447            let grp_g = examples::cyclic_group_structure(6);
448            let grp_h = examples::cyclic_group_structure(6);
449            let f = Isomorphism {
450                left_group: &grp_g,
451                right_group: &grp_h,
452                left_func: vec![0, 2, 4, 0, 2, 4],
453                right_func: vec![0, 5, 4, 3, 2, 1],
454            };
455            match f.check_state() {
456                Ok(()) => panic!(),
457                Err(_) => {}
458            }
459        }
460    }
461
462    #[test]
463    fn homomorphism_to_isomorphism() {
464        {
465            //happy
466            let grp_g = examples::cyclic_group_structure(7);
467            let grp_h = examples::cyclic_group_structure(7);
468            let f = Homomorphism {
469                domain: &grp_g,
470                range: &grp_h,
471                func: vec![0, 3, 6, 2, 5, 1, 4],
472            };
473            f.check_state().unwrap();
474            match f.to_isomorphism() {
475                Some(_f_iso) => {}
476                None => panic!(),
477            }
478        }
479
480        {
481            //size missmatch
482            let grp_g = examples::cyclic_group_structure(3);
483            let grp_h = examples::cyclic_group_structure(6);
484            let f = Homomorphism {
485                domain: &grp_g,
486                range: &grp_h,
487                func: vec![0, 2, 4],
488            };
489            f.check_state().unwrap();
490            match f.to_isomorphism() {
491                Some(_f_iso) => panic!(),
492                None => {}
493            }
494        }
495
496        {
497            //not surjective
498            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            match f.to_isomorphism() {
507                Some(__iso) => panic!(),
508                None => {}
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            match find_isomorphism(&grp_g, &grp_h) {
520                Some(f) => {
521                    assert!(std::ptr::eq(&grp_g, f.left_group));
522                    assert!(std::ptr::eq(&grp_h, f.right_group));
523                }
524                None => panic!(),
525            }
526        }
527
528        {
529            let grp_g = examples::symmetric_group_structure(3);
530            let grp_h = examples::cyclic_group_structure(6);
531
532            match find_isomorphism(&grp_g, &grp_h) {
533                Some(_f) => panic!(),
534                None => {}
535            }
536        }
537
538        {
539            let grp_g = examples::symmetric_group_structure(5);
540            let mut grp_h = FinitelyGeneratedGroupPresentation::new();
541            let a = grp_h.add_generator();
542            let b = grp_h.add_generator();
543            let c = grp_h.add_generator();
544            grp_h.add_relation(a.pow(2));
545            grp_h.add_relation(b.pow(2));
546            grp_h.add_relation(c.pow(2));
547            grp_h.add_relation((&a * &c).pow(2));
548            grp_h.add_relation((&a * &b).pow(3));
549            grp_h.add_relation((&b * &c).pow(5));
550            let grp_h = grp_h.into_finite_group(); //A5 x C2
551
552            match find_isomorphism(&grp_g, &grp_h) {
553                Some(_f) => panic!(),
554                None => {}
555            }
556        }
557    }
558}