use std::cell::Cell;
use std::rc::Rc;
use std::time::{Duration, Instant};
use super::cadical_ffi::{Bounded, CaDiCal, ClauseIterator, Terminator, note_solver_unavailable};
use super::renumber::Renumber;
use crate::cnf::occ;
use crate::cnf::VarId;
use crate::cnf::{Clause, CnfFormula, Literal};
#[derive(Clone)]
pub struct WallClockTerminator {
deadline: Rc<Cell<Instant>>,
}
impl WallClockTerminator {
pub fn new(budget: Duration) -> Self {
Self {
deadline: Rc::new(Cell::new(Instant::now() + budget)),
}
}
pub fn deadline_handle(&self) -> DeadlineHandle {
DeadlineHandle(Rc::clone(&self.deadline))
}
}
impl Terminator for WallClockTerminator {
fn terminated(&mut self) -> bool {
Instant::now() >= self.deadline.get()
}
}
#[derive(Clone)]
pub struct DeadlineHandle(Rc<Cell<Instant>>);
impl DeadlineHandle {
pub fn set(&self, deadline: Instant) {
self.0.set(deadline);
}
}
pub(super) fn preprocess_cadical_with_meter(
formula: &CnfFormula,
rounds: i32,
deadline: Option<Instant>,
meter: &mut super::meter::PreprocessMeter,
) -> (CnfFormula, usize) {
let budget = meter
.deadline_or_none(deadline)
.map(crate::budget::remaining);
preprocess_cadical_budgeted_with_meter(formula, rounds, budget, meter)
}
fn cadical_freeze_run(
formula: &CnfFormula,
appears: &[bool],
rounds: i32,
budget: Option<Duration>,
meter: &mut super::meter::PreprocessMeter,
) -> Option<(Vec<Clause>, Vec<Literal>)> {
let num_vars = formula.num_vars;
let mut solver = CaDiCal::new()?;
for clause in &formula.clauses {
for lit in &clause.literals {
solver.add(lit.to_dimacs());
}
solver.add(0);
}
for var_idx in 0..num_vars {
if appears[var_idx as usize] {
solver.freeze(VarId(var_idx).to_dimacs());
}
}
let literals = || formula.clauses.iter().map(|c| c.literals.len()).sum();
let _status = match budget {
Some(b) => {
let mut bounded = Bounded::new(&mut solver, WallClockTerminator::new(b));
meter.simplify(&mut bounded, rounds, literals)
}
None => meter.simplify(&mut solver, rounds, literals),
};
let mut forced_vars = Vec::new();
for var_idx in 0..num_vars {
if !appears[var_idx as usize] {
continue;
}
let var = VarId(var_idx);
let v = solver.fixed(var.to_dimacs());
if v != 0 {
forced_vars.push(Literal::new(var, v > 0));
}
}
let mut collector = ClauseCollector {
clauses: Vec::new(),
};
solver.traverse_clauses(&mut collector);
let clauses: Vec<Clause> = collector
.clauses
.into_iter()
.map(|dimacs_lits| Clause::new(dimacs_lits.into_iter().map(Literal::from).collect()))
.collect();
Some((clauses, forced_vars))
}
#[cfg(test)]
pub(super) fn preprocess_cadical_budgeted(
formula: &CnfFormula,
rounds: i32,
budget: Option<Duration>,
) -> (CnfFormula, usize) {
let mut meter = super::meter::PreprocessMeter::new(crate::config::PreprocessClock::WallClock);
preprocess_cadical_budgeted_with_meter(formula, rounds, budget, &mut meter)
}
pub(super) fn preprocess_cadical_budgeted_with_meter(
formula: &CnfFormula,
rounds: i32,
budget: Option<Duration>,
meter: &mut super::meter::PreprocessMeter,
) -> (CnfFormula, usize) {
let num_vars = formula.num_vars;
if formula.clauses.is_empty() {
return (formula.clone(), 0);
}
let appears = occ::appearance_mask(&formula.clauses, num_vars as usize);
let n_appear = appears.iter().filter(|&&a| a).count() as u32;
let run: Option<(Vec<Clause>, Vec<Literal>)> = if n_appear == num_vars {
cadical_freeze_run(formula, &appears, rounds, budget, meter)
} else {
let compaction = Renumber::keeping(num_vars as usize, |v| appears[v.idx()]);
let compact_nv = compaction.num_new_vars();
let compact_clauses: Vec<Clause> = formula
.clauses
.iter()
.map(|c| {
Clause::new(
c.literals
.iter()
.filter_map(|l| compaction.apply_lit(*l))
.collect(),
)
})
.collect();
let compact_formula = CnfFormula {
num_vars: compact_nv,
clauses: compact_clauses,
};
let compact_appears = vec![true; compact_nv as usize];
cadical_freeze_run(&compact_formula, &compact_appears, rounds, budget, meter).map(
|(compact_clauses, compact_forced)| {
let clauses = compact_clauses
.into_iter()
.map(|c| {
Clause::new(
c.literals
.iter()
.map(|l| compaction.apply_inverse_lit(*l))
.collect(),
)
})
.collect();
let forced = compact_forced
.iter()
.map(|l| compaction.apply_inverse_lit(*l))
.collect();
(clauses, forced)
},
)
};
let Some((mut clauses, forced_orig)) = run else {
note_solver_unavailable("cadical", "the formula is left unsimplified");
return (formula.clone(), 0);
};
let forced_count = forced_orig.len();
for lit in forced_orig {
clauses.push(Clause::new(vec![lit]));
}
(CnfFormula { num_vars, clauses }, forced_count)
}
struct ClauseCollector {
clauses: Vec<Vec<i32>>,
}
impl ClauseIterator for ClauseCollector {
fn clause(&mut self, clause: &[i32]) -> bool {
self.clauses.push(clause.to_vec());
true
}
}