varisat_internal_proof/
binary_format.rs

1//! Binary format for varisat proofs.
2use std::io::{self, BufRead, Write};
3
4use anyhow::Error;
5
6use varisat_formula::{Lit, Var};
7
8use crate::vli_enc::{read_u64, write_u64};
9
10use super::{ClauseHash, DeleteClauseProof, ProofStep};
11
12macro_rules! step_codes {
13    ($counter:expr, $name:ident, ) => {
14        const $name: u64 = $counter;
15    };
16    ($counter:expr, $name:ident, $($names:ident),* ,) => {
17        const $name: u64 = $counter;
18        step_codes!($counter + 1, $($names),* ,);
19    };
20}
21
22step_codes!(
23    0,
24    CODE_SOLVER_VAR_NAME_UPDATE,
25    CODE_SOLVER_VAR_NAME_REMOVE,
26    CODE_USER_VAR_NAME_UPDATE,
27    CODE_USER_VAR_NAME_REMOVE,
28    CODE_DELETE_VAR,
29    CODE_CHANGE_SAMPLING_MODE_SAMPLE,
30    CODE_CHANGE_SAMPLING_MODE_WITNESS,
31    CODE_AT_CLAUSE_RED,
32    CODE_AT_CLAUSE_IRRED,
33    CODE_UNIT_CLAUSES,
34    CODE_DELETE_CLAUSE_REDUNDANT,
35    CODE_DELETE_CLAUSE_SIMPLIFIED,
36    CODE_DELETE_CLAUSE_SATISFIED,
37    CODE_CHANGE_HASH_BITS,
38    CODE_MODEL,
39    CODE_ADD_CLAUSE,
40    CODE_ASSUMPTIONS,
41    CODE_FAILED_ASSUMPTIONS,
42);
43
44// Using a random value here makes it unlikely that a corrupted proof will be silently truncated and
45// accepted
46const CODE_END: u64 = 0x9ac3391f4294c211;
47
48/// Writes a proof step in the varisat format
49pub fn write_step<'s>(target: &mut impl Write, step: &'s ProofStep<'s>) -> io::Result<()> {
50    match *step {
51        ProofStep::SolverVarName { global, solver } => {
52            if let Some(solver) = solver {
53                write_u64(&mut *target, CODE_SOLVER_VAR_NAME_UPDATE)?;
54                write_u64(&mut *target, global.index() as u64)?;
55                write_u64(&mut *target, solver.index() as u64)?;
56            } else {
57                write_u64(&mut *target, CODE_SOLVER_VAR_NAME_REMOVE)?;
58                write_u64(&mut *target, global.index() as u64)?;
59            }
60        }
61
62        ProofStep::UserVarName { global, user } => {
63            if let Some(user) = user {
64                write_u64(&mut *target, CODE_USER_VAR_NAME_UPDATE)?;
65                write_u64(&mut *target, global.index() as u64)?;
66                write_u64(&mut *target, user.index() as u64)?;
67            } else {
68                write_u64(&mut *target, CODE_USER_VAR_NAME_REMOVE)?;
69                write_u64(&mut *target, global.index() as u64)?;
70            }
71        }
72
73        ProofStep::DeleteVar { var } => {
74            write_u64(&mut *target, CODE_DELETE_VAR)?;
75            write_u64(&mut *target, var.index() as u64)?;
76        }
77
78        ProofStep::ChangeSamplingMode { var, sample } => {
79            if sample {
80                write_u64(&mut *target, CODE_CHANGE_SAMPLING_MODE_SAMPLE)?;
81            } else {
82                write_u64(&mut *target, CODE_CHANGE_SAMPLING_MODE_WITNESS)?;
83            }
84            write_u64(&mut *target, var.index() as u64)?;
85        }
86
87        ProofStep::AddClause { clause } => {
88            write_u64(&mut *target, CODE_ADD_CLAUSE)?;
89            write_literals(&mut *target, clause)?;
90        }
91
92        ProofStep::AtClause {
93            redundant,
94            clause,
95            propagation_hashes,
96        } => {
97            if redundant {
98                write_u64(&mut *target, CODE_AT_CLAUSE_RED)?;
99            } else {
100                write_u64(&mut *target, CODE_AT_CLAUSE_IRRED)?;
101            }
102            write_literals(&mut *target, clause)?;
103            write_hashes(&mut *target, propagation_hashes)?;
104        }
105
106        ProofStep::UnitClauses { units } => {
107            write_u64(&mut *target, CODE_UNIT_CLAUSES)?;
108            write_unit_clauses(&mut *target, units)?;
109        }
110
111        ProofStep::DeleteClause { clause, proof } => {
112            match proof {
113                DeleteClauseProof::Redundant => {
114                    write_u64(&mut *target, CODE_DELETE_CLAUSE_REDUNDANT)?;
115                }
116                DeleteClauseProof::Simplified => {
117                    write_u64(&mut *target, CODE_DELETE_CLAUSE_SIMPLIFIED)?;
118                }
119                DeleteClauseProof::Satisfied => {
120                    write_u64(&mut *target, CODE_DELETE_CLAUSE_SATISFIED)?;
121                }
122            }
123
124            write_literals(&mut *target, clause)?;
125        }
126
127        ProofStep::ChangeHashBits { bits } => {
128            write_u64(&mut *target, CODE_CHANGE_HASH_BITS)?;
129            write_u64(&mut *target, bits as u64)?;
130        }
131
132        ProofStep::Model { assignment } => {
133            write_u64(&mut *target, CODE_MODEL)?;
134            write_literals(&mut *target, assignment)?;
135        }
136
137        ProofStep::Assumptions { assumptions } => {
138            write_u64(&mut *target, CODE_ASSUMPTIONS)?;
139            write_literals(&mut *target, assumptions)?;
140        }
141
142        ProofStep::FailedAssumptions {
143            failed_core,
144            propagation_hashes,
145        } => {
146            write_u64(&mut *target, CODE_FAILED_ASSUMPTIONS)?;
147            write_literals(&mut *target, failed_core)?;
148            write_hashes(&mut *target, propagation_hashes)?;
149        }
150
151        ProofStep::End => {
152            write_u64(&mut *target, CODE_END)?;
153        }
154    }
155
156    Ok(())
157}
158
159#[derive(Default)]
160pub struct Parser {
161    lit_buf: Vec<Lit>,
162    hash_buf: Vec<ClauseHash>,
163    unit_buf: Vec<(Lit, ClauseHash)>,
164}
165
166impl Parser {
167    pub fn parse_step<'a>(&'a mut self, source: &mut impl BufRead) -> Result<ProofStep<'a>, Error> {
168        let code = read_u64(&mut *source)?;
169        match code {
170            CODE_SOLVER_VAR_NAME_UPDATE => {
171                let global = Var::from_index(read_u64(&mut *source)? as usize);
172                let solver = Some(Var::from_index(read_u64(&mut *source)? as usize));
173                Ok(ProofStep::SolverVarName { global, solver })
174            }
175            CODE_SOLVER_VAR_NAME_REMOVE => {
176                let global = Var::from_index(read_u64(&mut *source)? as usize);
177                Ok(ProofStep::SolverVarName {
178                    global,
179                    solver: None,
180                })
181            }
182            CODE_USER_VAR_NAME_UPDATE => {
183                let global = Var::from_index(read_u64(&mut *source)? as usize);
184                let user = Some(Var::from_index(read_u64(&mut *source)? as usize));
185                Ok(ProofStep::UserVarName { global, user })
186            }
187            CODE_USER_VAR_NAME_REMOVE => {
188                let global = Var::from_index(read_u64(&mut *source)? as usize);
189                Ok(ProofStep::UserVarName { global, user: None })
190            }
191            CODE_DELETE_VAR => {
192                let var = Var::from_index(read_u64(&mut *source)? as usize);
193                Ok(ProofStep::DeleteVar { var })
194            }
195            CODE_CHANGE_SAMPLING_MODE_SAMPLE | CODE_CHANGE_SAMPLING_MODE_WITNESS => {
196                let var = Var::from_index(read_u64(&mut *source)? as usize);
197                Ok(ProofStep::ChangeSamplingMode {
198                    var,
199                    sample: code == CODE_CHANGE_SAMPLING_MODE_SAMPLE,
200                })
201            }
202            CODE_ADD_CLAUSE => {
203                read_literals(&mut *source, &mut self.lit_buf)?;
204                Ok(ProofStep::AddClause {
205                    clause: &self.lit_buf,
206                })
207            }
208            CODE_AT_CLAUSE_IRRED | CODE_AT_CLAUSE_RED => {
209                read_literals(&mut *source, &mut self.lit_buf)?;
210                read_hashes(&mut *source, &mut self.hash_buf)?;
211                Ok(ProofStep::AtClause {
212                    redundant: code == CODE_AT_CLAUSE_RED,
213                    clause: &self.lit_buf,
214                    propagation_hashes: &self.hash_buf,
215                })
216            }
217            CODE_UNIT_CLAUSES => {
218                read_unit_clauses(&mut *source, &mut self.unit_buf)?;
219                Ok(ProofStep::UnitClauses {
220                    units: &self.unit_buf,
221                })
222            }
223            CODE_DELETE_CLAUSE_REDUNDANT
224            | CODE_DELETE_CLAUSE_SIMPLIFIED
225            | CODE_DELETE_CLAUSE_SATISFIED => {
226                let proof = match code {
227                    CODE_DELETE_CLAUSE_REDUNDANT => DeleteClauseProof::Redundant,
228                    CODE_DELETE_CLAUSE_SIMPLIFIED => DeleteClauseProof::Simplified,
229                    CODE_DELETE_CLAUSE_SATISFIED => DeleteClauseProof::Satisfied,
230                    _ => unreachable!(),
231                };
232                read_literals(&mut *source, &mut self.lit_buf)?;
233                Ok(ProofStep::DeleteClause {
234                    clause: &self.lit_buf,
235                    proof,
236                })
237            }
238            CODE_CHANGE_HASH_BITS => {
239                let bits = read_u64(&mut *source)? as u32;
240                Ok(ProofStep::ChangeHashBits { bits })
241            }
242            CODE_MODEL => {
243                read_literals(&mut *source, &mut self.lit_buf)?;
244                Ok(ProofStep::Model {
245                    assignment: &self.lit_buf,
246                })
247            }
248            CODE_ASSUMPTIONS => {
249                read_literals(&mut *source, &mut self.lit_buf)?;
250                Ok(ProofStep::Assumptions {
251                    assumptions: &self.lit_buf,
252                })
253            }
254            CODE_FAILED_ASSUMPTIONS => {
255                read_literals(&mut *source, &mut self.lit_buf)?;
256                read_hashes(&mut *source, &mut self.hash_buf)?;
257                Ok(ProofStep::FailedAssumptions {
258                    failed_core: &self.lit_buf,
259                    propagation_hashes: &self.hash_buf,
260                })
261            }
262            CODE_END => Ok(ProofStep::End),
263            _ => anyhow::bail!("parse error"),
264        }
265    }
266}
267
268/// Writes a slice of literals for a varisat proof
269fn write_literals(target: &mut impl Write, literals: &[Lit]) -> io::Result<()> {
270    write_u64(&mut *target, literals.len() as u64)?;
271    for &lit in literals {
272        write_u64(&mut *target, lit.code() as u64)?;
273    }
274    Ok(())
275}
276
277/// Read a slice of literals from a varisat proof
278fn read_literals(source: &mut impl BufRead, literals: &mut Vec<Lit>) -> Result<(), io::Error> {
279    literals.clear();
280    let len = read_u64(&mut *source)? as usize;
281    literals.reserve(len);
282    for _ in 0..len {
283        literals.push(Lit::from_code(read_u64(&mut *source)? as usize));
284    }
285    Ok(())
286}
287
288/// Writes a slice of clause hashes for a varisat proof
289fn write_hashes(target: &mut impl Write, hashes: &[ClauseHash]) -> io::Result<()> {
290    write_u64(&mut *target, hashes.len() as u64)?;
291    for &hash in hashes {
292        write_u64(&mut *target, hash as u64)?;
293    }
294    Ok(())
295}
296
297/// Read a slice of clause hashes from a varisat proof
298fn read_hashes(source: &mut impl BufRead, hashes: &mut Vec<ClauseHash>) -> Result<(), io::Error> {
299    hashes.clear();
300    let len = read_u64(&mut *source)? as usize;
301    hashes.reserve(len);
302    for _ in 0..len {
303        hashes.push(read_u64(&mut *source)? as ClauseHash);
304    }
305    Ok(())
306}
307
308/// Writes a slice of unit clauses for a varisat proof
309fn write_unit_clauses(target: &mut impl Write, units: &[(Lit, ClauseHash)]) -> io::Result<()> {
310    write_u64(&mut *target, units.len() as u64)?;
311    for &(lit, hash) in units {
312        write_u64(&mut *target, lit.code() as u64)?;
313        write_u64(&mut *target, hash as u64)?;
314    }
315    Ok(())
316}
317
318/// Read a slice of unit clauses from a varisat proof
319fn read_unit_clauses(
320    source: &mut impl BufRead,
321    units: &mut Vec<(Lit, ClauseHash)>,
322) -> Result<(), io::Error> {
323    units.clear();
324    let len = read_u64(&mut *source)? as usize;
325    units.reserve(len);
326    for _ in 0..len {
327        let lit = Lit::from_code(read_u64(&mut *source)? as usize);
328        let hash = read_u64(&mut *source)? as ClauseHash;
329        units.push((lit, hash));
330    }
331    Ok(())
332}