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<
10    DomainT: Borrow<FiniteGroupMultiplicationTable> + Clone,
11    RangeT: Borrow<FiniteGroupMultiplicationTable> + Clone,
12> {
13    domain: DomainT,
14    range: RangeT,
15    func: Vec<usize>, //func : domain -> range    y = func[x]
16}
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        //is function
25        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        //is homomorphism
36        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        for x in 0..n {
65            match inv[self.func[x]] {
66                Some(_y) => {
67                    //self.func is not injective
68                    return None;
69                }
70                None => inv[self.func[x]] = Some(x),
71            }
72        }
73
74        let inv = inv
75            .into_iter()
76            .map(|x| match x {
77                Some(x_val) => x_val,
78                None => panic!(),
79            })
80            .collect::<Vec<usize>>();
81
82        Some(Isomorphism {
83            left_group: self.domain,
84            right_group: self.range,
85            left_func: self.func.clone(),
86            right_func: inv,
87        })
88    }
89}
90
91#[derive(Clone)]
92pub struct Isomorphism<
93    LeftGrpT: Borrow<FiniteGroupMultiplicationTable> + Clone,
94    RightGrpT: Borrow<FiniteGroupMultiplicationTable> + Clone,
95> {
96    left_group: LeftGrpT,
97    right_group: RightGrpT,
98    left_func: Vec<usize>,
99    right_func: Vec<usize>,
100}
101
102impl<
103    LeftGrpT: Borrow<FiniteGroupMultiplicationTable> + Clone,
104    RightGrpT: Borrow<FiniteGroupMultiplicationTable> + Clone,
105> Isomorphism<LeftGrpT, RightGrpT>
106{
107    pub fn check_state(&self) -> Result<(), &'static str> {
108        let left_hom = Homomorphism {
109            domain: self.left_group.borrow(),
110            range: self.right_group.borrow(),
111            func: self.left_func.clone(),
112        };
113        match left_hom.check_state() {
114            Ok(()) => {}
115            Err(msg) => {
116                return Err(msg);
117            }
118        }
119
120        let right_hom = Homomorphism {
121            domain: self.right_group.borrow(),
122            range: self.left_group.borrow(),
123            func: self.right_func.clone(),
124        };
125        match right_hom.check_state() {
126            Ok(()) => {}
127            Err(msg) => {
128                return Err(msg);
129            }
130        }
131
132        if self.left_group.borrow().size() != self.right_group.borrow().size() {
133            return Err("isomorphism group sizes dont match");
134        }
135
136        //are mutually inverse
137        //only need to check one of left/right inverse becasue injective/surjective individually imply bijective once the sizes are the same
138        for x in self.left_group.borrow().elems() {
139            if x != self.right_func[self.left_func[x]] {
140                return Err("isomorphism not inv");
141            }
142        }
143
144        Ok(())
145    }
146}
147
148//return an isomorphism from domain to range if one exists
149//return None if no isomorphism exists
150pub fn find_isomorphism<'a, 'b>(
151    domain: &'a FiniteGroupMultiplicationTable,
152    range: &'b FiniteGroupMultiplicationTable,
153) -> Option<Isomorphism<&'a FiniteGroupMultiplicationTable, &'b FiniteGroupMultiplicationTable>> {
154    let group_size = domain.size();
155    if group_size != range.size() {
156        return None;
157    }
158
159    //an element profile can be generated for each element in the range and domain
160    //an isomorphism can only every map elements of the same profile to each other
161    //use this is restrict the number of things to check
162    #[derive(Debug, PartialEq, Eq, Hash)]
163    struct ElementProfile {
164        order: usize,
165        mul_order: BTreeSet<usize>, //helped a lot when comparing non-isomorphic groups of order 144
166    }
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    //want to quickly lookup [element -> profile] on the domain side
182    let domain_elem_profiles: Vec<ElementProfile> = domain
183        .elems()
184        .map(|x| ElementProfile::new(x, domain))
185        .collect();
186
187    // println!("domain_elem_profiles {:?}", domain_elem_profiles);
188
189    //want to quickly lookup [profile -> elements] on the range side
190    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    // println!("range_elem_profiles {:?}", range_elem_profiles);
204
205    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: &Vec<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        //TODO: compute a smaller subset of possible image points
221        //e.g. each gen can only map to something of the same order
222        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(()); //there is no isomorphism
228                }
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    // println!("group size {}", group_size);
257    // println!("generating set {}", current_gen_info.quant_to_check);
258
259    'outer_loop: loop {
260        //loop over all possible images of the domain generators
261        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            //try to find a better generating set every few loops
265            //better meaning there are fewer possible images to check than we currently have left to go
266            if already_checked % 128 == 127 {
267                // println!("maybe better generating set {}", already_checked);
268                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                            // println!("better generating set {} {}", gen_info.quant_to_check, gen_info.gen_list.len());
274                            current_gen_info = gen_info;
275                            continue 'outer_loop;
276                        }
277                    }
278                    Err(_) => {
279                        return None;
280                    }
281                }
282            }
283
284            //compute homomorphism sending domain_gens -> image_gens
285            match 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                Some(f) => match f.to_isomorphism() {
298                    Some(f_iso) => {
299                        return Some(f_iso);
300                    }
301                    None => {}
302                },
303                None => {}
304            }
305            already_checked += 1;
306
307            //compute next gen_img
308            'for_label: {
309                for i in 0..current_gen_info.gen_list.len() {
310                    image_option_counter[i] += 1;
311                    if image_option_counter[i] == current_gen_info.image_options[i].len() {
312                        image_option_counter[i] = 0;
313                    } else {
314                        break 'for_label;
315                    }
316                }
317                break 'loop_label;
318            }
319        }
320        return None;
321    }
322}
323
324#[cfg(test)]
325mod homomorphism_tests {
326    use crate::free_group::todd_coxeter::FinitelyGeneratedGroupPresentation;
327
328    use super::*;
329
330    #[test]
331    fn homomorphism_state() {
332        {
333            //identity map
334            let grp_g = examples::cyclic_group_structure(6);
335            let grp_h = examples::cyclic_group_structure(6);
336            let f = Homomorphism {
337                domain: &grp_g,
338                range: &grp_h,
339                func: vec![0, 1, 2, 3, 4, 5],
340            };
341            match f.check_state() {
342                Ok(()) => {}
343                Err(_) => panic!(),
344            }
345        }
346
347        {
348            //injective map
349            let grp_g = examples::cyclic_group_structure(3);
350            let grp_h = examples::cyclic_group_structure(6);
351            let f = Homomorphism {
352                domain: &grp_g,
353                range: &grp_h,
354                func: vec![0, 2, 4],
355            };
356            match f.check_state() {
357                Ok(()) => {}
358                Err(_) => panic!(),
359            }
360        }
361
362        {
363            //surjective map
364            let grp_g = examples::cyclic_group_structure(6);
365            let grp_h = examples::cyclic_group_structure(3);
366            let f = Homomorphism {
367                domain: &grp_g,
368                range: &grp_h,
369                func: vec![0, 1, 2, 0, 1, 2],
370            };
371            match f.check_state() {
372                Ok(()) => {}
373                Err(_) => panic!(),
374            }
375        }
376
377        {
378            //bad func
379            let grp_g = examples::cyclic_group_structure(3);
380            let grp_h = examples::cyclic_group_structure(6);
381            let f = Homomorphism {
382                domain: &grp_g,
383                range: &grp_h,
384                func: vec![0, 1, 2, 3],
385            };
386            match f.check_state() {
387                Ok(()) => panic!(),
388                Err(_) => {}
389            }
390        }
391
392        {
393            //bad func
394            let grp_g = examples::cyclic_group_structure(6);
395            let grp_h = examples::cyclic_group_structure(3);
396            let f = Homomorphism {
397                domain: &grp_g,
398                range: &grp_h,
399                func: vec![0, 1, 2, 3, 4, 5, 6],
400            };
401            match f.check_state() {
402                Ok(()) => panic!(),
403                Err(_) => {}
404            }
405        }
406
407        {
408            //bad hom
409            let grp_g = examples::cyclic_group_structure(3);
410            let grp_h = examples::cyclic_group_structure(6);
411            let f = Homomorphism {
412                domain: &grp_g,
413                range: &grp_h,
414                func: vec![0, 1, 2],
415            };
416            match f.check_state() {
417                Ok(()) => panic!(),
418                Err(_) => {}
419            }
420        }
421    }
422
423    #[test]
424    fn isomorphism_state() {
425        {
426            //happy
427            let grp_g = examples::cyclic_group_structure(6);
428            let grp_h = examples::cyclic_group_structure(6);
429            let f = Isomorphism {
430                left_group: &grp_g,
431                right_group: &grp_h,
432                left_func: vec![0, 5, 4, 3, 2, 1],
433                right_func: vec![0, 5, 4, 3, 2, 1],
434            };
435            match f.check_state() {
436                Ok(()) => {}
437                Err(_) => panic!(),
438            }
439        }
440
441        {
442            //size missmatch
443            let grp_g = examples::cyclic_group_structure(3);
444            let grp_h = examples::cyclic_group_structure(6);
445            let f = Isomorphism {
446                left_group: &grp_g,
447                right_group: &grp_h,
448                left_func: vec![0, 2, 4],
449                right_func: vec![0, 1, 2, 0, 1, 2],
450            };
451            match f.check_state() {
452                Ok(()) => panic!(),
453                Err(_) => {}
454            }
455        }
456
457        {
458            //not inv
459            let grp_g = examples::cyclic_group_structure(6);
460            let grp_h = examples::cyclic_group_structure(6);
461            let f = Isomorphism {
462                left_group: &grp_g,
463                right_group: &grp_h,
464                left_func: vec![0, 2, 4, 0, 2, 4],
465                right_func: vec![0, 5, 4, 3, 2, 1],
466            };
467            match f.check_state() {
468                Ok(()) => panic!(),
469                Err(_) => {}
470            }
471        }
472    }
473
474    #[test]
475    fn homomorphism_to_isomorphism() {
476        {
477            //happy
478            let grp_g = examples::cyclic_group_structure(7);
479            let grp_h = examples::cyclic_group_structure(7);
480            let f = Homomorphism {
481                domain: &grp_g,
482                range: &grp_h,
483                func: vec![0, 3, 6, 2, 5, 1, 4],
484            };
485            f.check_state().unwrap();
486            match f.to_isomorphism() {
487                Some(_f_iso) => {}
488                None => panic!(),
489            }
490        }
491
492        {
493            //size missmatch
494            let grp_g = examples::cyclic_group_structure(3);
495            let grp_h = examples::cyclic_group_structure(6);
496            let f = Homomorphism {
497                domain: &grp_g,
498                range: &grp_h,
499                func: vec![0, 2, 4],
500            };
501            f.check_state().unwrap();
502            match f.to_isomorphism() {
503                Some(_f_iso) => panic!(),
504                None => {}
505            }
506        }
507
508        {
509            //not surjective
510            let grp_g = examples::cyclic_group_structure(6);
511            let grp_h = examples::cyclic_group_structure(6);
512            let f = Homomorphism {
513                domain: &grp_g,
514                range: &grp_h,
515                func: vec![0, 2, 4, 0, 2, 4],
516            };
517            f.check_state().unwrap();
518            match f.to_isomorphism() {
519                Some(__iso) => panic!(),
520                None => {}
521            }
522        }
523    }
524
525    #[test]
526    fn test_find_isomorphism() {
527        {
528            let grp_g = examples::symmetric_group_structure(3);
529            let grp_h = examples::dihedral_group_structure(3);
530
531            match find_isomorphism(&grp_g, &grp_h) {
532                Some(f) => {
533                    assert!(std::ptr::eq(&grp_g, f.left_group));
534                    assert!(std::ptr::eq(&grp_h, f.right_group));
535                }
536                None => panic!(),
537            }
538        }
539
540        {
541            let grp_g = examples::symmetric_group_structure(3);
542            let grp_h = examples::cyclic_group_structure(6);
543
544            match find_isomorphism(&grp_g, &grp_h) {
545                Some(_f) => panic!(),
546                None => {}
547            }
548        }
549
550        {
551            let grp_g = examples::symmetric_group_structure(5);
552            let mut grp_h = FinitelyGeneratedGroupPresentation::new();
553            let a = grp_h.add_generator();
554            let b = grp_h.add_generator();
555            let c = grp_h.add_generator();
556            grp_h.add_relation(a.pow(2));
557            grp_h.add_relation(b.pow(2));
558            grp_h.add_relation(c.pow(2));
559            grp_h.add_relation((&a * &c).pow(2));
560            grp_h.add_relation((&a * &b).pow(3));
561            grp_h.add_relation((&b * &c).pow(5));
562            let grp_h = grp_h.into_finite_group(); //A5 x C2
563
564            match find_isomorphism(&grp_g, &grp_h) {
565                Some(_f) => panic!(),
566                None => {}
567            }
568        }
569    }
570}