use super::execution::ElimSteps;
use super::graph::EliminationGraph;
#[derive(Clone)]
pub(crate) struct Reduced {
pub graph: EliminationGraph,
pub prefix: ElimSteps,
}
pub(crate) fn preprocess(mut graph: EliminationGraph) -> Reduced {
let mut prefix = ElimSteps::default();
let mut treewidth_lower_bound = 0usize;
loop {
let mut fired = peel_low_degree(&mut graph, &mut prefix);
fired |= eliminate_series_vertices(&mut graph, &mut prefix, &mut treewidth_lower_bound);
fired |= eliminate_simplicial_vertices(&mut graph, &mut prefix, &mut treewidth_lower_bound);
fired |=
eliminate_almost_simplicial_vertices(&mut graph, &mut prefix, treewidth_lower_bound);
if !fired {
break;
}
}
Reduced { graph, prefix }
}
fn eliminate_and_record(
graph: &mut EliminationGraph,
prefix: &mut ElimSteps,
vertex: u32,
) -> usize {
let neighbours = graph.live_neighbours(vertex);
let degree = neighbours.len();
let mut bag = Vec::with_capacity(degree + 1);
bag.push(vertex);
bag.extend(neighbours);
graph.eliminate(vertex);
prefix.sink().record(vertex, bag);
degree
}
fn eliminate_series_vertices(
graph: &mut EliminationGraph,
prefix: &mut ElimSteps,
treewidth_lower_bound: &mut usize,
) -> bool {
let mut fired = false;
for vertex in 0..graph.len() as u32 {
if !graph.active[vertex as usize] || graph.degree(vertex) != 2 {
continue;
}
let neighbours = graph.live_neighbours(vertex);
if !graph.contains_edge(neighbours[0], neighbours[1]) {
eliminate_and_record(graph, prefix, vertex);
*treewidth_lower_bound = (*treewidth_lower_bound).max(2);
fired = true;
}
}
fired
}
fn eliminate_simplicial_vertices(
graph: &mut EliminationGraph,
prefix: &mut ElimSteps,
treewidth_lower_bound: &mut usize,
) -> bool {
let mut fired = false;
for vertex in 0..graph.len() as u32 {
if graph.active[vertex as usize] && graph.degree(vertex) >= 2 && graph.is_simplicial(vertex)
{
let degree = eliminate_and_record(graph, prefix, vertex);
*treewidth_lower_bound = (*treewidth_lower_bound).max(degree);
fired = true;
}
}
fired
}
fn eliminate_almost_simplicial_vertices(
graph: &mut EliminationGraph,
prefix: &mut ElimSteps,
treewidth_lower_bound: usize,
) -> bool {
if treewidth_lower_bound < 2 {
return false;
}
let mut fired = false;
for vertex in 0..graph.len() as u32 {
if !graph.active[vertex as usize] {
continue;
}
let degree = graph.degree(vertex);
if degree < 2 || degree > treewidth_lower_bound {
continue;
}
let Some((left, right)) = almost_simplicial_nonedge(graph, vertex) else {
continue;
};
graph.add_edge(left, right);
eliminate_and_record(graph, prefix, vertex);
fired = true;
}
fired
}
fn peel_low_degree(graph: &mut EliminationGraph, prefix: &mut ElimSteps) -> bool {
let mut fired_any = false;
loop {
let mut fired = false;
for v in 0..graph.len() {
if !graph.active[v] {
continue;
}
match graph.degree(v as u32) {
0 => {
graph.active[v] = false;
graph.num_active -= 1;
prefix.sink().record(v as u32, vec![v as u32]);
fired = true;
}
1 => {
let neighbour = graph.live_neighbours(v as u32)[0];
graph.remove_without_fill(v as u32);
prefix.sink().record(v as u32, vec![v as u32, neighbour]);
fired = true;
}
_ => {}
}
}
if !fired {
return fired_any;
}
fired_any = true;
}
}
fn almost_simplicial_nonedge(graph: &EliminationGraph, v: u32) -> Option<(u32, u32)> {
let nbrs = graph.live_neighbours(v);
let mut miss: Option<(u32, u32)> = None;
for i in 0..nbrs.len() {
for j in (i + 1)..nbrs.len() {
if !graph.contains_edge(nbrs[i], nbrs[j]) {
if miss.is_some() {
return None;
}
miss = Some((nbrs[i], nbrs[j]));
}
}
}
miss
}