1pub mod aig;
34pub mod blast;
35mod cuts;
36pub mod domain;
37pub mod drup;
38mod exhaustion;
39pub mod finite;
40mod fp;
41mod relations;
42mod rule;
43mod sample_words;
44pub mod sat;
45#[cfg(test)]
46mod tests;
47
48use crate::error::Error;
49use crate::expr::{Context, Expr, OpCode};
50use crate::facts::Assumptions;
51use crate::hash::IdMap;
52use crate::{BitVec, SymbolKey, Width};
53
54pub use exhaustion::Exhaustion;
55pub use rule::{RuleOutcome, WidthReport, rule, rule_all_widths};
56
57use aig::{Aig, Cnf, L};
58use blast::Bits;
59pub use sat::Limits;
60use sat::{Answer, Lit, Solver, Step};
61
62#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
66pub enum SampleMode {
67 #[default]
69 Random,
70 Small,
75 Words,
81}
82
83#[derive(Copy, Clone, Debug, PartialEq, Eq)]
85#[non_exhaustive]
86pub struct Config {
87 pub max_conflicts: u64,
89 pub max_propagations: u64,
93 pub max_nodes: usize,
95 pub certificate: bool,
97 pub simplify: bool,
103 pub samples: u32,
109 pub sample_mode: SampleMode,
111 pub relational_lemmas: bool,
115 pub selector_branching: bool,
118 pub input_cancellation: bool,
122 pub join_factoring: bool,
125 pub xor3_encoding: bool,
128 pub carry_save_multiplication: bool,
131 pub word_sampling: bool,
137 pub exhaustive_inputs: u8,
148}
149
150impl Default for Config {
151 fn default() -> Self {
152 Config {
153 max_conflicts: 1_000_000,
154 max_propagations: u64::MAX,
155 max_nodes: 4_000_000,
156 certificate: false,
157 simplify: true,
158 samples: 256,
159 sample_mode: SampleMode::Random,
160 relational_lemmas: false,
161 selector_branching: false,
162 input_cancellation: false,
163 join_factoring: false,
164 xor3_encoding: false,
165 carry_save_multiplication: false,
166 word_sampling: false,
167 exhaustive_inputs: 0,
168 }
169 }
170}
171
172setters!(Config {
173 with_max_conflicts: max_conflicts: u64,
174 with_max_propagations: max_propagations: u64,
175 with_max_nodes: max_nodes: usize,
176 with_certificate: certificate: bool,
177 with_simplify: simplify: bool,
178 with_samples: samples: u32,
179 with_sample_mode: sample_mode: SampleMode,
180 with_relational_lemmas: relational_lemmas: bool,
181 with_selector_branching: selector_branching: bool,
182 with_input_cancellation: input_cancellation: bool,
183 with_join_factoring: join_factoring: bool,
184 with_xor3_encoding: xor3_encoding: bool,
185 with_carry_save_multiplication: carry_save_multiplication: bool,
186 with_word_sampling: word_sampling: bool,
187 with_exhaustive_inputs: exhaustive_inputs: u8,
188});
189
190impl Config {
191 pub fn limits(&self) -> Limits {
194 Limits {
195 conflicts: self.max_conflicts,
196 propagations: self.max_propagations,
197 }
198 }
199}
200
201fn simplifier() -> &'static crate::engine::Engine {
203 static ENGINE: std::sync::OnceLock<crate::engine::Engine> = std::sync::OnceLock::new();
204 ENGINE.get_or_init(|| {
205 #[cfg(feature = "mba")]
206 let strategy =
207 crate::engine::Strategy::deobfuscate().with_mba(crate::mba::MbaConfig::default());
208 #[cfg(not(feature = "mba"))]
209 let strategy = crate::engine::Strategy::deobfuscate();
210 crate::engine::Engine::builder()
211 .builtin()
212 .strategy(strategy)
213 .build()
214 .unwrap_or_else(|_| crate::engine::Engine::standard())
215 })
216}
217
218#[derive(Clone, Debug)]
221pub struct Certificate {
222 pub vars: u32,
224 pub clauses: Vec<Vec<Lit>>,
226 pub proof: Vec<Step>,
228 pub exhaustion: Option<Exhaustion>,
232}
233
234impl Certificate {
235 pub fn check(&self) -> Result<(), String> {
238 match &self.exhaustion {
239 Some(exhaustion) => exhaustion.check(),
240 None => drup::check(&self.clauses, &self.proof),
241 }
242 }
243
244 pub fn dimacs(&self) -> String {
246 let mut s = format!("p cnf {} {}\n", self.vars, self.clauses.len());
247 for c in &self.clauses {
248 for l in c {
249 s.push_str(&l.dimacs().to_string());
250 s.push(' ');
251 }
252 s.push_str("0\n");
253 }
254 s
255 }
256
257 pub fn drat(&self) -> String {
259 let mut s = String::new();
260 for step in &self.proof {
261 let (pre, c) = match step {
262 Step::Add(c) => ("", c),
263 Step::Delete(c) => ("d ", c),
264 };
265 s.push_str(pre);
266 for l in c {
267 s.push_str(&l.dimacs().to_string());
268 s.push(' ');
269 }
270 s.push_str("0\n");
271 }
272 s
273 }
274}
275
276#[derive(Clone, Debug)]
278#[non_exhaustive]
279pub enum Outcome {
280 Proved(Option<Certificate>),
282 Refuted(Vec<(SymbolKey, BitVec)>),
284 Unknown(Unknown),
286}
287
288#[derive(Clone, Debug, PartialEq, Eq)]
290#[non_exhaustive]
291pub enum Unknown {
292 Budget {
296 conflicts: u64,
298 propagations: u64,
300 },
301 TooLarge {
304 nodes: usize,
306 },
307 Unsupported(String),
310}
311
312impl Unknown {
313 pub fn is_budget(&self) -> bool {
315 matches!(self, Unknown::Budget { .. })
316 }
317}
318
319impl core::fmt::Display for Unknown {
320 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
321 match self {
322 Unknown::Budget {
323 conflicts,
324 propagations,
325 } => write!(
326 f,
327 "no answer within {conflicts} conflicts ({propagations} propagations)"
328 ),
329 Unknown::TooLarge { nodes } => {
330 write!(f, "the circuit is too large ({nodes} nodes built)")
331 }
332 Unknown::Unsupported(why) => f.write_str(why),
333 }
334 }
335}
336
337#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
339#[non_exhaustive]
340pub struct Stats {
341 pub nodes: usize,
343 pub samples: u64,
345 pub vars: u32,
347 pub clauses: usize,
349 pub learned: usize,
351 pub conflicts: u64,
353 pub decisions: u64,
355 pub propagations: u64,
357}
358
359pub(crate) struct Blaster<'c> {
361 pub(crate) cx: &'c mut Context,
362 pub(crate) g: Aig,
363 bits: IdMap<u32, Bits>,
364 pub(crate) symbols: Vec<(u32, Bits)>,
366 selectors: Vec<L>,
367 selector_hints: bool,
368 input_cancellation: bool,
369 join_factoring: bool,
370 carry_save_multiplication: bool,
371 max_nodes: usize,
372 too_large: bool,
374}
375
376impl<'c> Blaster<'c> {
377 pub(crate) fn new(cx: &'c mut Context, max_nodes: usize) -> Blaster<'c> {
378 Blaster {
379 cx,
380 g: Aig::new(),
381 bits: IdMap::default(),
382 symbols: Vec::new(),
383 selectors: Vec::new(),
384 selector_hints: false,
385 input_cancellation: false,
386 join_factoring: false,
387 carry_save_multiplication: false,
388 max_nodes,
389 too_large: false,
390 }
391 }
392
393 pub(crate) fn blast(&mut self, root: u32) -> Result<Bits, Error> {
395 let bits = &self.bits;
396 let order = self
397 .cx
398 .post_order_ids_pruned(&[root], |e| bits.contains_key(&e.index()));
399 for i in order {
400 if self.bits.contains_key(&i) {
401 continue;
402 }
403 if self.g.len() > self.max_nodes {
404 self.too_large = true;
405 return Err(Error::Unsupported("the circuit is too large".into()));
406 }
407 let b = self.node(i)?;
408 self.bits.insert(i, b);
409 }
410 Ok(self.bits[&root].clone())
411 }
412
413 fn node(&mut self, i: u32) -> Result<Bits, Error> {
414 use blast::*;
415 let n = self.cx.node(i);
416 let w = usize::from(n.width);
417 let get = |s: &Self, j: u32| s.bits[&j].clone();
418 let g = &mut self.g;
419 Ok(match n.op {
420 OpCode::Const => konst(&self.cx.const_val(i).unwrap_or(BitVec::zero(Width::W1))),
421 OpCode::Sym => {
422 let known = self.cx.declared_at(i);
425 let b: Bits = (0..w)
426 .map(|j| match known {
427 Some(k) if k.known_one().bit(j as u16) == Some(true) => aig::TRUE,
428 Some(k) if k.known_zero().bit(j as u16) == Some(true) => aig::FALSE,
429 _ => g.input(),
430 })
431 .collect();
432 self.symbols.push((i, b.clone()));
433 b
434 }
435 OpCode::Not => not(g, &self.bits[&n.a]),
436 OpCode::Neg => {
437 let a = get(self, n.a);
438 neg(&mut self.g, &a)
439 }
440 OpCode::Popcnt => popcnt(g, &self.bits[&n.a]),
441 OpCode::Clz => clz(g, &self.bits[&n.a]),
442 OpCode::Ctz => ctz(g, &self.bits[&n.a]),
443 OpCode::Bswap => bswap(&self.bits[&n.a]),
444 OpCode::BitRev => bitrev(&self.bits[&n.a]),
445 OpCode::Zext => {
446 let mut a = get(self, n.a);
447 a.resize(w, aig::FALSE);
448 a
449 }
450 OpCode::Sext => {
451 let mut a = get(self, n.a);
452 let top = *a.last().unwrap_or(&aig::FALSE);
453 a.resize(w, top);
454 a
455 }
456 OpCode::Extract => {
457 let a = get(self, n.a);
458 let lo = n.b as usize;
459 a[lo..lo + w].to_vec()
460 }
461 OpCode::Concat => {
462 let (hi, mut lo) = (get(self, n.a), get(self, n.b));
463 lo.extend(hi);
464 lo
465 }
466 OpCode::Select => {
467 let (c, t, e) = (get(self, n.a)[0], get(self, n.b), get(self, n.c));
468 mux(&mut self.g, c, &t, &e)
469 }
470 OpCode::Eq | OpCode::Ne | OpCode::Ult | OpCode::Ule | OpCode::Slt | OpCode::Sle => {
471 let (a, b) = (get(self, n.a), get(self, n.b));
472 let g = &mut self.g;
473 let r = match n.op {
474 OpCode::Eq if self.join_factoring => eq_factored(g, &a, &b),
475 OpCode::Ne if self.join_factoring => eq_factored(g, &a, &b) ^ 1,
476 OpCode::Eq => eq(g, &a, &b),
477 OpCode::Ne => eq(g, &a, &b) ^ 1,
478 OpCode::Ult => ult(g, &a, &b),
479 OpCode::Ule => ule(g, &a, &b),
480 OpCode::Slt => slt(g, &a, &b),
481 _ => sle(g, &a, &b),
482 };
483 vec![r]
484 }
485 op if op.as_bin().is_some() => {
486 let (a, b) = (get(self, n.a), get(self, n.b));
487 if self.selector_hints && matches!(op, OpCode::Shl | OpCode::LShr | OpCode::AShr) {
488 let unknown: Vec<_> = b
489 .iter()
490 .copied()
491 .filter(|&l| l > aig::TRUE)
492 .take(5)
493 .collect();
494 if !unknown.is_empty() && unknown.len() <= 4 {
495 self.selectors.extend(unknown);
496 }
497 }
498 let g = &mut self.g;
499 match op {
500 OpCode::Add => add(g, &a, &b),
501 OpCode::Sub => sub(g, &a, &b),
502 OpCode::Mul if self.carry_save_multiplication => mul_carry_save(g, &a, &b),
503 OpCode::Mul => mul(g, &a, &b),
504 OpCode::UMulHi => mulhi(g, &a, &b, false),
505 OpCode::SMulHi => mulhi(g, &a, &b, true),
506 OpCode::UDiv => udivrem(g, &a, &b).0,
507 OpCode::URem => udivrem(g, &a, &b).1,
508 OpCode::SDiv => sdiv(g, &a, &b),
509 OpCode::SRem => srem(g, &a, &b),
510 OpCode::And => and(g, &a, &b),
511 OpCode::Or => or(g, &a, &b),
512 OpCode::Xor if self.input_cancellation => xor_cancel_input(g, &a, &b),
513 OpCode::Xor => xor(g, &a, &b),
514 OpCode::Shl => shift(g, &a, &b, true, aig::FALSE),
515 OpCode::LShr => shift(g, &a, &b, false, aig::FALSE),
516 OpCode::AShr => {
517 let s = *a.last().unwrap_or(&aig::FALSE);
518 shift(g, &a, &b, false, s)
519 }
520 OpCode::RotL => rotate(g, &a, &b, true),
521 OpCode::RotR => rotate(g, &a, &b, false),
522 OpCode::Pdep => pdep(g, &a, &b),
523 _ => pext(g, &a, &b),
524 }
525 }
526 op if op.as_ext().is_some() => self.ext(i)?,
527 _ => {
528 let kids: Vec<Bits> = n.children().map(|c| self.bits[&c].clone()).collect();
529 fp::blast(self, i, &kids)?
530 }
531 })
532 }
533
534 fn ext(&mut self, i: u32) -> Result<Bits, Error> {
536 let n = self.cx.node(i);
537 let Some((_, k)) = n.op.as_ext() else {
538 return Err(Error::Unsupported("not an extension output".into()));
539 };
540 let args: Vec<Expr> = n.children().map(|c| self.cx.handle(c)).collect();
541 let reg = self
542 .cx
543 .registry
544 .clone()
545 .ok_or_else(|| Error::Unsupported("an extension without its registry".into()))?;
546 let op = reg
547 .op_at(n.aux)
548 .ok_or_else(|| Error::Unsupported("an extension without its operation".into()))?;
549 let Some(ex) = op.expand(self.cx, &args) else {
550 return Err(Error::Unsupported(format!(
551 "`{}` has no expansion to reason about",
552 op.name()
553 )));
554 };
555 let outs = ex?;
556 let e = self.cx.id(outs[k])?;
557 self.blast(e)
558 }
559
560 fn unknown(&self, e: Error) -> Result<Unknown, Error> {
563 match e {
564 _ if self.too_large => Ok(Unknown::TooLarge {
565 nodes: self.g.len(),
566 }),
567 Error::Unsupported(why) => Ok(Unknown::Unsupported(why)),
568 e => Err(e),
569 }
570 }
571}
572
573struct Search {
575 solver: Solver,
576 cnf: Cnf,
577}
578
579pub struct Question {
604 root: Expr,
606 symbols: Vec<(SymbolKey, Width, Bits)>,
609 search: Option<Box<Search>>,
610 outcome: Option<Outcome>,
611 stats: Stats,
612}
613
614impl core::fmt::Debug for Question {
615 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
616 f.debug_struct("Question")
617 .field("outcome", &self.outcome)
618 .field("stats", &self.stats)
619 .finish_non_exhaustive()
620 }
621}
622
623impl Question {
624 pub fn valid(cx: &mut Context, p: Expr, cfg: &Config) -> Result<Question, Error> {
627 Question::valid_under(cx, p, None, cfg)
628 }
629
630 pub fn equal(cx: &mut Context, a: Expr, b: Expr, cfg: &Config) -> Result<Question, Error> {
633 let (ai, bi) = (cx.id(a)?, cx.id(b)?);
634 if cx.wid(ai) != cx.wid(bi) {
635 return Err(crate::WidthError::Mismatch {
636 left: cx.wid(ai),
637 right: cx.wid(bi),
638 }
639 .into());
640 }
641 let p = cx.c_cmp(crate::CmpOp::Eq, ai, bi)?;
642 let p = cx.handle(p);
643 Question::valid(cx, p, cfg)
644 }
645
646 pub fn valid_under(
652 cx: &mut Context,
653 p: Expr,
654 assumptions: Option<&Assumptions>,
655 cfg: &Config,
656 ) -> Result<Question, Error> {
657 Self::valid_under_with_pairs(cx, p, assumptions, &[], cfg)
658 }
659
660 pub fn valid_with_pairs(
665 cx: &mut Context,
666 p: Expr,
667 pairs: &[finite::CheckedPair],
668 cfg: &Config,
669 ) -> Result<Question, Error> {
670 Self::valid_under_with_pairs(cx, p, None, pairs, cfg)
671 }
672
673 pub fn valid_under_with_pairs(
675 cx: &mut Context,
676 p: Expr,
677 assumptions: Option<&Assumptions>,
678 pairs: &[finite::CheckedPair],
679 cfg: &Config,
680 ) -> Result<Question, Error> {
681 let pi = cx.id(p)?;
682 if cx.wid(pi) != 1 {
683 return Err(crate::WidthError::Mismatch {
684 left: 1,
685 right: cx.wid(pi),
686 }
687 .into());
688 }
689 let mut q = Question {
690 root: p,
691 symbols: Vec::new(),
692 search: None,
693 outcome: None,
694 stats: Stats::default(),
695 };
696 let decided = |mut q: Question, o: Outcome| {
697 q.outcome = Some(o);
698 Ok(q)
699 };
700 let mut cons: Vec<u32> = Vec::new();
702 if let Some(a) = assumptions {
703 for predicate in a.predicates(cx)? {
704 cons.push(cx.id(predicate)?);
705 }
706 }
707 let mut root = pi;
709 for &c in &cons {
710 let nc = cx.c_un(crate::UnOp::Not, c)?;
711 root = cx.c_bin(crate::BinOp::Or, nc, root)?;
712 }
713 q.root = cx.handle(root);
714 let mut goal_node = pi;
716 if !cfg.certificate {
717 for pair in pairs {
718 let rewritten = pair.simplify(cx, cx.handle(goal_node))?;
719 goal_node = cx.id(rewritten)?;
720 }
721 if cx
722 .const_val(goal_node)
723 .is_some_and(|value| !value.is_zero())
724 {
725 return decided(q, Outcome::Proved(None));
726 }
727 }
728 if cfg.simplify && !cfg.certificate {
729 let run = match assumptions {
730 Some(a) => crate::engine::Run::default().with_assumptions(a),
731 None => crate::engine::Run::default(),
732 };
733 let goal = cx.handle(goal_node);
734 let out = simplifier().run(cx, &[goal], run)?.roots[0];
735 let s = cx.id(out.expr)?;
736 if cx.const_val(s).is_some_and(|v| !v.is_zero()) {
737 return decided(q, Outcome::Proved(None));
738 }
739 goal_node = s;
742 }
743 let mut word_sampled = false;
744 if (cfg.word_sampling || cfg.sample_mode == SampleMode::Words)
745 && cfg.samples != 0
746 && let Some(trial) = sample_words::try_sample(cx, goal_node, &cons, assumptions, cfg)
747 {
748 q.stats.samples = trial.samples;
749 word_sampled = true;
750 if let Some(mut model) = trial.model {
751 complete_model(cx, q.root, &mut model)?;
752 let outcome = q.refuted(cx, model)?;
753 return decided(q, outcome);
754 }
755 if trial.complete && !cfg.certificate {
756 return decided(q, Outcome::Proved(None));
757 }
758 }
759 let mut exhausted = false;
762 if cfg.exhaustive_inputs != 0
763 && let Some((failure, evaluated)) =
764 sample_words::try_exhaust(cx, goal_node, &cons, assumptions, cfg.exhaustive_inputs)
765 {
766 q.stats.samples += evaluated;
767 match failure {
768 Some(mut model) => {
769 complete_model(cx, q.root, &mut model)?;
770 let outcome = q.refuted(cx, model)?;
771 return decided(q, outcome);
772 }
773 None if !cfg.certificate => return decided(q, Outcome::Proved(None)),
774 None => exhausted = true,
775 }
776 }
777 let constant_goal = cuts::constant_true(cx, goal_node);
778 let mut b = Blaster::new(cx, cfg.max_nodes);
779 b.selector_hints = cfg.selector_branching;
780 b.input_cancellation = cfg.input_cancellation;
781 b.join_factoring = cfg.join_factoring;
782 b.carry_save_multiplication = cfg.carry_save_multiplication;
783 if constant_goal {
784 b.bits.insert(goal_node, vec![aig::TRUE]);
785 }
786 let mut blasted = || -> Result<(L, Vec<L>), Error> {
787 let goal = b.blast(goal_node)?[0];
788 let mut extra = Vec::new();
789 for &c in &cons {
790 extra.push(b.blast(c)?[0]);
791 }
792 Ok((goal, extra))
793 };
794 let (goal, extra) = match blasted() {
795 Ok(r) => r,
796 Err(e) => {
797 let why = b.unknown(e)?;
798 return decided(q, Outcome::Unknown(why));
799 }
800 };
801 q.stats.nodes = b.g.len();
802 if b.g.len() > cfg.max_nodes {
803 let why = Unknown::TooLarge { nodes: b.g.len() };
804 return decided(q, Outcome::Unknown(why));
805 }
806 for (s, bits) in &b.symbols {
808 let h = b.cx.handle(*s);
809 let key =
810 b.cx.symbol_id(h)?
811 .and_then(|id| b.cx.symbol_key(id))
812 .cloned()
813 .ok_or_else(|| Error::Contract("a symbol without its key".into()))?;
814 q.symbols.push((key, b.cx.width_of(*s), bits.clone()));
815 }
816 for id in b.cx.symbols_in(&[q.root])? {
817 if let (Some(k), Some(w)) = (b.cx.symbol_key(id).cloned(), b.cx.symbol_width(id))
818 && !q.symbols.iter().any(|(kk, _, _)| *kk == k)
819 {
820 let bits =
821 b.cx.find_symbol(&k)
822 .and_then(|e| b.cx.declared_at(e.index()))
823 .map(|known| blast::konst(&known.known_one()))
824 .unwrap_or_default();
825 q.symbols.push((k, w, bits));
826 }
827 }
828 if exhausted {
829 let exhaustion = Exhaustion::of(&b.g, goal, &extra);
830 if exhaustion.inputs() <= u32::from(cfg.exhaustive_inputs.min(63)) {
831 let cert = Certificate {
832 vars: 0,
833 clauses: Vec::new(),
834 proof: Vec::new(),
835 exhaustion: Some(exhaustion),
836 };
837 return decided(q, Outcome::Proved(Some(cert)));
838 }
839 }
840 let batches = if word_sampled {
842 0
843 } else {
844 cfg.samples.div_ceil(64)
845 };
846 for batch in 0..batches {
847 let vals = b.g.eval_words(|k| match cfg.sample_mode {
848 SampleMode::Random => sample(batch, k),
849 SampleMode::Small | SampleMode::Words => small_sample(batch, k),
850 });
851 q.stats.samples += 64;
852 let mut bad = !Aig::word(&vals, goal);
853 for &e in &extra {
854 bad &= Aig::word(&vals, e);
855 }
856 if bad != 0 {
857 let lane = bad.trailing_zeros();
858 let ce = Question::counterexample(&q.symbols, |l| {
859 (Aig::word(&vals, l) >> lane) & 1 == 1
860 });
861 let o = q.refuted(b.cx, ce)?;
862 return decided(q, o);
863 }
864 }
865 let mut solver = Solver::new();
866 if cfg.certificate {
867 solver.log_proof();
868 }
869 let mut roots = vec![goal];
870 roots.extend_from_slice(&extra);
871 let mut cnf = Cnf::encode_with_xor3(
872 &b.g,
873 &roots,
874 &mut solver,
875 cfg.certificate,
876 cfg.xor3_encoding,
877 );
878 for &e in &extra {
879 cnf.assert(e, &mut solver);
880 }
881 cnf.assert(goal ^ 1, &mut solver);
882 let mut asserted = vec![goal ^ 1];
883 asserted.extend_from_slice(&extra);
884 relations::strengthen(&b.g, &asserted, &cnf, &mut solver, cfg.relational_lemmas);
885 if cfg.selector_branching {
886 for &l in &b.selectors {
887 if let Some(l) = cnf.mapped_lit(l) {
888 solver.prefer_selector(l);
889 }
890 }
891 }
892 q.stats.vars = solver.num_vars();
893 q.stats.clauses = cnf.emitted();
894 q.stats.learned = solver.num_learnts();
895 q.stats.propagations = solver.propagations;
896 q.search = Some(Box::new(Search { solver, cnf }));
897 Ok(q)
898 }
899
900 pub fn solve(&mut self, cx: &mut Context, limits: Limits) -> Result<Outcome, Error> {
905 if let Some(o) = &self.outcome {
906 return Ok(o.clone());
907 }
908 let Some(s) = self.search.as_mut() else {
909 return Err(Error::Contract(
910 "a question neither decided nor open".into(),
911 ));
912 };
913 let answer = s.solver.solve_within(limits);
914 self.stats.learned = s.solver.num_learnts();
915 self.stats.conflicts = s.solver.conflicts;
916 self.stats.decisions = s.solver.decisions;
917 self.stats.propagations = s.solver.propagations;
918 let o = match answer {
919 Answer::Unknown => {
920 return Ok(Outcome::Unknown(Unknown::Budget {
921 conflicts: s.solver.conflicts,
922 propagations: s.solver.propagations,
923 }));
924 }
925 Answer::Unsat => {
926 let cert = s.solver.take_proof().map(|proof| Certificate {
927 vars: s.solver.num_vars(),
928 clauses: core::mem::take(&mut s.cnf.clauses),
929 proof,
930 exhaustion: None,
931 });
932 Outcome::Proved(cert)
933 }
934 Answer::Sat(model) => {
935 let cnf = &s.cnf;
936 let ce = Question::counterexample(&self.symbols, |l| cnf.value(l, &model));
937 self.refuted(cx, ce)?
938 }
939 };
940 self.search = None;
941 self.outcome = Some(o.clone());
942 Ok(o)
943 }
944
945 pub fn outcome(&self) -> Option<&Outcome> {
947 self.outcome.as_ref()
948 }
949
950 pub fn stats(&self) -> Stats {
952 self.stats
953 }
954
955 fn counterexample(symbols: &[(SymbolKey, Width, Bits)], bit: impl Fn(L) -> bool) -> Model {
957 symbols
958 .iter()
959 .map(|(k, w, bits)| {
960 let mut limbs = [0u64; 8];
961 for (j, &b) in bits.iter().enumerate() {
962 if bit(b) {
963 limbs[j / 64] |= 1 << (j % 64);
964 }
965 }
966 (k.clone(), BitVec::wrapping_from_limbs(*w, &limbs))
967 })
968 .collect()
969 }
970
971 fn refuted(&self, cx: &mut Context, ce: Model) -> Result<Outcome, Error> {
974 let env =
975 crate::FnEnv(|k: &SymbolKey, _| ce.iter().find(|(kk, _)| kk == k).map(|(_, v)| *v));
976 let v = cx.eval(&[self.root], &env)?[0];
977 if v.is_zero() {
978 Ok(Outcome::Refuted(ce))
979 } else {
980 Err(Error::Contract(format!(
981 "the prover's counterexample does not refute {}",
982 cx.display(self.root)
983 )))
984 }
985 }
986}
987
988fn complete_model(cx: &mut Context, root: Expr, model: &mut Model) -> Result<(), Error> {
991 for id in cx.symbols_in(&[root])? {
992 let key = cx.symbol_key(id).unwrap().clone();
993 if !model.iter().any(|(k, _)| *k == key) {
994 let symbol = cx.find_symbol(&key).unwrap();
995 let value = cx.declared_known(symbol)?.map_or_else(
996 || BitVec::zero(cx.symbol_width(id).unwrap()),
997 |k| k.known_one(),
998 );
999 model.push((key, value));
1000 }
1001 }
1002 Ok(())
1003}
1004
1005fn small_sample(batch: u32, k: u32) -> u64 {
1007 const LOW: [u64; 6] = [
1008 0xaaaa_aaaa_aaaa_aaaa,
1009 0xcccc_cccc_cccc_cccc,
1010 0xf0f0_f0f0_f0f0_f0f0,
1011 0xff00_ff00_ff00_ff00,
1012 0xffff_0000_ffff_0000,
1013 0xffff_ffff_0000_0000,
1014 ];
1015 if k < 6 {
1016 LOW[k as usize]
1017 } else if k - 6 < 32 && (batch >> (k - 6)) & 1 == 1 {
1018 u64::MAX
1019 } else {
1020 0
1021 }
1022}
1023
1024fn sample(batch: u32, k: u32) -> u64 {
1027 let mut z = (u64::from(batch) << 32 | u64::from(k)).wrapping_add(0x9e37_79b9_7f4a_7c15);
1028 z = (z ^ (z >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9);
1029 z = (z ^ (z >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb);
1030 z ^= z >> 31;
1031 if batch == 0 { (z & !0b11) | 0b10 } else { z }
1032}
1033
1034pub fn valid(cx: &mut Context, p: Expr, cfg: &Config) -> Result<Outcome, Error> {
1037 valid_under(cx, p, None, cfg)
1038}
1039
1040pub fn valid_under(
1043 cx: &mut Context,
1044 p: Expr,
1045 assumptions: Option<&Assumptions>,
1046 cfg: &Config,
1047) -> Result<Outcome, Error> {
1048 Question::valid_under(cx, p, assumptions, cfg)?.solve(cx, cfg.limits())
1049}
1050
1051pub fn equal(cx: &mut Context, a: Expr, b: Expr, cfg: &Config) -> Result<Outcome, Error> {
1054 Question::equal(cx, a, b, cfg)?.solve(cx, cfg.limits())
1055}
1056
1057pub type Model = Vec<(SymbolKey, BitVec)>;
1059
1060pub fn satisfy(
1064 cx: &mut Context,
1065 p: Expr,
1066 cfg: &Config,
1067) -> Result<Result<Option<Model>, Unknown>, Error> {
1068 let np = cx.un(crate::UnOp::Not, p)?;
1069 Ok(match valid(cx, np, cfg)? {
1070 Outcome::Proved(_) => Ok(None),
1071 Outcome::Refuted(m) => Ok(Some(m)),
1072 Outcome::Unknown(why) => Err(why),
1073 })
1074}