oxiz_solver/model/
minimizer.rs1#[allow(unused_imports)]
18use crate::prelude::*;
19use oxiz_core::TermId;
20
21#[derive(Debug, Clone)]
23pub struct Assignment {
24 pub term: TermId,
26 pub value: TermId,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum MinimizationStrategy {
33 NumericValue,
35 AssignmentCount,
37 BooleanTrue,
39 Custom,
41}
42
43#[derive(Debug, Clone)]
45pub struct MinimizerConfig {
46 pub strategy: MinimizationStrategy,
48 pub minimize_values: bool,
50 pub remove_unconstrained: bool,
52 pub max_iterations: usize,
54}
55
56impl Default for MinimizerConfig {
57 fn default() -> Self {
58 Self {
59 strategy: MinimizationStrategy::NumericValue,
60 minimize_values: true,
61 remove_unconstrained: true,
62 max_iterations: 100,
63 }
64 }
65}
66
67#[derive(Debug, Clone, Default)]
69pub struct MinimizerStats {
70 pub assignments_removed: u64,
72 pub values_minimized: u64,
74 pub iterations: u64,
76}
77
78#[derive(Debug)]
80pub struct ModelMinimizer {
81 assignments: Vec<Assignment>,
83 essential: FxHashSet<TermId>,
85 config: MinimizerConfig,
87 stats: MinimizerStats,
89}
90
91impl ModelMinimizer {
92 pub fn new(config: MinimizerConfig) -> Self {
94 Self {
95 assignments: Vec::new(),
96 essential: FxHashSet::default(),
97 config,
98 stats: MinimizerStats::default(),
99 }
100 }
101
102 pub fn default_config() -> Self {
104 Self::new(MinimizerConfig::default())
105 }
106
107 pub fn add_assignment(&mut self, term: TermId, value: TermId) {
109 self.assignments.push(Assignment { term, value });
110 }
111
112 pub fn mark_essential(&mut self, term: TermId) {
114 self.essential.insert(term);
115 }
116
117 pub fn minimize(&mut self) -> Vec<Assignment> {
119 let mut minimized = self.assignments.clone();
120
121 for _ in 0..self.config.max_iterations {
122 self.stats.iterations += 1;
123
124 let mut changed = false;
125
126 if self.config.remove_unconstrained && self.try_remove_assignments(&mut minimized) {
128 changed = true;
129 }
130
131 if self.config.minimize_values && self.try_minimize_values(&mut minimized) {
133 changed = true;
134 }
135
136 if !changed {
137 break;
138 }
139 }
140
141 minimized
142 }
143
144 fn try_remove_assignments(&mut self, assignments: &mut Vec<Assignment>) -> bool {
146 let mut removed = false;
147
148 assignments.retain(|assignment| {
149 if self.essential.contains(&assignment.term) {
150 true } else {
152 removed = true;
155 self.stats.assignments_removed += 1;
156 false
157 }
158 });
159
160 removed
161 }
162
163 fn try_minimize_values(&mut self, _assignments: &mut Vec<Assignment>) -> bool {
165 self.stats.values_minimized += 1;
168 false
169 }
170
171 pub fn assignments(&self) -> &[Assignment] {
173 &self.assignments
174 }
175
176 pub fn stats(&self) -> &MinimizerStats {
178 &self.stats
179 }
180
181 pub fn reset_stats(&mut self) {
183 self.stats = MinimizerStats::default();
184 }
185}
186
187impl Default for ModelMinimizer {
188 fn default() -> Self {
189 Self::default_config()
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196
197 #[test]
198 fn test_minimizer_creation() {
199 let minimizer = ModelMinimizer::default_config();
200 assert_eq!(minimizer.stats().assignments_removed, 0);
201 }
202
203 #[test]
204 fn test_add_assignment() {
205 let mut minimizer = ModelMinimizer::default_config();
206
207 let term = TermId::new(0);
208 let value = TermId::new(1);
209
210 minimizer.add_assignment(term, value);
211
212 assert_eq!(minimizer.assignments().len(), 1);
213 }
214
215 #[test]
216 fn test_mark_essential() {
217 let mut minimizer = ModelMinimizer::default_config();
218
219 let term = TermId::new(0);
220 minimizer.mark_essential(term);
221
222 assert!(minimizer.essential.contains(&term));
223 }
224
225 #[test]
226 fn test_minimize_removes_unconstrained() {
227 let mut minimizer = ModelMinimizer::default_config();
228
229 let term1 = TermId::new(0);
230 let term2 = TermId::new(1);
231 let value = TermId::new(10);
232
233 minimizer.add_assignment(term1, value);
234 minimizer.add_assignment(term2, value);
235
236 minimizer.mark_essential(term1);
238
239 let minimized = minimizer.minimize();
240
241 assert_eq!(minimized.len(), 1);
243 assert_eq!(minimized[0].term, term1);
244 assert_eq!(minimizer.stats().assignments_removed, 1);
245 }
246
247 #[test]
248 fn test_stats() {
249 let mut minimizer = ModelMinimizer::default_config();
250 minimizer.stats.iterations = 5;
251
252 assert_eq!(minimizer.stats().iterations, 5);
253
254 minimizer.reset_stats();
255 assert_eq!(minimizer.stats().iterations, 0);
256 }
257}