use std::fmt::{self, Debug, Formatter};
use pindakaas::solver::propagation::SolvingActions;
use tracing::trace;
use crate::{
IntSet, IntVal,
actions::{
BoolInspectionActions, BoolPropagationActions, DecisionActions, IntDecisionActions,
IntEvent, IntInspectionActions, IntPropagationActions, PropagationActions,
ReasoningContext, ReasoningEngine, Trailed, TrailingActions,
},
constraints::{Conflict, DeferredReason, Reason, ReasonBuilder},
helpers::bytes::Bytes,
solver::{
BoxedPropagator, IntLitMeaning,
decision::{Decision, integer::LazyLitDef},
engine::{Engine, LitPropagation, PropRef, State, trace_new_lit},
view::{View, boolean::BoolView},
},
};
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
enum ChangeRequest {
SetLowerBound(IntVal),
SetUpperBound(IntVal),
SetValue(IntVal),
RemoveValue(IntVal),
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
enum ChangeType {
Redundant,
New,
Conflicting,
}
struct ReasonTracePrint<'a>(&'a Result<Reason<Decision<bool>>, bool>);
pub struct SolvingContext<'a> {
pub(crate) slv: &'a mut dyn SolvingActions,
pub(crate) state: &'a mut State,
pub(crate) current_prop: PropRef,
}
impl BoolInspectionActions<SolvingContext<'_>> for Decision<bool> {
fn val(&self, ctx: &SolvingContext<'_>) -> Option<bool> {
self.val(ctx.state)
}
}
impl<'a> BoolPropagationActions<SolvingContext<'a>> for Decision<bool> {
fn fix(
&self,
ctx: &mut SolvingContext<'a>,
val: bool,
reason: impl ReasonBuilder<SolvingContext<'a>>,
) -> Result<(), Conflict<Decision<bool>>> {
if val { *self } else { !(*self) }.require(ctx, reason)
}
fn require(
&self,
ctx: &mut SolvingContext<'a>,
reason: impl ReasonBuilder<SolvingContext<'a>>,
) -> Result<(), Conflict<Decision<bool>>> {
match self.val(&ctx.state.trail) {
Some(true) => Ok(()),
Some(false) => Err(Conflict::new(ctx, Some(*self), reason)),
None => {
ctx.propagate_lit(*self, reason, None);
Ok(())
}
}
}
}
impl IntDecisionActions<SolvingContext<'_>> for Decision<IntVal> {
fn lit(&self, ctx: &mut SolvingContext<'_>, meaning: IntLitMeaning) -> View<bool> {
let var = &mut ctx.state.int_vars[self.idx()];
let new_var = |def: LazyLitDef| {
let v = ctx.slv.new_observed_var();
ctx.state.statistics.lazy_literals += 1;
ctx.state.trail.grow_to_boolvar(v);
trace_new_lit!(*self, def, v);
ctx.state.bool_to_int.insert_lazy(v, *self, def.meaning);
for cl in def.meaning.defining_clauses(
v.into(),
def.prev.map(Into::into),
def.next.map(Into::into),
) {
ctx.state.clauses.push_back(cl);
}
v
};
var.lit(meaning, new_var).0
}
}
impl IntInspectionActions<SolvingContext<'_>> for Decision<IntVal> {
fn bounds(&self, ctx: &SolvingContext<'_>) -> (IntVal, IntVal) {
self.bounds(ctx.state)
}
fn domain(&self, ctx: &SolvingContext<'_>) -> IntSet {
self.domain(ctx.state)
}
fn in_domain(&self, ctx: &SolvingContext<'_>, val: IntVal) -> bool {
self.in_domain(ctx.state, val)
}
fn lit_meaning(&self, ctx: &SolvingContext<'_>, lit: View<bool>) -> Option<IntLitMeaning> {
self.lit_meaning(ctx.state, lit)
}
fn max(&self, ctx: &SolvingContext<'_>) -> IntVal {
self.max(ctx.state)
}
fn max_lit(&self, ctx: &SolvingContext<'_>) -> View<bool> {
self.max_lit(ctx.state)
}
fn min(&self, ctx: &SolvingContext<'_>) -> IntVal {
self.min(ctx.state)
}
fn min_lit(&self, ctx: &SolvingContext<'_>) -> View<bool> {
self.min_lit(ctx.state)
}
fn try_lit(&self, ctx: &SolvingContext<'_>, meaning: IntLitMeaning) -> Option<View<bool>> {
self.try_lit(ctx.state, meaning)
}
fn val(&self, ctx: &SolvingContext<'_>) -> Option<IntVal> {
self.val(ctx.state)
}
}
impl<'a> IntPropagationActions<SolvingContext<'a>> for Decision<IntVal> {
fn fix(
&self,
ctx: &mut SolvingContext<'a>,
val: IntVal,
reason: impl ReasonBuilder<SolvingContext<'a>>,
) -> Result<(), Conflict<Decision<bool>>> {
ctx.propagate_int(*self, ChangeRequest::SetValue(val), reason)
}
fn remove_val(
&self,
ctx: &mut SolvingContext<'a>,
val: IntVal,
reason: impl ReasonBuilder<SolvingContext<'a>>,
) -> Result<(), Conflict<Decision<bool>>> {
ctx.propagate_int(*self, ChangeRequest::RemoveValue(val), reason)
}
fn tighten_max(
&self,
ctx: &mut SolvingContext<'a>,
val: IntVal,
reason: impl ReasonBuilder<SolvingContext<'a>>,
) -> Result<(), Conflict<Decision<bool>>> {
ctx.propagate_int(*self, ChangeRequest::SetUpperBound(val), reason)
}
fn tighten_min(
&self,
ctx: &mut SolvingContext<'a>,
val: IntVal,
reason: impl ReasonBuilder<SolvingContext<'a>>,
) -> Result<(), Conflict<Decision<bool>>> {
ctx.propagate_int(*self, ChangeRequest::SetLowerBound(val), reason)
}
}
impl Debug for ReasonTracePrint<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self.0 {
Err(false) => write!(f, "false"),
Err(true) => write!(f, "[]"),
Ok(Reason::Eager(conj)) => conj
.iter()
.map(|&l| l.0.into())
.collect::<Vec<i32>>()
.fmt(f),
Ok(Reason::Lazy(_)) => write!(f, "lazy"),
&Ok(Reason::Simple(l)) => vec![i32::from(l.0)].fmt(f),
}
}
}
impl<'a> SolvingContext<'a> {
pub(crate) fn new(slv: &'a mut dyn SolvingActions, state: &'a mut State) -> Self {
Self {
slv,
state,
current_prop: PropRef::INVALID,
}
}
#[inline]
fn propagate_int(
&mut self,
iv: Decision<IntVal>,
change_req: ChangeRequest,
reason: impl ReasonBuilder<Self>,
) -> Result<(), Conflict<Decision<bool>>> {
let (lb, ub) = self.state.int_vars[iv.idx()].bounds(self);
let check = match change_req {
ChangeRequest::SetValue(i) if lb == i && ub == i => ChangeType::Redundant,
ChangeRequest::SetValue(i) if i < lb || i > ub => ChangeType::Conflicting,
ChangeRequest::RemoveValue(i) if i < lb || i > ub => ChangeType::Redundant,
ChangeRequest::SetLowerBound(i) if i <= lb => ChangeType::Redundant,
ChangeRequest::SetLowerBound(i) if i > ub => ChangeType::Conflicting,
ChangeRequest::SetUpperBound(i) if i >= ub => ChangeType::Redundant,
ChangeRequest::SetUpperBound(i) if i < lb => ChangeType::Conflicting,
_ => ChangeType::New,
};
if check == ChangeType::Redundant {
return Ok(());
}
let new_var = |def: LazyLitDef| {
let v = self.slv.new_observed_var();
self.state.trail.grow_to_boolvar(v);
trace_new_lit!(iv, def, v);
self.state.bool_to_int.insert_lazy(v, iv, def.meaning);
for cl in def.meaning.defining_clauses(
v.into(),
def.prev.map(Into::into),
def.next.map(Into::into),
) {
self.state.clauses.push_back(cl);
}
v
};
let (bv, lit_req) = self.state.int_vars[iv.idx()].lit(
match change_req {
ChangeRequest::SetLowerBound(i) => IntLitMeaning::GreaterEq(i),
ChangeRequest::SetUpperBound(i) => IntLitMeaning::Less(i + 1),
ChangeRequest::SetValue(i) => IntLitMeaning::Eq(i),
ChangeRequest::RemoveValue(i) => IntLitMeaning::NotEq(i),
},
new_var,
);
let lit = match bv.0 {
BoolView::Const(true) => return Ok(()),
BoolView::Const(false) => return Err(Conflict::new(self, None, reason)),
BoolView::Lit(lit) => lit,
};
if check == ChangeType::Conflicting {
return Err(Conflict::new(self, lit.into(), reason));
}
match lit.val(&self.state.trail) {
Some(true) => return Ok(()),
Some(false) => return Err(Conflict::new(self, lit.into(), reason)),
None => {}
}
let event = match lit_req {
IntLitMeaning::Eq(_) => IntEvent::Fixed,
IntLitMeaning::NotEq(_) => IntEvent::Domain,
IntLitMeaning::GreaterEq(i) if i == ub => IntEvent::Fixed,
IntLitMeaning::GreaterEq(_) => IntEvent::LowerBound,
IntLitMeaning::Less(i) if i == lb + 1 => IntEvent::Fixed,
IntLitMeaning::Less(_) => IntEvent::UpperBound,
};
self.propagate_lit(lit, reason, Some((iv, event)));
match lit_req {
IntLitMeaning::Eq(val) => {
self.state.int_vars[iv.idx()].notify_lower_bound(&mut self.state.trail, val);
self.state.int_vars[iv.idx()].notify_upper_bound(&mut self.state.trail, val);
}
IntLitMeaning::NotEq(_) => {}
IntLitMeaning::GreaterEq(lb) => {
self.state.int_vars[iv.idx()].notify_lower_bound(&mut self.state.trail, lb);
}
IntLitMeaning::Less(ub) => {
self.state.int_vars[iv.idx()].notify_upper_bound(&mut self.state.trail, ub - 1);
}
};
Ok(())
}
#[inline]
fn propagate_lit(
&mut self,
lit: Decision<bool>,
reason: impl ReasonBuilder<Self>,
event: Option<(Decision<IntVal>, IntEvent)>,
) {
let reason = Reason::from_view(reason.build_reason(self));
trace!(
target: "solver",
lit = i32::from(lit.0),
reason = ?ReasonTracePrint(&reason),
prop = self.current_prop.index(),
"propagate"
);
self.state.propagation_queue.push_back(LitPropagation {
lit: lit.0,
reason,
event,
});
let _prev = self.state.trail.assign_lit(lit.0);
debug_assert_eq!(_prev, None);
}
pub(crate) fn run_propagators(&mut self, propagators: &mut [BoxedPropagator]) {
while let Some(p) = self.state.propagator_queue.pop() {
debug_assert!(!self.state.failed);
debug_assert!(self.state.conflict.is_none());
self.current_prop = PropRef::from_raw(p);
let prop = propagators[self.current_prop.index()].as_mut();
let res = prop.propagate(self);
self.state.statistics.propagations += 1;
self.current_prop = PropRef::INVALID;
if let Err(conflict) = res {
trace!(
target: "solver",
lit = conflict
.subject
.map(|s| i32::from(s.0))
.unwrap_or_default(),
reason = ?ReasonTracePrint(&Ok(conflict.reason.clone())),
"conflict detected"
);
debug_assert!(self.state.conflict.is_none());
self.state.failed = true;
self.state.conflict = Some(conflict);
}
if self.state.conflict.is_some() || !self.state.propagation_queue.is_empty() {
return;
}
}
}
}
impl Debug for SolvingContext<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("SolvingContext")
.field("state", &self.state)
.field("current_prop", &self.current_prop)
.finish()
}
}
impl DecisionActions for SolvingContext<'_> {
fn num_conflicts(&self) -> u64 {
self.state.statistics.conflicts
}
}
impl PropagationActions for SolvingContext<'_> {
fn declare_conflict(&mut self, reason: impl ReasonBuilder<Self>) -> Conflict<Decision<bool>> {
Conflict::new(self, None, reason)
}
fn deferred_reason(&self, data: u64) -> DeferredReason {
DeferredReason {
propagator: self.current_prop.index() as u32,
data,
}
}
}
impl ReasoningContext for SolvingContext<'_> {
type Atom = <Engine as ReasoningEngine>::Atom;
type Conflict = <Engine as ReasoningEngine>::Conflict;
}
impl TrailingActions for SolvingContext<'_> {
fn set_trailed<T: Bytes>(&mut self, i: Trailed<T>, v: T) -> T {
self.state.set_trailed(i, v)
}
fn trailed<T: Bytes>(&self, i: Trailed<T>) -> T {
self.state.trailed(i)
}
}
impl<'a> BoolPropagationActions<SolvingContext<'a>> for View<bool> {
fn fix(
&self,
ctx: &mut SolvingContext<'a>,
val: bool,
reason: impl ReasonBuilder<SolvingContext<'a>>,
) -> Result<(), Conflict<Decision<bool>>> {
if val { *self } else { !(*self) }.require(ctx, reason)
}
fn require(
&self,
ctx: &mut SolvingContext<'a>,
reason: impl ReasonBuilder<SolvingContext<'a>>,
) -> Result<(), Conflict<Decision<bool>>> {
match self.0 {
BoolView::Lit(lit) => lit.require(ctx, reason),
BoolView::Const(false) => Err(Conflict::new(ctx, None, reason)),
BoolView::Const(true) => Ok(()),
}
}
}