oxiz_solver/
delayed_combination.rs1#[allow(unused_imports)]
18use crate::prelude::*;
19use oxiz_core::TermId;
20
21pub type TheoryId = usize;
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
26pub struct SharedTerm {
27 pub term: TermId,
29 pub theories: u64, }
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
35pub struct DeferredEquality {
36 pub lhs: TermId,
38 pub rhs: TermId,
40 pub source_theory: TheoryId,
42}
43
44#[derive(Debug, Clone)]
46pub struct DelayedCombinationConfig {
47 pub lazy_sharing: bool,
49 pub conflict_driven: bool,
51 pub max_deferred: usize,
53}
54
55impl Default for DelayedCombinationConfig {
56 fn default() -> Self {
57 Self {
58 lazy_sharing: true,
59 conflict_driven: true,
60 max_deferred: 1000,
61 }
62 }
63}
64
65#[derive(Debug, Clone, Default)]
67pub struct DelayedCombinationStats {
68 pub equalities_deferred: u64,
70 pub equalities_propagated: u64,
72 pub forced_propagations: u64,
74 pub conflicts_detected: u64,
76}
77
78#[derive(Debug)]
80pub struct DelayedCombination {
81 shared_terms: FxHashMap<TermId, SharedTerm>,
83 deferred: Vec<DeferredEquality>,
85 active_theories: u64,
87 config: DelayedCombinationConfig,
89 stats: DelayedCombinationStats,
91}
92
93impl DelayedCombination {
94 pub fn new(config: DelayedCombinationConfig) -> Self {
96 Self {
97 shared_terms: FxHashMap::default(),
98 deferred: Vec::new(),
99 active_theories: 0,
100 config,
101 stats: DelayedCombinationStats::default(),
102 }
103 }
104
105 pub fn default_config() -> Self {
107 Self::new(DelayedCombinationConfig::default())
108 }
109
110 pub fn register_shared_term(&mut self, term: TermId, theory: TheoryId) {
112 let entry = self
113 .shared_terms
114 .entry(term)
115 .or_insert(SharedTerm { term, theories: 0 });
116
117 entry.theories |= 1 << theory;
118 }
119
120 pub fn is_shared(&self, term: TermId) -> bool {
122 self.shared_terms
123 .get(&term)
124 .map(|st| st.theories.count_ones() > 1)
125 .unwrap_or(false)
126 }
127
128 pub fn defer_equality(&mut self, lhs: TermId, rhs: TermId, source: TheoryId) {
130 if !self.config.lazy_sharing {
131 self.propagate_equality(lhs, rhs, source);
133 return;
134 }
135
136 self.deferred.push(DeferredEquality {
137 lhs,
138 rhs,
139 source_theory: source,
140 });
141 self.stats.equalities_deferred += 1;
142
143 if self.deferred.len() >= self.config.max_deferred {
145 self.force_propagation();
146 }
147 }
148
149 fn propagate_equality(&mut self, _lhs: TermId, _rhs: TermId, _source: TheoryId) {
151 self.stats.equalities_propagated += 1;
153 }
154
155 pub fn force_propagation(&mut self) {
157 if self.deferred.is_empty() {
158 return;
159 }
160
161 self.stats.forced_propagations += 1;
162
163 let equalities: Vec<_> = self.deferred.drain(..).collect();
165 for eq in equalities {
166 self.propagate_equality(eq.lhs, eq.rhs, eq.source_theory);
167 }
168 }
169
170 pub fn handle_conflict(&mut self) {
172 if !self.config.conflict_driven {
173 return;
174 }
175
176 self.stats.conflicts_detected += 1;
177
178 self.force_propagation();
180 }
181
182 pub fn activate_theory(&mut self, theory: TheoryId) {
184 self.active_theories |= 1 << theory;
185 }
186
187 pub fn is_theory_active(&self, theory: TheoryId) -> bool {
189 (self.active_theories & (1 << theory)) != 0
190 }
191
192 pub fn get_sharing_theories(&self, term: TermId) -> Vec<TheoryId> {
194 if let Some(shared) = self.shared_terms.get(&term) {
195 let mut theories = Vec::new();
196 for i in 0..64 {
197 if (shared.theories & (1 << i)) != 0 {
198 theories.push(i);
199 }
200 }
201 theories
202 } else {
203 Vec::new()
204 }
205 }
206
207 pub fn stats(&self) -> &DelayedCombinationStats {
209 &self.stats
210 }
211
212 pub fn reset_stats(&mut self) {
214 self.stats = DelayedCombinationStats::default();
215 }
216}
217
218impl Default for DelayedCombination {
219 fn default() -> Self {
220 Self::default_config()
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227
228 #[test]
229 fn test_delayed_combination_creation() {
230 let dc = DelayedCombination::default_config();
231 assert_eq!(dc.stats().equalities_deferred, 0);
232 }
233
234 #[test]
235 fn test_register_shared_term() {
236 let mut dc = DelayedCombination::default_config();
237
238 let term = TermId::new(0);
239 dc.register_shared_term(term, 0);
240 dc.register_shared_term(term, 1);
241
242 assert!(dc.is_shared(term));
243 }
244
245 #[test]
246 fn test_defer_equality() {
247 let mut dc = DelayedCombination::default_config();
248
249 let lhs = TermId::new(0);
250 let rhs = TermId::new(1);
251
252 dc.defer_equality(lhs, rhs, 0);
253
254 assert_eq!(dc.stats().equalities_deferred, 1);
255 assert_eq!(dc.deferred.len(), 1);
256 }
257
258 #[test]
259 fn test_force_propagation() {
260 let mut dc = DelayedCombination::default_config();
261
262 let lhs = TermId::new(0);
263 let rhs = TermId::new(1);
264
265 dc.defer_equality(lhs, rhs, 0);
266 dc.force_propagation();
267
268 assert_eq!(dc.deferred.len(), 0);
269 assert_eq!(dc.stats().equalities_propagated, 1);
270 assert_eq!(dc.stats().forced_propagations, 1);
271 }
272
273 #[test]
274 fn test_activate_theory() {
275 let mut dc = DelayedCombination::default_config();
276
277 dc.activate_theory(2);
278 assert!(dc.is_theory_active(2));
279 assert!(!dc.is_theory_active(3));
280 }
281
282 #[test]
283 fn test_get_sharing_theories() {
284 let mut dc = DelayedCombination::default_config();
285
286 let term = TermId::new(0);
287 dc.register_shared_term(term, 1);
288 dc.register_shared_term(term, 3);
289
290 let theories = dc.get_sharing_theories(term);
291
292 assert_eq!(theories.len(), 2);
293 assert!(theories.contains(&1));
294 assert!(theories.contains(&3));
295 }
296
297 #[test]
298 fn test_handle_conflict() {
299 let mut dc = DelayedCombination::default_config();
300
301 let lhs = TermId::new(0);
302 let rhs = TermId::new(1);
303
304 dc.defer_equality(lhs, rhs, 0);
305 dc.handle_conflict();
306
307 assert_eq!(dc.deferred.len(), 0);
308 assert_eq!(dc.stats().conflicts_detected, 1);
309 }
310}