use std::{
cmp,
ops::{Bound, Range, RangeBounds},
};
use super::{card, CollectClauses, ConstraintEncodingError, EnforceError};
use crate::{
clause,
instances::ManageVars,
types::{
constraints::{PbConstraint, PbEqConstr, PbLbConstr, PbUbConstr},
Clause, Lit,
},
utils::unreachable_err,
};
pub mod gte;
pub use gte::GeneralizedTotalizer;
pub mod simulators;
pub type InvertedGeneralizedTotalizer = simulators::Inverted<GeneralizedTotalizer>;
pub type DoubleGeneralizedTotalizer =
simulators::Double<GeneralizedTotalizer, InvertedGeneralizedTotalizer>;
pub mod dpw;
pub use dpw::DynamicPolyWatchdog;
pub mod adder;
pub use adder::BinaryAdder;
#[cfg(feature = "proof-logging")]
pub mod cert;
pub trait Encode {
fn weight_sum(&self) -> usize;
fn next_higher(&self, val: usize) -> usize {
val + 1
}
fn next_lower(&self, val: usize) -> usize {
val - 1
}
}
pub trait BoundUpper: Encode {
fn encode_ub<Col, R>(
&mut self,
range: R,
collector: &mut Col,
var_manager: &mut dyn ManageVars,
) -> Result<(), crate::OutOfMemory>
where
Col: CollectClauses,
R: RangeBounds<usize>;
fn enforce_ub(&self, ub: usize) -> Result<Vec<Lit>, EnforceError>;
fn encode_ub_constr<Col>(
constr: PbUbConstr,
collector: &mut Col,
var_manager: &mut dyn ManageVars,
) -> Result<(), ConstraintEncodingError>
where
Col: CollectClauses,
Self: FromIterator<(Lit, usize)> + Sized,
{
let (lits, ub) = constr.decompose();
if ub < 0 {
return Err(ConstraintEncodingError::Unsat);
}
let ub = ub.unsigned_abs();
let mut enc = Self::from_iter(lits);
enc.encode_ub(ub..=ub, collector, var_manager)?;
collector.extend_clauses(
enc.enforce_ub(ub)
.unwrap()
.into_iter()
.map(|unit| clause![unit]),
)?;
Ok(())
}
fn coarse_ub(&self, ub: usize) -> usize {
ub
}
}
pub trait BoundLower: Encode {
fn encode_lb<Col, R>(
&mut self,
range: R,
collector: &mut Col,
var_manager: &mut dyn ManageVars,
) -> Result<(), crate::OutOfMemory>
where
Col: CollectClauses,
R: RangeBounds<usize>;
fn enforce_lb(&self, lb: usize) -> Result<Vec<Lit>, EnforceError>;
fn encode_lb_constr<Col>(
constr: PbLbConstr,
collector: &mut Col,
var_manager: &mut dyn ManageVars,
) -> Result<(), ConstraintEncodingError>
where
Col: CollectClauses,
Self: FromIterator<(Lit, usize)> + Sized,
{
let (lits, lb) = constr.decompose();
let lb = if lb < 0 {
return Ok(()); } else {
lb.unsigned_abs()
};
if lb > lits.iter().fold(0, |sum, (_, w)| sum + *w) {
return Err(ConstraintEncodingError::Unsat);
}
let mut enc = Self::from_iter(lits);
enc.encode_lb(lb..=lb, collector, var_manager)?;
collector.extend_clauses(
enc.enforce_lb(lb)
.unwrap()
.into_iter()
.map(|unit| clause![unit]),
)?;
Ok(())
}
fn coarse_lb(&self, lb: usize) -> usize {
lb
}
}
pub trait BoundBoth: BoundUpper + BoundLower {
fn encode_both<Col, R>(
&mut self,
range: R,
collector: &mut Col,
var_manager: &mut dyn ManageVars,
) -> Result<(), crate::OutOfMemory>
where
Col: CollectClauses,
R: RangeBounds<usize> + Clone,
{
self.encode_ub(range.clone(), collector, var_manager)?;
self.encode_lb(range, collector, var_manager)?;
Ok(())
}
fn enforce_eq(&self, b: usize) -> Result<Vec<Lit>, EnforceError> {
let mut assumps = self.enforce_ub(b)?;
assumps.extend(self.enforce_lb(b)?);
Ok(assumps)
}
fn encode_eq_constr<Col>(
constr: PbEqConstr,
collector: &mut Col,
var_manager: &mut dyn ManageVars,
) -> Result<(), ConstraintEncodingError>
where
Col: CollectClauses,
Self: FromIterator<(Lit, usize)> + Sized,
{
let (lits, b) = constr.decompose();
if b < 0 {
return Err(ConstraintEncodingError::Unsat);
}
let b = b.unsigned_abs();
if b > lits.iter().fold(0, |sum, (_, w)| sum + *w) {
return Err(ConstraintEncodingError::Unsat);
}
let mut enc = Self::from_iter(lits);
enc.encode_both(b..=b, collector, var_manager)?;
collector.extend_clauses(
enc.enforce_eq(b)
.unwrap()
.into_iter()
.map(|unit| clause![unit]),
)?;
Ok(())
}
fn encode_constr<Col>(
constr: PbConstraint,
collector: &mut Col,
var_manager: &mut dyn ManageVars,
) -> Result<(), ConstraintEncodingError>
where
Col: CollectClauses,
Self: FromIterator<(Lit, usize)> + Sized,
{
match constr {
PbConstraint::Ub(constr) => Self::encode_ub_constr(constr, collector, var_manager),
PbConstraint::Lb(constr) => Self::encode_lb_constr(constr, collector, var_manager),
PbConstraint::Eq(constr) => Self::encode_eq_constr(constr, collector, var_manager),
}
}
}
pub trait EncodeIncremental: Encode {
fn reserve(&mut self, var_manager: &mut dyn ManageVars);
}
pub trait BoundUpperIncremental: BoundUpper + EncodeIncremental {
fn encode_ub_change<Col, R>(
&mut self,
range: R,
collector: &mut Col,
var_manager: &mut dyn ManageVars,
) -> Result<(), crate::OutOfMemory>
where
Col: CollectClauses,
R: RangeBounds<usize>;
}
pub trait BoundLowerIncremental: BoundLower + EncodeIncremental {
fn encode_lb_change<Col, R>(
&mut self,
range: R,
collector: &mut Col,
var_manager: &mut dyn ManageVars,
) -> Result<(), crate::OutOfMemory>
where
Col: CollectClauses,
R: RangeBounds<usize>;
}
pub trait BoundBothIncremental: BoundUpperIncremental + BoundLowerIncremental + BoundBoth {
fn encode_both_change<Col, R>(
&mut self,
range: R,
collector: &mut Col,
var_manager: &mut dyn ManageVars,
) -> Result<(), crate::OutOfMemory>
where
Col: CollectClauses,
R: RangeBounds<usize> + Clone,
{
self.encode_ub_change(range.clone(), collector, var_manager)?;
self.encode_lb_change(range, collector, var_manager)?;
Ok(())
}
}
pub type DefUpperBounding = GeneralizedTotalizer;
pub type DefLowerBounding = InvertedGeneralizedTotalizer;
pub type DefBothBounding = DoubleGeneralizedTotalizer;
pub type DefIncUpperBounding = GeneralizedTotalizer;
pub type DefIncLowerBounding = InvertedGeneralizedTotalizer;
pub type DefIncBothBounding = DoubleGeneralizedTotalizer;
#[must_use]
pub fn new_default_ub() -> impl BoundUpper + Extend<(Lit, usize)> {
DefUpperBounding::default()
}
#[must_use]
pub fn new_default_lb() -> impl BoundLower + Extend<(Lit, usize)> {
DefLowerBounding::default()
}
#[must_use]
pub fn new_default_both() -> impl BoundBoth + Extend<(Lit, usize)> {
DefBothBounding::default()
}
#[must_use]
pub fn new_default_inc_ub() -> impl BoundUpperIncremental + Extend<(Lit, usize)> {
DefIncUpperBounding::default()
}
#[must_use]
pub fn new_default_inc_lb() -> impl BoundLower + Extend<(Lit, usize)> {
DefIncLowerBounding::default()
}
#[must_use]
pub fn new_default_inc_both() -> impl BoundBoth + Extend<(Lit, usize)> {
DefIncBothBounding::default()
}
pub fn default_encode_pb_constraint<Col: CollectClauses>(
constr: PbConstraint,
collector: &mut Col,
var_manager: &mut dyn ManageVars,
) -> Result<(), crate::OutOfMemory> {
encode_pb_constraint::<DefBothBounding, Col>(constr, collector, var_manager)
}
pub fn encode_pb_constraint<PBE: BoundBoth + FromIterator<(Lit, usize)>, Col: CollectClauses>(
constr: PbConstraint,
collector: &mut Col,
var_manager: &mut dyn ManageVars,
) -> Result<(), crate::OutOfMemory> {
if constr.is_tautology() {
return Ok(());
}
if constr.is_unsat() {
return collector.add_clause(Clause::new());
}
if constr.is_positive_assignment() {
return collector
.extend_clauses(constr.into_lits().into_iter().map(|(lit, _)| clause![lit]));
}
if constr.is_negative_assignment() {
return collector
.extend_clauses(constr.into_lits().into_iter().map(|(lit, _)| clause![!lit]));
}
if constr.is_clause() {
return collector.add_clause(unreachable_err!(constr.into_clause()));
}
if constr.is_card() {
let card = unreachable_err!(constr.into_card_constr());
return card::default_encode_cardinality_constraint(card, collector, var_manager);
}
match PBE::encode_constr(constr, collector, var_manager) {
Ok(()) => Ok(()),
Err(ConstraintEncodingError::OutOfMemory(err)) => Err(err),
Err(ConstraintEncodingError::Unsat) => unreachable!(),
}
}
fn prepare_ub_range<Enc: Encode, R: RangeBounds<usize>>(enc: &Enc, range: R) -> Range<usize> {
(match range.start_bound() {
Bound::Included(b) => *b,
Bound::Excluded(b) => b + 1,
Bound::Unbounded => 0,
})..match range.end_bound() {
Bound::Included(b) => cmp::min(b + 1, enc.weight_sum()),
Bound::Excluded(b) => cmp::min(*b, enc.weight_sum()),
Bound::Unbounded => enc.weight_sum(),
}
}
fn prepare_lb_range<Enc: Encode, R: RangeBounds<usize>>(enc: &Enc, range: R) -> Range<usize> {
(match range.start_bound() {
Bound::Included(b) => cmp::max(*b, 1),
Bound::Excluded(b) => cmp::max(b + 1, 1),
Bound::Unbounded => 1,
})..match range.end_bound() {
Bound::Included(b) => cmp::min(b + 1, enc.weight_sum() + 1),
Bound::Excluded(b) => cmp::min(*b, enc.weight_sum() + 1),
Bound::Unbounded => enc.weight_sum() + 1,
}
}
fn prepare_both_range<Enc: Encode, R: RangeBounds<usize>>(enc: &Enc, range: R) -> Range<usize> {
(match range.start_bound() {
Bound::Included(b) => *b,
Bound::Excluded(b) => b + 1,
Bound::Unbounded => 1,
})..match range.end_bound() {
Bound::Included(b) => cmp::min(b + 1, enc.weight_sum() + 1),
Bound::Excluded(b) => cmp::min(*b, enc.weight_sum() + 1),
Bound::Unbounded => enc.weight_sum() + 1,
}
}