1use std::io;
4
5use pigeons::{AbsConstraintId, Conclusion, ConstraintId, OutputGuarantee, Proof};
6use rustsat::{
7 encodings::{cert::CollectClauses as CollectCertClauses, CollectClauses},
8 solvers::SolverResult,
9 types::{Clause, Lit, RsHashSet, Var},
10};
11use rustsat_cadical::{CaDiCaL, CaDiCaLClause, ClauseId, ProofTracerHandle, TraceProof};
12
13#[derive(Debug)]
14pub struct CadicalTracer<ProofW: io::Write> {
15 proof: Option<Proof<ProofW>>,
17 cmap: ConstraintMapper,
19 core_id: Option<AbsConstraintId>,
21 weakened_clauses: RsHashSet<ClauseId>,
23 assumptions: Vec<Lit>,
24}
25
26impl<ProofW: io::Write> CadicalTracer<ProofW> {
27 pub fn new(proof: Proof<ProofW>) -> Self {
28 CadicalTracer {
29 proof: Some(proof),
30 cmap: ConstraintMapper::default(),
31 core_id: None,
32 weakened_clauses: RsHashSet::default(),
33 assumptions: vec![],
34 }
35 }
36
37 pub fn proof_mut(&mut self) -> &mut Proof<ProofW> {
38 self.proof.as_mut().expect("expected proof")
39 }
40
41 pub fn take_proof(&mut self) -> Proof<ProofW> {
42 let Some(proof) = self.proof.take() else {
43 panic!("expected proof")
44 };
45 proof
46 }
47
48 pub fn replace_proof(&mut self, proof: Proof<ProofW>) -> Option<Proof<ProofW>> {
49 self.proof.replace(proof)
50 }
51
52 pub fn core_id(&self) -> Option<AbsConstraintId> {
53 self.core_id
54 }
55}
56
57impl<ProofW: io::Write> CadicalTracer<ProofW>
58where
59 ProofW: io::Write,
60{
61 fn add_derived(
62 &mut self,
63 id: ClauseId,
64 redundant: bool,
65 clause: &CaDiCaLClause,
66 antecedents: &[ClauseId],
67 ) -> AbsConstraintId {
68 debug_assert!(!antecedents.is_empty());
69 let proof = self.proof.as_mut().expect("expected proof");
70 let veripb_id = write_derived(
71 proof,
72 clause,
73 antecedents.iter().map(|id| self.cmap.map(*id)),
74 );
75 self.cmap.add_clause_checked(veripb_id, id);
76 #[cfg(feature = "verbose")]
77 proof
78 .equals(clause, Some(pigeons::ConstraintId::last(1)))
79 .expect("failed to write proof");
80 if !redundant {
81 proof
82 .move_ids_to_core([pigeons::ConstraintId::from(veripb_id)])
83 .expect("failed to write proof");
84 }
85 veripb_id
86 }
87}
88
89impl<ProofW: io::Write> TraceProof for CadicalTracer<ProofW>
90where
91 ProofW: io::Write,
92{
93 fn add_original_clause(
94 &mut self,
95 id: ClauseId,
96 _redundant: bool,
97 _clause: &CaDiCaLClause,
98 restored: bool,
99 ) {
100 if restored {
101 return;
102 }
103 debug_assert_eq!(usize::try_from(id.0).unwrap(), self.cmap.map.len());
104 let proof = self.proof.as_mut().expect("expected proof");
105 #[cfg(feature = "verbose")]
106 {
107 proof
108 .equals(_clause, Some(self.cmap.map(id).into()))
109 .expect("failed to write proof");
110 }
111 let veripb_id = self.cmap.map(id);
112 if veripb_id >= proof.first_proof_id() {
113 proof
114 .move_ids_to_core([veripb_id.into()])
115 .expect("failed to write proof");
116 }
117 }
118
119 fn add_derived_clause(
120 &mut self,
121 id: ClauseId,
122 redundant: bool,
123 clause: &CaDiCaLClause,
124 antecedents: &[ClauseId],
125 ) {
126 self.add_derived(id, redundant, clause, antecedents);
127 }
128
129 fn delete_clause(&mut self, id: ClauseId, redundant: bool, _clause: &CaDiCaLClause) {
130 if !redundant {
131 return;
136 }
137 let id = self.cmap.map(id);
138 let proof = self.proof.as_mut().expect("expected proof");
139 proof
140 .delete_ids::<Var, CaDiCaLClause, _, _>([id.into()], None)
141 .expect("failed to write proof")
142 }
143
144 fn strengthen(&mut self, id: ClauseId) {
145 self.proof
146 .as_mut()
147 .expect("expected proof")
148 .move_ids_to_core([self.cmap.map(id).into()])
149 .expect("failed to write proof");
150 }
151
152 fn weaken_minus(&mut self, id: ClauseId, _clause: &CaDiCaLClause) {
153 self.weakened_clauses.insert(id);
154 }
155
156 fn report_status(&mut self, status: SolverResult, id: ClauseId) {
157 let id = if id.0 > 0 {
158 Some(self.cmap.map(id))
159 } else {
160 None
161 };
162 #[cfg(feature = "verbose")]
163 self.proof
164 .as_mut()
165 .expect("expected proof")
166 .comment(&format_args!(
167 "CaDiCaL: finished solving: {status}, id: {id:?}",
168 ))
169 .expect("failed to write proof");
170 if let Some(id) = id {
171 debug_assert_eq!(status, SolverResult::Unsat);
172 self.proof
173 .as_mut()
174 .expect("expected proof")
175 .update_default_conclusion::<Var>(
176 OutputGuarantee::None,
177 &Conclusion::Unsat(Some(ConstraintId::from(id))),
178 );
179 #[cfg(feature = "verbose")]
180 self.proof
181 .as_mut()
182 .expect("expected proof")
183 .equals(&rustsat::clause![], Some(ConstraintId::from(id)))
184 .expect("failed to write proof");
185 }
186 }
187
188 fn solve_query(&mut self) {
189 #[cfg(feature = "verbose")]
190 {
191 use itertools::Itertools;
192 let proof = self.proof.as_mut().expect("expected proof");
193 proof
194 .comment(&"CaDiCaL: start solving with the following assumptions")
195 .expect("failed to write proof");
196 proof
197 .comment(&format_args!(
198 "[{}]",
199 self.assumptions
200 .iter()
201 .map(|l| pigeons::Axiom::from(*l))
202 .format(", ")
203 ))
204 .expect("failed to write proof");
205 }
206 }
207
208 fn add_assumption(&mut self, assumption: Lit) {
209 self.assumptions.push(assumption)
210 }
211
212 fn reset_assumptions(&mut self) {
213 self.assumptions.clear()
214 }
215
216 fn add_assumption_clause(
217 &mut self,
218 id: ClauseId,
219 clause: &CaDiCaLClause,
220 antecedents: &[ClauseId],
221 ) {
222 let id = self.add_derived(id, true, clause, antecedents);
230 self.core_id = Some(id);
231 }
232}
233
234fn write_derived<ProofW, I>(
235 proof: &mut Proof<ProofW>,
236 _clause: &CaDiCaLClause,
237 antecedents: I,
238) -> AbsConstraintId
239where
240 ProofW: io::Write,
241 I: IntoIterator<Item = AbsConstraintId, IntoIter: DoubleEndedIterator>,
242{
243 cfg_if::cfg_if! {
244 if #[cfg(feature = "rup-hints")] {
245 use pigeons::ConstraintId;
246 proof.reverse_unit_prop(_clause, antecedents.into_iter().map(ConstraintId::from))
247 .expect("failed to write proof")
248 } else {
249 use pigeons::{OperationLike, OperationSequence};
250 let mut antecedents = antecedents.into_iter().rev();
251 let Some(first) = antecedents.next() else {
252 panic!("need antecedents for derived clause")
253 };
254 let derivation = antecedents.fold(OperationSequence::<rustsat::types::Var>::from(first), |der, id| {
255 (der + id).saturate()
256 });
257 proof.operations(&derivation).expect("failed to write proof")
258 }
259 }
260}
261
262#[derive(Default, Debug)]
263struct ConstraintMapper {
264 map: Vec<AbsConstraintId>,
265}
266
267impl ConstraintMapper {
268 pub fn add_clause(&mut self, id: AbsConstraintId) {
269 self.map.push(id);
270 }
271
272 pub fn add_clause_checked(&mut self, id: AbsConstraintId, clause_id: ClauseId) {
273 self.map.push(id);
274 assert_eq!(u64::try_from(self.map.len()).unwrap(), clause_id.0);
275 }
276
277 pub fn map(&self, id: ClauseId) -> AbsConstraintId {
278 self.map[usize::try_from(id.0 - 1).expect("`ClauseId` does not fit in `usize`")]
279 }
280}
281
282pub struct CadicalCertCollector<'cadical, 'term, 'learn, ProofW: io::Write> {
283 cadical: &'cadical mut CaDiCaL<'term, 'learn>,
284 pt_handle: &'cadical ProofTracerHandle<CadicalTracer<ProofW>>,
285}
286
287impl<'cadical, 'term, 'learn, ProofW: io::Write>
288 CadicalCertCollector<'cadical, 'term, 'learn, ProofW>
289{
290 pub fn new(
291 cadical: &'cadical mut CaDiCaL<'term, 'learn>,
292 pt_handle: &'cadical ProofTracerHandle<CadicalTracer<ProofW>>,
293 ) -> Self {
294 Self { cadical, pt_handle }
295 }
296}
297
298impl<ProofW: io::Write + 'static> CollectCertClauses for CadicalCertCollector<'_, '_, '_, ProofW>
299where
300 ProofW: io::Write,
301{
302 fn extend_cert_clauses<T>(&mut self, cl_iter: T) -> Result<(), rustsat::OutOfMemory>
303 where
304 T: IntoIterator<Item = (Clause, AbsConstraintId)>,
305 {
306 for (cl, id) in cl_iter.into_iter() {
307 self.add_cert_clause(cl, id)?;
308 }
309 Ok(())
310 }
311
312 fn add_cert_clause(
313 &mut self,
314 cl: Clause,
315 id: AbsConstraintId,
316 ) -> Result<(), rustsat::OutOfMemory> {
317 let tracer = self.cadical.proof_tracer_mut(self.pt_handle);
318 tracer.cmap.add_clause(id);
319 self.cadical.add_clause(cl)
320 }
321}
322
323impl<ProofW: io::Write> CollectClauses for CadicalCertCollector<'_, '_, '_, ProofW> {
325 fn n_clauses(&self) -> usize {
326 self.cadical.n_clauses()
327 }
328
329 fn extend_clauses<T>(&mut self, cl_iter: T) -> Result<(), rustsat::OutOfMemory>
330 where
331 T: IntoIterator<Item = Clause>,
332 {
333 self.cadical.extend_clauses(cl_iter)
334 }
335
336 fn add_clause(&mut self, cl: Clause) -> Result<(), rustsat::OutOfMemory> {
337 self.cadical.add_clause(cl)
338 }
339}