Skip to main content

csp_solver/constraint/
dispatch.rs

1//! ConstraintEnum: devirtualized dispatch for hot-path constraints.
2
3use crate::domain::Domain;
4use crate::variable::Variable;
5
6use super::all_different::AllDifferent;
7use super::all_different_except::AllDifferentExcept;
8use super::lambda::LambdaConstraint;
9use super::not_equal::NotEqual;
10use super::soft::SoftLambdaConstraint;
11use super::traits::{Constraint, Revision, VarId};
12
13/// Enum-dispatched constraint storage. Avoids vtable indirection for built-in types.
14pub enum ConstraintEnum<D: Domain> {
15    NotEqual(NotEqual),
16    AllDifferent(AllDifferent),
17    AllDifferentExcept(AllDifferentExcept<D::Value>),
18    Lambda(LambdaConstraint<D>),
19    Soft(SoftLambdaConstraint<D>),
20    Custom(Box<dyn Constraint<D>>),
21}
22
23impl<D: Domain> std::fmt::Debug for ConstraintEnum<D> {
24    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25        match self {
26            Self::NotEqual(c) => c.fmt(f),
27            Self::AllDifferent(c) => c.fmt(f),
28            Self::AllDifferentExcept(c) => c.fmt(f),
29            Self::Lambda(c) => c.fmt(f),
30            Self::Soft(c) => c.fmt(f),
31            Self::Custom(c) => c.fmt(f),
32        }
33    }
34}
35
36impl<D: Domain> ConstraintEnum<D>
37where
38    D::Value: PartialEq,
39{
40    #[inline]
41    pub fn scope(&self) -> &[VarId] {
42        match self {
43            Self::NotEqual(c) => &c.scope,
44            Self::AllDifferent(c) => &c.scope,
45            Self::AllDifferentExcept(c) => &c.scope,
46            Self::Lambda(c) => &c.scope,
47            Self::Soft(c) => &c.scope,
48            Self::Custom(c) => c.scope(),
49        }
50    }
51
52    #[inline]
53    pub fn check(&self, assignment: &[Option<D::Value>]) -> bool {
54        match self {
55            Self::NotEqual(c) => c.check_impl(assignment),
56            Self::AllDifferent(c) => c.check_impl(assignment),
57            Self::AllDifferentExcept(c) => c.check_impl(assignment),
58            Self::Lambda(c) => (c.checker)(assignment),
59            Self::Soft(_) => true, // soft constraints never reject
60            Self::Custom(c) => c.check(assignment),
61        }
62    }
63
64    #[inline]
65    pub fn revise(&self, vars: &mut [Variable<D>], depth: usize) -> Revision {
66        match self {
67            Self::NotEqual(c) => c.revise_impl(vars, depth),
68            Self::AllDifferent(c) => c.revise_impl(vars, depth),
69            Self::AllDifferentExcept(c) => c.revise_impl(vars, depth),
70            Self::Lambda(c) => <LambdaConstraint<D> as Constraint<D>>::revise(c, vars, depth),
71            Self::Soft(_) => Revision::Unchanged, // soft constraints don't prune
72            Self::Custom(c) => c.revise(vars, depth),
73        }
74    }
75
76    /// For soft constraints, compute the penalty if the underlying predicate
77    /// is violated. Returns 0.0 for hard constraints or satisfied soft constraints.
78    #[inline]
79    pub fn soft_penalty(&self, assignment: &[Option<D::Value>]) -> f64 {
80        match self {
81            Self::Soft(c) => {
82                if c.is_satisfied(assignment) {
83                    0.0
84                } else {
85                    c.penalty
86                }
87            }
88            _ => 0.0,
89        }
90    }
91}