1 /******************************************************************************
2 * Top contributors (to current version):
3 * Haniel Barbosa, Andrew Reynolds
5 * This file is part of the cvc5 project.
7 * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS
8 * in the top-level source directory and their institutional affiliations.
9 * All rights reserved. See the file COPYING in the top-level source
10 * directory for licensing information.
11 * ****************************************************************************
13 * Implementation of the proof manager for Minisat.
16 #include "prop/sat_proof_manager.h"
18 #include "options/proof_options.h"
19 #include "proof/proof_node_algorithm.h"
20 #include "proof/theory_proof_step_buffer.h"
21 #include "prop/cnf_stream.h"
22 #include "prop/minisat/minisat.h"
27 SatProofManager::SatProofManager(Env
& env
,
28 Minisat::Solver
* solver
,
32 d_cnfStream(cnfStream
),
33 d_resChains(d_env
.getProofNodeManager(), true, userContext()),
34 d_resChainPg(userContext(), d_env
.getProofNodeManager()),
35 d_assumptions(userContext()),
36 d_conflictLit(undefSatVariable
),
37 d_optResLevels(userContext()),
38 d_optResManager(userContext(), &d_resChains
, d_optResProofs
)
40 d_true
= NodeManager::currentNM()->mkConst(true);
41 d_false
= NodeManager::currentNM()->mkConst(false);
44 void SatProofManager::printClause(const Minisat::Clause
& clause
)
46 for (size_t i
= 0, size
= clause
.size(); i
< size
; ++i
)
48 SatLiteral satLit
= MinisatSatSolver::toSatLiteral(clause
[i
]);
49 Trace("sat-proof") << satLit
<< " ";
53 Node
SatProofManager::getClauseNode(const Minisat::Clause
& clause
)
55 std::vector
<Node
> clauseNodes
;
56 for (size_t i
= 0, size
= clause
.size(); i
< size
; ++i
)
58 SatLiteral satLit
= MinisatSatSolver::toSatLiteral(clause
[i
]);
59 clauseNodes
.push_back(d_cnfStream
->getNode(satLit
));
61 // order children by node id
62 std::sort(clauseNodes
.begin(), clauseNodes
.end());
63 return NodeManager::currentNM()->mkNode(kind::OR
, clauseNodes
);
66 void SatProofManager::startResChain(const Minisat::Clause
& start
)
68 if (TraceIsOn("sat-proof"))
70 Trace("sat-proof") << "SatProofManager::startResChain: ";
72 Trace("sat-proof") << "\n";
74 d_resLinks
.emplace_back(getClauseNode(start
), Node::null(), true);
77 void SatProofManager::addResolutionStep(Minisat::Lit lit
, bool redundant
)
79 SatLiteral satLit
= MinisatSatSolver::toSatLiteral(lit
);
80 Node litNode
= d_cnfStream
->getNodeCache()[satLit
];
81 bool negated
= satLit
.isNegated();
82 Assert(!negated
|| litNode
.getKind() == kind::NOT
);
85 Trace("sat-proof") << "SatProofManager::addResolutionStep: {"
86 << satLit
.isNegated() << "} [" << satLit
<< "] "
88 // if lit is negated then the chain resolution construction will use it as a
89 // pivot occurring as is in the second clause and the node under the
90 // negation in the first clause
91 d_resLinks
.emplace_back(d_cnfStream
->getNodeCache()[~satLit
],
92 negated
? litNode
[0] : litNode
,
97 Trace("sat-proof") << "SatProofManager::addResolutionStep: redundant lit "
98 << satLit
<< " stored\n";
99 d_redundantLits
.push_back(satLit
);
103 void SatProofManager::addResolutionStep(const Minisat::Clause
& clause
,
106 SatLiteral satLit
= MinisatSatSolver::toSatLiteral(lit
);
107 Node litNode
= d_cnfStream
->getNodeCache()[satLit
];
108 bool negated
= satLit
.isNegated();
109 Assert(!negated
|| litNode
.getKind() == kind::NOT
);
110 Node clauseNode
= getClauseNode(clause
);
111 // if lit is negative then the chain resolution construction will use it as a
112 // pivot occurring as is in the second clause and the node under the
113 // negation in the first clause, which means that the third argument of the
114 // tuple must be false
115 d_resLinks
.emplace_back(clauseNode
, negated
? litNode
[0] : litNode
, negated
);
116 if (TraceIsOn("sat-proof"))
118 Trace("sat-proof") << "SatProofManager::addResolutionStep: {"
119 << satLit
.isNegated() << "} [" << ~satLit
<< "] ";
121 Trace("sat-proof") << "\nSatProofManager::addResolutionStep:\t"
122 << clauseNode
<< " - lvl " << clause
.level() + 1 << "\n";
126 void SatProofManager::endResChain(Minisat::Lit lit
)
128 SatLiteral satLit
= MinisatSatSolver::toSatLiteral(lit
);
129 Trace("sat-proof") << "SatProofManager::endResChain: curr user level: "
130 << userContext()->getLevel() << "\n";
131 Trace("sat-proof") << "SatProofManager::endResChain: chain_res for "
133 endResChain(d_cnfStream
->getNode(satLit
), {satLit
});
136 void SatProofManager::endResChain(const Minisat::Clause
& clause
)
138 if (TraceIsOn("sat-proof"))
140 Trace("sat-proof") << "SatProofManager::endResChain: curr user level: "
141 << userContext()->getLevel() << "\n";
142 Trace("sat-proof") << "SatProofManager::endResChain: chain_res for ";
145 std::set
<SatLiteral
> clauseLits
;
146 for (unsigned i
= 0, size
= clause
.size(); i
< size
; ++i
)
148 clauseLits
.insert(MinisatSatSolver::toSatLiteral(clause
[i
]));
150 Node conclusion
= getClauseNode(clause
);
151 int clauseLevel
= clause
.level() + 1;
152 if (clauseLevel
< userContext()->getLevel())
154 Assert(!d_optResLevels
.count(conclusion
));
155 d_optResLevels
[conclusion
] = clauseLevel
;
156 Trace("sat-proof") << "SatProofManager::endResChain: ..clause's lvl "
157 << clause
.level() + 1 << " below curr user level "
158 << userContext()->getLevel() << "\n";
160 endResChain(conclusion
, clauseLits
);
163 void SatProofManager::endResChain(Node conclusion
,
164 const std::set
<SatLiteral
>& conclusionLits
)
166 Trace("sat-proof") << ", " << conclusion
<< "\n";
167 if (d_resChains
.hasGenerator(conclusion
))
170 << "SatProofManager::endResChain: skip repeated proof of " << conclusion
174 d_redundantLits
.clear();
177 // first process redundant literals
178 std::set
<SatLiteral
> visited
;
179 unsigned pos
= d_resLinks
.size();
180 for (SatLiteral satLit
: d_redundantLits
)
182 processRedundantLit(satLit
, conclusionLits
, visited
, pos
);
184 d_redundantLits
.clear();
185 // build resolution chain
186 // the conclusion is stored already in the arguments because of the
187 // possibility of reordering
188 std::vector
<Node
> children
, args
{conclusion
};
189 for (unsigned i
= 0, size
= d_resLinks
.size(); i
< size
; ++i
)
193 std::tie(clause
, pivot
, posFirst
) = d_resLinks
[i
];
194 children
.push_back(clause
);
195 Trace("sat-proof") << "SatProofManager::endResChain: ";
198 Trace("sat-proof") << "{" << posFirst
<< "} ["
199 << d_cnfStream
->getTranslationCache()[pivot
] << "] ";
201 // special case for clause (or l1 ... ln) being a single literal
202 // corresponding itself to a clause, which is indicated by the pivot being
203 // of the form (not (or l1 ... ln))
204 if (clause
.getKind() == kind::OR
205 && !(pivot
.getKind() == kind::NOT
&& pivot
[0].getKind() == kind::OR
206 && pivot
[0] == clause
))
208 for (unsigned j
= 0, sizeJ
= clause
.getNumChildren(); j
< sizeJ
; ++j
)
210 Trace("sat-proof") << d_cnfStream
->getTranslationCache()[clause
[j
]];
213 Trace("sat-proof") << ", ";
219 Assert(d_cnfStream
->getTranslationCache().find(clause
)
220 != d_cnfStream
->getTranslationCache().end())
221 << "clause node " << clause
222 << " treated as unit has no literal. Pivot is " << pivot
<< "\n";
223 Trace("sat-proof") << d_cnfStream
->getTranslationCache()[clause
];
225 Trace("sat-proof") << " : ";
228 args
.push_back(posFirst
? d_true
: d_false
);
229 args
.push_back(pivot
);
230 Trace("sat-proof") << "{" << posFirst
<< "} [" << pivot
<< "] ";
232 Trace("sat-proof") << clause
<< "\n";
237 if (children
.size() == 1)
239 Trace("sat-proof") << "SatProofManager::endResChain: no-op. The conclusion "
240 << conclusion
<< " is set-equal to premise "
241 << children
[0] << "\n";
244 // whether trivial cycle
245 for (const Node
& child
: children
)
247 if (conclusion
== child
)
250 << "SatProofManager::endResChain: no-op. The conclusion "
251 << conclusion
<< " is equal to a premise\n";
255 // since the conclusion can be both reordered and without duplicates and the
256 // SAT solver does not record this information, we use a MACRO_RESOLUTION
257 // step, which bypasses these. Note that we could generate a chain resolution
258 // rule here by explicitly computing the detailed steps, but leave this for
260 ProofStep
ps(PfRule::MACRO_RESOLUTION_TRUST
, children
, args
);
261 // note that we must tell the proof generator to overwrite if repeated
262 d_resChainPg
.addStep(conclusion
, ps
);
263 // the premises of this resolution may not have been justified yet, so we do
264 // not pass assumptions to check closedness
265 d_resChains
.addLazyStep(conclusion
, &d_resChainPg
);
268 void SatProofManager::processRedundantLit(
270 const std::set
<SatLiteral
>& conclusionLits
,
271 std::set
<SatLiteral
>& visited
,
274 Trace("sat-proof") << push
275 << "SatProofManager::processRedundantLit: Lit: " << lit
277 if (visited
.count(lit
))
279 Trace("sat-proof") << "already visited\n" << pop
;
282 Minisat::Solver::TCRef reasonRef
=
283 d_solver
->reason(Minisat::var(MinisatSatSolver::toMinisatLit(lit
)));
284 if (reasonRef
== Minisat::Solver::TCRef_Undef
)
286 Trace("sat-proof") << "unit, add link to lit " << lit
<< " at pos: " << pos
290 Node litNode
= d_cnfStream
->getNodeCache()[lit
];
291 bool negated
= lit
.isNegated();
292 Assert(!negated
|| litNode
.getKind() == kind::NOT
);
294 d_resLinks
.emplace(d_resLinks
.begin() + pos
,
295 d_cnfStream
->getNodeCache()[~lit
],
296 negated
? litNode
[0] : litNode
,
300 Assert(reasonRef
>= 0 && reasonRef
< d_solver
->ca
.size())
301 << "reasonRef " << reasonRef
<< " and d_satSolver->ca.size() "
302 << d_solver
->ca
.size() << "\n";
303 const Minisat::Clause
& reason
= d_solver
->ca
[reasonRef
];
304 if (TraceIsOn("sat-proof"))
306 Trace("sat-proof") << "reason: ";
308 Trace("sat-proof") << "\n";
310 // Since processRedundantLit calls can reallocate memory in the SAT solver due
311 // to explaining stuff, we directly get the literals and the clause node here
312 std::vector
<SatLiteral
> toProcess
;
313 for (unsigned i
= 1, size
= reason
.size(); i
< size
; ++i
)
315 toProcess
.push_back(MinisatSatSolver::toSatLiteral(reason
[i
]));
317 Node clauseNode
= getClauseNode(reason
);
318 // check if redundant literals in the reason. The first literal is the one we
319 // will be eliminating, so we check the others
320 for (unsigned i
= 0, size
= toProcess
.size(); i
< size
; ++i
)
322 SatLiteral satLit
= toProcess
[i
];
323 // if literal does not occur in the conclusion we process it as well
324 if (!conclusionLits
.count(satLit
))
326 processRedundantLit(satLit
, conclusionLits
, visited
, pos
);
329 Assert(!visited
.count(lit
));
331 Trace("sat-proof") << "clause, add link to lit " << lit
<< " at pos: " << pos
334 // add the step before steps for children. Note that the step is with the
335 // reason, not only with ~lit, since the learned clause is built under the
336 // assumption that the redundant literal is removed via the resolution with
337 // the explanation of its negation
338 Node litNode
= d_cnfStream
->getNodeCache()[lit
];
339 bool negated
= lit
.isNegated();
340 Assert(!negated
|| litNode
.getKind() == kind::NOT
);
341 d_resLinks
.emplace(d_resLinks
.begin() + pos
,
343 negated
? litNode
[0] : litNode
,
347 void SatProofManager::explainLit(SatLiteral lit
,
348 std::unordered_set
<TNode
>& premises
)
350 Trace("sat-proof") << push
<< "SatProofManager::explainLit: Lit: " << lit
;
351 Node litNode
= d_cnfStream
->getNode(lit
);
352 Trace("sat-proof") << " [" << litNode
<< "]\n";
353 // We don't need to explain nodes who are inputs. Note that it's *necessary*
354 // to avoid attempting such explanations because they can introduce cycles at
355 // the node level. For example, if a literal l depends on an input clause C
356 // but a literal l', node-equivalent to C, depends on l, we may have a cycle
357 // when building the overall SAT proof.
358 if (d_assumptions
.contains(litNode
))
361 << "SatProofManager::explainLit: input assumption, ABORT\n"
365 // We don't need to explain nodes who already have proofs.
367 // Note that if we had two literals for (= a b) and (= b a) and we had already
368 // a proof for (= a b) this test would return true for (= b a), which could
369 // lead to open proof. However we should never have two literals like this in
370 // the SAT solver since they'd be rewritten to the same one
371 if (d_resChainPg
.hasProofFor(litNode
))
373 Trace("sat-proof") << "SatProofManager::explainLit: already justified "
374 << lit
<< ", ABORT\n"
378 Minisat::Solver::TCRef reasonRef
=
379 d_solver
->reason(Minisat::var(MinisatSatSolver::toMinisatLit(lit
)));
380 if (reasonRef
== Minisat::Solver::TCRef_Undef
)
382 Trace("sat-proof") << "SatProofManager::explainLit: no SAT reason\n" << pop
;
385 Assert(reasonRef
>= 0 && reasonRef
< d_solver
->ca
.size())
386 << "reasonRef " << reasonRef
<< " and d_satSolver->ca.size() "
387 << d_solver
->ca
.size() << "\n";
388 const Minisat::Clause
& reason
= d_solver
->ca
[reasonRef
];
389 unsigned size
= reason
.size();
390 if (TraceIsOn("sat-proof"))
392 Trace("sat-proof") << "SatProofManager::explainLit: with clause: ";
394 Trace("sat-proof") << "\n";
396 #ifdef CVC5_ASSERTIONS
397 // pedantically check that the negation of the literal to explain *does not*
398 // occur in the reason, otherwise we will loop forever
399 for (unsigned i
= 0; i
< size
; ++i
)
401 AlwaysAssert(~MinisatSatSolver::toSatLiteral(reason
[i
]) != lit
)
402 << "cyclic justification\n";
405 // add the reason clause first
406 std::vector
<Node
> children
{getClauseNode(reason
)}, args
;
407 // save in the premises
408 premises
.insert(children
.back());
409 // Since explainLit calls can reallocate memory in the
410 // SAT solver, we directly get the literals we need to explain so we no
411 // longer depend on the reference to reason
412 std::vector
<Node
> toExplain
{children
.back().begin(), children
.back().end()};
413 Trace("sat-proof") << push
;
414 for (unsigned i
= 0; i
< size
; ++i
)
416 #ifdef CVC5_ASSERTIONS
417 // pedantically make sure that the reason stays the same
418 const Minisat::Clause
& reloadedReason
= d_solver
->ca
[reasonRef
];
419 AlwaysAssert(size
== static_cast<unsigned>(reloadedReason
.size()));
420 AlwaysAssert(children
[0] == getClauseNode(reloadedReason
));
422 SatLiteral currLit
= d_cnfStream
->getTranslationCache()[toExplain
[i
]];
423 // ignore the lit we are trying to explain...
428 std::unordered_set
<TNode
> childPremises
;
429 explainLit(~currLit
, childPremises
);
430 // save to resolution chain premises / arguments
431 Assert(d_cnfStream
->getNodeCache().find(currLit
)
432 != d_cnfStream
->getNodeCache().end());
433 children
.push_back(d_cnfStream
->getNodeCache()[~currLit
]);
434 Node currLitNode
= d_cnfStream
->getNodeCache()[currLit
];
435 bool negated
= currLit
.isNegated();
436 Assert(!negated
|| currLitNode
.getKind() == kind::NOT
);
437 // note this is the opposite of what is done in addResolutionStep. This is
438 // because here the clause, which contains the literal being analyzed, is
439 // the first clause rather than the second
440 args
.push_back(!negated
? d_true
: d_false
);
441 args
.push_back(negated
? currLitNode
[0] : currLitNode
);
442 // add child premises and the child itself
443 premises
.insert(childPremises
.begin(), childPremises
.end());
444 premises
.insert(d_cnfStream
->getNodeCache()[~currLit
]);
446 if (TraceIsOn("sat-proof"))
448 Trace("sat-proof") << pop
<< "SatProofManager::explainLit: chain_res for "
449 << lit
<< ", " << litNode
<< " with clauses:\n";
450 for (unsigned i
= 0, csize
= children
.size(); i
< csize
; ++i
)
452 Trace("sat-proof") << "SatProofManager::explainLit: " << children
[i
];
455 Trace("sat-proof") << " [" << args
[(2 * i
) - 2] << ", "
456 << args
[(2 * i
) - 1] << "]";
458 Trace("sat-proof") << "\n";
461 // if justification of children contains the expected conclusion, avoid the
462 // cyclic proof by aborting.
463 if (premises
.count(litNode
))
465 Trace("sat-proof") << "SatProofManager::explainLit: CYCLIC PROOF of " << lit
466 << " [" << litNode
<< "], ABORT\n"
470 Trace("sat-proof") << pop
;
472 args
.insert(args
.begin(), litNode
);
473 ProofStep
ps(PfRule::MACRO_RESOLUTION_TRUST
, children
, args
);
474 d_resChainPg
.addStep(litNode
, ps
);
475 // the premises in the limit of the justification may correspond to other
476 // links in the chain which have, themselves, literals yet to be justified. So
477 // we are not ready yet to check closedness w.r.t. CNF transformation of the
478 // preprocessed assertions
479 d_resChains
.addLazyStep(litNode
, &d_resChainPg
);
482 void SatProofManager::finalizeProof(Node inConflictNode
,
483 const std::vector
<SatLiteral
>& inConflict
)
486 << "SatProofManager::finalizeProof: conflicting clause node: "
487 << inConflictNode
<< "\n";
489 if (inConflictNode
== d_false
)
493 if (TraceIsOn("sat-proof-debug2"))
495 Trace("sat-proof-debug2")
496 << push
<< "SatProofManager::finalizeProof: saved proofs in chain:\n";
497 std::map
<Node
, std::shared_ptr
<ProofNode
>> links
= d_resChains
.getLinks();
498 std::unordered_set
<Node
> skip
;
499 for (const std::pair
<const Node
, std::shared_ptr
<ProofNode
>>& link
: links
)
501 if (skip
.count(link
.first
))
505 auto it
= d_cnfStream
->getTranslationCache().find(link
.first
);
506 if (it
!= d_cnfStream
->getTranslationCache().end())
508 Trace("sat-proof-debug2")
509 << "SatProofManager::finalizeProof: " << it
->second
;
511 // a refl step added due to double elim negation, ignore
512 else if (link
.second
->getRule() == PfRule::REFL
)
519 Trace("sat-proof-debug2") << "SatProofManager::finalizeProof:";
520 Assert(link
.first
.getKind() == kind::OR
) << link
.first
;
521 for (const Node
& n
: link
.first
)
523 it
= d_cnfStream
->getTranslationCache().find(n
);
524 Assert(it
!= d_cnfStream
->getTranslationCache().end());
525 Trace("sat-proof-debug2") << it
->second
<< " ";
528 Trace("sat-proof-debug2") << "\n";
529 Trace("sat-proof-debug2")
530 << "SatProofManager::finalizeProof: " << link
.first
<< "\n";
532 Node cur
= link
.first
;
533 std::shared_ptr
<ProofNode
> pfn
= link
.second
;
534 while (pfn
->getRule() != PfRule::MACRO_RESOLUTION_TRUST
)
536 Assert(pfn
->getChildren().size() == 1
537 && pfn
->getChildren()[0]->getRule() == PfRule::ASSUME
)
538 << *link
.second
.get() << "\n"
540 cur
= pfn
->getChildren()[0]->getResult();
541 // retrieve justification of assumption in the links
542 Assert(links
.find(cur
) != links
.end());
544 // ignore it in the rest of the outside loop
547 std::vector
<Node
> fassumps
;
548 expr::getFreeAssumptions(pfn
.get(), fassumps
);
549 Trace("sat-proof-debug2") << push
;
550 for (const Node
& fa
: fassumps
)
552 Trace("sat-proof-debug2") << "SatProofManager::finalizeProof: - ";
553 it
= d_cnfStream
->getTranslationCache().find(fa
);
554 if (it
!= d_cnfStream
->getTranslationCache().end())
556 Trace("sat-proof-debug2") << it
->second
<< "\n";
559 // then it's a clause
560 Assert(fa
.getKind() == kind::OR
);
561 for (const Node
& n
: fa
)
563 it
= d_cnfStream
->getTranslationCache().find(n
);
564 Assert(it
!= d_cnfStream
->getTranslationCache().end());
565 Trace("sat-proof-debug2") << it
->second
<< " ";
567 Trace("sat-proof-debug2") << "\n";
569 Trace("sat-proof-debug2") << pop
;
570 Trace("sat-proof-debug2")
571 << "SatProofManager::finalizeProof: " << *pfn
.get() << "\n=======\n";
574 Trace("sat-proof-debug2") << pop
;
576 // We will resolve away of the literals l_1...l_n in inConflict. At this point
577 // each ~l_i must be either explainable, the result of a previously saved
578 // resolution chain, or an input. In account of it possibly being the first,
579 // we call explainLit on each ~l_i while accumulating the children and
580 // arguments for the resolution step to conclude false.
581 std::vector
<Node
> children
{inConflictNode
}, args
;
582 std::unordered_set
<TNode
> premises
;
583 for (unsigned i
= 0, size
= inConflict
.size(); i
< size
; ++i
)
585 Assert(d_cnfStream
->getNodeCache().find(inConflict
[i
])
586 != d_cnfStream
->getNodeCache().end());
587 std::unordered_set
<TNode
> childPremises
;
588 explainLit(~inConflict
[i
], childPremises
);
589 Node negatedLitNode
= d_cnfStream
->getNodeCache()[~inConflict
[i
]];
590 // save to resolution chain premises / arguments
591 children
.push_back(negatedLitNode
);
592 Node litNode
= d_cnfStream
->getNodeCache()[inConflict
[i
]];
593 bool negated
= inConflict
[i
].isNegated();
594 Assert(!negated
|| litNode
.getKind() == kind::NOT
);
595 // note this is the opposite of what is done in addResolutionStep. This is
596 // because here the clause, which contains the literal being analyzed, is
597 // the first clause rather than the second
598 args
.push_back(!negated
? d_true
: d_false
);
599 args
.push_back(negated
? litNode
[0] : litNode
);
600 // add child premises and the child itself
601 premises
.insert(childPremises
.begin(), childPremises
.end());
602 premises
.insert(negatedLitNode
);
603 Trace("sat-proof") << "===========\n";
605 if (TraceIsOn("sat-proof"))
607 Trace("sat-proof") << "SatProofManager::finalizeProof: chain_res for false "
609 for (unsigned i
= 0, size
= children
.size(); i
< size
; ++i
)
611 Trace("sat-proof") << "SatProofManager::finalizeProof: " << children
[i
];
614 Trace("sat-proof") << " [" << args
[i
- 1] << "]";
616 Trace("sat-proof") << "\n";
620 args
.insert(args
.begin(), d_false
);
621 ProofStep
ps(PfRule::MACRO_RESOLUTION_TRUST
, children
, args
);
622 d_resChainPg
.addStep(d_false
, ps
);
623 // not yet ready to check closedness because maybe only now we will justify
624 // literals used in resolutions
625 d_resChains
.addLazyStep(d_false
, &d_resChainPg
);
626 // Fix point justification of literals in leaves of the proof of false
631 Trace("sat-proof") << "expand assumptions to prove false\n";
632 std::shared_ptr
<ProofNode
> pfn
= d_resChains
.getProofFor(d_false
);
634 Trace("sat-proof-debug") << "sat proof of flase: " << *pfn
.get() << "\n";
635 std::vector
<Node
> fassumps
;
636 expr::getFreeAssumptions(pfn
.get(), fassumps
);
637 if (TraceIsOn("sat-proof"))
639 for (const Node
& fa
: fassumps
)
641 auto it
= d_cnfStream
->getTranslationCache().find(fa
);
642 if (it
!= d_cnfStream
->getTranslationCache().end())
644 Trace("sat-proof") << "- " << it
->second
<< "\n";
645 Trace("sat-proof") << " - " << fa
<< "\n";
648 // then it's a clause
649 std::stringstream ss
;
650 Assert(fa
.getKind() == kind::OR
);
651 for (const Node
& n
: fa
)
653 it
= d_cnfStream
->getTranslationCache().find(n
);
654 Assert(it
!= d_cnfStream
->getTranslationCache().end());
655 ss
<< it
->second
<< " ";
657 Trace("sat-proof") << "- " << ss
.str() << "\n";
658 Trace("sat-proof") << " - " << fa
<< "\n";
662 // for each assumption, see if it has a reason
663 for (const Node
& fa
: fassumps
)
665 // ignore already processed assumptions
666 if (premises
.count(fa
))
668 Trace("sat-proof") << "already processed assumption " << fa
<< "\n";
671 // ignore input assumptions. This is necessary to avoid rare collisions
672 // between input clauses and literals that are equivalent at the node
673 // level. In trying to justify the literal below, if it was previously
674 // propagated (say, in a previous check-sat call that survived the
675 // user-context changes) but no longer holds, then we may introduce a
676 // bogus proof for it, rather than keeping it as an input.
677 if (d_assumptions
.contains(fa
))
679 Trace("sat-proof") << "input assumption " << fa
<< "\n";
682 // ignore non-literals
683 auto it
= d_cnfStream
->getTranslationCache().find(fa
);
684 if (it
== d_cnfStream
->getTranslationCache().end())
686 Trace("sat-proof") << "no lit assumption " << fa
<< "\n";
690 Trace("sat-proof") << "lit assumption (" << it
->second
<< "), " << fa
692 // mark another iteration for the loop, as some resolution link may be
693 // connected because of the new justifications
695 std::unordered_set
<TNode
> childPremises
;
696 explainLit(it
->second
, childPremises
);
697 // add the premises used in the justification. We know they will have
698 // been as expanded as possible
699 premises
.insert(childPremises
.begin(), childPremises
.end());
700 // add free assumption itself
704 // now we should be able to close it
705 if (options::proofCheck() == options::ProofCheckMode::EAGER
)
707 std::vector
<Node
> assumptionsVec
;
708 for (const Node
& a
: d_assumptions
)
710 assumptionsVec
.push_back(a
);
712 d_resChains
.addLazyStep(d_false
, &d_resChainPg
, assumptionsVec
);
716 void SatProofManager::storeUnitConflict(Minisat::Lit inConflict
)
718 Assert(d_conflictLit
== undefSatVariable
);
719 d_conflictLit
= MinisatSatSolver::toSatLiteral(inConflict
);
722 void SatProofManager::finalizeProof()
724 Assert(d_conflictLit
!= undefSatVariable
);
726 << "SatProofManager::finalizeProof: conflicting (lazy) satLit: "
727 << d_conflictLit
<< "\n";
728 finalizeProof(d_cnfStream
->getNode(d_conflictLit
), {d_conflictLit
});
729 // reset since if in incremental mode this may be used again
730 d_conflictLit
= undefSatVariable
;
733 void SatProofManager::finalizeProof(Minisat::Lit inConflict
, bool adding
)
735 SatLiteral satLit
= MinisatSatSolver::toSatLiteral(inConflict
);
736 Trace("sat-proof") << "SatProofManager::finalizeProof: conflicting satLit: "
738 Node clauseNode
= d_cnfStream
->getNode(satLit
);
741 registerSatAssumptions({clauseNode
});
743 finalizeProof(clauseNode
, {satLit
});
746 void SatProofManager::finalizeProof(const Minisat::Clause
& inConflict
,
749 if (TraceIsOn("sat-proof"))
752 << "SatProofManager::finalizeProof: conflicting clause: ";
753 printClause(inConflict
);
754 Trace("sat-proof") << "\n";
756 std::vector
<SatLiteral
> clause
;
757 for (unsigned i
= 0, size
= inConflict
.size(); i
< size
; ++i
)
759 clause
.push_back(MinisatSatSolver::toSatLiteral(inConflict
[i
]));
761 Node clauseNode
= getClauseNode(inConflict
);
764 registerSatAssumptions({clauseNode
});
766 finalizeProof(clauseNode
, clause
);
769 std::shared_ptr
<ProofNode
> SatProofManager::getProof()
771 std::shared_ptr
<ProofNode
> pfn
= d_resChains
.getProofFor(d_false
);
774 pfn
= d_env
.getProofNodeManager()->mkAssume(d_false
);
779 void SatProofManager::registerSatLitAssumption(Minisat::Lit lit
)
781 Trace("sat-proof") << "SatProofManager::registerSatLitAssumption: - "
782 << d_cnfStream
->getNode(
783 MinisatSatSolver::toSatLiteral(lit
))
785 d_assumptions
.insert(
786 d_cnfStream
->getNode(MinisatSatSolver::toSatLiteral(lit
)));
789 void SatProofManager::registerSatAssumptions(const std::vector
<Node
>& assumps
)
791 for (const Node
& a
: assumps
)
793 Trace("sat-proof") << "SatProofManager::registerSatAssumptions: - " << a
795 d_assumptions
.insert(a
);
799 void SatProofManager::notifyPop()
801 for (context::CDHashMap
<Node
, int>::const_iterator it
=
802 d_optResLevels
.begin();
803 it
!= d_optResLevels
.end();
806 // Save into map the proof of the resolution chain. We copy to prevent the
807 // proof node saved to be restored to suffering unintended updates. This is
809 std::shared_ptr
<ProofNode
> clauseResPf
=
810 d_env
.getProofNodeManager()->clone(d_resChains
.getProofFor(it
->first
));
811 Assert(clauseResPf
&& clauseResPf
->getRule() != PfRule::ASSUME
);
812 d_optResProofs
[it
->second
].push_back(clauseResPf
);