oxiz_solver/model/
minimizer.rs1use oxiz_core::TermId;
18use rustc_hash::FxHashSet;
19
20#[derive(Debug, Clone)]
22pub struct Assignment {
23 pub term: TermId,
25 pub value: TermId,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum MinimizationStrategy {
32 NumericValue,
34 AssignmentCount,
36 BooleanTrue,
38 Custom,
40}
41
42#[derive(Debug, Clone)]
44pub struct MinimizerConfig {
45 pub strategy: MinimizationStrategy,
47 pub minimize_values: bool,
49 pub remove_unconstrained: bool,
51 pub max_iterations: usize,
53}
54
55impl Default for MinimizerConfig {
56 fn default() -> Self {
57 Self {
58 strategy: MinimizationStrategy::NumericValue,
59 minimize_values: true,
60 remove_unconstrained: true,
61 max_iterations: 100,
62 }
63 }
64}
65
66#[derive(Debug, Clone, Default)]
68pub struct MinimizerStats {
69 pub assignments_removed: u64,
71 pub values_minimized: u64,
73 pub iterations: u64,
75}
76
77#[derive(Debug)]
79pub struct ModelMinimizer {
80 assignments: Vec<Assignment>,
82 essential: FxHashSet<TermId>,
84 config: MinimizerConfig,
86 stats: MinimizerStats,
88}
89
90impl ModelMinimizer {
91 pub fn new(config: MinimizerConfig) -> Self {
93 Self {
94 assignments: Vec::new(),
95 essential: FxHashSet::default(),
96 config,
97 stats: MinimizerStats::default(),
98 }
99 }
100
101 pub fn default_config() -> Self {
103 Self::new(MinimizerConfig::default())
104 }
105
106 pub fn add_assignment(&mut self, term: TermId, value: TermId) {
108 self.assignments.push(Assignment { term, value });
109 }
110
111 pub fn mark_essential(&mut self, term: TermId) {
113 self.essential.insert(term);
114 }
115
116 pub fn minimize(&mut self) -> Vec<Assignment> {
118 let mut minimized = self.assignments.clone();
119
120 for _ in 0..self.config.max_iterations {
121 self.stats.iterations += 1;
122
123 let mut changed = false;
124
125 if self.config.remove_unconstrained && self.try_remove_assignments(&mut minimized) {
127 changed = true;
128 }
129
130 if self.config.minimize_values && self.try_minimize_values(&mut minimized) {
132 changed = true;
133 }
134
135 if !changed {
136 break;
137 }
138 }
139
140 minimized
141 }
142
143 fn try_remove_assignments(&mut self, assignments: &mut Vec<Assignment>) -> bool {
145 let mut removed = false;
146
147 assignments.retain(|assignment| {
148 if self.essential.contains(&assignment.term) {
149 true } else {
151 removed = true;
154 self.stats.assignments_removed += 1;
155 false
156 }
157 });
158
159 removed
160 }
161
162 fn try_minimize_values(&mut self, _assignments: &mut Vec<Assignment>) -> bool {
164 self.stats.values_minimized += 1;
167 false
168 }
169
170 pub fn assignments(&self) -> &[Assignment] {
172 &self.assignments
173 }
174
175 pub fn stats(&self) -> &MinimizerStats {
177 &self.stats
178 }
179
180 pub fn reset_stats(&mut self) {
182 self.stats = MinimizerStats::default();
183 }
184}
185
186impl Default for ModelMinimizer {
187 fn default() -> Self {
188 Self::default_config()
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195
196 #[test]
197 fn test_minimizer_creation() {
198 let minimizer = ModelMinimizer::default_config();
199 assert_eq!(minimizer.stats().assignments_removed, 0);
200 }
201
202 #[test]
203 fn test_add_assignment() {
204 let mut minimizer = ModelMinimizer::default_config();
205
206 let term = TermId::new(0);
207 let value = TermId::new(1);
208
209 minimizer.add_assignment(term, value);
210
211 assert_eq!(minimizer.assignments().len(), 1);
212 }
213
214 #[test]
215 fn test_mark_essential() {
216 let mut minimizer = ModelMinimizer::default_config();
217
218 let term = TermId::new(0);
219 minimizer.mark_essential(term);
220
221 assert!(minimizer.essential.contains(&term));
222 }
223
224 #[test]
225 fn test_minimize_removes_unconstrained() {
226 let mut minimizer = ModelMinimizer::default_config();
227
228 let term1 = TermId::new(0);
229 let term2 = TermId::new(1);
230 let value = TermId::new(10);
231
232 minimizer.add_assignment(term1, value);
233 minimizer.add_assignment(term2, value);
234
235 minimizer.mark_essential(term1);
237
238 let minimized = minimizer.minimize();
239
240 assert_eq!(minimized.len(), 1);
242 assert_eq!(minimized[0].term, term1);
243 assert_eq!(minimizer.stats().assignments_removed, 1);
244 }
245
246 #[test]
247 fn test_stats() {
248 let mut minimizer = ModelMinimizer::default_config();
249 minimizer.stats.iterations = 5;
250
251 assert_eq!(minimizer.stats().iterations, 5);
252
253 minimizer.reset_stats();
254 assert_eq!(minimizer.stats().iterations, 0);
255 }
256}