use std::collections::BinaryHeap;
use std::time::Instant;
use super::{
CHEAP_MODE_MAX_ACTIVE, DEADLINE_CHECK_STRIDE, ElimEntry, ElimExit, ElimSink, ElimStop,
EliminationGraph, drain_clique_tail, exceeds_width_bound, take_bag,
};
use crate::deadline::expired;
pub(super) enum Seeded {
Ready,
CheapMode,
Bailed,
}
pub(super) enum AfterElim {
Continue,
EnterCheapMode,
Bail,
}
pub(super) trait ElimPolicy {
type Entry: Ord + ElimEntry;
const CHEAP_MODE: bool;
const MAINTAIN_BITSET: bool;
const ZERO_SCORE_IS_SIMPLICIAL: bool;
fn heap(&mut self) -> &mut BinaryHeap<Self::Entry>;
fn push(&mut self, graph: &EliminationGraph, v: u32, score: u64);
fn live_score(&mut self, graph: &EliminationGraph, v: u32) -> u64;
fn seed(
&mut self,
graph: &mut EliminationGraph,
_: Option<Instant>,
_: Option<Instant>,
) -> Seeded {
for v in 0..graph.len() {
if graph.active[v] {
let score = self.live_score(graph, v as u32);
self.push(graph, v as u32, score);
}
}
Seeded::Ready
}
fn pop(&mut self) -> Option<(u32, u64)> {
self.heap().pop().map(|e| (e.vertex(), e.snapshot()))
}
fn rescore_on_pop(&mut self, graph: &EliminationGraph, v: u32) -> Option<u64> {
Some(self.live_score(graph, v))
}
fn after_eliminate(
&mut self,
_graph: &EliminationGraph,
_nbrs: &[u32],
_cheap_mode: bool,
_deadline: Option<Instant>,
) -> AfterElim {
AfterElim::Continue
}
}
pub(super) fn eliminate_greedy<P: ElimPolicy>(
policy: &mut P,
graph: &mut EliminationGraph,
mut sink: ElimSink<'_>,
stop: ElimStop,
) -> ElimExit {
let ElimStop {
soft_deadline,
hard_deadline,
width_bound,
} = stop;
if P::MAINTAIN_BITSET && graph.should_promote_bitset() {
graph.promote_bitset();
}
let mut cheap_mode = match policy.seed(graph, soft_deadline, hard_deadline) {
Seeded::Ready => false,
Seeded::CheapMode => true,
Seeded::Bailed => return ElimExit::DeadlineReached,
};
let mut nbrs_buf = Vec::new();
let mut check_counter = 0u32;
let mut clique_residual = false;
while let Some((v, snapshot)) = policy.pop() {
if !graph.active[v as usize] {
continue; }
if cheap_mode {
if expired(hard_deadline) {
return ElimExit::DeadlineReached;
}
} else {
check_counter += 1;
if check_counter >= DEADLINE_CHECK_STRIDE {
check_counter = 0;
if expired(hard_deadline) {
return ElimExit::DeadlineReached;
}
if P::CHEAP_MODE && expired(soft_deadline) {
if graph.num_active > CHEAP_MODE_MAX_ACTIVE {
return ElimExit::DeadlineReached;
}
cheap_mode = true;
}
if P::MAINTAIN_BITSET && graph.should_promote_bitset() {
graph.promote_bitset();
}
}
}
if !cheap_mode
&& let Some(live) = policy.rescore_on_pop(graph, v)
&& live != snapshot
{
policy.push(graph, v, live);
continue;
}
let bag = take_bag(graph, v, &mut nbrs_buf);
let bag_len = bag.len();
if !clique_residual && graph.is_residual_clique() {
clique_residual = true;
}
let simplicial = P::ZERO_SCORE_IS_SIMPLICIAL && !cheap_mode && snapshot == 0;
if clique_residual || simplicial {
graph.remove_without_fill_nbrs(v, &nbrs_buf);
} else {
graph.eliminate_with_nbrs(v, &nbrs_buf);
}
sink.record(v, bag);
if exceeds_width_bound(bag_len, width_bound) {
return ElimExit::WidthLimitExceeded;
}
if clique_residual {
if exceeds_width_bound(graph.num_active, width_bound) {
return ElimExit::WidthLimitExceeded;
}
drain_clique_tail(graph, &mut sink, policy.heap(), &mut nbrs_buf);
return ElimExit::Complete;
}
match policy.after_eliminate(graph, &nbrs_buf, cheap_mode, soft_deadline) {
AfterElim::Continue => {}
AfterElim::EnterCheapMode => {
debug_assert!(P::CHEAP_MODE, "core without cheap mode asked to enter it");
if P::CHEAP_MODE {
cheap_mode = true;
}
}
AfterElim::Bail => return ElimExit::DeadlineReached,
}
}
ElimExit::Complete
}