1use crate::{
4 as_error,
5 assignment::{stable_under_unit_propagation, Assignment},
6 clause::{Reason, RedundancyProperty},
7 clausedatabase::ClauseStorage,
8 literal::Literal,
9 memory::{Array, HeapSpace, Vector},
10 parser::{run_parser, HashTable, Parser},
11 puts, requires,
12};
13use serde_derive::{Deserialize, Serialize};
14
15#[derive(Serialize, Deserialize, Debug, Default)]
17pub struct Sick {
18 pub proof_format: String,
20 pub proof_step: Option<usize>,
22 pub natural_model: Vector<Literal>,
24 pub witness: Option<Vector<Witness>>,
26}
27
28#[derive(Clone, Serialize, Deserialize, Debug, Default)]
30pub struct Witness {
31 pub failing_clause: Vector<Literal>,
33 pub failing_model: Vector<Literal>,
35 pub pivot: Option<Literal>,
37}
38
39impl HeapSpace for Sick {
41 fn heap_space(&self) -> usize {
42 self.natural_model.heap_space() + self.witness.as_ref().map_or(0, HeapSpace::heap_space)
43 }
44}
45
46impl HeapSpace for Witness {
47 fn heap_space(&self) -> usize {
48 self.failing_clause.heap_space() + self.failing_model.heap_space()
49 }
50}
51
52pub fn check_incorrectness_certificate(
55 formula_filename: &str,
56 proof_filename: &str,
57 sick: Sick,
58 verbose: bool,
59) -> Result<(), ()> {
60 match check_incorrectness_certificate_aux(formula_filename, proof_filename, sick, verbose) {
61 Ok(()) => Ok(()),
62 Err(string) => {
63 as_error!({
64 puts!("{}\n", &string);
65 puts!("Proof claimed incorrect but validation failed, please report a bug!\n");
66 });
67 Err(())
68 }
69 }
70}
71
72fn check_incorrectness_certificate_aux(
75 formula_filename: &str,
76 proof_filename: &str,
77 sick: Sick,
78 verbose: bool,
79) -> Result<(), String> {
80 let redundancy_property = match sick.proof_format.as_ref() {
81 "DRAT-arbitrary-pivot" => RedundancyProperty::RAT,
82 "DRAT-pivot-is-first-literal" => RedundancyProperty::RAT,
83 "PR" => RedundancyProperty::PR,
84 format => return Err(format!("Unsupported proof format: {}", format)),
85 };
86 let mut clause_ids = HashTable::new();
87 let mut parser = Parser::default();
88 parser.max_proof_steps = sick.proof_step;
89 parser.verbose = verbose;
90 run_parser(
91 &mut parser,
92 formula_filename,
93 proof_filename,
94 &mut clause_ids,
95 );
96
97 let mut assignment = Assignment::new(parser.maxvar);
98 for &literal in &sick.natural_model {
99 if assignment[-literal] {
100 return Err(format!(
101 "Natural model is inconsistent in variable {}",
102 literal.variable()
103 ));
104 }
105 assignment.push(literal, Reason::assumed());
106 }
107 let proof_step = match sick.proof_step {
108 Some(proof_step) => proof_step,
109 None => {
110 return Ok(());
111 }
112 };
113 if proof_step > parser.proof.len() {
114 return Err(format!(
115 "Specified proof step exceeds proof size: {}",
116 proof_step
117 ));
118 }
119 let lemma = parser.proof[proof_step - 1].clause();
120 requires!(lemma.index < parser.clause_db.number_of_clauses());
121 if parser.clause_db.clause(lemma).is_empty() {
122 return Ok(());
123 }
124 let natural_model_length = assignment.len();
125
126 for &literal in parser.clause_db.clause(lemma) {
127 if !assignment[-literal] {
128 return Err(format!(
129 "Natural model does not falsify lemma literal {}",
130 literal
131 ));
132 }
133 }
134 clause_ids.delete_clause(&parser.clause_db, lemma);
136 for &clause in &clause_ids {
137 if clause < lemma
138 && !stable_under_unit_propagation(&assignment, parser.clause_db.clause(clause))
139 {
140 return Err(format!(
141 "Natural model is not the shared UP-model for clause {}",
142 parser.clause_db.clause_to_string(clause)
143 ));
144 }
145 }
146 let witnesses = sick.witness.unwrap_or_else(Vector::new);
147 const PIVOT: &str = "RAT requires to specify a pivot for each witness";
148 if redundancy_property == RedundancyProperty::RAT {
149 let mut specified_pivots: Vec<Literal> = witnesses
150 .iter()
151 .map(|witness| witness.pivot.expect(PIVOT))
152 .collect();
153 let mut expected_pivots: Vec<Literal> = parser
154 .clause_db
155 .clause(lemma)
156 .iter()
157 .filter(|&&l| l != Literal::BOTTOM)
158 .cloned()
159 .collect();
160 if !specified_pivots.is_empty() {
161 match sick.proof_format.as_ref() {
162 "DRAT-arbitrary-pivot" => {
163 specified_pivots.sort();
164 expected_pivots.sort();
165 if specified_pivots != expected_pivots {
166 return Err(format!("Using proof_format = \"{}\": you need exactly one counterexample for each pivot in the lemma\nlemma: {}\npivots: {}",
167 sick.proof_format,
168 expected_pivots.iter().map(|l| format!("{}", l)).collect::<Vec<_>>().join(" "),
169 specified_pivots.iter().map(|l| format!("{}", l)).collect::<Vec<_>>().join(" "),
170 ));
171 }
172 }
173 "DRAT-pivot-is-first-literal" => {
174 if specified_pivots.len() > 1 {
175 return Err(format!("Using proof_format = \"{}\", the first literal must be specified as pivot and nothing else",
176 sick.proof_format));
177 }
178 }
179 _ => unreachable!(),
180 };
181 }
182 }
183 for witness in witnesses {
184 parser.clause_db.open_clause();
185 for &literal in &witness.failing_clause {
186 parser.clause_db.push_literal(literal, verbose);
187 }
188 parser.clause_db.push_literal(Literal::new(0), verbose);
189 let failing_clause_tmp = parser.clause_db.last_clause();
190 let failing_clause = match clause_ids.find_equal_clause(
191 &parser.clause_db,
192 failing_clause_tmp,
193 false,
194 ) {
195 Some(clause) => clause,
196 None => {
197 return Err(format!(
198 "Failing clause is not present in the formula: {}",
199 parser.clause_db.clause_to_string(failing_clause_tmp)
200 ))
201 }
202 };
203 parser.clause_db.pop_clause();
204 let lemma_slice = parser.clause_db.clause(lemma);
205 for &literal in &witness.failing_model {
206 if assignment[-literal] {
207 return Err(format!(
208 "Failing model is inconsistent in variable {}",
209 literal.variable()
210 ));
211 }
212 assignment.push(literal, Reason::assumed());
213 }
214 let resolvent: Vec<Literal> = match redundancy_property {
215 RedundancyProperty::RAT => {
216 let pivot = witness.pivot.expect(PIVOT);
217 lemma_slice
218 .iter()
219 .chain(witness.failing_clause.iter())
220 .filter(|&&lit| lit.variable() != pivot.variable())
221 .cloned()
222 .collect()
223 }
224 RedundancyProperty::PR => {
225 let mut literal_is_in_witness =
226 Array::new(false, parser.maxvar.array_size_for_literals());
227 for &literal in parser.witness_db.witness(lemma) {
228 literal_is_in_witness[literal] = true;
229 }
230 if witness
231 .failing_clause
232 .iter()
233 .any(|&lit| literal_is_in_witness[lit])
234 {
235 return Err(format!(
236 "Reduct of failing clause {} is satisified under witness {}",
237 parser.clause_db.clause_to_string(failing_clause),
238 parser.witness_db.witness_to_string(failing_clause)
239 ));
240 }
241 lemma_slice
242 .iter()
243 .chain(
244 witness
245 .failing_clause
246 .iter()
247 .filter(|&&lit| !literal_is_in_witness[-lit]),
248 )
249 .cloned()
250 .collect()
251 }
252 };
253 if redundancy_property == RedundancyProperty::RAT {
255 for &literal in &resolvent {
256 if !assignment[-literal] {
257 return Err(format!(
258 "Failing model does not falsify resolvent literal {}",
259 literal
260 ));
261 }
262 }
263 }
264 for &clause in &clause_ids {
265 if clause < lemma
266 && !stable_under_unit_propagation(&assignment, parser.clause_db.clause(clause))
267 {
268 return Err(format!(
269 "Failing model is not the shared UP-model for clause {}",
270 parser.clause_db.clause_to_string(clause)
271 ));
272 }
273 }
274 while assignment.len() > natural_model_length {
275 assignment.pop();
276 }
277 }
278 Ok(())
279}