diff options
Diffstat (limited to 'vendor/varisat/src/prop/watch.rs')
-rw-r--r-- | vendor/varisat/src/prop/watch.rs | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/vendor/varisat/src/prop/watch.rs b/vendor/varisat/src/prop/watch.rs new file mode 100644 index 000000000..095ac86a9 --- /dev/null +++ b/vendor/varisat/src/prop/watch.rs @@ -0,0 +1,135 @@ +//! Watchlists to detect clauses that became unit. +//! +//! Each (long) clause has always two watches pointing to it. The watches are kept in the watchlists +//! of two different literals of the clause. Whenever the watches are moved to different literals +//! the litereals of the clause are permuted so the watched literals are in position 0 and 1. +//! +//! When a clause is not unit under the current assignment, the watched literals point at two +//! non-false literals. When a clause is unit and thus propagating, the true literal is watched and +//! in position 0, the other watched literal is the one with the largest decision level and kept in +//! position 1. When a clause becomes satisfied before becoming unit the watches can be kept as they +//! were. +//! +//! When a literal is assigned false that invariant can be invalidated. This can be detected by +//! scanning the watches of the assigned literal. When the assignment is processed the watches are +//! moved to restore that invariant. Unless there is a conflict, i.e. a clause with no non-false +//! literals, this can always be done. This also finds all clauses that became unit. The new unit +//! clauses are exactly those clauses where no two non-false literals can be found. +//! +//! There is no need to update watchlists on backtracking, as unassigning variables cannot +//! invalidate the invariant. +//! +//! See [Section 4.5.1 of the "Handbook of Satisfiability"][handbook-ch4] for more details and +//! references. +//! +//! As a further optimization we use blocking literals. This means that each watch stores a literal +//! of the clause that is different from the watched literal. It can be the other watched literal or +//! any unwatched literal. When that literal is true, the clause is already satisfied, meaning that +//! no watches need to be updated. This can be detected by just looking at the watch, avoiding +//! access of the clause database. This variant was introduced by [Niklas Sörensson and Niklas Eén +//! in "MINISAT 2.1 and MINISAT++1.0 — SAT Race 2008 Editions"][minisat-2.1]. +//! +//! [handbook-ch4]: https://www.satassociation.org/articles/FAIA185-0131.pdf +//! [minisat-2.1]: https://www.cril.univ-artois.fr/SAT09/solvers/booklet.pdf + +use partial_ref::{partial, PartialRef}; + +use varisat_formula::Lit; + +use crate::{ + clause::{db, ClauseRef}, + context::{parts::*, Context}, +}; + +/// A watch on a long clause. +#[derive(Copy, Clone)] +pub struct Watch { + /// Clause which has the referring lit in position 0 or 1. + pub cref: ClauseRef, + /// A lit of the clause, different from the referring lit. + pub blocking: Lit, +} + +/// Watchlists to detect clauses that became unit. +pub struct Watchlists { + /// Contains only valid data for indices of assigned variables. + watches: Vec<Vec<Watch>>, + /// Whether watchlists are present + enabled: bool, +} + +impl Default for Watchlists { + fn default() -> Watchlists { + Watchlists { + watches: vec![], + enabled: true, + } + } +} + +impl Watchlists { + /// Update structures for a new variable count. + pub fn set_var_count(&mut self, count: usize) { + self.watches.resize(count * 2, vec![]); + } + + /// Start watching a clause. + /// + /// `lits` have to be the first two literals of the given clause. + pub fn watch_clause(&mut self, cref: ClauseRef, lits: [Lit; 2]) { + if !self.enabled { + return; + } + + for i in 0..2 { + let watch = Watch { + cref, + blocking: lits[i ^ 1], + }; + self.add_watch(!lits[i], watch); + } + } + + /// Return watches for a given literal. + pub fn watched_by_mut(&mut self, lit: Lit) -> &mut Vec<Watch> { + &mut self.watches[lit.code()] + } + + /// Make a literal watch a clause. + pub fn add_watch(&mut self, lit: Lit, watch: Watch) { + self.watches[lit.code()].push(watch) + } + + /// Are watchlists enabled. + pub fn enabled(&self) -> bool { + self.enabled + } + + /// Clear and disable watchlists. + /// + /// Actual clearing of the watchlists is done on re-enabling of the watchlists. + pub fn disable(&mut self) { + self.enabled = false; + } +} + +/// Enable and rebuild watchlists. +pub fn enable_watchlists(mut ctx: partial!(Context, mut WatchlistsP, ClauseAllocP, ClauseDbP)) { + let (watchlists, mut ctx) = ctx.split_part_mut(WatchlistsP); + if watchlists.enabled { + return; + } + + for watchlist in watchlists.watches.iter_mut() { + watchlist.clear(); + } + + watchlists.enabled = true; + + let (alloc, mut ctx) = ctx.split_part(ClauseAllocP); + + for cref in db::clauses_iter(&ctx.borrow()) { + let lits = alloc.clause(cref).lits(); + watchlists.watch_clause(cref, [lits[0], lits[1]]); + } +} |