algebraeon_groups/composition_table/
generating_set.rsuse std::collections::{BTreeSet, HashSet};
use super::group::*;
use super::homomorphism::*;
pub struct GeneratingSet<'a> {
group: &'a Group,
gens: Vec<usize>, elems: Vec<Vec<usize>>, }
impl<'a> GeneratingSet<'a> {
pub fn check_state(&self) -> Result<(), &'static str> {
for g in &self.gens {
if !(*g < self.group.size()) {
return Err("bad generator");
}
}
if self.elems.len() != self.group.size() {
return Err("bad elems len");
}
for x in self.group.elems() {
if x != self.group.mul_many(
&self.elems[x]
.iter()
.map(|gen_idx| self.gens[*gen_idx])
.collect(),
) {
return Err("incorrect word of gens for elem");
}
}
Ok(())
}
pub fn size(&self) -> usize {
self.gens.len()
}
pub fn gens(&self) -> &Vec<usize> {
&self.gens
}
pub fn generated_homomorphism<'b>(
&self,
partial_func: &Vec<usize>,
range_group: &'b Group,
) -> Result<Option<Homomorphism<&'a Group, &'b Group>>, &'static str> {
if partial_func.len() != self.gens.len() {
return Err("partial func entries should corespond to images for each generator");
}
for x in partial_func {
if !(*x < range_group.size()) {
return Err("partial func has invalid element from range group");
}
}
let func: Vec<usize> = self
.group
.elems()
.map(|x| {
range_group.mul_many(&self.elems[x].iter().map(|g| partial_func[*g]).collect())
})
.collect();
for x in self.group.elems() {
for g in &self.gens {
if func[self.group.mul(x, *g)] != range_group.mul(func[x], func[*g]) {
return Ok(None);
}
}
}
Ok(Some(Homomorphism::new_unchecked(
self.group,
range_group,
func,
)))
}
}
impl Group {
fn try_find_generating_set(&self, max_size: Option<usize>) -> Result<GeneratingSet, ()> {
let mut missing = HashSet::new();
for x in self.elems() {
missing.insert(x);
}
missing.remove(&self.ident());
let mut sg = BTreeSet::new();
sg.insert(self.ident());
let mut gen_words = vec![vec![]; self.size()];
gen_words[self.ident()] = vec![];
let mut gens = vec![];
while sg.len() < self.size() {
match max_size {
Some(max_size_val) => {
if gens.len() == max_size_val {
return Err(());
}
}
None => {}
}
let new_g = *missing.iter().next().unwrap(); gens.push(new_g);
let mut y;
let mut boundary: Vec<usize> = vec![];
for s in sg.clone() {
y = self.mul(s, new_g);
if !sg.contains(&y) {
sg.insert(y);
missing.remove(&y);
gen_words[y] = gen_words[s].clone();
gen_words[y].push(gens.len() - 1);
boundary.push(y);
}
}
let mut next_boundary: Vec<usize> = vec![];
while boundary.len() > 0 {
for x in &boundary {
for (g_idx, g) in gens.iter().enumerate() {
y = self.mul(*x, *g);
if !sg.contains(&y) {
sg.insert(y);
missing.remove(&y);
gen_words[y] = gen_words[*x].clone();
gen_words[y].push(g_idx);
next_boundary.push(y);
}
}
}
boundary = next_boundary.clone();
next_boundary = vec![];
}
}
Ok(GeneratingSet {
group: &self,
gens: gens,
elems: gen_words,
})
}
pub fn generating_set(&self) -> GeneratingSet {
self.try_find_generating_set(None).unwrap()
}
pub fn small_generating_set(&self, attempts: Option<usize>) -> GeneratingSet {
let a = attempts.unwrap_or(12);
assert!(a > 0);
let mut smallest_gens = self.generating_set();
for _i in 0..a - 1 {
match self.try_find_generating_set(Some(smallest_gens.size() - 1)) {
Ok(gens) => {
smallest_gens = gens;
}
Err(_) => {}
}
}
return smallest_gens;
}
}
#[cfg(test)]
mod generating_set_tests {
use super::*;
#[test]
fn test_generating_set() {
let grp = examples::cyclic_group_structure(10);
let g_set = grp.generating_set();
g_set.check_state().unwrap();
let grp = examples::dihedral_group_structure(12);
let g_set = grp.generating_set();
g_set.check_state().unwrap();
let grp = examples::symmetric_group_structure(4);
let g_set = grp.generating_set();
g_set.check_state().unwrap();
let grp = examples::symmetric_group_structure(5);
let g_set = grp.generating_set();
g_set.check_state().unwrap();
}
}