algebraeon_groups/composition_table/
generating_set.rs

1use std::collections::{BTreeSet, HashSet};
2
3use super::group::*;
4use super::homomorphism::*;
5
6pub struct GeneratingSet<'a> {
7    group: &'a Group,
8    gens: Vec<usize>,       //subset of group which generates it
9    elems: Vec<Vec<usize>>, //each element of group written as a product of gens
10}
11
12impl<'a> GeneratingSet<'a> {
13    pub fn check_state(&self) -> Result<(), &'static str> {
14        for g in &self.gens {
15            if !(*g < self.group.size()) {
16                return Err("bad generator");
17            }
18        }
19
20        if self.elems.len() != self.group.size() {
21            return Err("bad elems len");
22        }
23
24        for x in self.group.elems() {
25            if x != self.group.mul_many(
26                &self.elems[x]
27                    .iter()
28                    .map(|gen_idx| self.gens[*gen_idx])
29                    .collect(),
30            ) {
31                return Err("incorrect word of gens for elem");
32            }
33        }
34
35        Ok(())
36    }
37
38    pub fn size(&self) -> usize {
39        self.gens.len()
40    }
41
42    pub fn gens(&self) -> &Vec<usize> {
43        &self.gens
44    }
45
46    pub fn generated_homomorphism<'b>(
47        &self,
48        partial_func: &Vec<usize>,
49        range_group: &'b Group,
50    ) -> Result<Option<Homomorphism<&'a Group, &'b Group>>, &'static str> {
51        if partial_func.len() != self.gens.len() {
52            return Err("partial func entries should corespond to images for each generator");
53        }
54        for x in partial_func {
55            if !(*x < range_group.size()) {
56                return Err("partial func has invalid element from range group");
57            }
58        }
59
60        //try to make a function from self.group -> range sending self.gens -> the images as in partial_func
61        let func: Vec<usize> = self
62            .group
63            .elems()
64            .map(|x| {
65                range_group.mul_many(&self.elems[x].iter().map(|g| partial_func[*g]).collect())
66            })
67            .collect();
68
69        //check if func is a homomorphism i.e. f(xy) = f(x)f(y) for all x, y in range group
70        //only need to check for all x and for all generators y
71        for x in self.group.elems() {
72            for g in &self.gens {
73                if func[self.group.mul(x, *g)] != range_group.mul(func[x], func[*g]) {
74                    return Ok(None);
75                }
76            }
77        }
78
79        Ok(Some(Homomorphism::new_unchecked(
80            self.group,
81            range_group,
82            func,
83        )))
84    }
85}
86
87impl Group {
88    fn try_find_generating_set(&self, max_size: Option<usize>) -> Result<GeneratingSet, ()> {
89        let mut missing = HashSet::new();
90        for x in self.elems() {
91            missing.insert(x);
92        }
93        missing.remove(&self.ident());
94
95        let mut sg = BTreeSet::new();
96        sg.insert(self.ident());
97        let mut gen_words = vec![vec![]; self.size()];
98        gen_words[self.ident()] = vec![];
99        let mut gens = vec![];
100        while sg.len() < self.size() {
101            //if we are going to need more gens than max_size allows, then give up
102            match max_size {
103                Some(max_size_val) => {
104                    if gens.len() == max_size_val {
105                        return Err(());
106                    }
107                }
108                None => {}
109            }
110
111            //add a new generator
112            let new_g = *missing.iter().next().unwrap(); //random choice of new generator to try
113            gens.push(new_g);
114
115            //compute the subgroup generated by gens
116            let mut y;
117            let mut boundary: Vec<usize> = vec![];
118            for s in sg.clone() {
119                y = self.mul(s, new_g);
120                if !sg.contains(&y) {
121                    sg.insert(y);
122                    missing.remove(&y);
123                    gen_words[y] = gen_words[s].clone();
124                    gen_words[y].push(gens.len() - 1);
125                    boundary.push(y);
126                }
127            }
128            let mut next_boundary: Vec<usize> = vec![];
129            while boundary.len() > 0 {
130                for x in &boundary {
131                    for (g_idx, g) in gens.iter().enumerate() {
132                        y = self.mul(*x, *g);
133                        if !sg.contains(&y) {
134                            sg.insert(y);
135                            missing.remove(&y);
136                            gen_words[y] = gen_words[*x].clone();
137                            gen_words[y].push(g_idx);
138                            next_boundary.push(y);
139                        }
140                    }
141                }
142                boundary = next_boundary.clone();
143                next_boundary = vec![];
144            }
145        }
146
147        Ok(GeneratingSet {
148            group: &self,
149            gens: gens,
150            elems: gen_words,
151        })
152    }
153
154    pub fn generating_set(&self) -> GeneratingSet {
155        self.try_find_generating_set(None).unwrap()
156    }
157
158    pub fn small_generating_set(&self, attempts: Option<usize>) -> GeneratingSet {
159        let a = attempts.unwrap_or(12);
160        assert!(a > 0);
161
162        let mut smallest_gens = self.generating_set();
163        for _i in 0..a - 1 {
164            match self.try_find_generating_set(Some(smallest_gens.size() - 1)) {
165                Ok(gens) => {
166                    smallest_gens = gens;
167                }
168                Err(_) => {}
169            }
170        }
171
172        return smallest_gens;
173    }
174}
175
176#[cfg(test)]
177mod generating_set_tests {
178    use super::*;
179
180    #[test]
181    fn test_generating_set() {
182        let grp = examples::cyclic_group_structure(10);
183        let g_set = grp.generating_set();
184        g_set.check_state().unwrap();
185
186        let grp = examples::dihedral_group_structure(12);
187        let g_set = grp.generating_set();
188        g_set.check_state().unwrap();
189
190        let grp = examples::symmetric_group_structure(4);
191        let g_set = grp.generating_set();
192        g_set.check_state().unwrap();
193
194        let grp = examples::symmetric_group_structure(5);
195        let g_set = grp.generating_set();
196        g_set.check_state().unwrap();
197    }
198}