1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
//! # Interfaces to SAT Solvers
//!
//! This module holds types and functions regarding SAT solvers. The main
//! element is the [`Solve`] trait that every SAT solver in this library
//! implements.
//!
//! ## Available Solvers
//!
//! Solvers are available through separate crates.
//!
//! ### CaDiCaL
//!
//! [CaDiCaL](https://github.com/arminbiere/cadical) is a fully incremental SAT
//! solver by Armin Biere implemented in C++. It includes incremental
//! inprocessing. It is available through the
//! [`rustsat-cadical`](https://crates.io/crates/rustsat-cadical) crate.
//!
//! #### References
//!
//! - Armin Biere and Katalin Fazekas and Mathias Fleury and Maximillian
//! Heisinger: _CaDiCaL, Kissat, Paracooba, Plingeling and Treengeling
//! Entering the SAT Competition 2020_, SAT Competition 2020.
//! - Original Repository:
//! [https://github.com/arminbiere/cadical](https://github.com/arminbiere/cadical)
//! - Solver crate:
//! [https://crates.io/crates/rustsat-cadical](https://crates.io/crates/rustsat-cadical)
//!
//! ### Kissat
//!
//! [Kissat](https://github.com/arminbiere/kissat) is a non-incremental SAT
//! solver by Armin Biere implemented in C. It is available through the
//! [`rustsat-kissat`](https://crates.io/crates/rustsat-kissat) crate.
//!
//! #### References
//!
//! - Armin Biere and Katalin Fazekas and Mathias Fleury and Maximillian
//! Heisinger: _CaDiCaL, Kissat, Paracooba, Plingeling and Treengeling
//! Entering the SAT Competition 2020_, SAT Competition 2020.
//! - Repository:
//! [https://github.com/arminbiere/kissat](https://github.com/arminbiere/kissat)
//! - Solver crate:
//! [https://github.com/chrjabs/rustsat-kissat](https://github.com/chrjabs/rustsat-kissat)
//!
//! ### Minisat
//!
//! [Minisat](https://github.com/niklasso/minisat) is an incremental SAT solver
//! by Niklas Een and Niklas Sörensson. It is available through the
//! [`rustsat-minisat`](https://crates.io/crates/rustsat-minisat) crate.
//!
//! #### References
//!
//! - Niklas Een and Niklas Sörensson (2003): _An Extensible SAT-solver_, SAT
//! 2003.
//! - Repository:
//! [https://github.com/niklasso/minisat](https://github.com/niklasso/minisat)
//! - Solver crate:
//! [https://crates.io/crates/rustsat-minisat](https://crates.io/crates/rustsat-minisat)
//! - Fork used in solver crate:
//! [https://github.com/chrjabs/minisat](https://github.com/chrjabs/minisat)
//!
//! ### Glucose
//!
//! [Glucose](https://www.labri.fr/perso/lsimon/research/glucose/) is a SAT
//! solver based on Minisat and developed by Gilles Audemard and Laurent Simon.
//! It is available through the
//! [`rustsat-glucose`](https://crates.io/crates/rustsat-glucose) crate.
//!
//! #### References
//!
//! - Gilles Audemard and Laurent Simon: _Predicting Learnt Clauses Quality in
//! Modern SAT Solvers_, IJCAI 2009.
//! - More references at the [Glucose
//! webpage](https://www.labri.fr/perso/lsimon/research/glucose/)
//! - Solver crate:
//! [https://crates.io/crates/rustsat-glucose](https://crates.io/crates/rustsat-glucose)
//! - Fork used in solver crate:
//! [https://github.com/chrjabs/glucose4](https://github.com/chrjabs/glucose4)
//!
//! ### IPASIR
//!
//! [IPASIR](https://github.com/biotomas/ipasir) is a C API for incremental SAT
//! solvers. RustSAT's IPASIR interface is disabled by default since linking to
//! multiple solvers implementing IPASIR at the same time is not possible. The
//! main reason for including the IPASIR interface in RustSAT is to make it
//! easier to include solvers that don't have Rust interface crates. For this,
//! make sure to not depend on any other SAT solver crate to avoid potential
//! namespace conflicts. Then enable the `ipasir` feature and modify the
//! following lines in the build script `build.rs` accordingly.
//!
//! ```text
//! // Link to custom IPASIR solver
//! // Modify this for linking to your static library
//! // Uncomment and modify this for linking to your static library
//! // The name of the library should be _without_ the prefix 'lib' and the suffix '.a'
//! //println!("cargo:rustc-link-lib=static=<path-to-your-static-lib>");
//! //println!("cargo:rustc-link-search=<name-of-your-static-lib>");
//! // If your IPASIR solver links to the C++ stdlib, uncomment the next four lines
//! //#[cfg(target_os = "macos")]
//! //println!("cargo:rustc-flags=-l dylib=c++");
//! //#[cfg(not(target_os = "macos"))]
//! //println!("cargo:rustc-flags=-l dylib=stdc++");
//! ```
use crate::{
clause,
encodings::CollectClauses,
instances::Cnf,
types::{Assignment, Clause, Lit, TernaryVal, Var},
};
use core::time::Duration;
use std::fmt;
#[cfg(feature = "ipasir")]
mod ipasir;
#[cfg(feature = "ipasir")]
pub use ipasir::IpasirSolver;
use thiserror::Error;
/// Trait for all SAT solvers in this library.
/// Solvers outside of this library can also implement this trait to be able to
/// use them with this library.
pub trait Solve: Extend<Clause> {
/// Gets a signature of the solver implementation
fn signature(&self) -> &'static str;
/// Reserves memory in the solver until a maximum variables, if the solver
/// supports it
fn reserve(&mut self, _max_var: Var) -> SolveMightFail {
Ok(())
}
/// Solves the internal CNF formula without any assumptions.
fn solve(&mut self) -> Result<SolverResult, SolverError>;
/// Gets a solution found by the solver.
///
/// # Errors
///
/// - If the solver is not in the satisfied state
/// - A specific implementation might return other errors
fn solution(&self, high_var: Var) -> Result<Assignment, SolverError> {
let mut assignment = Vec::new();
let len = high_var.idx32() + 1;
assignment.reserve(len as usize);
for idx in 0..len {
let lit = Lit::positive(idx);
assignment.push(self.lit_val(lit)?);
}
Ok(Assignment::from(assignment))
}
/// Same as [`Solve::lit_val`], but for variables.
fn var_val(&self, var: Var) -> Result<TernaryVal, SolverError> {
self.lit_val(var.pos_lit())
}
/// Gets an assignment of a variable in the solver.
///
/// # Errors
///
/// - If the solver is not in the satisfied state
/// - A specific implementation might return other errors
fn lit_val(&self, lit: Lit) -> Result<TernaryVal, SolverError>;
/// Adds a clause to the solver
/// If the solver is in the satisfied or unsatisfied state before, it is in
/// the input state afterwards.
fn add_clause(&mut self, clause: Clause) -> SolveMightFail;
/// Like [`Solve::add_clause`] but for unit clauses (clauses with one literal).
fn add_unit(&mut self, lit: Lit) -> SolveMightFail {
self.add_clause(clause![lit])
}
/// Like [`Solve::add_clause`] but for clauses with two literals.
fn add_binary(&mut self, lit1: Lit, lit2: Lit) -> SolveMightFail {
self.add_clause(clause![lit1, lit2])
}
/// Like [`Solve::add_clause`] but for clauses with three literals.
fn add_ternary(&mut self, lit1: Lit, lit2: Lit, lit3: Lit) -> SolveMightFail {
self.add_clause(clause![lit1, lit2, lit3])
}
/// Adds all clauses from a [`Cnf`] instance.
fn add_cnf(&mut self, cnf: Cnf) -> SolveMightFail {
cnf.into_iter().try_for_each(|cl| self.add_clause(cl))
}
}
/// Trait for all SAT solvers in this library.
/// Solvers outside of this library can also implement this trait to be able to
/// use them with this library.
pub trait SolveIncremental: Solve {
/// Solves the internal CNF formula under assumptions.
fn solve_assumps(&mut self, assumps: &[Lit]) -> Result<SolverResult, SolverError>;
/// Gets a core found by an unsatisfiable query.
/// A core is a clause entailed by the formula that contains only inverted
/// literals of the assumptions.
fn core(&mut self) -> Result<Vec<Lit>, SolverError>;
}
/// Trait for all solvers that can be terminated by a termination callback.
pub trait Terminate<'term> {
/// Attaches a termination callback to the solver. During solving this
/// callback is regularly called and the solver terminates if the callback
/// returns [`ControlSignal::Terminate`]. Only a single callback can be
/// attached at any time, attaching a second callback drops the first one.
fn attach_terminator<CB>(&mut self, cb: CB)
where
CB: FnMut() -> ControlSignal + 'term;
/// Detaches the terminator
fn detach_terminator(&mut self);
}
/// Trait for all solvers that can pass out learned clauses via a callback.
pub trait Learn<'learn> {
/// Attaches a learner callback to the solver. This callback is called every
/// time a clause of length up to `max_len` is learned.
fn attach_learner<CB>(&mut self, cb: CB, max_len: usize)
where
CB: FnMut(Clause) + 'learn;
/// Detaches the learner
fn detach_learner(&mut self);
}
/// Trait for all solvers that can be asynchronously interrupt.
pub trait Interrupt {
type Interrupter: InterruptSolver + Send + 'static;
/// Gets a thread safe interrupter object that can be used to terminate the solver
fn interrupter(&mut self) -> Self::Interrupter;
}
/// A thread safe interrupter for a solver
pub trait InterruptSolver: Sync {
/// Interrupts the solver asynchronously
fn interrupt(&self);
}
/// Trait for all solvers that can force a face for a literal
pub trait PhaseLit {
/// Forces the default decision phase of a variable to a certain value
fn phase_lit(&mut self, lit: Lit) -> Result<(), SolverError>;
/// Undoes the effect of a call to [`PhaseLit::phase_lit`]
fn unphase_var(&mut self, var: Var) -> Result<(), SolverError>;
/// Undoes the effect of a call to [`PhaseLit::phase_lit`]
fn unphase_lit(&mut self, lit: Lit) -> Result<(), SolverError> {
self.unphase_var(lit.var())
}
}
/// Trait for freezing and melting variables in solvers with pre-/inprocessing.
pub trait FreezeVar {
/// Freezes a variable so that it is not removed in pre-/inprocessing
fn freeze_var(&mut self, var: Var) -> Result<(), SolverError>;
/// Melts a variable after it had been frozen
fn melt_var(&mut self, var: Var) -> Result<(), SolverError>;
/// Checks if a variable is frozen
fn is_frozen(&mut self, var: Var) -> Result<bool, SolverError>;
}
/// Trait for all solvers that can flip a literal in the current assignment
pub trait FlipLit {
/// Attempts flipping the literal in the given assignment and returns `true` if successful
fn flip_lit(&mut self, lit: Lit) -> Result<bool, SolverError>;
/// Checks if the literal can be flipped in the given assignment without flipping it
fn is_flippable(&mut self, lit: Lit) -> Result<bool, SolverError>;
}
/// Trait for all solvers that can limit the number of conflicts
pub trait LimitConflicts {
/// Sets or removes a limit on the number of conflicts
fn limit_conflicts(&mut self, limit: Option<u32>) -> Result<(), SolverError>;
}
/// Trait for all solvers that can limit the number of decisions
pub trait LimitDecisions {
/// Sets or removes a limit on the number of decisions
fn limit_decisions(&mut self, limit: Option<u32>) -> Result<(), SolverError>;
}
/// Trait for all solvers that can limit the number of propagations
pub trait LimitPropagations {
/// Sets or removes a limit on the number of propagations
fn limit_propagations(&mut self, limit: Option<u32>) -> Result<(), SolverError>;
}
/// Trait for all solvers allowing access to internal search statistics
pub trait GetInternalStats {
/// Gets the number of propagations
fn propagations(&self) -> usize;
/// Gets the number of decisions
fn decisions(&self) -> usize;
/// Gets the number of conflicts
fn conflicts(&self) -> usize;
}
/// Return type of solver calls that don't return but might fail
pub type SolveMightFail = Result<(), SolverError>;
#[allow(dead_code)]
type TermCallbackPtr<'a> = Box<dyn FnMut() -> ControlSignal + 'a>;
#[allow(dead_code)]
type LearnCallbackPtr<'a> = Box<dyn FnMut(Clause) + 'a>;
#[allow(dead_code)]
/// Double boxing is necessary to get thin pointers for casting
type OptTermCallbackStore<'a> = Option<Box<TermCallbackPtr<'a>>>;
#[allow(dead_code)]
/// Double boxing is necessary to get thin pointers for casting
type OptLearnCallbackStore<'a> = Option<Box<LearnCallbackPtr<'a>>>;
/// Solver statistics
#[derive(Clone, PartialEq, Default)]
pub struct SolverStats {
/// The number of satisfiable queries executed
pub n_sat: usize,
/// The number of unsatisfiable queries executed
pub n_unsat: usize,
/// The number of terminated queries executed
pub n_terminated: usize,
/// The number of clauses in the solver
pub n_clauses: usize,
/// The highest variable in the solver
pub max_var: Option<Var>,
/// The average length of the clauses added to the solver
pub avg_clause_len: f32,
/// The total CPU time spent solving
pub cpu_solve_time: Duration,
}
/// Trait for solvers that track certain statistics.
pub trait SolveStats {
/// Gets the available statistics from the solver
fn stats(&self) -> SolverStats;
/// Gets the number of satisfiable queries executed.
fn n_sat_solves(&self) -> usize {
self.stats().n_sat
}
/// Gets the number of unsatisfiable queries executed.
fn n_unsat_solves(&self) -> usize {
self.stats().n_unsat
}
/// Gets the number of queries that were prematurely terminated.
fn n_terminated(&self) -> usize {
self.stats().n_terminated
}
/// Gets the total number of queries executed.
fn n_solves(&self) -> usize {
self.n_sat_solves() + self.n_unsat_solves() + self.n_terminated()
}
/// Gets the number of clauses in the solver.
fn n_clauses(&self) -> usize {
self.stats().n_clauses
}
/// Gets the variable with the highest index in the solver, if any.
/// If all variables below have been used, the index of this variable +1 is
/// the number of variables in the solver.
fn max_var(&self) -> Option<Var> {
self.stats().max_var
}
/// Get number of variables.
/// Note: this is only correct if all variables are used in order!
fn n_vars(&self) -> usize {
match self.max_var() {
Some(var) => var.idx() + 1,
None => 0,
}
}
/// Gets the average length of all clauses in the solver.
fn avg_clause_len(&self) -> f32 {
self.stats().avg_clause_len
}
/// Gets the total CPU time spent solving.
fn cpu_solve_time(&self) -> Duration {
self.stats().cpu_solve_time
}
}
/// States that the solver can be in.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SolverState {
/// Configuration of the solver must be done in this state, before any clauses are added
Configuring,
/// Input state, while adding clauses.
Input,
/// The query was found satisfiable.
Sat,
/// The query was found unsatisfiable.
Unsat,
/// Solver is in error state.
/// For example after trying to add a clause to a non-incremental solver after solving.
Error(String),
}
impl fmt::Display for SolverState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SolverState::Configuring => write!(f, "CONFIGURING"),
SolverState::Input => write!(f, "INPUT"),
SolverState::Sat => write!(f, "SAT"),
SolverState::Unsat => write!(f, "UNSAT"),
SolverState::Error(desc) => write!(f, "Error: {}", desc),
}
}
}
/// Return value for solving queries.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SolverResult {
/// The query was found satisfiable.
Sat,
/// The query was found unsatisfiable.
Unsat,
/// The query was prematurely interrupted.
Interrupted,
}
impl fmt::Display for SolverResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SolverResult::Sat => write!(f, "SAT"),
SolverResult::Unsat => write!(f, "UNSAT"),
SolverResult::Interrupted => write!(f, "Interrupted"),
}
}
}
/// Return type for solver terminator callbacks
#[derive(Debug, PartialEq, Eq)]
pub enum ControlSignal {
/// Variant for the solver to continue
Continue,
/// Variant for the solver to terminate
Terminate,
}
/// Type representing solver errors
#[derive(Error, Debug, Clone, PartialEq, Eq)]
pub enum SolverError {
/// An API with a description
#[error("API error: {0}")]
Api(String),
/// The solver was expected to be in the second [`SolverState`], but it is in the first.
#[error("solvers needs to be in state {1} but was in state {0}")]
State(SolverState, SolverState),
}
impl<S: Solve + SolveStats> CollectClauses for S {
fn n_clauses(&self) -> usize {
self.n_clauses()
}
}