cadical_sys/lib.rs
1//! Rust bindings for the `CaDiCaL` SAT Solver, providing low-level access to one of the most efficient Boolean Satisfiability (SAT) solving libraries.
2//!
3//! # Overview
4//!
5//! `cadical-sys` offers complete Rust bindings to the `CaDiCaL` SAT solver using the `cxx` crate, enabling seamless interoperability between Rust and C++ SAT solving capabilities.
6//!
7//! ## What is a SAT Solver?
8//!
9//! A SAT (Boolean Satisfiability) solver is a computational tool that determines whether there exists an assignment of boolean variables that makes a given boolean formula true. SAT solvers are crucial in:
10//! - Formal verification
11//! - Hardware design
12//! - AI planning
13//! - Cryptanalysis
14//! - Constraint solving
15//!
16//! ## About `CaDiCaL`
17//!
18//! [CaDiCaL](https://github.com/arminbiere/cadical) is a state-of-the-art, modern SAT solver developed by Armin Biere. Known for its:
19//! - High performance
20//! - Extensive features
21//! - Compact implementation
22//! - Advanced conflict-driven clause learning (CDCL) techniques
23//!
24//! # Features
25//!
26//! - Complete binding of `CaDiCaL` C++ API
27//! - Safe Rust wrappers using `cxx` (where possible)
28//! - Support for:
29//! - Adding clauses
30//! - Solving boolean satisfiability problems
31//! - Assumption handling
32//! - Advanced solver configuration
33//! - Proof tracing
34//! - Incremental solving
35//!
36//! # Installation
37//!
38//! Add to your `Cargo.toml`:
39//!
40//! ```toml
41//! [dependencies]
42//! cadical-sys = "0.1.0" # Replace with most recent version
43//! ```
44//!
45//! # Usage Examples
46//!
47//! ## Basic SAT solving example
48//! ```rust
49//! use cadical_sys::Status;
50//! use cadical_sys::CaDiCal;
51//!
52//! // Create a new solver instance
53//! let mut solver = CaDiCal::new();
54//!
55//! // Add clauses (representing a simple propositional logic problem)
56//! // For example, (x1 OR x2) AND (NOT x1 OR x3) AND (NOT x2 OR NOT x3)
57//! solver.clause2(1, 2); // x1 OR x2
58//! solver.clause2(-1, 3); // NOT x1 OR x3
59//! solver.clause2(-2, -3); // NOT x2 OR NOT x3
60//!
61//! // Solve the problem
62//! let status = solver.solve();
63//! match status {
64//! Status::SATISFIABLE => {
65//! // Get variable assignments
66//! println!("x1: {}", solver.val(1));
67//! println!("x2: {}", solver.val(2));
68//! println!("x3: {}", solver.val(3));
69//! },
70//! Status::UNSATISFIABLE => println!("No solution exists"),
71//! Status::UNKNOWN => println!("Solution status unknown")
72//! }
73//! ```
74//!
75//! ## Advanced example with assumptions and configuration
76//! ```rust
77//! use cadical_sys::Status;
78//! use cadical_sys::CaDiCal;
79//!
80//! let mut solver = CaDiCal::new();
81//!
82//! // Configure the solver
83//! solver.configure("plain".to_string());
84//!
85//! // Set some options
86//! solver.set("verbose".to_string(), 1);
87//!
88//! // Add complex clauses
89//! solver.clause3(1, 2, 3); // x1 OR x2 OR x3
90//! solver.clause3(-1, -2, -3); // NOT x1 OR NOT x2 OR NOT x3
91//!
92//! // Make assumptions
93//! solver.assume(1); // Assume x1 is true
94//!
95//! // Solve with assumptions
96//! let status = solver.solve();
97//!
98//! // Check solving results
99//! if status == Status::SATISFIABLE {
100//! // Interact with solved model
101//! let num_vars = solver.vars();
102//! for var in 1..=num_vars {
103//! println!("Variable {}: {}", var, solver.val(var));
104//! }
105//! }
106//! ```
107//!
108//! ## Example of reading DIMACS file and solving
109//! ```rust
110//! use cadical_sys::Status;
111//! use cadical_sys::CaDiCal;
112//!
113//! let mut solver = CaDiCal::new();
114//! let mut var_count = 0;
115//!
116//! // Read a DIMACS CNF file
117//! let result = solver.read_dimacs1(
118//! "./tests/problem.cnf".to_string(),
119//! "my_problem".to_string(),
120//! &mut var_count,
121//! 0
122//! );
123//!
124//! // Solve the problem from the file
125//! let status = solver.solve();
126//!
127//! // Write out results or extension
128//! if status == Status::SATISFIABLE {
129//! solver.write_extension("/tmp/solution.ext".to_string());
130//! }
131//! ```
132//!
133//! ## Demonstrating advanced solver interactions
134//! ```rust
135//! use cadical_sys::CaDiCal;
136//!
137//! let mut solver = CaDiCal::new();
138//!
139//! // Reserve variable space
140//! solver.reserve(1000);
141//!
142//! // Add observed variables for tracking
143//! solver.add_observed_var(42);
144//!
145//! // Perform simplification
146//! let simplify_status = solver.simplify(2);
147//!
148//! // Get solver statistics
149//! solver.statistics();
150//! solver.resources();
151//! ```
152//!
153//! # Performance Considerations
154//!
155//! - `CaDiCaL` is highly optimized for complex boolean satisfiability problems
156//! - Recommended for problems with thousands to millions of variables
157//! - Lower overhead compared to many other SAT solvers
158//!
159//! # Limitations
160//!
161//! - Requires understanding of boolean logic and SAT solving
162//! - Performance depends on problem complexity
163//! - Advanced features require deep knowledge of SAT solving techniques
164//!
165//! # Contributing
166//!
167//! Contributions are welcome! Please file issues or submit pull requests on the GitHub repository.
168//!
169//! # License
170//!
171//! `CaDiCaL` is distributed under the MIT License. Check the original repository for detailed licensing information.
172//!
173//! # References
174//!
175//! - [CaDiCaL GitHub Repository](https://github.com/arminbiere/cadical)
176//! - [cxx Rust Bindings](https://cxx.rs/)
177//! - [SAT Solver Overview](https://en.wikipedia.org/wiki/Boolean_satisfiability_problem)
178//!
179//! # Acknowledgments
180//!
181//! Special thanks to Armin Biere for developing and maintaining `CaDiCaL`.
182
183use bridge::ffi;
184use cxx::UniquePtr;
185
186/// This module contains the FFI bindings to the `CaDiCaL` SAT solver.
187/// Some functions are unsafe due to necessity.
188pub mod bridge;
189
190/// The SAT competition standardized the exit code of SAT solvers to the
191/// following which then is also used return code for 'solve' functions.
192/// In the following example we use those constants for brevity though.
193#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
194pub enum Status {
195 SATISFIABLE = 10,
196 UNSATISFIABLE = 20,
197 UNKNOWN = 0,
198}
199
200impl From<i32> for Status {
201 fn from(val: i32) -> Self {
202 match val {
203 10 => Status::SATISFIABLE,
204 20 => Status::UNSATISFIABLE,
205 0 => Status::UNKNOWN,
206 _ => unreachable!(),
207 }
208 }
209}
210
211/// States are represented by a bit-set in order to combine them.
212#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
213pub enum State {
214 /// during initialization (invalid)
215 INITIALIZING = 1,
216 /// configure options (with 'set')
217 CONFIGURING = 2,
218 /// ready to call 'solve'
219 STEADY = 4,
220 /// adding clause literals (zero missing)
221 ADDING = 8,
222 /// while solving (within 'solve')
223 SOLVING = 16,
224 /// satisfiable allows 'val'
225 SATISFIED = 32,
226 /// unsatisfiable allows 'failed'
227 UNSATISFIED = 64,
228 /// during and after deletion (invalid)
229 DELETING = 128,
230
231 /// These combined states are used to check contracts.
232 /// CONFIGURING | STEADY | SATISFIED | UNSATISFIED,
233 READY = 102,
234 /// READY | ADDING
235 VALID = 110,
236 /// INITIALIZING | DELETING
237 INVALID = 129,
238}
239
240impl From<i32> for State {
241 fn from(val: i32) -> Self {
242 match val {
243 1 => State::INITIALIZING,
244 2 => State::CONFIGURING,
245 4 => State::STEADY,
246 8 => State::ADDING,
247 16 => State::SOLVING,
248 32 => State::SATISFIED,
249 64 => State::UNSATISFIED,
250 128 => State::DELETING,
251 102 => State::READY,
252 110 => State::VALID,
253 129 => State::INVALID,
254 _ => unreachable!(),
255 }
256 }
257}
258
259pub struct CaDiCal {
260 solver: UniquePtr<ffi::Solver>,
261 last_terminator: Option<UniquePtr<ffi::Terminator>>,
262 last_learner: Option<UniquePtr<ffi::Learner>>,
263 last_external_propagator: Option<UniquePtr<ffi::ExternalPropagator>>,
264 last_fixed_listener: Option<UniquePtr<ffi::FixedAssignmentListener>>,
265 last_tracer: Option<UniquePtr<ffi::Tracer>>,
266}
267
268impl Clone for CaDiCal {
269 fn clone(&self) -> Self {
270 let mut r = Self::new();
271 Self::copy(self, &mut r);
272 r
273 }
274}
275
276impl Default for CaDiCal {
277 fn default() -> Self {
278 Self::new()
279 }
280}
281
282impl CaDiCal {
283 #[must_use]
284 #[inline]
285 pub fn new() -> Self {
286 Self {
287 solver: ffi::constructor(),
288 last_terminator: None,
289 last_learner: None,
290 last_external_propagator: None,
291 last_fixed_listener: None,
292 last_tracer: None,
293 }
294 }
295
296 /// Core functionality as in the IPASIR incremental SAT solver interface.
297 /// (recall 'READY = CONFIGURING | STEADY | SATISFIED | UNSATISFIED').
298 /// Further note that 'lit' is required to be different from '`INT_MIN`' and
299 /// different from '0' except for 'add'.
300 ///
301 /// Add valid literal to clause or zero to terminate clause.
302 ///
303 /// require (VALID) recall 'VALID = READY | ADDING'
304 ///
305 /// if (lit) ensure (ADDING) and thus VALID but not READY
306 ///
307 /// if (!lit) ensure (STEADY ) and thus READY
308 ///
309 #[inline]
310 pub fn add(&mut self, lit: i32) {
311 ffi::add(&mut self.solver, lit);
312 }
313
314 /// Here are functions simplifying clause addition. The given literals
315 /// should all be valid (different from '`INT_MIN`' and different from '0').
316 ///
317 /// require (VALID)
318 /// ensure (STEADY )
319 ///
320 #[inline]
321 pub fn clause1(&mut self, l1: i32) {
322 ffi::clause1(&mut self.solver, l1);
323 }
324
325 #[inline]
326 pub fn clause2(&mut self, l1: i32, l2: i32) {
327 ffi::clause2(&mut self.solver, l1, l2);
328 }
329
330 #[inline]
331 pub fn clause3(&mut self, l1: i32, l2: i32, l3: i32) {
332 ffi::clause3(&mut self.solver, l1, l2, l3);
333 }
334
335 #[inline]
336 pub fn clause4(&mut self, l1: i32, l2: i32, l3: i32, l4: i32) {
337 ffi::clause4(&mut self.solver, l1, l2, l3, l4);
338 }
339
340 #[inline]
341 pub fn clause5(&mut self, l1: i32, l2: i32, l3: i32, l4: i32, l5: i32) {
342 ffi::clause5(&mut self.solver, l1, l2, l3, l4, l5);
343 }
344
345 #[inline]
346 pub fn clause6(&mut self, v: &[i32]) {
347 ffi::clause6(&mut self.solver, v);
348 }
349
350 /// This function can be used to check if the formula is already
351 /// inconsistent (contains the empty clause or was proven to be
352 /// root-level unsatisfiable).
353 #[inline]
354 pub fn inconsistent(&mut self) -> bool {
355 ffi::inconsistent(&mut self.solver)
356 }
357
358 /// Assume valid non zero literal for next call to 'solve'. These
359 /// assumptions are reset after the call to 'solve' as well as after
360 /// returning from 'simplify' and 'lookahead.
361 ///
362 /// require (READY)
363 /// ensure (STEADY )
364 ///
365 #[inline]
366 pub fn assume(&mut self, lit: i32) {
367 ffi::assume(&mut self.solver, lit);
368 }
369
370 /// Try to solve the current formula. Returns
371 ///
372 /// 0 = UNKNOWN (limit reached or interrupted through 'terminate')
373 /// 10 = SATISFIABLE
374 /// 20 = UNSATISFIABLE
375 ///
376 /// require (READY)
377 /// ensure (STEADY | SATISFIED | UNSATISFIED)
378 ///
379 /// Note, that while in this call the solver actually transitions to state
380 /// 'SOLVING', which however is only visible from a different context,
381 /// i.e., from a different thread or from a signal handler. Only right
382 /// before returning from this call it goes into a 'READY' state.
383 ///
384 #[inline]
385 pub fn solve(&mut self) -> Status {
386 ffi::solve(&mut self.solver).into()
387 }
388
389 /// Get value (-lit=false, lit=true) of valid non-zero literal.
390 ///
391 /// require (SATISFIED)
392 /// ensure (SATISFIED)
393 ///
394 #[inline]
395 pub fn val(&mut self, lit: i32) -> i32 {
396 ffi::val(&mut self.solver, lit)
397 }
398
399 /// Try to flip the value of the given literal without falsifying the
400 /// formula. Returns 'true' if this was successful. Otherwise the model is
401 /// not changed and 'false' is returned. If a literal was eliminated or
402 /// substituted flipping will fail on that literal and in particular the
403 /// solver will not taint it nor restore any clauses.
404 ///
405 /// The 'flip' function can only flip the value of a variables not acting
406 /// as witness on the reconstruction stack.
407 ///
408 /// As a side effect of calling this function first all assigned variables
409 /// are propagated again without using blocking literal. Thus the first
410 /// call to this function after obtaining a model adds a substantial
411 /// overhead. Subsequent calls will not need to properly propagate again.
412 ///
413 /// Furthermore if the reconstruction stack is non-empty and has been
414 /// traversed to reconstruct a full extended model for eliminated
415 /// variables (and to satisfy removed blocked clauses), the values of these
416 /// witness variables obtained via 'val' before become invalid. The user
417 /// thus will need to call 'val' again after calling 'flip' which will
418 /// trigger then a traversal of the reconstruction stack.
419 ///
420 /// So try to avoid mixing 'flip' and 'val' (for efficiency only).
421 /// Further, this functionality is currently not supported in the presence
422 /// of an external propagator.
423 ///
424 /// require (SATISFIED)
425 /// ensure (SATISFIED)
426 ///
427 #[inline]
428 pub fn flip(&mut self, lit: i32) -> bool {
429 ffi::flip(&mut self.solver, lit)
430 }
431
432 /// Same as 'flip' without actually flipping it. This functionality is
433 /// currently not supported in the presence of an external propagator.
434 ///
435 /// require (SATISFIED)
436 /// ensure (SATISFIED)
437 ///
438 #[inline]
439 pub fn flippable(&mut self, lit: i32) -> bool {
440 ffi::flippable(&mut self.solver, lit)
441 }
442
443 /// Determine whether the valid non-zero literal is in the core.
444 /// Returns 'true' if the literal is in the core and 'false' otherwise.
445 /// Note that the core does not have to be minimal.
446 ///
447 /// require (UNSATISFIED)
448 /// ensure (UNSATISFIED)
449 ///
450 #[inline]
451 pub fn failed(&mut self, lit: i32) -> bool {
452 ffi::failed(&mut self.solver, lit)
453 }
454
455 /// Add call-back which is checked regularly for termination. There can
456 /// only be one terminator connected. If a second (non-zero) one is added
457 /// the first one is implicitly disconnected.
458 ///
459 /// require (VALID)
460 /// ensure (VALID)
461 ///
462 #[inline]
463 #[allow(clippy::missing_panics_doc)]
464 pub fn connect_terminator<'a, 'b: 'a, T: Terminator>(&'a mut self, terminator: &'b mut T) {
465 fn f<T: Terminator>(state: *mut u8) -> bool {
466 let ptr: *mut T = state.cast::<T>();
467 let i = unsafe { &mut *ptr };
468 i.terminated()
469 }
470 let terminator =
471 unsafe { ffi::new_terminator(std::ptr::from_mut(terminator).cast::<u8>(), f::<T>) };
472 self.last_terminator = Some(terminator);
473 ffi::connect_terminator(&mut self.solver, self.last_terminator.as_mut().unwrap());
474 }
475
476 #[inline]
477 pub fn disconnect_terminator(&mut self) {
478 ffi::disconnect_terminator(&mut self.solver);
479 self.last_terminator = None;
480 }
481
482 /// Add call-back which allows to export learned clauses.
483 ///
484 /// require (VALID)
485 /// ensure (VALID)
486 ///
487 #[inline]
488 #[allow(clippy::missing_panics_doc)]
489 pub fn connect_learner<'a, 'b: 'a, T: Learner>(&'a mut self, learner: &'b mut T) {
490 fn learning<T: Learner>(state: *mut u8, size: i32) -> bool {
491 let ptr: *mut T = state.cast::<T>();
492 let i = unsafe { &mut *ptr };
493 i.learning(size)
494 }
495
496 fn learn<T: Learner>(state: *mut u8, lit: i32) {
497 let ptr: *mut T = state.cast::<T>();
498 let i = unsafe { &mut *ptr };
499 i.learn(lit);
500 }
501
502 let learner = unsafe {
503 ffi::new_learner(
504 std::ptr::from_mut(learner).cast::<u8>(),
505 learning::<T>,
506 learn::<T>,
507 )
508 };
509 self.last_learner = Some(learner);
510 ffi::connect_learner(&mut self.solver, self.last_learner.as_mut().unwrap());
511 }
512
513 #[inline]
514 pub fn disconnect_learner(&mut self) {
515 ffi::disconnect_learner(&mut self.solver);
516 self.last_learner = None;
517 }
518
519 /// Add call-back which allows to observe when a variable is fixed.
520 ///
521 /// require (VALID)
522 /// ensure (VALID)
523 ///
524 #[allow(clippy::missing_panics_doc)]
525 pub fn connect_fixed_listener<'a, 'b: 'a, F: FixedAssignmentListener>(
526 &'a mut self,
527 fixed_listener: &'b mut F,
528 ) {
529 fn notify_fixed_assignment<F: FixedAssignmentListener>(state: *mut u8, lit: i32) {
530 let ptr: *mut F = state.cast::<F>();
531 let i = unsafe { &mut *ptr };
532 i.notify_fixed_assignment(lit);
533 }
534
535 let s = std::ptr::from_mut(fixed_listener).cast::<u8>();
536
537 let listener =
538 unsafe { ffi::new_fixed_assignment_listener(s, notify_fixed_assignment::<F>) };
539
540 // Store the listener in self to prevent it from being dropped
541 self.last_fixed_listener = Some(listener);
542
543 // Use the stored listener
544 ffi::connect_fixed_listener(&mut self.solver, self.last_fixed_listener.as_mut().unwrap());
545 }
546
547 #[inline]
548 pub fn disconnect_fixed_listener(&mut self) {
549 ffi::disconnect_fixed_listener(&mut self.solver);
550 self.last_fixed_listener = None;
551 }
552
553 /// Add call-back which allows to learn, propagate and backtrack based on
554 /// external constraints. Only one external propagator can be connected
555 /// and after connection every related variables must be 'observed' (use
556 /// '`add_observed_var`' function).
557 /// Disconnection of the external propagator resets all the observed
558 /// variables.
559 ///
560 /// require (VALID)
561 /// ensure (VALID)
562 ///
563 #[allow(clippy::missing_panics_doc)]
564 pub fn connect_external_propagator<'a, 'b: 'a, T: ExternalPropagator>(
565 &'a mut self,
566 propagator: &'b mut T,
567 ) {
568 fn notify_assignment<T: ExternalPropagator>(state: *mut u8, x: &[i32]) {
569 let ptr: *mut T = state.cast::<T>();
570 let i = unsafe { &mut *ptr };
571 i.notify_assignment(x);
572 }
573 fn notify_new_decision_level<T: ExternalPropagator>(state: *mut u8) {
574 let ptr: *mut T = state.cast::<T>();
575 let i = unsafe { &mut *ptr };
576 i.notify_new_decision_level();
577 }
578 fn notify_backtrack<T: ExternalPropagator>(state: *mut u8, x: usize) {
579 let ptr: *mut T = state.cast::<T>();
580 let i = unsafe { &mut *ptr };
581 i.notify_backtrack(x);
582 }
583 fn cb_check_found_model<T: ExternalPropagator>(state: *mut u8, x: &[i32]) -> bool {
584 let ptr: *mut T = state.cast::<T>();
585 let i = unsafe { &mut *ptr };
586 i.cb_check_found_model(x)
587 }
588 fn cb_decide<T: ExternalPropagator>(state: *mut u8) -> i32 {
589 let ptr: *mut T = state.cast::<T>();
590 let i = unsafe { &mut *ptr };
591 i.cb_decide()
592 }
593 fn cb_propagate<T: ExternalPropagator>(state: *mut u8) -> i32 {
594 let ptr: *mut T = state.cast::<T>();
595 let i = unsafe { &mut *ptr };
596 i.cb_propagate()
597 }
598 fn cb_add_reason_clause_lit<T: ExternalPropagator>(state: *mut u8, x: i32) -> i32 {
599 let ptr: *mut T = state.cast::<T>();
600 let i = unsafe { &mut *ptr };
601 i.cb_add_reason_clause_lit(x)
602 }
603 fn cb_has_external_clause<T: ExternalPropagator>(state: *mut u8, x: *mut bool) -> bool {
604 let ptr: *mut T = state.cast::<T>();
605 let i = unsafe { &mut *ptr };
606 i.cb_has_external_clause(unsafe { &mut *x })
607 }
608 fn cb_add_external_clause_lit<T: ExternalPropagator>(state: *mut u8) -> i32 {
609 let ptr: *mut T = state.cast::<T>();
610 let i = unsafe { &mut *ptr };
611 i.cb_add_external_clause_lit()
612 }
613 let is_lazy = propagator.is_lazy();
614 let are_reasons_forgettable = propagator.are_reasons_forgettable();
615
616 let external_propagator = unsafe {
617 ffi::new_external_propagator(
618 std::ptr::from_mut(propagator).cast::<u8>(),
619 is_lazy,
620 are_reasons_forgettable,
621 notify_assignment::<T>,
622 notify_new_decision_level::<T>,
623 notify_backtrack::<T>,
624 cb_check_found_model::<T>,
625 cb_decide::<T>,
626 cb_propagate::<T>,
627 cb_add_reason_clause_lit::<T>,
628 cb_has_external_clause::<T>,
629 cb_add_external_clause_lit::<T>,
630 )
631 };
632
633 self.last_external_propagator = Some(external_propagator);
634 ffi::connect_external_propagator(
635 &mut self.solver,
636 self.last_external_propagator.as_mut().unwrap(),
637 );
638 }
639 pub fn disconnect_external_propagator(&mut self) {
640 ffi::disconnect_external_propagator(&mut self.solver);
641 self.last_external_propagator = None;
642 }
643
644 /// Mark as 'observed' those variables that are relevant to the external
645 /// propagator. External propagation, clause addition during search and
646 /// notifications are all over these observed variabes.
647 /// A variable can not be observed witouth having an external propagator
648 /// connected. Observed variables are "frozen" internally, and so
649 /// inprocessing will not consider them as candidates for elimination.
650 /// An observed variable is allowed to be a fresh variable and it can be
651 /// added also during solving.
652 ///
653 /// require (`VALID_OR_SOLVING`)
654 /// ensure (`VALID_OR_SOLVING`)
655 ///
656 #[inline]
657 pub fn add_observed_var(&mut self, var: i32) {
658 ffi::add_observed_var(&mut self.solver, var);
659 }
660
661 /// Removes the 'observed' flag from the given variable. A variable can be
662 /// set unobserved only between solve calls, not during it (to guarantee
663 /// that no yet unexplained external propagation involves it).
664 ///
665 /// require (VALID)
666 /// ensure (VALID)
667 ///
668 #[inline]
669 pub fn remove_observed_var(&mut self, var: i32) {
670 ffi::remove_observed_var(&mut self.solver, var);
671 }
672
673 /// Removes all the 'observed' flags from the variables. Disconnecting the
674 /// propagator invokes this step as well.
675 ///
676 /// require (VALID)
677 /// ensure (VALID)
678 ///
679 #[inline]
680 pub fn reset_observed_vars(&mut self) {
681 ffi::reset_observed_vars(&mut self.solver);
682 }
683
684 /// Get reason of valid observed literal (true = it is an observed variable
685 /// and it got assigned by a decision during the CDCL loop. Otherwise:
686 /// false.
687 ///
688 /// require (`VALID_OR_SOLVING`)
689 /// ensure (`VALID_OR_SOLVING`)
690 ///
691 #[inline]
692 pub fn is_decision(&mut self, lit: i32) -> bool {
693 ffi::is_decision(&mut self.solver, lit)
694 }
695
696 /// Force solve to backtrack to certain decision level. Can be called only
697 /// during '`cb_decide`' of a connected External Propagator.
698 /// Invoking in any other time will not have an effect.
699 /// If the call had an effect, the External Propagator will be notified about
700 /// the backtrack via '`notify_backtrack`'.
701 ///
702 /// require (SOLVING)
703 /// ensure (SOLVING)
704 ///
705 #[inline]
706 pub fn force_backtrack(&mut self, new_level: usize) {
707 ffi::force_backtrack(&mut self.solver, new_level);
708 }
709
710 //------------------------------------------------------------------------
711 /// Adds a literal to the constraint clause. Same functionality as 'add'
712 /// but the clause only exists for the next call to solve (same lifetime as
713 /// assumptions). Only one constraint may exists at a time. A new
714 /// constraint replaces the old. The main application of this functonality
715 /// is the model checking algorithm IC3. See our FMCAD'21 paper
716 /// [FroleyksBiere-FMCAD'19] for more details.
717 ///
718 /// Add valid literal to the constraint clause or zero to terminate it.
719 ///
720 /// require (VALID) /// recall 'VALID = READY |
721 /// ADDING' if (lit) ensure (ADDING) /// and thus VALID but not
722 /// READY if (!lit) && !`adding_clause` ensure (STEADY ) // and thus READY
723 ///
724 #[inline]
725 pub fn constrain(&mut self, lit: i32) {
726 ffi::constrain(&mut self.solver, lit);
727 }
728
729 /// Determine whether the constraint was used to proof the
730 /// unsatisfiability. Note that the formula might still be unsatisfiable
731 /// without the constraint.
732 ///
733 /// require (UNSATISFIED)
734 /// ensure (UNSATISFIED)
735 ///
736 #[inline]
737 pub fn constraint_failed(&mut self) -> bool {
738 ffi::constraint_failed(&mut self.solver)
739 }
740
741 //------------------------------------------------------------------------
742 /// This function determines a good splitting literal. The result can be
743 /// zero if the formula is proven to be satisfiable or unsatisfiable. This
744 /// can then be checked by 'state ()'. If the formula is empty and
745 /// the function is not able to determine satisfiability also zero is
746 /// returned but the state remains steady.
747 ///
748 /// require (READY)
749 /// ensure (STEADY |SATISFIED|UNSATISFIED)
750 ///
751 #[inline]
752 pub fn lookahead(&mut self) -> i32 {
753 ffi::lookahead(&mut self.solver)
754 }
755
756 #[inline]
757 pub fn generate_cubes(&mut self, x: i32, min_depth: i32, result_cubes: &mut Vec<i32>) -> i32 {
758 ffi::generate_cubes(&mut self.solver, x, min_depth, result_cubes)
759 }
760
761 #[inline]
762 pub fn reset_assumptions(&mut self) {
763 ffi::reset_assumptions(&mut self.solver);
764 }
765
766 #[inline]
767 pub fn reset_constraint(&mut self) {
768 ffi::reset_constraint(&mut self.solver);
769 }
770
771 /// Return the current state of the solver as defined above.
772 ///
773 #[must_use]
774 #[inline]
775 pub fn state(&self) -> State {
776 ffi::state(&self.solver).into()
777 }
778
779 /// Similar to 'state ()' but using the staddard competition exit codes of
780 /// '10' for 'SATISFIABLE', '20' for 'UNSATISFIABLE' and '0' otherwise.
781 ///
782 #[must_use]
783 #[inline]
784 pub fn status(&self) -> Status {
785 ffi::status(&self.solver).into()
786 }
787
788 /// return version string
789 #[must_use]
790 #[inline]
791 pub fn version() -> String {
792 ffi::version()
793 }
794
795 /*----------------------------------------------------------------------*/
796 /// Copy 'this' into a fresh 'other'. The copy procedure is not a deep
797 /// clone, but only copies irredundant clauses and units. It also makes
798 /// sure that witness reconstruction works with the copy as with the
799 /// original formula such that both solvers have the same models.
800 /// Assumptions are not copied. Options however are copied as well as
801 /// flags which remember the current state of variables in preprocessing.
802 ///
803 /// require (READY) /// for 'this'
804 /// ensure (READY) /// for 'this'
805 ///
806 /// other.require (CONFIGURING)
807 /// other.ensure (CONFIGURING | STEADY )
808 ///
809 #[inline]
810 pub fn copy(source: &CaDiCal, destination: &mut CaDiCal) {
811 ffi::copy(&source.solver, &mut destination.solver);
812 }
813
814 /*----------------------------------------------------------------------*/
815 /// Variables are usually added and initialized implicitly whenever a
816 /// literal is used as an argument except for the functions 'val', 'fixed',
817 /// 'failed' and 'frozen'. However, the library internally keeps a maximum
818 /// variable index, which can be queried.
819 ///
820 /// require (VALID | SOLVING)
821 /// ensure (VALID | SOLVING)
822 ///
823 #[inline]
824 pub fn vars(&mut self) -> i32 {
825 ffi::vars(&mut self.solver)
826 }
827
828 /// Increase the maximum variable index explicitly. This function makes
829 /// sure that at least '`min_max_var`' variables are initialized. Since it
830 /// might need to reallocate tables, it destroys a satisfying assignment
831 /// and has the same state transition and conditions as 'assume' etc.
832 ///
833 /// require (READY)
834 /// ensure (STEADY )
835 ///
836 #[inline]
837 pub fn reserve(&mut self, min_max_var: i32) {
838 ffi::reserve(&mut self.solver, min_max_var);
839 }
840
841 // pub fn `trace_api_calls(&mut` self, file: String);
842
843 //------------------------------------------------------------------------
844 /// Option handling.
845 /// Determine whether 'name' is a valid option name.
846 ///
847 #[must_use]
848 #[inline]
849 pub fn is_valid_option(name: String) -> bool {
850 ffi::is_valid_option(name)
851 }
852
853 /// Determine whether 'name' enables a specific preprocessing technique.
854 ///
855 #[must_use]
856 #[inline]
857 pub fn is_preprocessing_option(name: String) -> bool {
858 ffi::is_preprocessing_option(name)
859 }
860
861 /// Determine whether 'arg' is a valid long option of the form '--<name>',
862 /// '--<name>=<val>' or '--no-<name>' similar to '`set_long_option`' below.
863 /// Legal values are 'true', 'false', or '[-]<mantissa>[e<exponent>]'.
864 #[must_use]
865 #[inline]
866 pub fn is_valid_long_option(arg: String) -> bool {
867 ffi::is_valid_long_option(arg)
868 }
869
870 /// Get the current value of the option 'name'. If 'name' is invalid then
871 /// zero is returned. Here '--...' arguments as invalid options.
872 ///
873 #[inline]
874 pub fn get(&mut self, name: String) -> i32 {
875 ffi::get(&mut self.solver, name)
876 }
877
878 /// Set the default verbose message prefix (default "c ").
879 ///
880 #[inline]
881 pub fn prefix(&mut self, verbose_message_prefix: String) {
882 ffi::prefix(&mut self.solver, verbose_message_prefix);
883 }
884
885 /// Explicit version of setting an option. If the option '<name>' exists
886 /// and '<val>' can be parsed then 'true' is returned. If the option value
887 /// is out of range the actual value is computed as the closest (minimum or
888 /// maximum) value possible, but still 'true' is returned.
889 ///
890 /// require (CONFIGURING)
891 /// ensure (CONFIGURING)
892 ///
893 /// Thus options can only bet set right after initialization.
894 ///
895 #[inline]
896 pub fn set(&mut self, name: String, val: i32) -> bool {
897 ffi::set(&mut self.solver, name, val)
898 }
899
900 /// This function accepts options in command line syntax:
901 ///
902 /// '--<name>=<val>', '--<name>', or '--no-<name>'
903 ///
904 /// It actually calls the previous 'set' function after parsing 'arg'. The
905 /// same values are expected as for '`is_valid_long_option`' above and as
906 /// with 'set' any value outside of the range of legal values for a
907 /// particular option are set to either the minimum or maximum depending on
908 /// which side of the valid interval they lie.
909 ///
910 /// require (CONFIGURING)
911 /// ensure (CONFIGURING)
912 ///
913 #[inline]
914 pub fn set_long_option(&mut self, arg: String) -> bool {
915 ffi::set_long_option(&mut self.solver, arg)
916 }
917
918 /// Determine whether 'name' is a valid configuration.
919 ///
920 #[must_use]
921 #[inline]
922 pub fn is_valid_configuration(name: String) -> bool {
923 ffi::is_valid_configuration(name)
924 }
925
926 /// Overwrite (some) options with the forced values of the configuration.
927 /// The result is 'true' iff the 'name' is a valid configuration.
928 ///
929 /// require (CONFIGURING)
930 /// ensure (CONFIGURING)
931 ///
932 #[inline]
933 pub fn configure(&mut self, name: String) -> bool {
934 ffi::configure(&mut self.solver, name)
935 }
936
937 /// Increase preprocessing and inprocessing limits by '10^<val>'. Values
938 /// below '0' are ignored and values above '9' are reduced to '9'.
939 ///
940 /// require (READY)
941 /// ensure (READY)
942 ///
943 #[inline]
944 pub fn optimize(&mut self, val: i32) {
945 ffi::optimize(&mut self.solver, val);
946 }
947
948 /// Specify search limits, where currently 'name' can be "conflicts",
949 /// "decisions", "preprocessing", or "localsearch". The first two limits
950 /// are unbounded by default. Thus using a negative limit for conflicts or
951 /// decisions switches back to the default of unlimited search (for that
952 /// particular limit). The preprocessing limit determines the number of
953 /// preprocessing rounds, which is zero by default. Similarly, the local
954 /// search limit determines the number of local search rounds (also zero by
955 /// default). As with 'set', the return value denotes whether the limit
956 /// 'name' is valid. These limits are only valid for the next 'solve' or
957 /// 'simplify' call and reset to their default after 'solve' returns (as
958 /// well as overwritten and reset during calls to 'simplify' and
959 /// 'lookahead'). We actually also have an internal "terminate" limit
960 /// which however should only be used for testing and debugging.
961 ///
962 /// require (READY)
963 /// ensure (READY)
964 ///
965 #[inline]
966 pub fn limit(&mut self, arg: String, val: i32) -> bool {
967 ffi::limit(&mut self.solver, arg, val)
968 }
969 #[inline]
970 pub fn is_valid_limit(&mut self, arg: String) -> bool {
971 ffi::is_valid_limit(&mut self.solver, arg)
972 }
973
974 /// The number of currently active variables and clauses can be queried by
975 /// these functions. Variables become active if a clause is added with it.
976 /// They become inactive if they are eliminated or fixed at the root level
977 /// Clauses become inactive if they are satisfied, subsumed, eliminated.
978 /// Redundant clauses are reduced regularly and thus the 'redundant'
979 /// function is less useful.
980 ///
981 /// require (VALID)
982 /// ensure (VALID)
983 ///
984 /// Number of active variables.
985 #[must_use]
986 #[inline]
987 pub fn active(&self) -> i32 {
988 ffi::active(&self.solver)
989 }
990
991 /// Number of active redundant clauses.
992 #[must_use]
993 #[inline]
994 pub fn redundant(&self) -> i64 {
995 ffi::redundant(&self.solver)
996 }
997
998 /// Number of active irredundant clauses.
999 #[must_use]
1000 #[inline]
1001 pub fn irredundant(&self) -> i64 {
1002 ffi::irredundant(&self.solver)
1003 }
1004
1005 //------------------------------------------------------------------------
1006 /// This function executes the given number of preprocessing rounds. It is
1007 /// similar to 'solve' with 'limits ("preprocessing", rounds)' except that
1008 /// no CDCL nor local search, nor lucky phases are executed. The result
1009 /// values are also the same: 0=UNKNOWN, 10=SATISFIABLE, 20=UNSATISFIABLE.
1010 /// As 'solve' it resets current assumptions and limits before returning.
1011 /// The numbers of rounds should not be negative. If the number of rounds
1012 /// is zero only clauses are restored (if necessary) and top level unit
1013 /// propagation is performed, which both take some time.
1014 ///
1015 /// require (READY)
1016 /// ensure (STEADY | SATISFIED | UNSATISFIED)
1017 ///
1018 #[inline]
1019 pub fn simplify(&mut self, rounds: i32) -> Status {
1020 ffi::simplify(&mut self.solver, rounds).into()
1021 }
1022
1023 //------------------------------------------------------------------------
1024 /// Force termination of 'solve' asynchronously.
1025 ///
1026 /// require (SOLVING | READY)
1027 /// ensure (STEADY ) /// actually not immediately (synchronously)
1028 ///
1029 #[inline]
1030 pub fn terminate(&mut self) {
1031 ffi::terminate(&mut self.solver);
1032 }
1033
1034 //------------------------------------------------------------------------
1035
1036 /// We have the following common reference counting functions, which avoid
1037 /// to restore clauses but require substantial user guidance. This was the
1038 /// only way to use inprocessing in incremental SAT solving in Lingeling
1039 /// (and before in `MiniSAT`'s 'freeze' / 'thaw') and which did not use
1040 /// automatic clause restoring. In general this is slower than
1041 /// restoring clauses and should not be used.
1042 ///
1043 /// In essence the user freezes variables which potentially are still
1044 /// needed in clauses added or assumptions used after the next 'solve'
1045 /// call. As in Lingeling you can freeze a variable multiple times, but
1046 /// then have to melt it the same number of times again in order to enable
1047 /// variable eliminating on it etc. The arguments can be literals
1048 /// (negative indices) but conceptually variables are frozen.
1049 ///
1050 /// In the old way of doing things without restore you should not use a
1051 /// variable incrementally (in 'add' or 'assume'), which was used before
1052 /// and potentially could have been eliminated in a previous 'solve' call.
1053 /// This can lead to spurious satisfying assignment. In order to check
1054 /// this API contract one can use the 'checkfrozen' option. This has the
1055 /// drawback that restoring clauses implicitly would fail with a fatal
1056 /// error message even if in principle the solver could just restore
1057 /// clauses. Thus this option is disabled by default.
1058 ///
1059 /// See our SAT'19 paper [FazekasBiereScholl-SAT'19] for more details.
1060 ///
1061 /// require (VALID)
1062 /// ensure (VALID)
1063 ///
1064 #[must_use]
1065 #[inline]
1066 pub fn frozen(&self, lit: i32) -> bool {
1067 ffi::frozen(&self.solver, lit)
1068 }
1069
1070 #[inline]
1071 pub fn freeze(&mut self, lit: i32) {
1072 ffi::freeze(&mut self.solver, lit);
1073 }
1074
1075 #[inline]
1076 pub fn melt(&mut self, lit: i32) {
1077 ffi::melt(&mut self.solver, lit);
1078 }
1079
1080 //------------------------------------------------------------------------
1081 /// Root level assigned variables can be queried with this function.
1082 /// It returns '1' if the literal is implied by the formula, '-1' if its
1083 /// negation is implied, or '0' if this is unclear at this point.
1084 ///
1085 /// require (VALID)
1086 /// ensure (VALID)
1087 ///
1088 #[must_use]
1089 #[inline]
1090 pub fn fixed(&self, lit: i32) -> i32 {
1091 ffi::fixed(&self.solver, lit)
1092 }
1093
1094 //------------------------------------------------------------------------
1095 /// Force the default decision phase of a variable to a certain value.
1096 ///
1097 #[inline]
1098 pub fn phase(&mut self, lit: i32) {
1099 ffi::phase(&mut self.solver, lit);
1100 }
1101
1102 #[inline]
1103 pub fn unphase(&mut self, lit: i32) {
1104 ffi::unphase(&mut self.solver, lit);
1105 }
1106
1107 //------------------------------------------------------------------------
1108 /// Enables clausal proof tracing in DRAT format and returns 'true' if
1109 /// successfully opened for writing. Writing proofs has to be enabled
1110 /// before calling 'solve', 'add' and 'dimacs', that is in state
1111 /// 'CONFIGURING'. Otherwise only partial proofs would be written.
1112 ///
1113 /// require (CONFIGURING)
1114 /// ensure (CONFIGURING)
1115 ///
1116 /// Write DRAT proof.
1117 #[inline]
1118 pub fn trace_proof1(&mut self, file: String, name: String) -> bool {
1119 ffi::trace_proof1(&mut self.solver, file, name)
1120 }
1121
1122 /// Open & write proof.
1123 #[inline]
1124 pub fn trace_proof2(&mut self, path: String) -> bool {
1125 ffi::trace_proof2(&mut self.solver, path)
1126 }
1127
1128 /// Flushing the proof trace file eventually calls 'fflush' on the actual
1129 /// file or pipe and thus if this function returns all the proof steps
1130 /// should have been written (with the same guarantees as 'fflush').
1131 ///
1132 /// The additional optional argument forces to print the number of addition
1133 /// and deletion steps in the proof even if the verbosity level is zero but
1134 /// not if quiet is set as well. The default for the stand-alone solver is
1135 /// to print this information (in the 'closing proof' section) but for API
1136 /// usage of the library we want to stay silent unless explicitly requested
1137 /// or verbosity is non-zero (and as explained quiet is not set).
1138 ///
1139 /// This function can be called multiple times.
1140 ///
1141 /// require (VALID)
1142 /// ensure (VALID)
1143 ///
1144 #[inline]
1145 pub fn flush_proof_trace(&mut self, print: bool) {
1146 ffi::flush_proof_trace(&mut self.solver, print);
1147 }
1148
1149 /// Close proof trace early. Similar to 'flush' we allow the user to
1150 /// control with 'print' in a more fine-grained way whether statistics
1151 /// about the size of the written proof file and if compressed on-the-fly
1152 /// the number of actual bytes written (including deflation percentage) are
1153 /// printed. Before actually closing (or detaching in case of writing to
1154 /// '<stdout>') we check whether '`flush_proof_trace`' was called since the
1155 /// last time a proof step (addition or deletion) was traced. If this is
1156 /// not the case we would call '`flush_proof_trace`' with the same 'print'
1157 /// argument.
1158 ///
1159 /// require (VALID)
1160 /// ensure (VALID)
1161 ///
1162 #[inline]
1163 pub fn close_proof_trace(&mut self, print: bool) {
1164 ffi::close_proof_trace(&mut self.solver, print);
1165 }
1166
1167 // /// Enables clausal proof tracing with or without antecedents using
1168 // /// the Tracer interface defined in 'tracer.hpp'
1169 // ///
1170 // /// InternalTracer, StatTracer and FileTracer for internal use
1171 // ///
1172 // /// require (CONFIGURING)
1173 // /// ensure (CONFIGURING)
1174 // ///
1175 #[allow(clippy::missing_panics_doc)]
1176 #[allow(clippy::too_many_lines)]
1177 pub fn connect_proof_tracer1<'a, 'b: 'a, T: ProofTracer>(
1178 &'a mut self,
1179 tracer: &'b mut T,
1180 antecedents: bool,
1181 ) {
1182 fn add_original_clause<T: ProofTracer>(
1183 state: *mut u8,
1184 id: u64,
1185 redundant: bool,
1186 clause: &[i32],
1187 restored: bool,
1188 ) {
1189 let ptr: *mut T = state.cast::<T>();
1190 let t = unsafe { &mut *ptr };
1191 t.add_original_clause(id, redundant, clause, restored);
1192 }
1193
1194 fn add_derived_clause<T: ProofTracer>(
1195 state: *mut u8,
1196 id: u64,
1197 redundant: bool,
1198 clause: &[i32],
1199 antecedents: &[u64],
1200 ) {
1201 let ptr: *mut T = state.cast::<T>();
1202 let t = unsafe { &mut *ptr };
1203 t.add_derived_clause(id, redundant, clause, antecedents);
1204 }
1205
1206 fn delete_clause<T: ProofTracer>(state: *mut u8, id: u64, redundant: bool, clause: &[i32]) {
1207 let ptr: *mut T = state.cast::<T>();
1208 let t = unsafe { &mut *ptr };
1209 t.delete_clause(id, redundant, clause);
1210 }
1211
1212 fn weaken_minus<T: ProofTracer>(state: *mut u8, id: u64, clause: &[i32]) {
1213 let ptr: *mut T = state.cast::<T>();
1214 let t = unsafe { &mut *ptr };
1215 t.weaken_minus(id, clause);
1216 }
1217
1218 fn strengthen<T: ProofTracer>(state: *mut u8, id: u64) {
1219 let ptr: *mut T = state.cast::<T>();
1220 let t = unsafe { &mut *ptr };
1221 t.strengthen(id);
1222 }
1223
1224 fn finalize_clause<T: ProofTracer>(state: *mut u8, id: u64, clause: &[i32]) {
1225 let ptr: *mut T = state.cast::<T>();
1226 let t = unsafe { &mut *ptr };
1227 t.finalize_clause(id, clause);
1228 }
1229
1230 fn add_assumption<T: ProofTracer>(state: *mut u8, lit: i32) {
1231 let ptr: *mut T = state.cast::<T>();
1232 let t = unsafe { &mut *ptr };
1233 t.add_assumption(lit);
1234 }
1235
1236 fn add_constraint<T: ProofTracer>(state: *mut u8, clause: &[i32]) {
1237 let ptr: *mut T = state.cast::<T>();
1238 let t = unsafe { &mut *ptr };
1239 t.add_constraint(clause);
1240 }
1241
1242 fn reset_assumptions<T: ProofTracer>(state: *mut u8) {
1243 let ptr: *mut T = state.cast::<T>();
1244 let t = unsafe { &mut *ptr };
1245 t.reset_assumptions();
1246 }
1247
1248 fn add_assumption_clause<T: ProofTracer>(
1249 state: *mut u8,
1250 id: u64,
1251 clause: &[i32],
1252 antecedents: &[u64],
1253 ) {
1254 let ptr: *mut T = state.cast::<T>();
1255 let t = unsafe { &mut *ptr };
1256 t.add_assumption_clause(id, clause, antecedents);
1257 }
1258
1259 fn conclude_sat<T: ProofTracer>(state: *mut u8, conclusion_type: i32, model: &[i32]) {
1260 let ptr: *mut T = state.cast::<T>();
1261 let t = unsafe { &mut *ptr };
1262 t.conclude_sat(conclusion_type, model);
1263 }
1264
1265 fn conclude_unsat<T: ProofTracer>(
1266 state: *mut u8,
1267 conclusion_type: i32,
1268 clause_ids: &[u64],
1269 ) {
1270 let ptr: *mut T = state.cast::<T>();
1271 let t = unsafe { &mut *ptr };
1272 t.conclude_unsat(conclusion_type, clause_ids);
1273 }
1274
1275 fn conclude_unknown<T: ProofTracer>(state: *mut u8, trail: &[i32]) {
1276 let ptr: *mut T = state.cast::<T>();
1277 let t = unsafe { &mut *ptr };
1278 t.conclude_unknown(trail);
1279 }
1280
1281 let tracer_ptr = unsafe {
1282 ffi::new_tracer(
1283 std::ptr::from_mut(tracer).cast::<u8>(),
1284 add_original_clause::<T>,
1285 add_derived_clause::<T>,
1286 delete_clause::<T>,
1287 weaken_minus::<T>,
1288 strengthen::<T>,
1289 finalize_clause::<T>,
1290 add_assumption::<T>,
1291 add_constraint::<T>,
1292 reset_assumptions::<T>,
1293 add_assumption_clause::<T>,
1294 conclude_sat::<T>,
1295 conclude_unsat::<T>,
1296 conclude_unknown::<T>,
1297 )
1298 };
1299
1300 // Store the tracer to prevent it from being dropped
1301 self.last_tracer = Some(tracer_ptr);
1302
1303 // Connect the tracer to the solver
1304 ffi::connect_proof_tracer1(
1305 &mut self.solver,
1306 self.last_tracer.as_mut().unwrap(),
1307 antecedents,
1308 );
1309 }
1310
1311 // pub fn connect_proof_tracer2(
1312 // &mut self,
1313 // tracer: &mut UniquePtr<InternalTracer>,
1314 // antecedents: bool,
1315 // ) {
1316 // todo!()
1317 // }
1318 // pub fn connect_proof_tracer3(&mut self, tracer: &mut UniquePtr<StatTracer>, antecedents: bool) {
1319 // todo!()
1320 // }
1321
1322 // pub fn connect_proof_tracer4(&mut self, tracer: &mut UniquePtr<FileTracer>, antecedents: bool) {
1323 // todo!()
1324 // }
1325
1326 /// Triggers the conclusion of incremental proofs.
1327 /// if the solver is SATISFIED it will trigger extend ()
1328 /// and give the model to the proof tracer through `conclude_sat` ()
1329 /// if the solver is UNSATISFIED it will trigger failing ()
1330 /// which will learn new clauses as explained below:
1331 /// In case of failed assumptions will provide a core negated
1332 /// as a clause through the proof tracer interface.
1333 /// With a failing contraint these can be multiple clauses.
1334 /// Then it will trigger a `conclude_unsat` event with the id(s)
1335 /// of the newly learnt clauses or the id of the global conflict.
1336 ///
1337 /// require (SATISFIED || UNSATISFIED)
1338 /// ensure (SATISFIED || UNSATISFIED)
1339 ///
1340 #[inline]
1341 pub fn conclude(&mut self) {
1342 ffi::conclude(&mut self.solver);
1343 }
1344
1345 // /// Disconnect proof tracer. If this is not done before deleting
1346 // /// the tracer will be deleted. Returns true if successful.
1347 // ///
1348 // /// require (VALID)
1349 // /// ensure (VALID)
1350 // ///
1351 pub fn disconnect_proof_tracer1(&mut self) {
1352 if let Some(tracer) = &mut self.last_tracer {
1353 ffi::disconnect_proof_tracer1(&mut self.solver, tracer);
1354 }
1355 self.last_tracer = None;
1356 }
1357 // pub fn disconnect_proof_tracer2(&mut self, tracer: &mut UniquePtr<StatTracer>) -> bool {
1358 // todo!()
1359 // }
1360 // pub fn disconnect_proof_tracer3(&mut self, tracer: &mut UniquePtr<FileTracer>) -> bool {
1361 // todo!()
1362 // }
1363
1364 /// print usage information for long options
1365 #[inline]
1366 pub fn usage() {
1367 ffi::usage();
1368 }
1369
1370 /// print configuration usage options
1371 #[inline]
1372 pub fn configurations() {
1373 ffi::configurations();
1374 }
1375
1376 /// require (!DELETING)
1377 /// ensure (!DELETING)
1378 ///
1379 /// print statistics
1380 #[inline]
1381 pub fn statistics(&mut self) {
1382 ffi::statistics(&mut self.solver);
1383 }
1384
1385 /// print resource usage (time and memory)
1386 #[inline]
1387 pub fn resources(&mut self) {
1388 ffi::resources(&mut self.solver);
1389 }
1390
1391 /// require (VALID)
1392 /// ensure (VALID)
1393 ///
1394 /// print current option and value list
1395 #[inline]
1396 pub fn options(&mut self) {
1397 ffi::options(&mut self.solver);
1398 }
1399
1400 //------------------------------------------------------------------------
1401 /// Traverse irredundant clauses or the extension stack in reverse order.
1402 ///
1403 /// The return value is false if traversal is aborted early due to one of
1404 /// the visitor functions returning false. See description of the
1405 /// iterators below for more details on how to use these functions.
1406 ///
1407 /// require (VALID)
1408 /// ensure (VALID)
1409 ///
1410 #[inline]
1411 pub fn traverse_clauses<I: ClauseIterator>(&self, i: &mut I) -> bool {
1412 fn f<I: ClauseIterator>(state: *mut u8, clause: &[i32]) -> bool {
1413 let ptr: *mut I = state.cast::<I>();
1414 let i = unsafe { &mut *ptr };
1415 i.clause(clause)
1416 }
1417 let mut iter =
1418 unsafe { ffi::new_clause_iterator(std::ptr::from_mut(i).cast::<u8>(), f::<I>) };
1419 ffi::traverse_clauses(&self.solver, &mut iter)
1420 }
1421
1422 #[inline]
1423 pub fn traverse_witnesses_backward<I: WitnessIterator>(&self, i: &mut I) -> bool {
1424 fn f<I: WitnessIterator>(state: *mut u8, clause: &[i32], witness: &[i32], id: u64) -> bool {
1425 let ptr: *mut I = state.cast::<I>();
1426 let i = unsafe { &mut *ptr };
1427 i.witness(clause, witness, id)
1428 }
1429 let mut iter =
1430 unsafe { ffi::new_witness_iterator(std::ptr::from_mut(i).cast::<u8>(), f::<I>) };
1431 ffi::traverse_witnesses_backward(&self.solver, &mut iter)
1432 }
1433
1434 #[inline]
1435 pub fn traverse_witnesses_forward<I: WitnessIterator>(&self, i: &mut I) -> bool {
1436 fn f<I: WitnessIterator>(state: *mut u8, clause: &[i32], witness: &[i32], id: u64) -> bool {
1437 let ptr: *mut I = state.cast::<I>();
1438 let i = unsafe { &mut *ptr };
1439 i.witness(clause, witness, id)
1440 }
1441 let mut iter =
1442 unsafe { ffi::new_witness_iterator(std::ptr::from_mut(i).cast::<u8>(), f::<I>) };
1443 ffi::traverse_witnesses_forward(&self.solver, &mut iter)
1444 }
1445
1446 //------------------------------------------------------------------------
1447 /// Files with explicit path argument support compressed input and output
1448 /// if appropriate helper functions 'gzip' etc. are available. They are
1449 /// called through opening a pipe to an external command.
1450 ///
1451 /// If the 'strict' argument is zero then the number of variables and
1452 /// clauses specified in the DIMACS headers are ignored, i.e., the header
1453 /// 'p cnf 0 0' is always legal. If the 'strict' argument is larger '1'
1454 /// strict formatting of the header is required, i.e., single spaces
1455 /// everywhere and no trailing white space.
1456 ///
1457 /// Returns zero if successful and otherwise an error message.
1458 ///
1459 /// require (VALID)
1460 /// ensure (VALID)
1461 ///
1462 #[inline]
1463 pub fn read_dimacs1(
1464 &mut self,
1465 file: String,
1466 name: String,
1467 vars: &mut i32,
1468 strict: i32,
1469 ) -> String {
1470 ffi::read_dimacs1(&mut self.solver, file, name, vars, strict)
1471 }
1472
1473 #[inline]
1474 pub fn read_dimacs2(&mut self, path: String, vars: &mut i32, strict: i32) -> String {
1475 ffi::read_dimacs2(&mut self.solver, path, vars, strict)
1476 }
1477
1478 /// The following routines work the same way but parse both DIMACS and
1479 /// INCCNF files (with 'p inccnf' header and 'a <cube>' lines). If the
1480 /// parser finds and 'p inccnf' header or cubes then '*incremental' is set
1481 /// to true and the cubes are stored in the given vector (each cube
1482 /// terminated by a zero).
1483 #[inline]
1484 pub fn read_dimacs3(
1485 &mut self,
1486 file: String,
1487 name: String,
1488 vars: &mut i32,
1489 strict: i32,
1490 incremental: &mut bool,
1491 cubes: &mut Vec<i32>,
1492 ) -> String {
1493 ffi::read_dimacs3(
1494 &mut self.solver,
1495 file,
1496 name,
1497 vars,
1498 strict,
1499 incremental,
1500 cubes,
1501 )
1502 }
1503
1504 #[inline]
1505 pub fn read_dimacs4(
1506 &mut self,
1507 path: String,
1508 vars: &mut i32,
1509 strict: i32,
1510 incremental: &mut bool,
1511 cubes: &mut Vec<i32>,
1512 ) -> String {
1513 ffi::read_dimacs4(&mut self.solver, path, vars, strict, incremental, cubes)
1514 }
1515
1516 //------------------------------------------------------------------------
1517 /// Write current irredundant clauses and all derived unit clauses
1518 /// to a file in DIMACS format. Clauses on the extension stack are
1519 /// not included, nor any redundant clauses.
1520 ///
1521 /// The '`min_max_var`' parameter gives a lower bound on the number '<vars>'
1522 /// of variables used in the DIMACS 'p cnf <vars> ...' header.
1523 ///
1524 /// Returns zero if successful and otherwise an error message.
1525 ///
1526 /// require (VALID)
1527 /// ensure (VALID)
1528 ///
1529 #[inline]
1530 pub fn write_dimacs(&mut self, path: String, min_max_var: i32) -> String {
1531 ffi::write_dimacs(&mut self.solver, path, min_max_var)
1532 }
1533
1534 /// The extension stack for reconstruction a solution can be written too.
1535 ///
1536 #[inline]
1537 pub fn write_extension(&mut self, path: String) -> String {
1538 ffi::write_extension(&mut self.solver, path)
1539 }
1540
1541 /// Print build configuration to a file with prefix 'c '. If the file
1542 /// is '<stdout>' or '<stderr>' then terminal color codes might be used.
1543 ///
1544 #[inline]
1545 pub fn build(file: String, prefix: String) {
1546 ffi::build(file, prefix);
1547 }
1548}
1549
1550/// Connected terminators are checked for termination regularly. If the
1551/// 'terminate' function of the terminator returns true the solver is
1552/// terminated synchronously as soon it calls this function.
1553pub trait Terminator {
1554 fn terminated(&mut self) -> bool;
1555}
1556
1557/// Connected learners which can be used to export learned clauses.
1558/// The 'learning' can check the size of the learn clause and only if it
1559/// returns true then the individual literals of the learned clause are given
1560/// to the learn through 'learn' one by one terminated by a zero literal.
1561pub trait Learner {
1562 fn learning(&mut self, size: i32) -> bool;
1563 fn learn(&mut self, lit: i32);
1564}
1565
1566/// Connected listener gets notified whenever the truth value of a variable is
1567/// fixed (for example during inprocessing or due to some derived unit clauses).
1568pub trait FixedAssignmentListener {
1569 fn notify_fixed_assignment(&mut self, lit: i32);
1570}
1571
1572use std::vec::Vec;
1573
1574/// Allows to connect an external propagator to propagate values to variables
1575/// with an external clause as a reason or to learn new clauses during the
1576/// CDCL loop (without restart).
1577pub trait ExternalPropagator {
1578 /// lazy propagator only checks complete assignments
1579 fn is_lazy(&mut self) -> bool {
1580 false
1581 }
1582
1583 /// Reason external clauses can be deleted
1584 fn are_reasons_forgettable(&mut self) -> bool {
1585 false
1586 }
1587
1588 /// Notify the propagator about assignments to observed variables.
1589 /// The notification is not necessarily eager. It usually happens before
1590 /// the call of propagator callbacks and when a driving clause is leading
1591 /// to an assignment.
1592 fn notify_assignment(&mut self, lits: &[i32]);
1593
1594 fn notify_new_decision_level(&mut self);
1595
1596 fn notify_backtrack(&mut self, new_level: usize);
1597
1598 /// Check by the external propagator the found complete solution (after
1599 /// solution reconstruction). If it returns false, the propagator must
1600 /// provide an external clause during the next callback.
1601 fn cb_check_found_model(&mut self, model: &[i32]) -> bool;
1602
1603 /// Ask the external propagator for the next decision literal. If it
1604 /// returns 0, the solver makes its own choice.
1605 fn cb_decide(&mut self) -> i32 {
1606 0
1607 }
1608
1609 /// Ask the external propagator if there is an external propagation to make
1610 /// under the current assignment. It returns either a literal to be
1611 /// propagated or 0, indicating that there is no external propagation under
1612 /// the current assignment.
1613 fn cb_propagate(&mut self) -> i32 {
1614 0
1615 }
1616
1617 /// Ask the external propagator for the reason clause of a previous
1618 /// external propagation step (done by `cb_propagate`). The clause must be
1619 /// added literal-by-literal closed with a 0. Further, the clause must
1620 /// contain the propagated literal.
1621 ///
1622 /// The clause will be learned as an Irredundant Non-Forgettable Clause (see
1623 /// below at '`cb_has_external_clause`' more details about it).
1624 fn cb_add_reason_clause_lit(&mut self, _propagated_lit: i32) -> i32 {
1625 0
1626 }
1627
1628 /// The following two functions are used to add external clauses to the
1629 /// solver during the CDCL loop. The external clause is added
1630 /// literal-by-literal and learned by the solver as an irredundant
1631 /// (original) input clause. The clause can be arbitrary, but if it is
1632 /// root-satisfied or tautology, the solver will ignore it without learning
1633 /// it. Root-falsified literals are eagerly removed from the clause.
1634 /// Falsified clauses trigger conflict analysis, propagating clauses
1635 /// trigger propagation. In case chrono is 0, the solver backtracks to
1636 /// propagate the new literal on the right decision level, otherwise it
1637 /// potentially will be an out-of-order assignment on the current level.
1638 /// Unit clauses always (unless root-satisfied, see above) trigger
1639 /// backtracking (independently from the value of the chrono option and
1640 /// independently from being falsified or satisfied or unassigned) to level
1641 /// 0. Empty clause (or root falsified clause, see above) makes the problem
1642 /// unsat and stops the search immediately. A literal 0 must close the
1643 /// clause.
1644 ///
1645 /// The external propagator indicates that there is a clause to add.
1646 /// The parameter of the function allows the user to indicate that how
1647 /// 'forgettable' is the external clause. Forgettable clauses are allowed
1648 /// to be removed by the SAT solver during clause database reduction.
1649 /// However, it is up to the solver to decide when actually the clause is
1650 /// deleted. For example, unit clauses, even forgettable ones, will not be
1651 /// deleted. In case the clause is not 'forgettable' (the parameter is false),
1652 /// the solver considers the clause to be irredundant.
1653 ///
1654 /// In case the solver produces incremental proofs, these external clauses
1655 /// are added to the proof during solving at real-time, i.e., the proof
1656 /// checker can ignore them until that point (so added as input clause, but
1657 /// input after the query line).
1658 ///
1659 /// Reason clauses of external propagation steps are assumed to be
1660 /// forgettable, parameter '`reason_forgettable`' can be used to change it.
1661 ///
1662 /// Currently, every external clause is expected to be over observed
1663 /// (therefore frozen) variables, hence no tainting or restore steps
1664 /// are performed upon their addition. This will be changed in later
1665 /// versions probably.
1666 fn cb_has_external_clause(&mut self, is_forgettable: &mut bool) -> bool;
1667
1668 /// The actual function called to add the external clause.
1669 fn cb_add_external_clause_lit(&mut self) -> i32;
1670}
1671
1672/// Allows to traverse all remaining irredundant clauses. Satisfied and
1673/// eliminated clauses are not included, nor any derived units unless such
1674/// a unit literal is frozen. Falsified literals are skipped. If the solver
1675/// is inconsistent only the empty clause is traversed.
1676///
1677/// If 'clause' returns false traversal aborts early.
1678pub trait ClauseIterator {
1679 fn clause(&mut self, clause: &[i32]) -> bool;
1680}
1681
1682/// Allows to traverse all clauses on the extension stack together with their
1683/// witness cubes. If the solver is inconsistent, i.e., an empty clause is
1684/// found and the formula is unsatisfiable, then nothing is traversed.
1685///
1686/// The clauses traversed in '`traverse_clauses`' together with the clauses on
1687/// the extension stack are logically equivalent to the original clauses.
1688/// See our SAT'19 paper for more details.
1689///
1690/// The witness literals can be used to extend and fix an assignment on the
1691/// remaining clauses to satisfy the clauses on the extension stack too.
1692///
1693/// All derived units of non-frozen variables are included too.
1694///
1695/// If 'witness' returns false traversal aborts early.
1696pub trait WitnessIterator {
1697 fn witness(&mut self, clause: &[i32], witness: &[i32], id: u64) -> bool;
1698}
1699
1700/// Trait for proof tracing that allows you to track proof events in real-time.
1701/// This is useful for SMT solvers that need to interleave SAT solver clauses
1702/// with theory clauses to generate eDRAT proofs.
1703pub trait ProofTracer {
1704 /// Called when an original clause is added to the solver
1705 fn add_original_clause(&mut self, id: u64, redundant: bool, clause: &[i32], restored: bool);
1706
1707 /// Called when a derived clause is learned by the SAT solver
1708 fn add_derived_clause(&mut self, id: u64, redundant: bool, clause: &[i32], antecedents: &[u64]);
1709
1710 /// Called when a clause is deleted
1711 fn delete_clause(&mut self, id: u64, redundant: bool, clause: &[i32]);
1712
1713 /// Called when a clause is weakened (might be restored later)
1714 fn weaken_minus(&mut self, id: u64, clause: &[i32]);
1715
1716 /// Called when a clause is strengthened
1717 fn strengthen(&mut self, id: u64);
1718
1719 /// Called when a clause is finalized
1720 fn finalize_clause(&mut self, id: u64, clause: &[i32]);
1721
1722 /// Called when an assumption is added
1723 fn add_assumption(&mut self, lit: i32);
1724
1725 /// Called when a constraint is added
1726 fn add_constraint(&mut self, clause: &[i32]);
1727
1728 /// Called when assumptions are reset
1729 fn reset_assumptions(&mut self);
1730
1731 /// Called when an assumption clause is added (negation of failing assumptions)
1732 fn add_assumption_clause(&mut self, id: u64, clause: &[i32], antecedents: &[u64]);
1733
1734 /// Called when the solver concludes SAT
1735 fn conclude_sat(&mut self, conclusion_type: i32, model: &[i32]);
1736
1737 /// Called when the solver concludes UNSAT
1738 fn conclude_unsat(&mut self, conclusion_type: i32, clause_ids: &[u64]);
1739
1740 /// Called when the solver concludes UNKNOWN
1741 fn conclude_unknown(&mut self, trail: &[i32]);
1742}