//! Checker tests, that require a Solver instance, so they cannot be unit tests of the //! varisat-checker crate. use anyhow::Error; use proptest::prelude::*; use varisat::{ checker::{Checker, ProofTranscriptProcessor, ProofTranscriptStep}, dimacs::write_dimacs, CnfFormula, ExtendFormula, Lit, ProofFormat, Solver, Var, }; use varisat_formula::test::{conditional_pigeon_hole, sgen_unsat_formula}; proptest! { #[test] fn checked_unsat_via_dimacs(formula in sgen_unsat_formula(1..7usize)) { let mut dimacs = vec![]; let mut proof = vec![]; let mut solver = Solver::new(); write_dimacs(&mut dimacs, &formula).unwrap(); solver.write_proof(&mut proof, ProofFormat::Varisat); solver.add_dimacs_cnf(&mut &dimacs[..]).unwrap(); prop_assert_eq!(solver.solve().ok(), Some(false)); solver.close_proof().map_err(|e| TestCaseError::fail(e.to_string()))?; drop(solver); let mut checker = Checker::new(); checker.add_dimacs_cnf(&mut &dimacs[..]).unwrap(); checker.check_proof(&mut &proof[..]).unwrap(); } #[test] fn sgen_checked_unsat_incremental_clauses(formula in sgen_unsat_formula(1..7usize)) { let mut proof = vec![]; let mut solver = Solver::new(); solver.write_proof(&mut proof, ProofFormat::Varisat); let mut expected_models = 0; // Add all clauses incrementally so they are recorded in the proof solver.solve().unwrap(); expected_models += 1; let mut last_state = Some(true); for clause in formula.iter() { solver.add_clause(clause); let state = solver.solve().ok(); if state != last_state { prop_assert_eq!(state, Some(false)); prop_assert_eq!(last_state, Some(true)); last_state = state; } if state == Some(true) { expected_models += 1; } } prop_assert_eq!(last_state, Some(false)); drop(solver); #[derive(Default)] struct FoundModels { counter: usize, unsat: bool, } impl ProofTranscriptProcessor for FoundModels { fn process_step( &mut self, step: &ProofTranscriptStep, ) -> Result<(), Error> { if let ProofTranscriptStep::Model { .. } = step { self.counter += 1; } else if let ProofTranscriptStep::Unsat = step { self.unsat = true; } Ok(()) } } let mut found_models = FoundModels::default(); let mut checker = Checker::new(); checker.add_transcript(&mut found_models); checker.check_proof(&mut &proof[..]).unwrap(); prop_assert_eq!(found_models.counter, expected_models); prop_assert!(found_models.unsat); } #[test] fn pigeon_hole_checked_unsat_assumption_core( (enable_row, columns, formula) in conditional_pigeon_hole(1..5usize, 1..5usize), ) { let mut proof = vec![]; let mut solver = Solver::new(); solver.write_proof(&mut proof, ProofFormat::Varisat); let mut expected_sat = 0; let mut expected_unsat = 0; solver.solve().unwrap(); expected_sat += 1; solver.add_formula(&formula); prop_assert_eq!(solver.solve().ok(), Some(true)); expected_sat += 1; let mut assumptions = enable_row; assumptions.push(Lit::positive(Var::from_index(formula.var_count() + 10))); solver.assume(&assumptions); prop_assert_eq!(solver.solve().ok(), Some(false)); expected_unsat += 1; let mut candidates = solver.failed_core().unwrap().to_owned(); let mut core: Vec = vec![]; while !candidates.is_empty() { solver.assume(&candidates[0..candidates.len() - 1]); match solver.solve() { Err(_) => unreachable!(), Ok(true) => { expected_sat += 1; let skipped = *candidates.last().unwrap(); core.push(skipped); let single_clause = CnfFormula::from(Some([skipped])); solver.add_formula(&single_clause); }, Ok(false) => { expected_unsat += 1; candidates = solver.failed_core().unwrap().to_owned(); } } } prop_assert_eq!(core.len(), columns + 1); drop(solver); #[derive(Default)] struct CountResults { sat: usize, unsat: usize, } impl ProofTranscriptProcessor for CountResults { fn process_step( &mut self, step: &ProofTranscriptStep, ) -> Result<(), Error> { match step { ProofTranscriptStep::Model { .. } => { self.sat += 1; } ProofTranscriptStep::Unsat | ProofTranscriptStep::FailedAssumptions { .. } => { self.unsat += 1; } _ => (), } Ok(()) } } let mut count_results = CountResults::default(); let mut checker = Checker::new(); checker.add_transcript(&mut count_results); checker.check_proof(&mut &proof[..]).unwrap(); prop_assert_eq!(count_results.sat, expected_sat); prop_assert_eq!(count_results.unsat, expected_unsat); } }