use std::collections::BTreeMap;
use std::time::{Duration, Instant};
use crate::formula::Formula;
use crate::relation::{CanonicalRelation, Relation};
use crate::saturate::{
BranchState, Budget, Engine, ExternalBranches, Limit, Stats,
};
use crate::triples::Triples;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Config {
pub max_depth: u32,
pub sharing: bool,
pub max_branches: Option<u64>,
pub timeout: Option<Duration>,
pub branch_state: BranchState,
}
impl Default for Config {
fn default() -> Self {
Config {
max_depth: 2,
sharing: true,
max_branches: None,
timeout: None,
branch_state: BranchState::Rollback,
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Model(BTreeMap<String, bool>);
impl Model {
pub(crate) fn from_map(map: BTreeMap<String, bool>) -> Model {
Model(map)
}
pub fn get(&self, variable: &str) -> Option<bool> {
self.0.get(variable).copied()
}
pub fn iter(&self) -> impl Iterator<Item = (&str, bool)> {
self.0.iter().map(|(k, v)| (k.as_str(), *v))
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Unknown {
DepthExhausted,
BranchLimit,
Timeout,
ModelCheckFailed,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SatResult {
Sat(Model),
Unsat,
Unknown(Unknown),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ProofResult {
Valid,
Invalid(Model),
Unknown(Unknown),
}
#[derive(Clone, Debug)]
pub struct Report<R> {
pub result: R,
pub stats: Stats,
pub relation: CanonicalRelation,
pub timing: Timing,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Timing {
pub init: Duration,
pub solve: Duration,
pub merge: Duration,
pub prolog: Duration,
pub to_prolog: Duration,
pub from_prolog: Duration,
}
pub(crate) type RawReport = (Raw, Stats, CanonicalRelation, Timing);
pub(crate) fn sat_report(
(raw, stats, relation, timing): RawReport,
) -> Report<SatResult> {
let result = match raw {
Raw::Refuted => SatResult::Unsat,
Raw::Model(m) => SatResult::Sat(m),
Raw::Unknown(u) => SatResult::Unknown(u),
};
Report { result, stats, relation, timing }
}
pub(crate) fn proof_report(
(raw, stats, relation, timing): RawReport,
) -> Report<ProofResult> {
let result = match raw {
Raw::Refuted => ProofResult::Valid,
Raw::Model(m) => ProofResult::Invalid(m),
Raw::Unknown(u) => ProofResult::Unknown(u),
};
Report { result, stats, relation, timing }
}
pub(crate) enum Raw {
Refuted,
Model(Model),
Unknown(Unknown),
}
#[derive(Clone, Debug, Default)]
pub struct Solver {
config: Config,
}
impl Solver {
pub fn new(config: Config) -> Solver {
Solver { config }
}
pub fn config(&self) -> &Config {
&self.config
}
pub fn check_sat(&self, formula: &Formula) -> Report<SatResult> {
self.check_sat_triples(&Triples::from_formula(
formula,
self.config.sharing,
))
}
pub fn prove(&self, formula: &Formula) -> Report<ProofResult> {
self.prove_triples(&Triples::from_formula(
formula,
self.config.sharing,
))
}
pub fn check_sat_triples(&self, triples: &Triples) -> Report<SatResult> {
sat_report(self.run(triples, true, None))
}
pub fn prove_triples(&self, triples: &Triples) -> Report<ProofResult> {
proof_report(self.run(triples, false, None))
}
pub(crate) fn run(
&self,
triples: &Triples,
root_value: bool,
external: Option<&mut dyn ExternalBranches>,
) -> RawReport {
let t0 = Instant::now();
let budget = Budget {
max_branches: self.config.max_branches,
deadline: self.config.timeout.map(|t| Instant::now() + t),
};
let mut engine = Engine::new(
triples.node_count(),
triples.triples(),
budget,
self.config.branch_state,
);
if let Some(external) = external {
engine.set_external(external);
}
let mut rel = Relation::new(triples.node_count());
engine.start(&mut rel, triples.root(), root_value);
let t1 = Instant::now();
let limit = engine.saturate(&mut rel, self.config.max_depth).err();
let timing = Timing {
init: t1 - t0,
solve: t1.elapsed(),
merge: engine.merge_time,
..Timing::default()
};
let raw = if rel.is_contradiction() {
Raw::Refuted
} else if let Some(model) = extract_model(triples, &rel, root_value) {
model
} else {
Raw::Unknown(match limit {
Some(Limit::Branches) => Unknown::BranchLimit,
Some(Limit::Time) => Unknown::Timeout,
None => Unknown::DepthExhausted,
})
};
(raw, engine.stats, rel.canonical(), timing)
}
}
fn extract_model(
triples: &Triples,
rel: &Relation,
root_value: bool,
) -> Option<Raw> {
let mut model = BTreeMap::new();
for (name, node) in triples.variables() {
model.insert(name.clone(), rel.value(*node)?);
}
let values = triples.evaluate(|name| model[name]);
let root = triples.root();
let ok = values[root.node() as usize] != root.is_negated();
Some(if ok == root_value {
Raw::Model(Model(model))
} else {
Raw::Unknown(Unknown::ModelCheckFailed)
})
}