oxiz_solver/propagation/
priority_queue.rs1use oxiz_sat::Lit;
18use rustc_hash::FxHashMap;
19use std::cmp::Ordering;
20use std::collections::BinaryHeap;
21
22pub type Priority = u8;
24
25pub type PropagationId = usize;
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum PropagationType {
31 Boolean,
33 Equality,
35 Theory(u8),
37 Heuristic,
39}
40
41impl PropagationType {
42 pub fn priority(&self) -> Priority {
44 match self {
45 PropagationType::Boolean => 0,
46 PropagationType::Equality => 1,
47 PropagationType::Theory(theory_id) => 2 + theory_id,
48 PropagationType::Heuristic => 255,
49 }
50 }
51}
52
53#[derive(Debug, Clone)]
55pub struct Propagation {
56 pub id: PropagationId,
58 pub prop_type: PropagationType,
60 pub priority: Priority,
62 pub literal: Option<Lit>,
64 pub timestamp: u64,
66}
67
68impl PartialEq for Propagation {
69 fn eq(&self, other: &Self) -> bool {
70 self.id == other.id
71 }
72}
73
74impl Eq for Propagation {}
75
76impl PartialOrd for Propagation {
77 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
78 Some(self.cmp(other))
79 }
80}
81
82impl Ord for Propagation {
83 fn cmp(&self, other: &Self) -> Ordering {
84 other
87 .priority
88 .cmp(&self.priority)
89 .then_with(|| other.timestamp.cmp(&self.timestamp))
90 }
91}
92
93#[derive(Debug, Clone)]
95pub struct PriorityQueueConfig {
96 pub use_timestamps: bool,
98 pub max_size: usize,
100}
101
102impl Default for PriorityQueueConfig {
103 fn default() -> Self {
104 Self {
105 use_timestamps: true,
106 max_size: 100_000,
107 }
108 }
109}
110
111#[derive(Debug, Clone, Default)]
113pub struct PriorityQueueStats {
114 pub enqueued: u64,
116 pub dequeued: u64,
118 pub peak_size: usize,
120 pub by_type: FxHashMap<u8, u64>,
122}
123
124#[derive(Debug)]
126pub struct PropagationQueue {
127 heap: BinaryHeap<Propagation>,
129 next_id: PropagationId,
131 timestamp: u64,
133 config: PriorityQueueConfig,
135 stats: PriorityQueueStats,
137}
138
139impl PropagationQueue {
140 pub fn new(config: PriorityQueueConfig) -> Self {
142 Self {
143 heap: BinaryHeap::new(),
144 next_id: 0,
145 timestamp: 0,
146 config,
147 stats: PriorityQueueStats::default(),
148 }
149 }
150
151 pub fn default_config() -> Self {
153 Self::new(PriorityQueueConfig::default())
154 }
155
156 pub fn enqueue(&mut self, prop_type: PropagationType, literal: Option<Lit>) -> PropagationId {
158 let id = self.next_id;
159 self.next_id += 1;
160
161 let priority = prop_type.priority();
162 let timestamp = if self.config.use_timestamps {
163 let ts = self.timestamp;
164 self.timestamp += 1;
165 ts
166 } else {
167 0
168 };
169
170 let prop = Propagation {
171 id,
172 prop_type,
173 priority,
174 literal,
175 timestamp,
176 };
177
178 self.heap.push(prop);
179 self.stats.enqueued += 1;
180
181 if self.heap.len() > self.stats.peak_size {
183 self.stats.peak_size = self.heap.len();
184 }
185
186 *self.stats.by_type.entry(priority).or_insert(0) += 1;
188
189 id
190 }
191
192 pub fn dequeue(&mut self) -> Option<Propagation> {
194 let prop = self.heap.pop()?;
195 self.stats.dequeued += 1;
196 Some(prop)
197 }
198
199 pub fn peek(&self) -> Option<&Propagation> {
201 self.heap.peek()
202 }
203
204 pub fn is_empty(&self) -> bool {
206 self.heap.is_empty()
207 }
208
209 pub fn len(&self) -> usize {
211 self.heap.len()
212 }
213
214 pub fn clear(&mut self) {
216 self.heap.clear();
217 self.timestamp = 0;
218 }
219
220 pub fn stats(&self) -> &PriorityQueueStats {
222 &self.stats
223 }
224
225 pub fn reset_stats(&mut self) {
227 self.stats = PriorityQueueStats::default();
228 }
229}
230
231impl Default for PropagationQueue {
232 fn default() -> Self {
233 Self::default_config()
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240
241 #[test]
242 fn test_queue_creation() {
243 let queue = PropagationQueue::default_config();
244 assert!(queue.is_empty());
245 assert_eq!(queue.len(), 0);
246 }
247
248 #[test]
249 fn test_priority_ordering() {
250 use oxiz_sat::Var;
251 let mut queue = PropagationQueue::default_config();
252
253 let _heuristic = queue.enqueue(PropagationType::Heuristic, None);
255 let _theory = queue.enqueue(PropagationType::Theory(2), None);
256 let _equality = queue.enqueue(PropagationType::Equality, None);
257 let boolean = queue.enqueue(PropagationType::Boolean, Some(Lit::pos(Var::new(0))));
258
259 let first = queue.dequeue().unwrap();
261 assert_eq!(first.id, boolean);
262 assert_eq!(first.priority, 0);
263 }
264
265 #[test]
266 fn test_timestamp_fifo() {
267 let mut queue = PropagationQueue::default_config();
268
269 let id1 = queue.enqueue(PropagationType::Theory(0), None);
271 let id2 = queue.enqueue(PropagationType::Theory(0), None);
272 let id3 = queue.enqueue(PropagationType::Theory(0), None);
273
274 assert_eq!(queue.dequeue().unwrap().id, id1);
276 assert_eq!(queue.dequeue().unwrap().id, id2);
277 assert_eq!(queue.dequeue().unwrap().id, id3);
278 }
279
280 #[test]
281 fn test_peek() {
282 use oxiz_sat::Var;
283 let mut queue = PropagationQueue::default_config();
284 let id = queue.enqueue(PropagationType::Boolean, Some(Lit::pos(Var::new(0))));
285
286 let peeked = queue.peek().unwrap();
287 assert_eq!(peeked.id, id);
288 assert_eq!(queue.len(), 1); let dequeued = queue.dequeue().unwrap();
291 assert_eq!(dequeued.id, id);
292 assert_eq!(queue.len(), 0);
293 }
294
295 #[test]
296 fn test_clear() {
297 use oxiz_sat::Var;
298 let mut queue = PropagationQueue::default_config();
299 queue.enqueue(PropagationType::Boolean, Some(Lit::pos(Var::new(0))));
300 queue.enqueue(PropagationType::Equality, None);
301
302 assert_eq!(queue.len(), 2);
303
304 queue.clear();
305 assert!(queue.is_empty());
306 }
307
308 #[test]
309 fn test_stats() {
310 use oxiz_sat::Var;
311 let mut queue = PropagationQueue::default_config();
312 queue.enqueue(PropagationType::Boolean, Some(Lit::pos(Var::new(0))));
313 queue.enqueue(PropagationType::Boolean, Some(Lit::pos(Var::new(1))));
314 queue.dequeue();
315
316 let stats = queue.stats();
317 assert_eq!(stats.enqueued, 2);
318 assert_eq!(stats.dequeued, 1);
319 assert_eq!(stats.peak_size, 2);
320 }
321}