oxiz_solver/
delayed_combination.rs1use oxiz_core::TermId;
18use rustc_hash::FxHashMap;
19
20pub type TheoryId = usize;
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
25pub struct SharedTerm {
26 pub term: TermId,
28 pub theories: u64, }
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
34pub struct DeferredEquality {
35 pub lhs: TermId,
37 pub rhs: TermId,
39 pub source_theory: TheoryId,
41}
42
43#[derive(Debug, Clone)]
45pub struct DelayedCombinationConfig {
46 pub lazy_sharing: bool,
48 pub conflict_driven: bool,
50 pub max_deferred: usize,
52}
53
54impl Default for DelayedCombinationConfig {
55 fn default() -> Self {
56 Self {
57 lazy_sharing: true,
58 conflict_driven: true,
59 max_deferred: 1000,
60 }
61 }
62}
63
64#[derive(Debug, Clone, Default)]
66pub struct DelayedCombinationStats {
67 pub equalities_deferred: u64,
69 pub equalities_propagated: u64,
71 pub forced_propagations: u64,
73 pub conflicts_detected: u64,
75}
76
77#[derive(Debug)]
79pub struct DelayedCombination {
80 shared_terms: FxHashMap<TermId, SharedTerm>,
82 deferred: Vec<DeferredEquality>,
84 active_theories: u64,
86 config: DelayedCombinationConfig,
88 stats: DelayedCombinationStats,
90}
91
92impl DelayedCombination {
93 pub fn new(config: DelayedCombinationConfig) -> Self {
95 Self {
96 shared_terms: FxHashMap::default(),
97 deferred: Vec::new(),
98 active_theories: 0,
99 config,
100 stats: DelayedCombinationStats::default(),
101 }
102 }
103
104 pub fn default_config() -> Self {
106 Self::new(DelayedCombinationConfig::default())
107 }
108
109 pub fn register_shared_term(&mut self, term: TermId, theory: TheoryId) {
111 let entry = self
112 .shared_terms
113 .entry(term)
114 .or_insert(SharedTerm { term, theories: 0 });
115
116 entry.theories |= 1 << theory;
117 }
118
119 pub fn is_shared(&self, term: TermId) -> bool {
121 self.shared_terms
122 .get(&term)
123 .map(|st| st.theories.count_ones() > 1)
124 .unwrap_or(false)
125 }
126
127 pub fn defer_equality(&mut self, lhs: TermId, rhs: TermId, source: TheoryId) {
129 if !self.config.lazy_sharing {
130 self.propagate_equality(lhs, rhs, source);
132 return;
133 }
134
135 self.deferred.push(DeferredEquality {
136 lhs,
137 rhs,
138 source_theory: source,
139 });
140 self.stats.equalities_deferred += 1;
141
142 if self.deferred.len() >= self.config.max_deferred {
144 self.force_propagation();
145 }
146 }
147
148 fn propagate_equality(&mut self, _lhs: TermId, _rhs: TermId, _source: TheoryId) {
150 self.stats.equalities_propagated += 1;
152 }
153
154 pub fn force_propagation(&mut self) {
156 if self.deferred.is_empty() {
157 return;
158 }
159
160 self.stats.forced_propagations += 1;
161
162 let equalities: Vec<_> = self.deferred.drain(..).collect();
164 for eq in equalities {
165 self.propagate_equality(eq.lhs, eq.rhs, eq.source_theory);
166 }
167 }
168
169 pub fn handle_conflict(&mut self) {
171 if !self.config.conflict_driven {
172 return;
173 }
174
175 self.stats.conflicts_detected += 1;
176
177 self.force_propagation();
179 }
180
181 pub fn activate_theory(&mut self, theory: TheoryId) {
183 self.active_theories |= 1 << theory;
184 }
185
186 pub fn is_theory_active(&self, theory: TheoryId) -> bool {
188 (self.active_theories & (1 << theory)) != 0
189 }
190
191 pub fn get_sharing_theories(&self, term: TermId) -> Vec<TheoryId> {
193 if let Some(shared) = self.shared_terms.get(&term) {
194 let mut theories = Vec::new();
195 for i in 0..64 {
196 if (shared.theories & (1 << i)) != 0 {
197 theories.push(i);
198 }
199 }
200 theories
201 } else {
202 Vec::new()
203 }
204 }
205
206 pub fn stats(&self) -> &DelayedCombinationStats {
208 &self.stats
209 }
210
211 pub fn reset_stats(&mut self) {
213 self.stats = DelayedCombinationStats::default();
214 }
215}
216
217impl Default for DelayedCombination {
218 fn default() -> Self {
219 Self::default_config()
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 #[test]
228 fn test_delayed_combination_creation() {
229 let dc = DelayedCombination::default_config();
230 assert_eq!(dc.stats().equalities_deferred, 0);
231 }
232
233 #[test]
234 fn test_register_shared_term() {
235 let mut dc = DelayedCombination::default_config();
236
237 let term = TermId::new(0);
238 dc.register_shared_term(term, 0);
239 dc.register_shared_term(term, 1);
240
241 assert!(dc.is_shared(term));
242 }
243
244 #[test]
245 fn test_defer_equality() {
246 let mut dc = DelayedCombination::default_config();
247
248 let lhs = TermId::new(0);
249 let rhs = TermId::new(1);
250
251 dc.defer_equality(lhs, rhs, 0);
252
253 assert_eq!(dc.stats().equalities_deferred, 1);
254 assert_eq!(dc.deferred.len(), 1);
255 }
256
257 #[test]
258 fn test_force_propagation() {
259 let mut dc = DelayedCombination::default_config();
260
261 let lhs = TermId::new(0);
262 let rhs = TermId::new(1);
263
264 dc.defer_equality(lhs, rhs, 0);
265 dc.force_propagation();
266
267 assert_eq!(dc.deferred.len(), 0);
268 assert_eq!(dc.stats().equalities_propagated, 1);
269 assert_eq!(dc.stats().forced_propagations, 1);
270 }
271
272 #[test]
273 fn test_activate_theory() {
274 let mut dc = DelayedCombination::default_config();
275
276 dc.activate_theory(2);
277 assert!(dc.is_theory_active(2));
278 assert!(!dc.is_theory_active(3));
279 }
280
281 #[test]
282 fn test_get_sharing_theories() {
283 let mut dc = DelayedCombination::default_config();
284
285 let term = TermId::new(0);
286 dc.register_shared_term(term, 1);
287 dc.register_shared_term(term, 3);
288
289 let theories = dc.get_sharing_theories(term);
290
291 assert_eq!(theories.len(), 2);
292 assert!(theories.contains(&1));
293 assert!(theories.contains(&3));
294 }
295
296 #[test]
297 fn test_handle_conflict() {
298 let mut dc = DelayedCombination::default_config();
299
300 let lhs = TermId::new(0);
301 let rhs = TermId::new(1);
302
303 dc.defer_equality(lhs, rhs, 0);
304 dc.handle_conflict();
305
306 assert_eq!(dc.deferred.len(), 0);
307 assert_eq!(dc.stats().conflicts_detected, 1);
308 }
309}