pub mod bool_array_element;
pub mod cumulative;
pub mod disjunctive;
pub mod int_abs;
pub mod int_array_element;
pub mod int_array_minimum;
pub mod int_div;
pub mod int_linear;
pub mod int_mul;
pub mod int_no_overlap;
pub mod int_pow;
pub mod int_set_contains;
pub mod int_table;
pub mod int_unique;
pub mod int_value_precede;
use std::{
any::Any,
error::Error,
fmt::{self, Debug},
iter::once,
mem,
};
use dyn_clone::DynClone;
use itertools::Itertools;
use pindakaas::Lit as RawLit;
use tracing::warn;
use crate::{
Conjunction, IntVal,
actions::{
BoolInitActions, BoolInspectionActions, BoolPropagationActions, BoolSimplificationActions,
IntEvent, IntExplanationActions, IntInitActions, IntInspectionActions,
IntPropagationActions, IntSimplificationActions, ReasoningContext, ReasoningEngine,
},
lower::{LoweringContext, LoweringError},
model::{self, Model},
solver::{
self,
engine::{Engine, State},
view::boolean::BoolView,
},
};
pub trait BoolModelActions<E>
where
E: ReasoningEngine,
Self: BoolSolverActions<E>
+ for<'a> BoolSimplificationActions<E::PropagationContext<'a>>
+ Into<model::View<bool>>,
{
}
pub trait BoolSolverActions<E>
where
E: ReasoningEngine + ?Sized,
Self: for<'a> BoolInitActions<E::InitializationContext<'a>>
+ for<'a> BoolInspectionActions<E::ExplanationContext<'a>>
+ for<'a> BoolInspectionActions<E::NotificationContext<'a>>
+ for<'a> BoolPropagationActions<E::PropagationContext<'a>>
+ Into<E::Atom>,
{
}
pub(crate) type BoxedConstraint = Box<dyn Constraint<Model>>;
pub(crate) type BoxedPropagator = Box<dyn Propagator<Engine>>;
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub(crate) enum CachedReason<B, Atom> {
Cached(Result<Reason<Atom>, bool>),
Builder(B),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Conflict<Atom> {
pub(crate) subject: Option<Atom>,
pub(crate) reason: Reason<Atom>,
}
pub trait Constraint<E: ReasoningEngine + ?Sized>: Any + Debug + DynClone + Propagator<E> {
fn simplify(
&mut self,
context: &mut E::PropagationContext<'_>,
) -> Result<SimplificationStatus, E::Conflict>;
fn to_solver(&self, context: &mut LoweringContext<'_>) -> Result<(), LoweringError>;
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct DeferredReason {
pub(crate) propagator: u32,
pub(crate) data: u64,
}
pub trait IntModelActions<E>
where
E: ReasoningEngine,
Self: IntSolverActions<E>
+ for<'a> IntSimplificationActions<E::PropagationContext<'a>>
+ Into<model::View<IntVal>>,
{
}
pub trait IntSolverActions<E>
where
E: ReasoningEngine + ?Sized,
Self: for<'a> IntInitActions<E::InitializationContext<'a>>
+ for<'a> IntExplanationActions<E::ExplanationContext<'a>>
+ for<'a> IntInspectionActions<E::NotificationContext<'a>>
+ for<'a> IntPropagationActions<E::PropagationContext<'a>>,
{
}
pub trait Propagator<E: ReasoningEngine + ?Sized>: Debug + DynClone + 'static {
fn advise_of_backtrack(&mut self, context: &mut E::NotificationContext<'_>) {
let _ = context;
unreachable!("propagator did not provide a backtrack advisor implementation")
}
fn advise_of_bool_change(
&mut self,
context: &mut E::NotificationContext<'_>,
data: u64,
) -> bool {
let _ = context;
let _ = data;
unreachable!("propagator did not provide a Boolean advisor implementation")
}
fn advise_of_int_change(
&mut self,
context: &mut E::NotificationContext<'_>,
data: u64,
event: IntEvent,
) -> bool {
let _ = context;
let _ = event;
let _ = data;
unreachable!("propagator did not provide an integer advisor implementation")
}
fn explain(
&mut self,
context: &mut E::ExplanationContext<'_>,
lit: E::Atom,
data: u64,
) -> Conjunction<E::Atom> {
let _ = context;
let _ = lit;
let _ = data;
panic!("propagator did not provide an explain implementation")
}
fn initialize(&mut self, context: &mut E::InitializationContext<'_>);
fn propagate(&mut self, context: &mut E::PropagationContext<'_>) -> Result<(), E::Conflict>;
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Reason<Atom> {
Lazy(DeferredReason),
Eager(Box<[Atom]>),
Simple(Atom),
}
pub trait ReasonBuilder<Context: ReasoningContext + ?Sized> {
fn build_reason(self, ctx: &mut Context) -> Result<Reason<Context::Atom>, bool>;
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum SimplificationStatus {
NoFixpoint,
Subsumed,
}
impl<E, B> BoolModelActions<E> for B
where
E: ReasoningEngine,
B: BoolSolverActions<E>
+ for<'a> BoolSimplificationActions<E::PropagationContext<'a>>
+ Into<model::View<bool>>,
{
}
impl<E, B> BoolSolverActions<E> for B
where
E: ReasoningEngine + ?Sized,
Self: for<'a> BoolInitActions<E::InitializationContext<'a>>
+ for<'a> BoolInspectionActions<E::ExplanationContext<'a>>
+ for<'a> BoolInspectionActions<E::NotificationContext<'a>>
+ for<'a> BoolPropagationActions<E::PropagationContext<'a>>
+ Into<E::Atom>,
{
}
impl Clone for BoxedConstraint {
fn clone(&self) -> BoxedConstraint {
dyn_clone::clone_box(&**self)
}
}
impl Clone for BoxedPropagator {
fn clone(&self) -> BoxedPropagator {
dyn_clone::clone_box(&**self)
}
}
impl<C> ReasonBuilder<C> for &[C::Atom]
where
C: ReasoningContext + ?Sized,
{
fn build_reason(self, _: &mut C) -> Result<Reason<C::Atom>, bool> {
Reason::from_iter(self.iter().cloned())
}
}
impl<C, const N: usize> ReasonBuilder<C> for &[C::Atom; N]
where
C: ReasoningContext + ?Sized,
{
fn build_reason(self, ctx: &mut C) -> Result<Reason<C::Atom>, bool> {
self[..].build_reason(ctx)
}
}
impl<C, const N: usize> ReasonBuilder<C> for [C::Atom; N]
where
C: ReasoningContext + ?Sized,
{
fn build_reason(self, _: &mut C) -> Result<Reason<C::Atom>, bool> {
Reason::from_iter(self)
}
}
impl<A, B> CachedReason<B, A> {
pub(crate) fn new(builder: B) -> Self {
CachedReason::Builder(builder)
}
}
impl<B, C> ReasonBuilder<C> for &'_ mut CachedReason<B, C::Atom>
where
C: ReasoningContext + ?Sized,
B: ReasonBuilder<C>,
{
fn build_reason(self, ctx: &mut C) -> Result<Reason<C::Atom>, bool> {
match self {
CachedReason::Cached(items) => items.clone(),
CachedReason::Builder(_) => {
let CachedReason::Builder(builder) =
mem::replace(self, CachedReason::Cached(Err(false)))
else {
unreachable!()
};
let reason = builder.build_reason(ctx);
*self = CachedReason::Cached(reason.clone());
reason
}
}
}
}
impl Conflict<solver::Decision<bool>> {
pub(crate) fn new<Ctx: ReasoningContext<Atom = solver::View<bool>> + ?Sized>(
ctx: &mut Ctx,
subject: Option<solver::Decision<bool>>,
reason: impl ReasonBuilder<Ctx>,
) -> Self {
match Reason::from_view(reason.build_reason(ctx)) {
Ok(reason) => Self { subject, reason },
Err(true) => match subject {
Some(subject) => Self {
subject: None,
reason: Reason::Simple(!subject),
},
None => {
warn!(
target: "solver",
"empty conflict detected; additional model simplification reasoning may be possible"
);
Self {
subject: None,
reason: Reason::Eager(Box::new([])),
}
}
},
Err(false) => unreachable!("invalid reason"),
}
}
}
impl<Atom: Debug> Error for Conflict<Atom> {}
impl<Atom: Debug> fmt::Display for Conflict<Atom> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Conflict detected: nogood {:?}", self.reason)
}
}
impl<C> ReasonBuilder<C> for DeferredReason
where
C: ReasoningContext + ?Sized,
{
fn build_reason(self, _: &mut C) -> Result<Reason<C::Atom>, bool> {
Ok(Reason::Lazy(self))
}
}
impl<C, F, I> ReasonBuilder<C> for F
where
C: ReasoningContext + ?Sized,
F: FnOnce(&mut C) -> I,
I: IntoIterator<Item = C::Atom>,
{
fn build_reason(self, ctx: &mut C) -> Result<Reason<C::Atom>, bool> {
Reason::from_iter(self(ctx))
}
}
impl<E, I> IntModelActions<E> for I
where
E: ReasoningEngine,
I: IntSolverActions<E>
+ for<'a> IntSimplificationActions<E::PropagationContext<'a>>
+ Into<model::View<IntVal>>,
{
}
impl<E, I> IntSolverActions<E> for I
where
E: ReasoningEngine + ?Sized,
I: for<'a> IntInitActions<E::InitializationContext<'a>>
+ for<'a> IntExplanationActions<E::ExplanationContext<'a>>
+ for<'a> IntInspectionActions<E::NotificationContext<'a>>
+ for<'a> IntPropagationActions<E::PropagationContext<'a>>,
{
}
impl<A> Reason<A> {
pub(crate) fn from_iter<I: IntoIterator<Item = A>>(iter: I) -> Result<Self, bool> {
let mut lits: Vec<_> = iter.into_iter().collect();
match lits.len() {
0 => Err(true),
1 => Ok(Reason::Simple(lits.remove(0))),
_ => Ok(Reason::Eager(lits.into_boxed_slice())),
}
}
}
impl Reason<solver::Decision<bool>> {
pub(crate) fn explain<Clause: FromIterator<RawLit>>(
&self,
props: &mut [BoxedPropagator],
actions: &mut State,
lit: Option<solver::Decision<bool>>,
) -> Clause {
match self {
&Reason::Lazy(DeferredReason {
propagator: prop,
data,
}) => {
let reason = props[prop as usize].explain(
actions,
lit.map(|lit| lit.into()).unwrap_or(true.into()),
data,
);
let v: Result<Vec<_>, _> = reason
.into_iter()
.filter_map(|v| match v.0 {
BoolView::Lit(lit) => Some(Ok(lit)),
BoolView::Const(false) => Some(Err(false)),
BoolView::Const(true) => None,
})
.collect();
match v {
Ok(v) => v,
Err(false) => panic!("invalid lazy reason"), Err(true) => Vec::new(),
}
.into_iter()
.map(|l| !l.0)
.chain(lit.map(|lit| lit.0))
.collect()
}
Reason::Eager(v) => v
.iter()
.map(|&l| !l.0)
.chain(lit.map(|lit| lit.0))
.collect(),
&Reason::Simple(reason) => once(!reason.0).chain(lit.map(|lit| lit.0)).collect(),
}
}
pub(crate) fn from_view(
reason: Result<Reason<solver::View<bool>>, bool>,
) -> Result<Self, bool> {
let v = match reason? {
Reason::Lazy(lazy) => return Ok(Self::Lazy(lazy)),
Reason::Eager(items) => items.into_vec(),
Reason::Simple(lit) => vec![lit],
};
let mut v: Vec<_> = v
.into_iter()
.filter_map(|v| match v.0 {
BoolView::Lit(lit) => Some(Ok(lit)),
BoolView::Const(false) => Some(Err(false)),
BoolView::Const(true) => None,
})
.try_collect()?;
match v.len() {
0 => Err(true),
1 => Ok(Reason::Simple(v.remove(0))),
_ => Ok(Reason::Eager(v.into_boxed_slice())),
}
}
}
impl<C> ReasonBuilder<C> for Vec<C::Atom>
where
C: ReasoningContext + ?Sized,
{
fn build_reason(self, _: &mut C) -> Result<Reason<C::Atom>, bool> {
Reason::from_iter(self)
}
}