cspsolver/
vars.rs

1use crate::prelude::*;
2use std::ops::{Index, IndexMut};
3
4/// Domain for a decision variable
5#[derive(Clone, Debug)]
6pub enum Var {
7    /// interval of integers
8    VarI { min: i32, max: i32 },
9    /// interval of floating-point numbers
10    VarF { min: f32, max: f32 },
11}
12
13#[derive(Copy, Clone, Debug)]
14pub enum Val {
15    /// Single integer value
16    ValI(i32),
17    /// Single floating-point value
18    ValF(f32),
19}
20
21impl Val {
22    /// Create an integer value
23    pub const fn int(value: i32) -> Self {
24        Val::ValI(value)
25    }
26
27    /// Create a floating-point value
28    pub const fn float(value: f32) -> Self {
29        Val::ValF(value)
30    }
31}
32
33impl From<i32> for Val {
34    fn from(value: i32) -> Self {
35        Val::ValI(value)
36    }
37}
38
39impl From<f32> for Val {
40    fn from(value: f32) -> Self {
41        Val::ValF(value)
42    }
43}
44
45impl PartialEq for Val {
46    fn eq(&self, other: &Self) -> bool {
47        match (self, other) {
48            (Val::ValI(a), Val::ValI(b)) => a == b,
49            (Val::ValF(a), Val::ValF(b)) => (a - b).abs() < VAR_EPSILON,
50            _ => false,
51        }
52    }
53}
54
55impl Eq for Val {}
56
57impl PartialOrd for Val {
58    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
59        match (self, other) {
60            (Val::ValI(a), Val::ValI(b)) => a.partial_cmp(b),
61            (Val::ValF(a), Val::ValF(b)) => a.partial_cmp(b),
62            (Val::ValI(a), Val::ValF(b)) => (*a as f32).partial_cmp(b),
63            (Val::ValF(a), Val::ValI(b)) => a.partial_cmp(&(*b as f32)),
64        }
65    }
66}
67
68impl Ord for Val {
69    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
70        self.partial_cmp(other).unwrap_or(std::cmp::Ordering::Equal)
71    }
72}
73
74impl std::ops::Add for Val {
75    type Output = Val;
76
77    fn add(self, other: Val) -> Val {
78        match (self, other) {
79            (Val::ValI(a), Val::ValI(b)) => Val::ValI(a + b),
80            (Val::ValF(a), Val::ValF(b)) => Val::ValF(a + b),
81            (Val::ValI(a), Val::ValF(b)) => Val::ValF(a as f32 + b),
82            (Val::ValF(a), Val::ValI(b)) => Val::ValF(a + b as f32),
83        }
84    }
85}
86
87impl std::iter::Sum for Val {
88    fn sum<I: Iterator<Item = Val>>(iter: I) -> Self {
89        iter.fold(Val::ValI(0), |acc, x| acc + x)
90    }
91}
92
93impl std::ops::Sub for Val {
94    type Output = Val;
95
96    fn sub(self, other: Val) -> Val {
97        match (self, other) {
98            (Val::ValI(a), Val::ValI(b)) => Val::ValI(a - b),
99            (Val::ValF(a), Val::ValF(b)) => Val::ValF(a - b),
100            (Val::ValI(a), Val::ValF(b)) => Val::ValF(a as f32 - b),
101            (Val::ValF(a), Val::ValI(b)) => Val::ValF(a - b as f32),
102        }
103    }
104}
105
106const VAR_EPSILON: f32 = 1e-6;
107impl Var {
108    /// Assigned variables have a domain reduced to a singleton.
109    pub fn is_assigned(&self) -> bool {
110        match self {
111            Var::VarI { min, max } => min == max,
112            Var::VarF { min, max } => close_enough(*min, *max, VAR_EPSILON),
113        }
114    }
115
116    /// Midpoint of domain for easier binary splits.
117    pub fn mid(&self) -> Val {
118        match self {
119            Var::VarI { min, max } => Val::ValI(min + (max - min) / 2),
120            Var::VarF { min, max } => Val::ValF(*min + (*max - *min) / 2.0),
121        }
122    }
123
124    /// Extract assignment for decision variable.
125    ///
126    /// # Panics
127    ///
128    /// This function will panic if the decision variable is not assigned.
129    pub fn get_assignment(&self) -> Val {
130        assert!(self.is_assigned());
131
132        match self {
133            Var::VarI { min, .. } => Val::ValI(*min),
134            Var::VarF { min, .. } => Val::ValF(*min),
135        }
136    }
137}
138
139/// Store decision variables and expose a limited interface to operate on them.
140#[derive(Clone, Debug, Default)]
141pub struct Vars(Vec<Var>);
142
143impl Vars {
144    /// Create a new decision variable.
145    pub fn new_var_with_bounds(&mut self, min: Val, max: Val) -> VarId {
146        let v = VarId(self.0.len());
147
148        match (min, max) {
149            (Val::ValI(min), Val::ValI(max)) => self.0.push(Var::VarI { min, max }),
150            (Val::ValF(min), Val::ValF(max)) => self.0.push(Var::VarF { min, max }),
151            _ => debug_assert!(false, "Mismatched variable types"),
152        }
153
154        v
155    }
156
157    /// Get handle to an unassigned decision variable.
158    pub fn get_unassigned_var(&self) -> Option<VarId> {
159        self.0.iter().position(|var| !var.is_assigned()).map(VarId)
160    }
161
162    /// Determine if all decision variables are assigned.
163    pub fn is_assigned_all(&self) -> bool {
164        self.get_unassigned_var().is_none()
165    }
166
167    /// Extract assignment for all decision variables.
168    ///
169    /// # Panics
170    ///
171    /// This function will panic if any decision variables are not assigned.
172    pub fn into_solution(self) -> Solution {
173        // Extract values for each decision variable
174        let values: Vec<_> = self.0.into_iter().map(|v| v.get_assignment()).collect();
175
176        Solution::from(values)
177    }
178}
179
180/// Decision variable handle that is not bound to a specific memory location.
181#[derive(Clone, Copy, Debug)]
182pub struct VarId(usize);
183
184impl Index<VarId> for Vars {
185    type Output = Var;
186
187    fn index(&self, index: VarId) -> &Self::Output {
188        &self.0[index.0]
189    }
190}
191
192impl IndexMut<VarId> for Vars {
193    fn index_mut(&mut self, index: VarId) -> &mut Self::Output {
194        &mut self.0[index.0]
195    }
196}
197
198impl Index<VarId> for Vec<Var> {
199    type Output = Var;
200
201    fn index(&self, index: VarId) -> &Self::Output {
202        &self[index.0]
203    }
204}
205
206impl IndexMut<VarId> for Vec<Var> {
207    fn index_mut(&mut self, index: VarId) -> &mut Self::Output {
208        &mut self[index.0]
209    }
210}
211
212impl Index<VarId> for Vec<Vec<PropId>> {
213    type Output = Vec<PropId>;
214
215    fn index(&self, index: VarId) -> &Self::Output {
216        &self[index.0]
217    }
218}
219
220impl IndexMut<VarId> for Vec<Vec<PropId>> {
221    fn index_mut(&mut self, index: VarId) -> &mut Self::Output {
222        &mut self[index.0]
223    }
224}
225
226impl Index<VarId> for Vec<Val> {
227    type Output = Val;
228
229    fn index(&self, index: VarId) -> &Self::Output {
230        &self[index.0]
231    }
232}
233
234impl IndexMut<VarId> for Vec<Val> {
235    fn index_mut(&mut self, index: VarId) -> &mut Self::Output {
236        &mut self[index.0]
237    }
238}
239
240/// Wrapper to provide specific helper methods for binary decision variables.
241#[derive(Clone, Copy, Debug)]
242pub struct VarIdBin(pub(crate) VarId);
243
244