use crate::datastructures::{Assignment, Model};
use crate::formulas::{EncodedFormula, Formula, FormulaFactory, Variable};
use crate::solver::maxsat_config::{MaxSatConfig, PbEncoding};
use crate::solver::maxsat_ffi::{MaxSatError, OpenWboSolver};
use std::collections::BTreeSet;
use std::fmt::Debug;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Algorithm {
Wbo,
Oll,
LinearSu,
PartMsu3,
Msu3,
}
impl Algorithm {
pub fn weighted(&self, config: &MaxSatConfig) -> bool {
match self {
Self::Wbo | Self::Oll => true,
Self::LinearSu => config.pb_encoding != PbEncoding::Adder,
Self::PartMsu3 | Self::Msu3 => false,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct MaxSatStats {
pub ub_cost: Option<u64>,
pub nb_satisfied: u64,
pub nb_cores: u64,
pub avg_core_size: f64,
pub nb_sym_clauses: u64,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MaxSatResult {
Unsatisfiable,
Optimum(u64),
Undef,
}
const SEL_PREFIX: &str = "@SEL_SOFT_";
pub struct MaxSatSolver {
algorithm: Algorithm,
config: MaxSatConfig,
solver: OpenWboSolver,
selector_variables: BTreeSet<Variable>,
}
impl MaxSatSolver {
pub fn new(algorithm: Algorithm) -> Result<Self, MaxSatError> {
Self::from_config(algorithm, MaxSatConfig::default())
}
pub fn from_config(algorithm: Algorithm, config: MaxSatConfig) -> Result<Self, MaxSatError> {
let solver = OpenWboSolver::new(&algorithm, &config)?;
Ok(Self { algorithm, config, solver, selector_variables: BTreeSet::new() })
}
pub fn is_weighted(&self) -> bool {
self.algorithm.weighted(&self.config)
}
pub fn reset(&mut self) {
self.solver = OpenWboSolver::new(&self.algorithm, &self.config).expect("Failed to reset the MaxSAT solver!");
self.selector_variables.clear();
}
pub fn add_hard_formula(&mut self, formula: EncodedFormula, f: &FormulaFactory) -> Result<(), MaxSatError> {
self.add_cnf(None, f.cnf_of(formula), f)
}
pub fn add_soft_formula(&mut self, weight: u64, formula: EncodedFormula, f: &FormulaFactory) -> Result<(), MaxSatError> {
if (formula.is_or() || formula.is_literal()) && formula.is_cnf(f) {
self.add_clause(Some(weight), formula, f)
} else {
let sel_var_name = format!("{SEL_PREFIX}{}", self.selector_variables.len());
let sel_var = f.var(&sel_var_name);
self.selector_variables.insert(sel_var);
let f1 = f.or(&[sel_var.negate().into(), formula]);
let neg_f = f.negate(formula);
let f2 = f.or(&[neg_f, sel_var.into()]);
self.add_hard_formula(f1, f)?;
self.add_hard_formula(f2, f)?;
self.add_clause(Some(weight), sel_var.into(), f)
}
}
pub fn solve(&mut self) -> Result<MaxSatResult, MaxSatError> {
let status = self.solver.status();
if status == Ok(MaxSatResult::Undef) {
self.solver.search()
} else {
status
}
}
pub fn status(&self) -> Result<MaxSatResult, MaxSatError> {
self.solver.status()
}
pub fn model(&mut self) -> Result<Model, MaxSatError> {
self.solver.model(&self.selector_variables)
}
pub fn assignment(&mut self) -> Result<Assignment, MaxSatError> {
self.solver.assignment(&self.selector_variables)
}
pub fn algorithm(&self) -> Algorithm {
self.algorithm.clone()
}
pub fn stats(&self) -> MaxSatStats {
self.solver.stats()
}
fn add_cnf(&mut self, weight: Option<u64>, formula: EncodedFormula, f: &FormulaFactory) -> Result<(), MaxSatError> {
match formula.unpack(f) {
Formula::True => Ok(()),
Formula::False | Formula::Lit(_) | Formula::Or(_) => self.add_clause(weight, formula, f),
Formula::And(ops) => {
for op in ops {
self.add_clause(weight, op, f)?;
}
Ok(())
}
_ => panic!("Formula must be in cnf form!"),
}
}
fn add_clause(&mut self, weight: Option<u64>, formula: EncodedFormula, f: &FormulaFactory) -> Result<(), MaxSatError> {
match weight {
Some(w) => self.solver.add_soft_clause(w, &formula, f),
None => self.solver.add_hard_clause(&formula, f),
}
}
}