use super::{Clause, CnfFormula, Literal, VarId, union_find};
impl CnfFormula {
pub fn detect_components(&self) -> Option<Vec<Vec<usize>>> {
detect_components_in(&self.clauses, self.num_vars)
}
pub fn extract_component(&self, clause_indices: &[usize]) -> (CnfFormula, Vec<VarId>) {
let mut var_set = std::collections::BTreeSet::new();
for &ci in clause_indices {
for lit in &self.clauses[ci].literals {
var_set.insert(lit.var);
}
}
let local_to_global: Vec<VarId> = var_set.iter().copied().collect();
let global_to_local: std::collections::HashMap<VarId, u32> = local_to_global
.iter()
.enumerate()
.map(|(i, &v)| (v, i as u32))
.collect();
let clauses = clause_indices
.iter()
.map(|&ci| {
let lits = self.clauses[ci]
.literals
.iter()
.map(|lit| Literal::new(VarId(global_to_local[&lit.var]), lit.positive))
.collect();
Clause::new(lits)
})
.collect();
let sub = CnfFormula {
num_vars: local_to_global.len() as u32,
clauses,
};
(sub, local_to_global)
}
}
pub fn detect_components_in(clauses: &[Clause], num_vars: u32) -> Option<Vec<Vec<usize>>> {
if clauses.len() <= 1 {
return None;
}
let mut uf = union_find::UnionFind::new(num_vars as usize + 1);
for clause in clauses {
if let [first, rest @ ..] = clause.literals.as_slice() {
for lit in rest {
uf.union(first.var.0 as usize, lit.var.0 as usize);
}
}
}
let mut by_rep: std::collections::HashMap<usize, Vec<usize>> = std::collections::HashMap::new();
for (i, clause) in clauses.iter().enumerate() {
let rep = clause
.literals
.first()
.map_or(0, |lit| uf.find(lit.var.0 as usize));
by_rep.entry(rep).or_default().push(i);
}
if by_rep.len() <= 1 {
return None;
}
let mut components: Vec<Vec<usize>> = by_rep.into_values().collect();
components.sort_by_cached_key(|c| {
let min_var = c
.iter()
.flat_map(|&ci| clauses[ci].literals.iter().map(|l| l.var.0))
.min()
.unwrap_or(u32::MAX);
(c.len(), min_var)
});
Some(components)
}