summaryrefslogtreecommitdiffstats
path: root/vendor/varisat/src/cdcl.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/varisat/src/cdcl.rs')
-rw-r--r--vendor/varisat/src/cdcl.rs264
1 files changed, 264 insertions, 0 deletions
diff --git a/vendor/varisat/src/cdcl.rs b/vendor/varisat/src/cdcl.rs
new file mode 100644
index 000000000..88ee76d6a
--- /dev/null
+++ b/vendor/varisat/src/cdcl.rs
@@ -0,0 +1,264 @@
+//! Conflict driven clause learning.
+
+use partial_ref::{partial, PartialRef};
+
+use varisat_internal_proof::ProofStep;
+
+use crate::{
+ analyze_conflict::analyze_conflict,
+ assumptions::{enqueue_assumption, EnqueueAssumption},
+ clause::{assess_learned_clause, bump_clause, db, decay_clause_activities},
+ context::{parts::*, Context},
+ decision::make_decision,
+ model::reconstruct_global_model,
+ proof,
+ prop::{backtrack, enqueue_assignment, propagate, Conflict, Reason},
+ state::SatState,
+ unit_simplify::{prove_units, unit_simplify},
+};
+
+/// Find a conflict, learn a clause and backtrack.
+pub fn conflict_step<'a>(
+ mut ctx: partial!(
+ Context<'a>,
+ mut AnalyzeConflictP,
+ mut AssignmentP,
+ mut AssumptionsP,
+ mut BinaryClausesP,
+ mut ClauseActivityP,
+ mut ClauseAllocP,
+ mut ClauseDbP,
+ mut ImplGraphP,
+ mut ModelP,
+ mut ProofP<'a>,
+ mut SolverStateP,
+ mut TmpDataP,
+ mut TmpFlagsP,
+ mut TrailP,
+ mut VariablesP,
+ mut VsidsP,
+ mut WatchlistsP,
+ ),
+) {
+ let conflict = find_conflict(ctx.borrow());
+
+ let conflict = match conflict {
+ Ok(()) => {
+ reconstruct_global_model(ctx.borrow());
+ return;
+ }
+ Err(FoundConflict::Assumption) => {
+ ctx.part_mut(SolverStateP).sat_state = SatState::UnsatUnderAssumptions;
+ return;
+ }
+ Err(FoundConflict::Conflict(conflict)) => conflict,
+ };
+
+ let backtrack_to = analyze_conflict(ctx.borrow(), conflict);
+
+ let (analyze, mut ctx) = ctx.split_part(AnalyzeConflictP);
+
+ for &cref in analyze.involved() {
+ bump_clause(ctx.borrow(), cref);
+ }
+
+ decay_clause_activities(ctx.borrow());
+
+ backtrack(ctx.borrow(), backtrack_to);
+
+ let clause = analyze.clause();
+
+ proof::add_step(
+ ctx.borrow(),
+ true,
+ &ProofStep::AtClause {
+ redundant: clause.len() > 2,
+ clause,
+ propagation_hashes: analyze.clause_hashes(),
+ },
+ );
+
+ let reason = match clause.len() {
+ 0 => {
+ ctx.part_mut(SolverStateP).sat_state = SatState::Unsat;
+ return;
+ }
+ 1 => Reason::Unit,
+ 2 => {
+ ctx.part_mut(BinaryClausesP)
+ .add_binary_clause([clause[0], clause[1]]);
+ Reason::Binary([clause[1]])
+ }
+ _ => {
+ let header = assess_learned_clause(ctx.borrow(), clause);
+ let cref = db::add_clause(ctx.borrow(), header, clause);
+ Reason::Long(cref)
+ }
+ };
+
+ enqueue_assignment(ctx.borrow(), clause[0], reason);
+}
+
+/// Return type of [`find_conflict`].
+///
+/// Specifies whether a conflict was found during propagation or while enqueuing assumptions.
+enum FoundConflict {
+ Conflict(Conflict),
+ Assumption,
+}
+
+impl From<Conflict> for FoundConflict {
+ fn from(conflict: Conflict) -> FoundConflict {
+ FoundConflict::Conflict(conflict)
+ }
+}
+
+/// Find a conflict.
+///
+/// Returns `Err` if a conflict was found and `Ok` if a satisfying assignment was found instead.
+fn find_conflict<'a>(
+ mut ctx: partial!(
+ Context<'a>,
+ mut AssignmentP,
+ mut AssumptionsP,
+ mut BinaryClausesP,
+ mut ClauseAllocP,
+ mut ClauseDbP,
+ mut ImplGraphP,
+ mut ProofP<'a>,
+ mut SolverStateP,
+ mut TmpFlagsP,
+ mut TrailP,
+ mut VariablesP,
+ mut VsidsP,
+ mut WatchlistsP,
+ ),
+) -> Result<(), FoundConflict> {
+ loop {
+ let propagation_result = propagate(ctx.borrow());
+
+ let new_unit = prove_units(ctx.borrow());
+
+ propagation_result?;
+
+ if new_unit {
+ unit_simplify(ctx.borrow());
+ }
+
+ match enqueue_assumption(ctx.borrow()) {
+ EnqueueAssumption::Enqueued => continue,
+ EnqueueAssumption::Conflict => return Err(FoundConflict::Assumption),
+ EnqueueAssumption::Done => (),
+ }
+
+ if !make_decision(ctx.borrow()) {
+ return Ok(());
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use proptest::prelude::*;
+
+ use partial_ref::IntoPartialRefMut;
+
+ use varisat_formula::{
+ cnf_formula,
+ test::{sat_formula, sgen_unsat_formula},
+ };
+
+ use crate::{load::load_clause, state::SatState};
+
+ #[test]
+ fn level_0_unsat() {
+ let mut ctx = Context::default();
+ let mut ctx = ctx.into_partial_ref_mut();
+
+ let formula = cnf_formula![
+ 1, 2, 3;
+ -1;
+ 1, -2;
+ 2, -3;
+ ];
+
+ for clause in formula.iter() {
+ load_clause(ctx.borrow(), clause);
+ }
+
+ while ctx.part(SolverStateP).sat_state == SatState::Unknown {
+ conflict_step(ctx.borrow());
+ }
+
+ assert_eq!(ctx.part(SolverStateP).sat_state, SatState::Unsat);
+ }
+
+ proptest! {
+ #[test]
+ fn sgen_unsat(formula in sgen_unsat_formula(1..7usize)) {
+ let mut ctx = Context::default();
+ let mut ctx = ctx.into_partial_ref_mut();
+
+ for clause in formula.iter() {
+ load_clause(ctx.borrow(), clause);
+ }
+
+ while ctx.part(SolverStateP).sat_state == SatState::Unknown {
+ conflict_step(ctx.borrow());
+ }
+
+ prop_assert_eq!(ctx.part(SolverStateP).sat_state, SatState::Unsat);
+ }
+
+ #[test]
+ fn sat(formula in sat_formula(4..20usize, 10..100usize, 0.05..0.2, 0.9..1.0)) {
+ let mut ctx = Context::default();
+ let mut ctx = ctx.into_partial_ref_mut();
+
+ for clause in formula.iter() {
+ load_clause(ctx.borrow(), clause);
+ }
+
+ while ctx.part(SolverStateP).sat_state == SatState::Unknown {
+ conflict_step(ctx.borrow());
+ }
+
+ prop_assert_eq!(ctx.part(SolverStateP).sat_state, SatState::Sat);
+
+ for clause in formula.iter() {
+ prop_assert!(clause.iter().any(|&lit| ctx.part(ModelP).lit_is_true(
+ lit.map_var(|user_var| ctx
+ .part(VariablesP)
+ .global_from_user()
+ .get(user_var)
+ .expect("no existing global var for user var"))
+ )));
+ }
+ }
+
+ #[test]
+ fn sgen_unsat_incremetal_clauses(formula in sgen_unsat_formula(1..7usize)) {
+ let mut ctx = Context::default();
+ let mut ctx = ctx.into_partial_ref_mut();
+
+ let mut last_state = SatState::Sat;
+
+ for clause in formula.iter() {
+ load_clause(ctx.borrow(), clause);
+ while ctx.part(SolverStateP).sat_state == SatState::Unknown {
+ conflict_step(ctx.borrow());
+ }
+
+ if ctx.part(SolverStateP).sat_state != last_state {
+ prop_assert_eq!(ctx.part(SolverStateP).sat_state, SatState::Unsat);
+ prop_assert_eq!(last_state, SatState::Sat);
+ last_state = ctx.part(SolverStateP).sat_state;
+ }
+ }
+
+ prop_assert_eq!(last_state, SatState::Unsat);
+ }
+ }
+}