1#![doc = include_str!("../README.md")]
2
3pub mod ast;
11mod enum_theory;
12mod lra_theory;
13#[cfg(feature = "parser")]
14pub mod parser;
15mod proxy;
16pub mod rational;
17mod sat_solver;
18#[cfg(feature = "solver")]
19pub mod solver;
20
21use crate::{
22 ast::{ArithExpr, BoolExpr, EnumExpr, Expr},
23 enum_theory::EnumTheory,
24 lra_theory::{LraTheory, SparseRow},
25 proxy::{ProxyRegistry, TheoryConstraint},
26 rational::{InfRational, Rational},
27 sat_solver::SatSolver,
28};
29use rug::Assign;
30pub use sat_solver::Lit;
31
32pub struct SeMiTONE {
37 registry: ProxyRegistry,
38 sat_solver: SatSolver,
39 lra_theory: LraTheory,
40 enum_theory: EnumTheory,
41 notified_len: usize,
42 user_scopes: Vec<(usize, usize)>,
43}
44
45impl Default for SeMiTONE {
46 fn default() -> Self {
47 Self::new()
48 }
49}
50
51impl SeMiTONE {
52 pub fn new() -> Self {
54 Self {
55 registry: ProxyRegistry::new(),
56 sat_solver: SatSolver::new(),
57 lra_theory: LraTheory::new(),
58 enum_theory: EnumTheory::new(),
59 notified_len: 0,
60 user_scopes: Vec::new(),
61 }
62 }
63
64 pub fn new_bool(&mut self) -> BoolExpr {
66 BoolExpr::Var(self.sat_solver.mk_var())
67 }
68
69 pub fn new_int(&mut self) -> ArithExpr {
71 ArithExpr::IntVar(self.lra_theory.mk_int())
72 }
73
74 pub fn new_real(&mut self) -> ArithExpr {
76 ArithExpr::RealVar(self.lra_theory.mk_real())
77 }
78
79 pub fn new_enum(&mut self, domain: impl IntoIterator<Item = i32>) -> EnumExpr {
81 EnumExpr::Var(self.enum_theory.mk_var(domain.into_iter().collect()))
82 }
83
84 pub fn num_vars(&self) -> usize {
89 self.sat_solver.num_vars()
90 }
91
92 pub fn add_clause(&mut self, clause: impl IntoIterator<Item = Lit>) -> Result<(), Vec<Lit>> {
100 self.sat_solver.add_clause(clause)
101 }
102
103 pub fn assert(&mut self, expr: &BoolExpr) -> bool {
111 self.assert_internal(expr, true)
112 }
113
114 fn assert_internal(&mut self, expr: &BoolExpr, polarity: bool) -> bool {
115 match (expr, polarity) {
116 (BoolExpr::Not(inner), _) => self.assert_internal(inner, !polarity),
117 (BoolExpr::And(args), true) | (BoolExpr::Or(args), false) => {
118 for arg in args {
119 if !self.assert_internal(arg, polarity) {
120 return false;
121 }
122 }
123 true
124 }
125 (BoolExpr::Or(args), true) | (BoolExpr::And(args), false) => {
126 let mut clause = Vec::with_capacity(args.len());
127 for arg in args {
128 let mut lit = self.encode_bool(arg);
129 if !polarity {
130 lit = !lit;
131 }
132 clause.push(lit);
133 }
134 self.sat_solver.add_clause(clause).is_ok()
135 }
136 (BoolExpr::Lt(e1, e2), _) | (BoolExpr::Le(e1, e2), _) | (BoolExpr::Ge(e1, e2), _) | (BoolExpr::Gt(e1, e2), _) => {
137 let (vars, const_term) = self.diff(e1, e2);
138 if vars.is_empty() {
139 let is_sat = match expr {
140 BoolExpr::Lt(_, _) => const_term.is_negative(),
141 BoolExpr::Le(_, _) => const_term.is_negative() || const_term.is_zero(),
142 BoolExpr::Ge(_, _) => !const_term.is_negative(),
143 BoolExpr::Gt(_, _) => const_term.is_positive(),
144 _ => unreachable!(),
145 };
146 return if polarity { is_sat } else { !is_sat };
147 }
148
149 let (is_upper_bound, eps_val) = match (expr, polarity) {
150 (BoolExpr::Lt(_, _), true) => (true, rug::Rational::from(-1)),
151 (BoolExpr::Lt(_, _), false) => (false, rug::Rational::from(0)),
152 (BoolExpr::Le(_, _), true) => (true, rug::Rational::from(0)),
153 (BoolExpr::Le(_, _), false) => (false, rug::Rational::from(1)),
154 (BoolExpr::Ge(_, _), true) => (false, rug::Rational::from(0)),
155 (BoolExpr::Ge(_, _), false) => (true, rug::Rational::from(-1)),
156 (BoolExpr::Gt(_, _), true) => (false, rug::Rational::from(1)),
157 (BoolExpr::Gt(_, _), false) => (true, rug::Rational::from(0)),
158 _ => unreachable!(),
159 };
160
161 let bound = InfRational::new(Rational::Finite(-const_term), eps_val);
162
163 if vars.len() == 1 {
164 let (var, coeff) = vars.iter().next().unwrap();
165 let final_bound = bound / coeff.clone();
166 if is_upper_bound == coeff.is_positive() { self.lra_theory.set_ub(None, *var, final_bound).is_ok() } else { self.lra_theory.set_lb(None, *var, final_bound).is_ok() }
167 } else {
168 let slack = self.lra_theory.get_or_create_slack(vars);
169 if is_upper_bound { self.lra_theory.set_ub(None, slack, bound).is_ok() } else { self.lra_theory.set_lb(None, slack, bound).is_ok() }
170 }
171 }
172 (BoolExpr::Eq(e1, e2), _) => {
173 if let (Expr::Arith(a1), Expr::Arith(a2)) = (&**e1, &**e2) {
174 let (vars, const_term) = self.diff(a1, a2);
175
176 if vars.is_empty() {
177 let is_sat = const_term.is_zero();
178 return if polarity { is_sat } else { !is_sat };
179 }
180
181 if polarity {
182 let bound = InfRational::new(Rational::Finite(-const_term), rug::Rational::from(0));
183
184 if vars.len() == 1 {
185 let (var, coeff) = vars.iter().next().unwrap();
186 let final_bound = bound / coeff.clone();
187 self.lra_theory.set_lb(None, *var, final_bound.clone()).is_ok() && self.lra_theory.set_ub(None, *var, final_bound).is_ok()
188 } else {
189 let slack = self.lra_theory.get_or_create_slack(vars);
190 self.lra_theory.set_lb(None, slack, bound.clone()).is_ok() && self.lra_theory.set_ub(None, slack, bound).is_ok()
191 }
192 } else {
193 let lt_lit = self.mk_le(a1, a2, true);
194 let gt_lit = self.mk_ge(a1, a2, true);
195 self.sat_solver.add_clause([lt_lit, gt_lit]).is_ok()
196 }
197 } else {
198 let mut lit = self.encode_eq(e1, e2);
199 if !polarity {
200 lit = !lit;
201 }
202 self.sat_solver.add_clause([lit]).is_ok()
203 }
204 }
205 _ => {
206 let mut lit = self.encode_bool(expr);
207 if !polarity {
208 lit = !lit;
209 }
210 self.sat_solver.add_clause([lit]).is_ok()
211 }
212 }
213 }
214
215 pub fn encode_bool(&mut self, expr: &BoolExpr) -> Lit {
224 match expr {
225 BoolExpr::True => Lit::TRUE,
226 BoolExpr::False => Lit::FALSE,
227 BoolExpr::Var(v) => Lit::new(*v, false),
228 BoolExpr::Not(inner) => match inner.as_ref() {
229 BoolExpr::Lt(a1, a2) => self.mk_ge(a1, a2, false),
230 BoolExpr::Le(a1, a2) => self.mk_ge(a1, a2, true),
231 BoolExpr::Ge(a1, a2) => self.mk_le(a1, a2, true),
232 BoolExpr::Gt(a1, a2) => self.mk_le(a1, a2, false),
233 _ => !self.encode_bool(inner),
234 },
235 BoolExpr::And(terms) => {
236 let mut lits = Vec::with_capacity(terms.len());
237 for term in terms {
238 lits.push(self.encode_bool(term));
239 }
240
241 let proxy_var = self.sat_solver.mk_var();
242 let proxy_lit = Lit::new(proxy_var, true);
243
244 for &lit in &lits {
245 self.sat_solver.add_clause([!proxy_lit, lit]).expect("Failed to add clause");
246 }
247
248 let mut big_clause: Vec<Lit> = lits.into_iter().map(|l| !l).collect();
249 big_clause.push(proxy_lit);
250 self.sat_solver.add_clause(big_clause).expect("Failed to add clause");
251
252 proxy_lit
253 }
254 BoolExpr::Or(terms) => {
255 let mut lits = Vec::with_capacity(terms.len());
256 for term in terms {
257 lits.push(self.encode_bool(term));
258 }
259
260 let proxy_var = self.sat_solver.mk_var();
261 let proxy_lit = Lit::new(proxy_var, true);
262
263 for &lit in &lits {
264 self.sat_solver.add_clause([!lit, proxy_lit]).expect("Failed to add clause");
265 }
266
267 let mut big_clause = lits;
268 big_clause.push(!proxy_lit);
269 self.sat_solver.add_clause(big_clause).expect("Failed to add clause");
270
271 proxy_lit
272 }
273 BoolExpr::Lt(e1, e2) => self.mk_le(e1, e2, true),
274 BoolExpr::Le(e1, e2) => self.mk_le(e1, e2, false),
275 BoolExpr::Ge(e1, e2) => self.mk_ge(e1, e2, false),
276 BoolExpr::Gt(e1, e2) => self.mk_ge(e1, e2, true),
277 BoolExpr::Eq(e1, e2) => self.encode_eq(e1, e2),
278 }
279 }
280
281 fn encode_eq(&mut self, expr1: &Expr, expr2: &Expr) -> Lit {
282 match (expr1, expr2) {
283 (Expr::Arith(a1), Expr::Arith(a2)) => self.mk_arith_eq(a1, a2),
284 (Expr::Bool(b1), Expr::Bool(b2)) => {
285 let l1 = self.encode_bool(b1);
286 let l2 = self.encode_bool(b2);
287 let proxy_var = self.sat_solver.mk_var();
288 let p = Lit::new(proxy_var, true);
289
290 self.sat_solver.add_clause([!p, l1, !l2]).expect("Failed to add clause");
291 self.sat_solver.add_clause([!p, !l1, l2]).expect("Failed to add clause");
292 self.sat_solver.add_clause([p, l1, l2]).expect("Failed to add clause");
293 self.sat_solver.add_clause([p, !l1, !l2]).expect("Failed to add clause");
294
295 p
296 }
297 (Expr::Enum(e1), Expr::Enum(e2)) => self.mk_enum_eq(e1, e2),
298 _ => panic!("Type mismatch in Eq: cannot compare different domains.\nLeft: {:?}\nRight: {:?}", expr1, expr2),
299 }
300 }
301
302 fn mk_enum_eq(&mut self, e1: &EnumExpr, e2: &EnumExpr) -> Lit {
303 match (e1, e2) {
304 (EnumExpr::Const(c1), EnumExpr::Const(c2)) => {
305 if c1 == c2 {
306 Lit::TRUE
307 } else {
308 Lit::FALSE
309 }
310 }
311 (EnumExpr::Var(v), EnumExpr::Const(c)) | (EnumExpr::Const(c), EnumExpr::Var(v)) => self.get_or_create_proxy(TheoryConstraint::EnumEq(*v, *c)),
312 (EnumExpr::Var(v1), EnumExpr::Var(v2)) => {
313 if v1 == v2 {
314 return Lit::TRUE;
315 }
316
317 let domain1 = self.enum_theory.initial_domains[*v1].clone();
318 let domain2 = self.enum_theory.initial_domains[*v2].clone();
319 let common: Vec<i32> = domain1.intersection(&domain2).copied().collect();
320
321 if common.is_empty() {
322 return Lit::FALSE;
323 }
324
325 let mut lits = Vec::with_capacity(common.len());
326 for val in common {
327 let p1 = self.get_or_create_proxy(TheoryConstraint::EnumEq(*v1, val));
328 let p2 = self.get_or_create_proxy(TheoryConstraint::EnumEq(*v2, val));
329
330 let and_proxy_var = self.sat_solver.mk_var();
331 let and_proxy = Lit::new(and_proxy_var, false);
332
333 self.sat_solver.add_clause([!and_proxy, p1]).unwrap();
334 self.sat_solver.add_clause([!and_proxy, p2]).unwrap();
335 self.sat_solver.add_clause([!p1, !p2, and_proxy]).unwrap();
336
337 lits.push(and_proxy);
338 }
339
340 let or_proxy_var = self.sat_solver.mk_var();
341 let or_proxy = Lit::new(or_proxy_var, false);
342
343 for &lit in &lits {
344 self.sat_solver.add_clause([!lit, or_proxy]).unwrap();
345 }
346
347 let mut big_clause = lits;
348 big_clause.push(!or_proxy);
349 self.sat_solver.add_clause(big_clause).unwrap();
350
351 or_proxy
352 }
353 }
354 }
355
356 fn mk_le(&mut self, e1: &ArithExpr, e2: &ArithExpr, strict: bool) -> Lit {
357 let (vars, const_term) = self.diff(e1, e2);
358
359 match vars.len() {
360 0 => {
361 if if strict { const_term.is_negative() } else { const_term.is_negative() || const_term.is_zero() } {
362 Lit::TRUE
363 } else {
364 Lit::FALSE
365 }
366 }
367 1 => {
368 let (var, coeff) = vars.iter().next().unwrap();
369 let bound = InfRational::new(Rational::Finite(-const_term / coeff), if strict { rug::Rational::from(-1) } else { rug::Rational::from(0) } / coeff);
370 let bound = if coeff.is_positive() { TheoryConstraint::LraUb(*var, bound) } else { TheoryConstraint::LraLb(*var, bound) };
371 self.get_or_create_proxy(bound)
372 }
373 _ => {
374 let slack = self.lra_theory.get_or_create_slack(vars);
375 let bound = TheoryConstraint::LraUb(slack, InfRational::new(Rational::Finite(-const_term), if strict { rug::Rational::from(-1) } else { rug::Rational::from(0) }));
376 self.get_or_create_proxy(bound)
377 }
378 }
379 }
380
381 fn mk_arith_eq(&mut self, e1: &ArithExpr, e2: &ArithExpr) -> Lit {
382 if e1 == e2 {
383 return Lit::TRUE;
384 }
385
386 let le_lit = self.mk_le(e1, e2, false);
387 let ge_lit = self.mk_ge(e1, e2, false);
388
389 let proxy_var = self.sat_solver.mk_var();
390 let p = Lit::new(proxy_var, false);
391
392 self.sat_solver.add_clause([!p, le_lit]).expect("Failed to add clause");
394 self.sat_solver.add_clause([!p, ge_lit]).expect("Failed to add clause");
396 self.sat_solver.add_clause([!le_lit, !ge_lit, p]).expect("Failed to add clause");
398
399 p
400 }
401
402 fn mk_ge(&mut self, e1: &ArithExpr, e2: &ArithExpr, strict: bool) -> Lit {
403 let (vars, const_term) = self.diff(e1, e2);
404
405 match vars.len() {
406 0 => {
407 if if strict { const_term.is_positive() } else { const_term.is_positive() || const_term.is_zero() } {
408 Lit::TRUE
409 } else {
410 Lit::FALSE
411 }
412 }
413 1 => {
414 let (var, coeff) = vars.iter().next().unwrap();
415 let bound = InfRational::new(Rational::Finite(-const_term / coeff), if strict { rug::Rational::from(1) } else { rug::Rational::from(0) } / coeff);
416 let bound = if coeff.is_positive() { TheoryConstraint::LraLb(*var, bound) } else { TheoryConstraint::LraUb(*var, bound) };
417 self.get_or_create_proxy(bound)
418 }
419 _ => {
420 let slack = self.lra_theory.get_or_create_slack(vars);
421 let bound = TheoryConstraint::LraLb(slack, InfRational::new(Rational::Finite(-const_term), if strict { rug::Rational::from(1) } else { rug::Rational::from(0) }));
422 self.get_or_create_proxy(bound)
423 }
424 }
425 }
426
427 fn diff(&self, e1: &ArithExpr, e2: &ArithExpr) -> (SparseRow, rug::Rational) {
428 let mut vars = SparseRow::new();
429 let mut const_term = rug::Rational::from(0);
430
431 let pos_one = rug::Rational::from(1);
432 let neg_one = rug::Rational::from(-1);
433
434 let mut temp = rug::Rational::new();
435
436 self.accumulate_expr(e1, &pos_one, &mut vars, &mut const_term, &mut temp);
437 self.accumulate_expr(e2, &neg_one, &mut vars, &mut const_term, &mut temp);
438
439 vars.retain(|_, c| *c != 0);
440
441 (vars, const_term)
442 }
443
444 fn accumulate_expr(&self, expr: &ArithExpr, scale: &rug::Rational, vars: &mut SparseRow, const_term: &mut rug::Rational, temp: &mut rug::Rational) {
445 match expr {
446 ArithExpr::Const(c) => {
447 temp.assign(c * scale);
448 *const_term += &*temp;
449 }
450 ArithExpr::IntVar(var) | ArithExpr::RealVar(var) => {
451 self.accumulate_var(*var, scale, vars, temp);
452 }
453 ArithExpr::Add(terms) => {
454 for term in terms {
455 self.accumulate_expr(term, scale, vars, const_term, temp);
456 }
457 }
458 ArithExpr::Mul(terms) => {
459 if terms.len() != 2 {
460 panic!("Only binary multiplication is supported in linear arithmetic");
461 }
462 let (first, second) = (&terms[0], &terms[1]);
463
464 match (first, second) {
466 (ArithExpr::Const(c), sub_expr) | (sub_expr, ArithExpr::Const(c)) => {
467 let mut new_scale = rug::Rational::new();
468 new_scale.assign(c * scale);
469 self.accumulate_expr(sub_expr, &new_scale, vars, const_term, temp);
470 }
471 _ => {
472 panic!("Non-linear arithmetic: multiplication between two non-constant expressions is not supported");
473 }
474 }
475 }
476 ArithExpr::Div(numerator, denominator) => {
477 if let ArithExpr::Const(c) = &**denominator {
479 if c.is_zero() {
480 panic!("Division by zero detected in AST");
481 }
482 let mut div_scale = rug::Rational::new();
483 div_scale.assign(scale / c);
484 self.accumulate_expr(numerator, &div_scale, vars, const_term, temp);
485 } else {
486 panic!("Non-linear arithmetic: division by a non-constant expression is not supported");
487 }
488 }
489 ArithExpr::Neg(sub_expr) => {
490 let mut neg_scale = rug::Rational::new();
491 neg_scale.assign(scale * -1);
492 self.accumulate_expr(sub_expr, &neg_scale, vars, const_term, temp);
493 }
494 }
495 }
496
497 fn accumulate_var(&self, var: usize, scale: &rug::Rational, vars: &mut SparseRow, temp: &mut rug::Rational) {
498 if let Some(basic_row) = self.lra_theory.tableau.get(&var) {
499 for (sub_var, sub_coeff) in basic_row.iter() {
500 temp.assign(sub_coeff * scale);
501 vars.add_coeff(*sub_var, temp);
502 }
503 } else {
504 vars.add_coeff(var, scale);
505 }
506 }
507
508 fn get_or_create_proxy(&mut self, constraint: TheoryConstraint) -> Lit {
509 if let Some(&sat_var) = self.registry.get_proxy(&constraint) {
510 sat_var
511 } else {
512 let sat_var = self.sat_solver.mk_var();
513 self.registry.register(constraint, Lit::new(sat_var, false));
514 Lit::new(sat_var, false)
515 }
516 }
517
518 pub fn decide(&mut self, lit: Lit) -> bool {
527 self.sat_solver.push();
528 self.lra_theory.push();
529 self.enum_theory.push();
530 self.sat_solver.enqueue_decision(lit)
531 }
532
533 pub fn decide_enum(&mut self, expr: &EnumExpr, value: i32) -> bool {
538 let eq_expr = TheoryConstraint::EnumEq(
539 match expr {
540 EnumExpr::Var(v) => *v,
541 EnumExpr::Const(_) => panic!("Cannot decide on a constant value"),
542 },
543 value,
544 );
545 let lit = self.get_or_create_proxy(eq_expr);
546 self.decide(lit)
547 }
548
549 pub fn decision_level(&self) -> usize {
553 self.sat_solver.decision_level()
554 }
555
556 pub fn cancel_until(&mut self, level: usize) {
561 self.sat_solver.cancel_until(level);
562 self.lra_theory.cancel_until(level);
563 self.enum_theory.cancel_until(level);
564 self.notified_len = self.sat_solver.trail.len();
565 }
566
567 pub fn get_trail_delta(&self, from_index: usize) -> &[Lit] {
571 &self.sat_solver.trail[from_index..]
572 }
573
574 pub fn current_trail_len(&self) -> usize {
576 self.sat_solver.trail.len()
577 }
578
579 pub fn get_trail_len_at_level(&self, level: usize) -> usize {
584 if level < self.sat_solver.trail_lim.len() { self.sat_solver.trail_lim[level] } else { self.sat_solver.trail.len() }
585 }
586
587 pub fn get_trail_slice(&self, start: usize, end: usize) -> &[Lit] {
589 &self.sat_solver.trail[start..end]
590 }
591
592 pub fn user_scopes_len(&self) -> usize {
594 self.user_scopes.len()
595 }
596
597 pub fn propagate(&mut self) -> Result<(), (usize, Vec<Lit>)> {
606 let base_level = self.user_scopes.last().map(|&(lvl, _)| lvl).unwrap_or(0);
607 if let Err((bt_level, conflict)) = self.sat_solver.propagate() {
608 return Err((bt_level.max(base_level), conflict));
609 }
610
611 while self.notified_len < self.sat_solver.trail.len() {
612 let lit = self.sat_solver.trail[self.notified_len];
613
614 if let Some(constraint) = self.registry.get_constraint(lit).or_else(|| self.registry.get_constraint(!lit)) {
615 let theory_result = match (constraint, lit.sign()) {
616 (TheoryConstraint::LraUb(var, bound), false) => self.lra_theory.set_ub(Some(lit), *var, bound.clone()),
617 (TheoryConstraint::LraLb(var, bound), true) => self.lra_theory.set_ub(Some(lit), *var, InfRational::new(bound.rational_part().clone(), if bound.infinitesimal_part().is_positive() { rug::Rational::from(0) } else { rug::Rational::from(-1) })),
618 (TheoryConstraint::LraLb(var, bound), false) => self.lra_theory.set_lb(Some(lit), *var, bound.clone()),
619 (TheoryConstraint::LraUb(var, bound), true) => self.lra_theory.set_lb(Some(lit), *var, InfRational::new(bound.rational_part().clone(), if bound.infinitesimal_part().is_negative() { rug::Rational::from(0) } else { rug::Rational::from(1) })),
620 (TheoryConstraint::EnumEq(var, val), false) => self.enum_theory.set_eq(Some(lit), *var, *val),
621 (TheoryConstraint::EnumEq(var, val), true) => self.enum_theory.set_neq(Some(lit), *var, *val),
622 };
623
624 if let Err(lemma) = theory_result {
625 return Err((self.compute_backtrack_level(&lemma, base_level), lemma));
626 }
627 }
628 self.notified_len += 1;
629 }
630
631 if let Err(conflict) = self.lra_theory.check() {
632 return Err((self.compute_backtrack_level(&conflict, base_level), conflict));
633 }
634 Ok(())
635 }
636
637 pub fn get_lit_val(&self, lit: Lit) -> Option<bool> {
639 self.sat_solver.lit_value(lit)
640 }
641
642 pub fn get_bool_val(&self, expr: &BoolExpr) -> Option<bool> {
644 match expr {
645 BoolExpr::True => Some(true),
646 BoolExpr::False => Some(false),
647 BoolExpr::Var(v) => *self.sat_solver.value(*v),
648 BoolExpr::Not(inner) => self.get_bool_val(inner).map(|val| !val),
649 BoolExpr::And(args) => {
650 let mut result = Some(true);
651 for arg in args {
652 match self.get_bool_val(arg) {
653 Some(val) => {
654 if !val {
655 return Some(false);
656 }
657 }
658 None => result = None,
659 }
660 }
661 result
662 }
663 BoolExpr::Or(args) => {
664 let mut result = Some(false);
665 for arg in args {
666 match self.get_bool_val(arg) {
667 Some(val) => {
668 if val {
669 return Some(true);
670 }
671 }
672 None => result = None,
673 }
674 }
675 result
676 }
677 BoolExpr::Lt(e1, e2) => {
678 let (lb1, ub1) = self.get_arith_bounds(e1)?;
679 let (lb2, ub2) = self.get_arith_bounds(e2)?;
680 if ub1 < lb2 {
681 Some(true)
682 } else if lb1 >= ub2 {
683 Some(false)
684 } else {
685 None
686 }
687 }
688 BoolExpr::Le(e1, e2) => {
689 let (lb1, ub1) = self.get_arith_bounds(e1)?;
690 let (lb2, ub2) = self.get_arith_bounds(e2)?;
691 if ub1 <= lb2 {
692 Some(true)
693 } else if lb1 > ub2 {
694 Some(false)
695 } else {
696 None
697 }
698 }
699 BoolExpr::Ge(e1, e2) => {
700 let (lb1, ub1) = self.get_arith_bounds(e1)?;
701 let (lb2, ub2) = self.get_arith_bounds(e2)?;
702 if lb1 >= ub2 {
703 Some(true)
704 } else if ub1 < lb2 {
705 Some(false)
706 } else {
707 None
708 }
709 }
710 BoolExpr::Gt(e1, e2) => {
711 let (lb1, ub1) = self.get_arith_bounds(e1)?;
712 let (lb2, ub2) = self.get_arith_bounds(e2)?;
713 if lb1 > ub2 {
714 Some(true)
715 } else if ub1 <= lb2 {
716 Some(false)
717 } else {
718 None
719 }
720 }
721 BoolExpr::Eq(e1, e2) => match (e1.as_ref(), e2.as_ref()) {
722 (Expr::Arith(a1), Expr::Arith(a2)) => {
723 let (lb1, ub1) = self.get_arith_bounds(a1)?;
724 let (lb2, ub2) = self.get_arith_bounds(a2)?;
725 if ub1 < lb2 || ub2 < lb1 {
726 Some(false)
727 } else if lb1 == ub1 && lb2 == ub2 && lb1 == lb2 {
728 Some(true)
729 } else {
730 None
731 }
732 }
733 (Expr::Bool(b1), Expr::Bool(b2)) => {
734 let val1 = self.get_bool_val(b1);
735 let val2 = self.get_bool_val(b2);
736 match (val1, val2) {
737 (Some(v1), Some(v2)) => Some(v1 == v2),
738 _ => None,
739 }
740 }
741 (Expr::Enum(e1), Expr::Enum(e2)) => {
742 let val1 = self.get_enum_val(e1);
743 let val2 = self.get_enum_val(e2);
744 match (val1, val2) {
745 (Some(v1), Some(v2)) => Some(v1 == v2),
746 _ => None,
747 }
748 }
749 _ => None,
750 },
751 }
752 }
753
754 pub fn get_arith_val(&self, expr: &ArithExpr) -> Option<InfRational> {
756 match expr {
757 ArithExpr::Const(c) => Some(InfRational::new(Rational::Finite(c.clone()), rug::Rational::from(0))),
758 ArithExpr::RealVar(v) | ArithExpr::IntVar(v) => Some(self.lra_theory.value(*v).clone()),
759 ArithExpr::Add(terms) => {
760 let mut sum = InfRational::new(Rational::Finite(rug::Rational::from(0)), rug::Rational::from(0));
761 for term in terms {
762 sum += self.get_arith_val(term)?;
763 }
764 Some(sum)
765 }
766 ArithExpr::Neg(term) => self.get_arith_val(term).map(|val| -val),
767 _ => None,
768 }
769 }
770
771 pub fn get_arith_bounds(&self, expr: &ArithExpr) -> Option<(InfRational, InfRational)> {
773 match expr {
774 ArithExpr::Const(c) => {
775 let val = InfRational::new(Rational::Finite(c.clone()), rug::Rational::from(0));
776 Some((val.clone(), val))
777 }
778 ArithExpr::RealVar(v) | ArithExpr::IntVar(v) => Some((self.lra_theory.lb(*v).clone(), self.lra_theory.ub(*v).clone())),
779 ArithExpr::Add(terms) => {
780 let mut sum_lb = InfRational::new(Rational::Finite(rug::Rational::from(0)), rug::Rational::from(0));
781 let mut sum_ub = sum_lb.clone();
782 for term in terms {
783 let (lb, ub) = self.get_arith_bounds(term)?;
784 sum_lb += lb;
785 sum_ub += ub;
786 }
787 Some((sum_lb, sum_ub))
788 }
789 ArithExpr::Neg(term) => {
790 let (lb, ub) = self.get_arith_bounds(term)?;
791 Some((-ub, -lb))
792 }
793 _ => None,
794 }
795 }
796
797 pub fn get_enum_val(&self, expr: &EnumExpr) -> Option<i32> {
799 match expr {
800 EnumExpr::Const(c) => Some(*c),
801 EnumExpr::Var(v) => {
802 let domain = &self.enum_theory.active_domains[*v];
803 if domain.len() == 1 { domain.iter().next().copied() } else { None }
804 }
805 }
806 }
807
808 pub fn push(&mut self) {
813 let clauses_len = self.sat_solver.clauses.len();
814
815 self.sat_solver.push();
816 self.lra_theory.push();
817 self.enum_theory.push();
818
819 let current_level = self.sat_solver.decision_level();
820 self.user_scopes.push((current_level, clauses_len));
821 }
822
823 pub fn pop(&mut self) {
827 if let Some((saved_level, saved_clauses_len)) = self.user_scopes.pop() {
828 let target_level = saved_level - 1;
829 self.sat_solver.cancel_until(target_level);
830 self.lra_theory.cancel_until(target_level);
831 self.enum_theory.cancel_until(target_level);
832 self.notified_len = self.sat_solver.trail.len();
833
834 for watch_list in self.sat_solver.watches.iter_mut() {
835 watch_list.retain(|&clause_idx| clause_idx < saved_clauses_len);
836 }
837 self.sat_solver.clauses.truncate(saved_clauses_len);
838 }
839 }
840
841 fn compute_backtrack_level(&self, lemma: &[Lit], root_level: usize) -> usize {
842 if lemma.len() <= 1 {
843 return root_level;
844 }
845
846 let mut levels: Vec<usize> = lemma.iter().map(|lit| self.sat_solver.level(lit.var()).expect("literal should have a decision level")).collect();
847
848 levels.sort_unstable_by(|a, b| b.cmp(a));
849
850 let max_level = levels[0];
851
852 if max_level <= root_level {
853 return root_level;
854 }
855
856 if levels.iter().filter(|&&l| l == max_level).count() == 1 { levels[1].max(root_level) } else { (max_level - 1).max(root_level) }
857 }
858
859 pub fn check_ints(&mut self) -> Result<(), (usize, Vec<Lit>)> {
865 if let Err((var, frac_val)) = self.lra_theory.check_ints() {
866 let base_level = self.user_scopes.last().map(|&(lvl, _)| lvl).unwrap_or(0);
867 if let Some((cut_row, f0)) = self.lra_theory.generate_gomory_cut(var) {
868 let cut_slack = self.lra_theory.get_or_create_slack(cut_row);
869 let bound = InfRational::new(Rational::Finite(f0), rug::Rational::from(0));
870 let cut_lit = self.get_or_create_proxy(TheoryConstraint::LraLb(cut_slack, bound));
871
872 let lemma = vec![cut_lit];
873 let base_level = self.user_scopes.last().map(|&(lvl, _)| lvl).unwrap_or(0);
874 return Err((base_level, lemma));
875 }
876
877 let Rational::Finite(inner_frac) = frac_val else {
878 unreachable!("Fractional variable in Branch & Bound must be finite");
879 };
880
881 let mut floor_val = inner_frac.clone();
882 floor_val.floor_mut();
883 let mut ceil_val = inner_frac;
884 ceil_val.ceil_mut();
885
886 let ub = InfRational::new(Rational::Finite(floor_val), rug::Rational::from(0));
887 let lb = InfRational::new(Rational::Finite(ceil_val), rug::Rational::from(0));
888
889 let lit_ub = self.get_or_create_proxy(TheoryConstraint::LraUb(var, ub));
890 let lit_lb = self.get_or_create_proxy(TheoryConstraint::LraLb(var, lb));
891
892 let lemma = vec![lit_ub, lit_lb];
893 return Err((base_level, lemma));
894 }
895 Ok(())
896 }
897}
898
899#[cold]
900#[inline(never)]
901pub(crate) fn out_of_bounds(var: usize) -> ! {
902 panic!("variable index out of bounds: {}", var);
903}
904
905#[cfg(test)]
906mod tests {
907 use super::*;
908
909 #[test]
910 fn test_pure_sat_resolution() {
911 let mut solver = SeMiTONE::new();
912
913 let a = solver.new_bool();
914 let b = solver.new_bool();
915 let c = solver.new_bool();
916
917 let expr = (&a | &b) & (&!&b | &c) & (!&b | !&c) & !&a;
920
921 let _ = solver.assert(&expr);
922 assert!(solver.propagate().is_err(), "The solver should detect that the system is unsatisfiable");
923 }
924
925 #[test]
926 fn test_early_bounding_unsat() {
927 let mut solver = SeMiTONE::new();
928 let x = solver.new_real();
929
930 let expr = (x.clone().gt(10)) & (x.lt(5));
932
933 let result = solver.assert(&expr);
934 assert!(!result, "The solver should detect that the system is unsatisfiable");
935 }
936
937 #[test]
938 fn test_equality_mutually_exclusive() {
939 let mut solver = SeMiTONE::new();
940 let x = solver.new_real();
941
942 let expr = (x.clone().eq(5)) & (x.gt(6));
944
945 let result = solver.assert(&expr);
946 assert!(!result, "The solver should detect that the system is unsatisfiable");
947 }
948
949 #[test]
950 fn test_simplex_system_unsat() {
951 let mut solver = SeMiTONE::new();
952 let x = solver.new_real();
953 let y = solver.new_real();
954
955 let eq_expr = (x.clone() + y.clone()).eq(10);
957 let gt_x = x.clone().gt(6);
959 let gt_y = y.clone().gt(6);
961
962 let expr = eq_expr & gt_x & gt_y;
963
964 let _ = solver.assert(&expr);
965 assert!(solver.propagate().is_err(), "The solver should detect that the system is unsatisfiable");
966 }
967
968 #[test]
969 fn test_constant_arithmetic_evaluations() {
970 let mut solver = SeMiTONE::new();
971
972 assert!(solver.assert(&ArithExpr::from(5).lt(10)));
973 assert!(solver.assert(&ArithExpr::from(5).le(5)));
974 assert!(solver.assert(&ArithExpr::from(10).ge(5)));
975 assert!(solver.assert(&ArithExpr::from(10).gt(5)));
976
977 assert!(!solver.assert(&ArithExpr::from(10).lt(5)));
978 assert!(!solver.assert(&ArithExpr::from(10).le(5)));
979 assert!(!solver.assert(&ArithExpr::from(5).ge(10)));
980 assert!(!solver.assert(&ArithExpr::from(5).gt(10)));
981
982 assert!(solver.assert(&!ArithExpr::from(10).lt(5)));
983 assert!(!solver.assert(&!ArithExpr::from(5).lt(10)));
984 }
985
986 #[test]
987 fn test_negated_variable_inequalities() {
988 let mut solver = SeMiTONE::new();
989 let x = solver.new_real();
990
991 solver.push();
992 assert!(solver.assert(&!x.clone().lt(5)));
993 assert!(!solver.assert(&x.clone().lt(4)));
994 solver.pop();
995
996 solver.push();
997 assert!(solver.assert(&!x.clone().le(5)));
998 assert!(!solver.assert(&x.clone().le(5)));
999 solver.pop();
1000
1001 solver.push();
1002 assert!(solver.assert(&!x.clone().ge(5)));
1003 assert!(!solver.assert(&x.clone().ge(5)));
1004 solver.pop();
1005
1006 solver.push();
1007 assert!(solver.assert(&!x.clone().gt(5)));
1008 assert!(!solver.assert(&x.clone().gt(5)));
1009 solver.pop();
1010 }
1011
1012 #[test]
1013 #[should_panic(expected = "Type mismatch in Eq")]
1014 fn test_encode_eq_type_mismatch_panic() {
1015 let mut solver = SeMiTONE::new();
1016 let a = solver.new_bool();
1017 let x = solver.new_real();
1018
1019 let bad_eq = BoolExpr::Eq(Box::new(Expr::Bool(a)), Box::new(Expr::Arith(x)));
1020
1021 let _ = solver.assert(&bad_eq);
1022 }
1023
1024 #[test]
1025 fn test_enum_out_of_domain_unsat() {
1026 let mut solver = SeMiTONE::new();
1027 let e = solver.new_enum(vec![1, 2]);
1028
1029 let expr = e.eq(3);
1030
1031 let _ = solver.assert(&expr);
1032 assert!(solver.propagate().is_err(), "The solver should detect that the enum variable cannot take a value outside its domain");
1033 }
1034
1035 #[test]
1036 fn test_enum_exhaustive_denial_integration() {
1037 let mut solver = SeMiTONE::new();
1038 let e = solver.new_enum(vec![1, 2]);
1039
1040 let expr = !(e.clone().eq(1)) & !(e.clone().eq(2));
1042
1043 let _ = solver.assert(&expr);
1044 assert!(solver.propagate().is_err(), "The solver should detect that the enum variable cannot take a value outside its domain");
1045 }
1046}