use std::{
cmp,
ops::{Bound, Range, RangeBounds},
};
use super::{CollectClauses, ConstraintEncodingError, EnforceError, NotEncoded};
use crate::{
clause,
instances::ManageVars,
types::{
constraints::{CardConstraint, CardEqConstr, CardLbConstr, CardUbConstr},
Clause, Lit,
},
utils::unreachable_err,
};
pub mod totalizer;
pub use totalizer::Totalizer;
pub mod simulators;
#[cfg(feature = "proof-logging")]
pub mod cert;
pub trait Encode {
fn n_lits(&self) -> usize;
}
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>, NotEncoded>;
fn encode_ub_constr<Col>(
constr: CardUbConstr,
collector: &mut Col,
var_manager: &mut dyn ManageVars,
) -> Result<(), crate::OutOfMemory>
where
Col: CollectClauses,
Self: FromIterator<Lit> + Sized,
{
let (lits, ub) = constr.decompose();
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(())
}
}
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: CardLbConstr,
collector: &mut Col,
var_manager: &mut dyn ManageVars,
) -> Result<(), ConstraintEncodingError>
where
Col: CollectClauses,
Self: FromIterator<Lit> + Sized,
{
let (lits, lb) = constr.decompose();
if lb > lits.len() {
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(())
}
}
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: CardEqConstr,
collector: &mut Col,
var_manager: &mut dyn ManageVars,
) -> Result<(), ConstraintEncodingError>
where
Col: CollectClauses,
Self: FromIterator<Lit> + Sized,
{
let (lits, b) = constr.decompose();
if b > lits.len() {
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: CardConstraint,
collector: &mut Col,
var_manager: &mut dyn ManageVars,
) -> Result<(), ConstraintEncodingError>
where
Col: CollectClauses,
Self: FromIterator<Lit> + Sized,
{
match constr {
CardConstraint::Ub(constr) => {
Self::encode_ub_constr(constr, collector, var_manager)?;
Ok(())
}
CardConstraint::Lb(constr) => Self::encode_lb_constr(constr, collector, var_manager),
CardConstraint::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)
}
}
pub type DefUpperBounding = Totalizer;
pub type DefLowerBounding = Totalizer;
pub type DefBothBounding = Totalizer;
pub type DefIncUpperBounding = Totalizer;
pub type DefIncLowerBounding = Totalizer;
pub type DefIncBothBounding = Totalizer;
#[must_use]
pub fn new_default_ub() -> impl BoundUpper {
DefUpperBounding::default()
}
#[must_use]
pub fn new_default_lb() -> impl BoundLower {
DefLowerBounding::default()
}
#[must_use]
pub fn new_default_both() -> impl BoundBoth {
DefBothBounding::default()
}
#[must_use]
pub fn new_default_inc_ub() -> impl BoundUpperIncremental {
DefIncUpperBounding::default()
}
#[must_use]
pub fn new_default_inc_lb() -> impl BoundLower {
DefIncLowerBounding::default()
}
#[must_use]
pub fn new_default_inc_both() -> impl BoundBoth {
DefIncBothBounding::default()
}
pub fn default_encode_cardinality_constraint<Col: CollectClauses>(
constr: CardConstraint,
collector: &mut Col,
var_manager: &mut dyn ManageVars,
) -> Result<(), crate::OutOfMemory> {
encode_cardinality_constraint::<DefBothBounding, Col>(constr, collector, var_manager)
}
pub fn encode_cardinality_constraint<CE: BoundBoth + FromIterator<Lit>, Col: CollectClauses>(
constr: CardConstraint,
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()));
}
match CE::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.n_lits()),
Bound::Excluded(b) => cmp::min(*b, enc.n_lits()),
Bound::Unbounded => enc.n_lits(),
}
}
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.n_lits() + 1),
Bound::Excluded(b) => cmp::min(*b, enc.n_lits() + 1),
Bound::Unbounded => enc.n_lits() + 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.n_lits() + 1),
Bound::Excluded(b) => cmp::min(*b, enc.n_lits() + 1),
Bound::Unbounded => enc.n_lits() + 1,
}
}