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