Skip to main content

propaga_core/
extended.rs

1use crate::VariableId;
2
3/// Snapshot of a set variable domain for propagation reads.
4#[derive(Clone, Debug, PartialEq, Eq)]
5pub struct SetDomainSnapshot {
6    /// Greatest lower bound elements.
7    pub glb: Vec<i32>,
8    /// Least upper bound elements.
9    pub lub: Vec<i32>,
10    /// Minimum cardinality.
11    pub card_min: usize,
12    /// Maximum cardinality.
13    pub card_max: usize,
14}
15
16impl SetDomainSnapshot {
17    #[must_use]
18    pub fn is_empty(&self) -> bool {
19        self.card_min > self.card_max
20            || self.glb.len() > self.card_max
21            || self.lub.len() < self.card_min
22            || !self.glb.iter().all(|value| self.lub.contains(value))
23    }
24
25    #[must_use]
26    pub fn undecided(&self) -> Vec<i32> {
27        self.lub
28            .iter()
29            .copied()
30            .filter(|value| !self.glb.contains(value))
31            .collect()
32    }
33}
34
35/// Snapshot of a float variable domain for propagation reads.
36#[derive(Clone, Debug, PartialEq)]
37pub struct FloatDomainSnapshot {
38    /// Lower bound.
39    pub min: f64,
40    /// Upper bound.
41    pub max: f64,
42    /// Excluded interior IEEE points.
43    pub holes: Vec<f64>,
44}
45
46impl FloatDomainSnapshot {
47    #[must_use]
48    pub fn is_empty(&self) -> bool {
49        self.min > self.max
50            || ((self.max - self.min).abs() <= f64::EPSILON
51                && self
52                    .holes
53                    .iter()
54                    .any(|hole| (*hole - self.min).abs() <= f64::EPSILON))
55    }
56
57    #[must_use]
58    pub fn is_fixed(&self) -> bool {
59        !self.is_empty()
60            && (self.max - self.min).abs() < f64::EPSILON
61            && !self
62                .holes
63                .iter()
64                .any(|hole| (*hole - self.min).abs() <= f64::EPSILON)
65    }
66
67    #[must_use]
68    pub fn contains(&self, value: f64) -> bool {
69        !self.is_empty()
70            && value >= self.min
71            && value <= self.max
72            && !self
73                .holes
74                .iter()
75                .any(|hole| (*hole - value).abs() <= f64::EPSILON)
76    }
77}
78
79/// Extended propagation operations for set and float variables.
80pub trait ExtendedPropagationContext {
81    fn set_domain(&self, var: VariableId) -> Option<SetDomainSnapshot>;
82    fn float_domain(&self, var: VariableId) -> Option<FloatDomainSnapshot>;
83    fn force_set_in(&mut self, var: VariableId, value: i32) -> bool;
84    fn force_set_out(&mut self, var: VariableId, value: i32) -> bool;
85    fn tighten_set_cardinality(
86        &mut self,
87        var: VariableId,
88        card_min: usize,
89        card_max: usize,
90    ) -> bool;
91    fn tighten_float_below(&mut self, var: VariableId, bound: f64) -> bool;
92    fn tighten_float_above(&mut self, var: VariableId, bound: f64) -> bool;
93    /// Excludes one IEEE point from a float domain (bound tighten or interior hole).
94    fn exclude_float_point(&mut self, var: VariableId, value: f64) -> bool;
95}