| //! Loading a formula into the solver. |
| use vec_mut_scan::VecMutScan; |
| |
| use partial_ref::{partial, PartialRef}; |
| |
| use varisat_formula::Lit; |
| use varisat_internal_proof::{DeleteClauseProof, ProofStep}; |
| |
| use crate::{ |
| clause::{db, ClauseHeader, Tier}, |
| context::{parts::*, Context}, |
| proof, |
| prop::{assignment, full_restart, Reason}, |
| state::SatState, |
| unit_simplify::resurrect_unit, |
| variables, |
| }; |
| |
| /// Adds a clause to the current formula. |
| /// |
| /// The input uses user variable names. |
| /// |
| /// Removes duplicated literals, ignores tautological clauses (eg. x v -x v y), handles empty |
| /// clauses and dispatches among unit, binary and long clauses. |
| pub fn load_clause<'a>( |
| mut ctx: partial!( |
| Context<'a>, |
| mut AnalyzeConflictP, |
| mut AssignmentP, |
| mut AssumptionsP, |
| mut BinaryClausesP, |
| mut ClauseAllocP, |
| mut ClauseDbP, |
| mut ImplGraphP, |
| mut ProofP<'a>, |
| mut SolverStateP, |
| mut TmpDataP, |
| mut TmpFlagsP, |
| mut TrailP, |
| mut VariablesP, |
| mut VsidsP, |
| mut WatchlistsP, |
| ), |
| user_lits: &[Lit], |
| ) { |
| match ctx.part(SolverStateP).sat_state { |
| SatState::Unsat => return, |
| SatState::Sat => { |
| ctx.part_mut(SolverStateP).sat_state = SatState::Unknown; |
| } |
| _ => {} |
| } |
| |
| ctx.part_mut(SolverStateP).formula_is_empty = false; |
| |
| // Restart the search when the user adds new clauses. |
| full_restart(ctx.borrow()); |
| |
| // Convert the clause from user to solver literals. |
| let (tmp_data, mut ctx_variables) = ctx.split_part_mut(TmpDataP); |
| variables::solver_from_user_lits(ctx_variables.borrow(), &mut tmp_data.lits, user_lits, true); |
| |
| let (tmp_data, mut ctx) = ctx.split_part_mut(TmpDataP); |
| |
| let lits = &mut tmp_data.lits; |
| let false_lits = &mut tmp_data.lits_2; |
| |
| lits.sort_unstable(); |
| lits.dedup(); |
| |
| proof::add_clause(ctx.borrow(), &lits); |
| |
| // Detect tautological clauses |
| let mut last = None; |
| |
| for &lit in lits.iter() { |
| if last == Some(!lit) { |
| return; |
| } |
| last = Some(lit); |
| } |
| |
| // If we're not a unit clause the contained variables are not isolated anymore. |
| if lits.len() > 1 { |
| for &lit in lits.iter() { |
| ctx.part_mut(VariablesP) |
| .var_data_solver_mut(lit.var()) |
| .isolated = false; |
| } |
| } |
| |
| // Remove satisfied clauses and handle false literals. |
| // |
| // Proof generation expects us to start with the actual input clauses. If we would remove false |
| // literals we would have to generate proof steps for that. This would result in derived clauses |
| // being added during loading. If we're running proof processors on the fly, they'd see those |
| // derived clauses interspersed with the input clauses. |
| // |
| // We don't want to require each proof processor to handle dervied clause additions during |
| // loading of the initial formula. Thus we need to handle clauses with false literals here. |
| false_lits.clear(); |
| |
| let mut lits_scan = VecMutScan::new(lits); |
| |
| let mut clause_is_true = false; |
| |
| // We move unassigned literals to the beginning to make sure we're going to watch unassigned |
| // literals. |
| while let Some(lit) = lits_scan.next() { |
| match ctx.part(AssignmentP).lit_value(*lit) { |
| Some(true) => { |
| clause_is_true = true; |
| break; |
| } |
| Some(false) => { |
| false_lits.push(lit.remove()); |
| } |
| None => (), |
| } |
| } |
| |
| drop(lits_scan); |
| |
| let will_conflict = lits.is_empty(); |
| |
| // We resurrect any removed false literals to ensure propagation by this new clause. This is |
| // also required to eventually simplify this clause. |
| for &lit in false_lits.iter() { |
| resurrect_unit(ctx.borrow(), !lit); |
| } |
| |
| lits.extend_from_slice(&false_lits); |
| |
| if clause_is_true { |
| if lits.len() > 1 { |
| proof::add_step( |
| ctx.borrow(), |
| true, |
| &ProofStep::DeleteClause { |
| clause: lits, |
| proof: DeleteClauseProof::Satisfied, |
| }, |
| ); |
| } |
| return; |
| } |
| |
| match lits[..] { |
| [] => ctx.part_mut(SolverStateP).sat_state = SatState::Unsat, |
| [lit] => { |
| if will_conflict { |
| ctx.part_mut(SolverStateP).sat_state = SatState::Unsat |
| } else { |
| assignment::enqueue_assignment(ctx.borrow(), lit, Reason::Unit) |
| } |
| } |
| [lit_0, lit_1] => { |
| ctx.part_mut(BinaryClausesP) |
| .add_binary_clause([lit_0, lit_1]); |
| } |
| _ => { |
| let mut header = ClauseHeader::new(); |
| header.set_tier(Tier::Irred); |
| |
| db::add_clause(ctx.borrow(), header, lits); |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| use partial_ref::IntoPartialRefMut; |
| |
| use varisat_formula::lits; |
| |
| use crate::clause::Tier; |
| |
| #[test] |
| fn unsat_on_empty_clause() { |
| let mut ctx = Context::default(); |
| let mut ctx = ctx.into_partial_ref_mut(); |
| |
| load_clause(ctx.borrow(), &[]); |
| |
| assert_eq!(ctx.part(SolverStateP).sat_state, SatState::Unsat); |
| } |
| |
| #[test] |
| fn unit_clauses() { |
| let mut ctx = Context::default(); |
| let mut ctx = ctx.into_partial_ref_mut(); |
| |
| load_clause(ctx.borrow(), &lits![1]); |
| |
| assert_eq!(ctx.part(TrailP).trail().len(), 1); |
| |
| load_clause(ctx.borrow(), &lits![3, -3]); |
| |
| assert_eq!(ctx.part(TrailP).trail().len(), 1); |
| |
| load_clause(ctx.borrow(), &lits![-2]); |
| |
| assert_eq!(ctx.part(TrailP).trail().len(), 2); |
| |
| load_clause(ctx.borrow(), &lits![1, 1]); |
| |
| assert_eq!(ctx.part(TrailP).trail().len(), 2); |
| |
| assert_eq!(ctx.part(SolverStateP).sat_state, SatState::Unknown); |
| |
| load_clause(ctx.borrow(), &lits![2]); |
| |
| assert_eq!(ctx.part(TrailP).trail().len(), 2); |
| |
| assert_eq!(ctx.part(SolverStateP).sat_state, SatState::Unsat); |
| } |
| |
| #[test] |
| fn binary_clauses() { |
| let mut ctx = Context::default(); |
| let mut ctx = ctx.into_partial_ref_mut(); |
| |
| load_clause(ctx.borrow(), &lits![1, 2]); |
| |
| assert_eq!(ctx.part(BinaryClausesP).count(), 1); |
| |
| load_clause(ctx.borrow(), &lits![-1, 3, 3]); |
| |
| assert_eq!(ctx.part(BinaryClausesP).count(), 2); |
| |
| load_clause(ctx.borrow(), &lits![4, -4]); |
| |
| assert_eq!(ctx.part(BinaryClausesP).count(), 2); |
| |
| assert_eq!(ctx.part(SolverStateP).sat_state, SatState::Unknown); |
| } |
| |
| #[test] |
| fn long_clauses() { |
| let mut ctx = Context::default(); |
| let mut ctx = ctx.into_partial_ref_mut(); |
| |
| load_clause(ctx.borrow(), &lits![1, 2, 3]); |
| |
| assert_eq!(ctx.part(ClauseDbP).count_by_tier(Tier::Irred), 1); |
| |
| load_clause(ctx.borrow(), &lits![-2, 3, 3, 4]); |
| |
| assert_eq!(ctx.part(ClauseDbP).count_by_tier(Tier::Irred), 2); |
| |
| load_clause(ctx.borrow(), &lits![4, -5, 5, 2]); |
| |
| assert_eq!(ctx.part(ClauseDbP).count_by_tier(Tier::Irred), 2); |
| |
| assert_eq!(ctx.part(SolverStateP).sat_state, SatState::Unknown); |
| } |
| } |