//! Internal proof format for the Varisat SAT solver. use varisat_formula::{Lit, Var}; pub mod binary_format; mod vli_enc; // Integer type used to store a hash of a clause. pub type ClauseHash = u64; /// Hash a single literal. /// /// Multiple literals can be combined with xor, as done in [`clause_hash`]. pub fn lit_hash(lit: Lit) -> ClauseHash { lit_code_hash(lit.code()) } /// Hash a single literal from a code. /// /// This doesn't require the code to correspond a valid literal. pub fn lit_code_hash(lit_code: usize) -> ClauseHash { // Constant based on the golden ratio provides good mixing for the resulting upper bits (!(lit_code as u64)).wrapping_mul(0x61c8864680b583ebu64) } /// A fast hash function for clauses (or other *sets* of literals). /// /// This hash function interprets the given slice as a set and will not change when the input is /// permuted. It does not handle duplicated items. pub fn clause_hash(lits: &[Lit]) -> ClauseHash { let mut hash = 0; for &lit in lits { hash ^= lit_hash(lit); } hash } /// Justifications for a simple clause deletion. #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum DeleteClauseProof { /// The clause is known to be redundant. Redundant, /// The clause is irred and subsumed by the clause added in the previous step. Simplified, /// The clause contains a true literal. /// /// Also used to justify deletion of tautological clauses. Satisfied, } /// A single proof step. /// /// Represents a mutation of the current formula and a justification for the mutation's validity. #[derive(Copy, Clone, Debug)] pub enum ProofStep<'a> { /// Update the global to solver var mapping. /// /// For proof checking, the solver variable names are only used for hash computations. SolverVarName { global: Var, solver: Option }, /// Update the global to user var mapping. /// /// A variable without user mapping is considered hidden by the checker. When a variable without /// user mapping gets a user mapping, the sampling mode is initialized to witness. /// /// It's not allowed to change a variable from one user name to another when the variable is in /// use. /// /// Clause additions and assumptions are only allowed to use variables with user mappings (and a /// non-witness sampling mode). UserVarName { global: Var, user: Option }, /// Delete a variable. /// /// This is only allowed for variables that are isolated and hidden. DeleteVar { var: Var }, /// Changes the sampling mode of a variable. /// /// This is only used to change between Sample and Witness. Hidden is managed by adding or /// removing a user var name. ChangeSamplingMode { var: Var, sample: bool }, /// Add a new input clause. /// /// This is only emitted for clauses added incrementally after an initial solve call. AddClause { clause: &'a [Lit] }, /// Add a clause that is an asymmetric tautoligy (AT). /// /// Assuming the negation of the clause's literals leads to a unit propagation conflict. /// /// The second slice contains the hashes of all clauses involved in the resulting conflict. The /// order of hashes is the order in which the clauses propagate when all literals of the clause /// are set false. /// /// When generating DRAT proofs the second slice is ignored and may be empty. AtClause { redundant: bool, clause: &'a [Lit], propagation_hashes: &'a [ClauseHash], }, /// Unit clauses found by top-level unit-propagation. /// /// Pairs of unit clauses and the original clause that became unit. Clauses are in chronological /// order. This is equivalent to multiple `AtClause` steps where the clause is unit and the /// propagation_hashes field contains just one hash, with the difference that this is not output /// for DRAT proofs. /// /// Ignored when generating DRAT proofs. UnitClauses { units: &'a [(Lit, ClauseHash)] }, /// Delete a clause consisting of the given literals. DeleteClause { clause: &'a [Lit], proof: DeleteClauseProof, }, /// Change the number of clause hash bits used ChangeHashBits { bits: u32 }, /// A (partial) assignment that satisfies all clauses and assumptions. Model { assignment: &'a [Lit] }, /// Change the active set of assumptions. /// /// This is checked against future model or failed assumptions steps. Assumptions { assumptions: &'a [Lit] }, /// A subset of the assumptions that make the formula unsat. FailedAssumptions { failed_core: &'a [Lit], propagation_hashes: &'a [ClauseHash], }, /// Signals the end of a proof. /// /// A varisat proof must end with this command or else the checker will complain about an /// incomplete proof. End, } impl<'a> ProofStep<'a> { /// Does this proof step use clause hashes? pub fn contains_hashes(&self) -> bool { match self { ProofStep::AtClause { .. } | ProofStep::UnitClauses { .. } | ProofStep::FailedAssumptions { .. } => true, ProofStep::SolverVarName { .. } | ProofStep::UserVarName { .. } | ProofStep::DeleteVar { .. } | ProofStep::ChangeSamplingMode { .. } | ProofStep::AddClause { .. } | ProofStep::DeleteClause { .. } | ProofStep::ChangeHashBits { .. } | ProofStep::Model { .. } | ProofStep::Assumptions { .. } | ProofStep::End => false, } } }