use super::*;
use crate::preprocess::dve::types::{self, DveFate};
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct SimplifyTelemetry {
pub total_ms: u64,
pub backbone_ms: Option<u64>,
pub equivalence_ms: Option<u64>,
pub dve_ms: Option<u64>,
pub backbone_found: usize,
pub backbone_probes: usize,
}
pub(crate) struct SimplifiedFormula {
pub original: CnfFormula,
pub equiv_reduced: Option<EquivReduction>,
pub dve_reduced: Option<DveReduction>,
pub preprocessed: Option<CnfFormula>,
pub stripped: Option<Stripped>,
pub telemetry: SimplifyTelemetry,
pub decision_trace: Option<crate::bundle::PreprocessDecisionTrace>,
}
pub(crate) struct Stripped {
pub formula: CnfFormula,
pub removed: VariableStripping,
}
pub(crate) struct VariableStripping {
pub backbone: Vec<(VarId, bool)>,
pub dead: Vec<VarId>,
pub renumbering: Renumber,
}
pub(crate) struct EquivReduction {
pub formula: CnfFormula,
pub mapping: EquivMapping,
pub renumbering: Renumber,
}
pub(crate) struct DveReduction {
pub formula: CnfFormula,
pub renumbering: Renumber,
pub fates: Vec<DveFate>,
}
impl DveReduction {
pub(crate) fn num_free(&self) -> usize {
self.free_vars().count()
}
pub(crate) fn free_vars(&self) -> impl Iterator<Item = usize> + '_ {
types::free_vars(&self.fates)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum OriginalFate {
Variable {
index: usize,
same_polarity: bool,
},
Forced(bool),
Unconstrained,
}
impl SimplifiedFormula {
pub(crate) fn reduced_formula(&self) -> &CnfFormula {
if let Some(ref dve) = self.dve_reduced {
return &dve.formula;
}
if let Some(ref eq) = self.equiv_reduced {
&eq.formula
} else if let Some(ref s) = self.stripped {
&s.formula
} else if let Some(ref pp) = self.preprocessed {
pp
} else {
&self.original
}
}
pub(crate) fn free_var_exp(&self) -> u32 {
let dve_free = self.dve_reduced.as_ref().map(|d| d.num_free()).unwrap_or(0);
let dead = self
.stripped
.as_ref()
.map(|s| s.removed.dead.len())
.unwrap_or(0);
(dve_free + dead) as u32
}
pub(crate) fn count_lift_pow2(&self, extra_pow2: u32) -> u32 {
self.free_var_exp() + extra_pow2
}
pub(crate) fn stripped_var_to_original(&self, sid: VarId) -> usize {
match self.stripped.as_ref() {
Some(s) => s.removed.renumbering.old_id(sid).idx(),
None => sid.idx(),
}
}
pub(crate) fn pre_dve_var_to_original(&self, j: usize) -> usize {
self.stripped_var_to_original(self.peel_equiv(VarId(j as u32)))
}
fn peel_equiv(&self, j: VarId) -> VarId {
match self.equiv_reduced.as_ref() {
Some(eq) => eq.renumbering.old_id(j),
None => j,
}
}
pub(crate) fn frozen_in_dve_space(
&self,
frozen: &rustc_hash::FxHashSet<VarId>,
dve_input_num_vars: u32,
) -> rustc_hash::FxHashSet<VarId> {
let mut out = rustc_hash::FxHashSet::default();
if frozen.is_empty() {
return out;
}
for j in 0..dve_input_num_vars {
let rep_s = self.peel_equiv(VarId(j));
let mut is_frozen =
frozen.contains(&VarId(self.stripped_var_to_original(rep_s) as u32));
if !is_frozen
&& let Some(eq) = self.equiv_reduced.as_ref()
&& let Some(partners) = eq.mapping.rep_to_equivs.get(&rep_s)
{
is_frozen = partners
.iter()
.any(|&p| frozen.contains(&VarId(self.stripped_var_to_original(p.var) as u32)));
}
if is_frozen {
out.insert(VarId(j));
}
}
out
}
pub(crate) fn reduced_var_to_original(&self, i: usize) -> usize {
let j = match self.dve_reduced.as_ref() {
Some(d) => d.renumbering.old_id(VarId(i as u32)).idx(),
None => i,
};
self.pre_dve_var_to_original(j)
}
pub(crate) fn stripped_forced_and_free(&self) -> (Vec<i32>, Vec<u32>) {
match self.stripped.as_ref() {
Some(s) => (
s.removed
.backbone
.iter()
.map(|&(v, pos)| Literal::new(v, pos).to_dimacs())
.collect(),
s.removed
.dead
.iter()
.map(|v| v.to_dimacs() as u32)
.collect(),
),
None => (Vec::new(), Vec::new()),
}
}
pub(crate) fn composed_var_map(
&self,
) -> crate::preprocess::VarMap<crate::cnf::Reduced, crate::cnf::Original> {
(0..self.reduced_formula().num_vars as usize)
.map(|j| Some(VarId(self.reduced_var_to_original(j) as u32).to_dimacs()))
.collect()
}
pub(crate) fn original_fates(&self) -> Vec<OriginalFate> {
debug_assert!(
self.dve_reduced.is_none(),
"a DVE-eliminated variable is determined by a definition, which no OriginalFate names",
);
let n = self.original.num_vars as usize;
let mut fates = vec![OriginalFate::Unconstrained; n];
let in_best = |sid: VarId| match self.equiv_reduced.as_ref() {
Some(eq) => {
let rep = eq.mapping.var_to_rep[sid.idx()];
let index = eq
.renumbering
.new_id(rep.var)
.expect("an equivalence representative survives into the reduced formula")
.idx();
OriginalFate::Variable {
index,
same_polarity: rep.positive,
}
}
None => OriginalFate::Variable {
index: sid.idx(),
same_polarity: true,
},
};
match self.stripped.as_ref() {
Some(s) => {
for &(var, positive) in &s.removed.backbone {
fates[var.idx()] = OriginalFate::Forced(positive);
}
for (sid, &original) in s.removed.renumbering.kept().iter().enumerate() {
fates[original.idx()] = in_best(VarId(sid as u32));
}
}
None => {
for (o, fate) in fates.iter_mut().enumerate() {
*fate = in_best(VarId(o as u32));
}
}
}
fates
}
pub(crate) fn promote_all_backbone_to_live(&mut self) {
let Some(s) = self.stripped.as_mut() else {
return;
};
if s.removed.renumbering.num_new_vars() != 0 {
return;
}
let (live_var, live_polarity) = s.removed.backbone.remove(0);
s.removed.renumbering = Renumber::of_kept(s.removed.renumbering.num_old_vars(), [live_var]);
s.formula = CnfFormula {
num_vars: 1,
clauses: vec![Clause::new(vec![Literal::new(VarId(0), live_polarity)])],
};
self.equiv_reduced = None;
}
}