GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/prop/sat_proof_manager.cpp Lines: 308 427 72.1 %
Date: 2021-08-16 Branches: 626 2072 30.2 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Haniel Barbosa, Andrew Reynolds
4
 *
5
 * This file is part of the cvc5 project.
6
 *
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
 * ****************************************************************************
12
 *
13
 * Implementation of the proof manager for Minisat.
14
 */
15
16
#include "prop/sat_proof_manager.h"
17
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"
23
24
namespace cvc5 {
25
namespace prop {
26
27
1254
SatProofManager::SatProofManager(Minisat::Solver* solver,
28
                                 CnfStream* cnfStream,
29
                                 context::UserContext* userContext,
30
1254
                                 ProofNodeManager* pnm)
31
    : d_solver(solver),
32
      d_cnfStream(cnfStream),
33
      d_pnm(pnm),
34
      d_resChains(pnm, true, userContext),
35
      d_resChainPg(userContext, pnm),
36
      d_assumptions(userContext),
37
1254
      d_conflictLit(undefSatVariable)
38
{
39
1254
  d_true = NodeManager::currentNM()->mkConst(true);
40
1254
  d_false = NodeManager::currentNM()->mkConst(false);
41
1254
}
42
43
void SatProofManager::printClause(const Minisat::Clause& clause)
44
{
45
  for (unsigned i = 0, size = clause.size(); i < size; ++i)
46
  {
47
    SatLiteral satLit = MinisatSatSolver::toSatLiteral(clause[i]);
48
    Trace("sat-proof") << satLit << " ";
49
  }
50
}
51
52
85040
Node SatProofManager::getClauseNode(SatLiteral satLit)
53
{
54
85040
  Assert(d_cnfStream->getNodeCache().find(satLit)
55
         != d_cnfStream->getNodeCache().end())
56
      << "SatProofManager::getClauseNode: literal " << satLit
57
      << " undefined.\n";
58
85040
  return d_cnfStream->getNodeCache()[satLit];
59
}
60
61
487118
Node SatProofManager::getClauseNode(const Minisat::Clause& clause)
62
{
63
974236
  std::vector<Node> clauseNodes;
64
2824408
  for (unsigned i = 0, size = clause.size(); i < size; ++i)
65
  {
66
2337290
    SatLiteral satLit = MinisatSatSolver::toSatLiteral(clause[i]);
67
2337290
    Assert(d_cnfStream->getNodeCache().find(satLit)
68
           != d_cnfStream->getNodeCache().end())
69
        << "SatProofManager::getClauseNode: literal " << satLit
70
        << " undefined\n";
71
2337290
    clauseNodes.push_back(d_cnfStream->getNodeCache()[satLit]);
72
  }
73
  // order children by node id
74
487118
  std::sort(clauseNodes.begin(), clauseNodes.end());
75
974236
  return NodeManager::currentNM()->mkNode(kind::OR, clauseNodes);
76
}
77
78
23735
void SatProofManager::startResChain(const Minisat::Clause& start)
79
{
80
23735
  if (Trace.isOn("sat-proof"))
81
  {
82
    Trace("sat-proof") << "SatProofManager::startResChain: ";
83
    printClause(start);
84
    Trace("sat-proof") << "\n";
85
  }
86
23735
  d_resLinks.emplace_back(getClauseNode(start), Node::null(), true);
87
23735
}
88
89
179050
void SatProofManager::addResolutionStep(Minisat::Lit lit, bool redundant)
90
{
91
179050
  SatLiteral satLit = MinisatSatSolver::toSatLiteral(lit);
92
358100
  Node litNode = d_cnfStream->getNodeCache()[satLit];
93
179050
  bool negated = satLit.isNegated();
94
179050
  Assert(!negated || litNode.getKind() == kind::NOT);
95
179050
  if (!redundant)
96
  {
97
285176
    Trace("sat-proof") << "SatProofManager::addResolutionStep: {"
98
285176
                       << satLit.isNegated() << "} [" << satLit << "] "
99
142588
                       << ~satLit << "\n";
100
    // if lit is negated then the chain resolution construction will use it as a
101
    // pivot occurring as is in the second clause and the node under the
102
    // negation in the first clause
103
285176
    d_resLinks.emplace_back(d_cnfStream->getNodeCache()[~satLit],
104
285176
                            negated ? litNode[0] : litNode,
105
427764
                            !satLit.isNegated());
106
  }
107
  else
108
  {
109
72924
    Trace("sat-proof") << "SatProofManager::addResolutionStep: redundant lit "
110
36462
                       << satLit << " stored\n";
111
36462
    d_redundantLits.push_back(satLit);
112
  }
113
179050
}
114
115
296763
void SatProofManager::addResolutionStep(const Minisat::Clause& clause,
116
                                        Minisat::Lit lit)
117
{
118
296763
  SatLiteral satLit = MinisatSatSolver::toSatLiteral(lit);
119
593526
  Node litNode = d_cnfStream->getNodeCache()[satLit];
120
296763
  bool negated = satLit.isNegated();
121
296763
  Assert(!negated || litNode.getKind() == kind::NOT);
122
593526
  Node clauseNode = getClauseNode(clause);
123
  // if lit is negative then the chain resolution construction will use it as a
124
  // pivot occurring as is in the second clause and the node under the
125
  // negation in the first clause, which means that the third argument of the
126
  // tuple must be false
127
296763
  d_resLinks.emplace_back(clauseNode, negated ? litNode[0] : litNode, negated);
128
296763
  if (Trace.isOn("sat-proof"))
129
  {
130
    Trace("sat-proof") << "SatProofManager::addResolutionStep: {"
131
                       << satLit.isNegated() << "} [" << ~satLit << "] ";
132
    printClause(clause);
133
    Trace("sat-proof") << "\nSatProofManager::addResolutionStep:\t"
134
                       << clauseNode << "\n";
135
  }
136
296763
}
137
138
2555
void SatProofManager::endResChain(Minisat::Lit lit)
139
{
140
2555
  SatLiteral satLit = MinisatSatSolver::toSatLiteral(lit);
141
5110
  Trace("sat-proof") << "SatProofManager::endResChain: chain_res for "
142
2555
                     << satLit;
143
2555
  endResChain(getClauseNode(satLit), {satLit});
144
2555
}
145
146
21180
void SatProofManager::endResChain(const Minisat::Clause& clause)
147
{
148
21180
  if (Trace.isOn("sat-proof"))
149
  {
150
    Trace("sat-proof") << "SatProofManager::endResChain: chain_res for ";
151
    printClause(clause);
152
  }
153
42360
  std::set<SatLiteral> clauseLits;
154
259963
  for (unsigned i = 0, size = clause.size(); i < size; ++i)
155
  {
156
238783
    clauseLits.insert(MinisatSatSolver::toSatLiteral(clause[i]));
157
  }
158
21180
  endResChain(getClauseNode(clause), clauseLits);
159
21180
}
160
161
23735
void SatProofManager::endResChain(Node conclusion,
162
                                  const std::set<SatLiteral>& conclusionLits)
163
{
164
23735
  Trace("sat-proof") << ", " << conclusion << "\n";
165
23735
  if (d_resChains.hasGenerator(conclusion))
166
  {
167
890
    Trace("sat-proof")
168
445
        << "SatProofManager::endResChain: skip repeated proof of " << conclusion
169
445
        << "\n";
170
    // clearing
171
445
    d_resLinks.clear();
172
445
    d_redundantLits.clear();
173
445
    return;
174
  }
175
  // first process redundant literals
176
46443
  std::set<SatLiteral> visited;
177
23290
  unsigned pos = d_resLinks.size();
178
59108
  for (SatLiteral satLit : d_redundantLits)
179
  {
180
35818
    processRedundantLit(satLit, conclusionLits, visited, pos);
181
  }
182
23290
  d_redundantLits.clear();
183
  // build resolution chain
184
  // the conclusion is stored already in the arguments because of the
185
  // possibility of reordering
186
46443
  std::vector<Node> children, args{conclusion};
187
582623
  for (unsigned i = 0, size = d_resLinks.size(); i < size; ++i)
188
  {
189
1118666
    Node clause, pivot;
190
    bool posFirst;
191
559333
    std::tie(clause, pivot, posFirst) = d_resLinks[i];
192
559333
    children.push_back(clause);
193
559333
    Trace("sat-proof") << "SatProofManager::endResChain:   ";
194
559333
    if (i > 0)
195
    {
196
1072086
      Trace("sat-proof") << "{" << posFirst << "} ["
197
536043
                         << d_cnfStream->getTranslationCache()[pivot] << "] ";
198
    }
199
    // special case for clause (or l1 ... ln) being a single literal
200
    // corresponding itself to a clause, which is indicated by the pivot being
201
    // of the form (not (or l1 ... ln))
202
1118666
    if (clause.getKind() == kind::OR
203
1677999
        && !(pivot.getKind() == kind::NOT && pivot[0].getKind() == kind::OR
204
559333
             && pivot[0] == clause))
205
    {
206
2215607
      for (unsigned j = 0, sizeJ = clause.getNumChildren(); j < sizeJ; ++j)
207
      {
208
1823855
        Trace("sat-proof") << d_cnfStream->getTranslationCache()[clause[j]];
209
1823855
        if (j < sizeJ - 1)
210
        {
211
1432103
          Trace("sat-proof") << ", ";
212
        }
213
      }
214
    }
215
    else
216
    {
217
335162
      Assert(d_cnfStream->getTranslationCache().find(clause)
218
             != d_cnfStream->getTranslationCache().end())
219
167581
          << "clause node " << clause
220
167581
          << " treated as unit has no literal. Pivot is " << pivot << "\n";
221
167581
      Trace("sat-proof") << d_cnfStream->getTranslationCache()[clause];
222
    }
223
559333
    Trace("sat-proof") << " : ";
224
559333
    if (i > 0)
225
    {
226
536043
      args.push_back(posFirst ? d_true : d_false);
227
536043
      args.push_back(pivot);
228
536043
      Trace("sat-proof") << "{" << posFirst << "} [" << pivot << "] ";
229
    }
230
559333
    Trace("sat-proof") << clause << "\n";
231
  }
232
  // clearing
233
23290
  d_resLinks.clear();
234
  // whether no-op
235
23290
  if (children.size() == 1)
236
  {
237
4
    Trace("sat-proof") << "SatProofManager::endResChain: no-op. The conclusion "
238
2
                       << conclusion << " is set-equal to premise "
239
2
                       << children[0] << "\n";
240
2
    return;
241
  }
242
  // whether trivial cycle
243
582214
  for (const Node& child : children)
244
  {
245
559061
    if (conclusion == child)
246
    {
247
270
      Trace("sat-proof")
248
135
          << "SatProofManager::endResChain: no-op. The conclusion "
249
135
          << conclusion << " is equal to a premise\n";
250
135
      return;
251
    }
252
  }
253
  // since the conclusion can be both reordered and without duplicates and the
254
  // SAT solver does not record this information, we use a MACRO_RESOLUTION
255
  // step, which bypasses these. Note that we could generate a chain resolution
256
  // rule here by explicitly computing the detailed steps, but leave this for
257
  // post-processing.
258
46306
  ProofStep ps(PfRule::MACRO_RESOLUTION_TRUST, children, args);
259
  // note that we must tell the proof generator to overwrite if repeated
260
23153
  d_resChainPg.addStep(conclusion, ps);
261
  // the premises of this resolution may not have been justified yet, so we do
262
  // not pass assumptions to check closedness
263
23153
  d_resChains.addLazyStep(conclusion, &d_resChainPg);
264
}
265
266
140965
void SatProofManager::processRedundantLit(
267
    SatLiteral lit,
268
    const std::set<SatLiteral>& conclusionLits,
269
    std::set<SatLiteral>& visited,
270
    unsigned pos)
271
{
272
281930
  Trace("sat-proof") << push
273
140965
                     << "SatProofManager::processRedundantLit: Lit: " << lit
274
140965
                     << "\n";
275
140965
  if (visited.count(lit))
276
  {
277
34415
    Trace("sat-proof") << "already visited\n" << pop;
278
96565
    return;
279
  }
280
  Minisat::Solver::TCRef reasonRef =
281
106550
      d_solver->reason(Minisat::var(MinisatSatSolver::toMinisatLit(lit)));
282
106550
  if (reasonRef == Minisat::Solver::TCRef_Undef)
283
  {
284
55470
    Trace("sat-proof") << "unit, add link to lit " << lit << " at pos: " << pos
285
27735
                       << "\n"
286
27735
                       << pop;
287
27735
    visited.insert(lit);
288
55470
    Node litNode = d_cnfStream->getNodeCache()[lit];
289
27735
    bool negated = lit.isNegated();
290
27735
    Assert(!negated || litNode.getKind() == kind::NOT);
291
292
83205
    d_resLinks.emplace(d_resLinks.begin() + pos,
293
55470
                       d_cnfStream->getNodeCache()[~lit],
294
55470
                       negated ? litNode[0] : litNode,
295
138675
                       !negated);
296
27735
    return;
297
  }
298
78815
  Assert(reasonRef >= 0 && reasonRef < d_solver->ca.size())
299
      << "reasonRef " << reasonRef << " and d_satSolver->ca.size() "
300
      << d_solver->ca.size() << "\n";
301
78815
  const Minisat::Clause& reason = d_solver->ca[reasonRef];
302
78815
  if (Trace.isOn("sat-proof"))
303
  {
304
    Trace("sat-proof") << "reason: ";
305
    printClause(reason);
306
    Trace("sat-proof") << "\n";
307
  }
308
  // check if redundant literals in the reason. The first literal is the one we
309
  // will be eliminating, so we check the others
310
257418
  for (unsigned i = 1, size = reason.size(); i < size; ++i)
311
  {
312
178603
    SatLiteral satLit = MinisatSatSolver::toSatLiteral(reason[i]);
313
    // if literal does not occur in the conclusion we process it as well
314
178603
    if (!conclusionLits.count(satLit))
315
    {
316
105147
      processRedundantLit(satLit, conclusionLits, visited, pos);
317
    }
318
  }
319
78815
  Assert(!visited.count(lit));
320
78815
  visited.insert(lit);
321
157630
  Trace("sat-proof") << "clause, add link to lit " << lit << " at pos: " << pos
322
78815
                     << "\n"
323
78815
                     << pop;
324
  // add the step before steps for children. Note that the step is with the
325
  // reason, not only with ~lit, since the learned clause is built under the
326
  // assumption that the redundant literal is removed via the resolution with
327
  // the explanation of its negation
328
157630
  Node clauseNode = getClauseNode(reason);
329
157630
  Node litNode = d_cnfStream->getNodeCache()[lit];
330
78815
  bool negated = lit.isNegated();
331
78815
  Assert(!negated || litNode.getKind() == kind::NOT);
332
236445
  d_resLinks.emplace(d_resLinks.begin() + pos,
333
                     clauseNode,
334
157630
                     negated ? litNode[0] : litNode,
335
315260
                     !negated);
336
}
337
338
34360
void SatProofManager::explainLit(SatLiteral lit,
339
                                 std::unordered_set<TNode>& premises)
340
{
341
34360
  Trace("sat-proof") << push << "SatProofManager::explainLit: Lit: " << lit;
342
52674
  Node litNode = getClauseNode(lit);
343
34360
  Trace("sat-proof") << " [" << litNode << "]\n";
344
  // Note that if we had two literals for (= a b) and (= b a) and we had already
345
  // a proof for (= a b) this test would return true for (= b a), which could
346
  // lead to open proof. However we should never have two literals like this in
347
  // the SAT solver since they'd be rewritten to the same one
348
34360
  if (d_resChainPg.hasProofFor(litNode))
349
  {
350
20934
    Trace("sat-proof") << "SatProofManager::explainLit: already justified "
351
10467
                       << lit << ", ABORT\n"
352
10467
                       << pop;
353
10467
    return;
354
  }
355
  Minisat::Solver::TCRef reasonRef =
356
23893
      d_solver->reason(Minisat::var(MinisatSatSolver::toMinisatLit(lit)));
357
23893
  if (reasonRef == Minisat::Solver::TCRef_Undef)
358
  {
359
5577
    Trace("sat-proof") << "SatProofManager::explainLit: no SAT reason\n" << pop;
360
5577
    return;
361
  }
362
18316
  Assert(reasonRef >= 0 && reasonRef < d_solver->ca.size())
363
      << "reasonRef " << reasonRef << " and d_satSolver->ca.size() "
364
      << d_solver->ca.size() << "\n";
365
18316
  const Minisat::Clause& reason = d_solver->ca[reasonRef];
366
18316
  unsigned size = reason.size();
367
18316
  if (Trace.isOn("sat-proof"))
368
  {
369
    Trace("sat-proof") << "SatProofManager::explainLit: with clause: ";
370
    printClause(reason);
371
    Trace("sat-proof") << "\n";
372
  }
373
#ifdef CVC5_ASSERTIONS
374
  // pedantically check that the negation of the literal to explain *does not*
375
  // occur in the reason, otherwise we will loop forever
376
65782
  for (unsigned i = 0; i < size; ++i)
377
  {
378
47466
    AlwaysAssert(~MinisatSatSolver::toSatLiteral(reason[i]) != lit)
379
        << "cyclic justification\n";
380
  }
381
#endif
382
  // add the reason clause first
383
36630
  std::vector<Node> children{getClauseNode(reason)}, args;
384
  // save in the premises
385
18316
  premises.insert(children.back());
386
  // Since explainLit calls can reallocate memory in the
387
  // SAT solver, we directly get the literals we need to explain so we no
388
  // longer depend on the reference to reason
389
36630
  std::vector<Node> toExplain{children.back().begin(), children.back().end()};
390
18316
  Trace("sat-proof") << push;
391
65782
  for (unsigned i = 0; i < size; ++i)
392
  {
393
#ifdef CVC5_ASSERTIONS
394
    // pedantically make sure that the reason stays the same
395
47466
    const Minisat::Clause& reloadedReason = d_solver->ca[reasonRef];
396
47466
    AlwaysAssert(size == static_cast<unsigned>(reloadedReason.size()));
397
47466
    AlwaysAssert(children[0] == getClauseNode(reloadedReason));
398
#endif
399
47466
    SatLiteral currLit = d_cnfStream->getTranslationCache()[toExplain[i]];
400
    // ignore the lit we are trying to explain...
401
47466
    if (currLit == lit)
402
    {
403
18316
      continue;
404
    }
405
58300
    std::unordered_set<TNode> childPremises;
406
29150
    explainLit(~currLit, childPremises);
407
    // save to resolution chain premises / arguments
408
29150
    Assert(d_cnfStream->getNodeCache().find(currLit)
409
           != d_cnfStream->getNodeCache().end());
410
29150
    children.push_back(d_cnfStream->getNodeCache()[~currLit]);
411
58300
    Node currLitNode = d_cnfStream->getNodeCache()[currLit];
412
29150
    bool negated = currLit.isNegated();
413
29150
    Assert(!negated || currLitNode.getKind() == kind::NOT);
414
    // note this is the opposite of what is done in addResolutionStep. This is
415
    // because here the clause, which contains the literal being analyzed, is
416
    // the first clause rather than the second
417
29150
    args.push_back(!negated ? d_true : d_false);
418
29150
    args.push_back(negated ? currLitNode[0] : currLitNode);
419
    // add child premises and the child itself
420
29150
    premises.insert(childPremises.begin(), childPremises.end());
421
29150
    premises.insert(d_cnfStream->getNodeCache()[~currLit]);
422
  }
423
18316
  if (Trace.isOn("sat-proof"))
424
  {
425
    Trace("sat-proof") << pop << "SatProofManager::explainLit: chain_res for "
426
                       << lit << ", " << litNode << " with clauses:\n";
427
    for (unsigned i = 0, csize = children.size(); i < csize; ++i)
428
    {
429
      Trace("sat-proof") << "SatProofManager::explainLit:   " << children[i];
430
      if (i > 0)
431
      {
432
        Trace("sat-proof") << " [" << args[i - 1] << "]";
433
      }
434
      Trace("sat-proof") << "\n";
435
    }
436
  }
437
  // if justification of children contains the expected conclusion, avoid the
438
  // cyclic proof by aborting.
439
18316
  if (premises.count(litNode))
440
  {
441
4
    Trace("sat-proof") << "SatProofManager::explainLit: CYCLIC PROOF of " << lit
442
2
                       << " [" << litNode << "], ABORT\n"
443
2
                       << pop;
444
2
    return;
445
  }
446
18314
  Trace("sat-proof") << pop;
447
  // create step
448
18314
  args.insert(args.begin(), litNode);
449
36628
  ProofStep ps(PfRule::MACRO_RESOLUTION_TRUST, children, args);
450
18314
  d_resChainPg.addStep(litNode, ps);
451
  // the premises in the limit of the justification may correspond to other
452
  // links in the chain which have, themselves, literals yet to be justified. So
453
  // we are not ready yet to check closedness w.r.t. CNF transformation of the
454
  // preprocessed assertions
455
18314
  d_resChains.addLazyStep(litNode, &d_resChainPg);
456
}
457
458
1362
void SatProofManager::finalizeProof(Node inConflictNode,
459
                                    const std::vector<SatLiteral>& inConflict)
460
{
461
2724
  Trace("sat-proof")
462
1362
      << "SatProofManager::finalizeProof: conflicting clause node: "
463
1362
      << inConflictNode << "\n";
464
  // nothing to do
465
1362
  if (inConflictNode == d_false)
466
  {
467
413
    return;
468
  }
469
949
  if (Trace.isOn("sat-proof-debug2"))
470
  {
471
    Trace("sat-proof-debug2")
472
        << push << "SatProofManager::finalizeProof: saved proofs in chain:\n";
473
    std::map<Node, std::shared_ptr<ProofNode>> links = d_resChains.getLinks();
474
    std::unordered_set<Node> skip;
475
    for (const std::pair<const Node, std::shared_ptr<ProofNode>>& link : links)
476
    {
477
      if (skip.count(link.first))
478
      {
479
        continue;
480
      }
481
      auto it = d_cnfStream->getTranslationCache().find(link.first);
482
      if (it != d_cnfStream->getTranslationCache().end())
483
      {
484
        Trace("sat-proof-debug2")
485
            << "SatProofManager::finalizeProof:  " << it->second;
486
      }
487
      // a refl step added due to double elim negation, ignore
488
      else if (link.second->getRule() == PfRule::REFL)
489
      {
490
        continue;
491
      }
492
      // a clause
493
      else
494
      {
495
        Trace("sat-proof-debug2") << "SatProofManager::finalizeProof:";
496
        Assert(link.first.getKind() == kind::OR) << link.first;
497
        for (const Node& n : link.first)
498
        {
499
          it = d_cnfStream->getTranslationCache().find(n);
500
          Assert(it != d_cnfStream->getTranslationCache().end());
501
          Trace("sat-proof-debug2") << it->second << " ";
502
        }
503
      }
504
      Trace("sat-proof-debug2") << "\n";
505
      Trace("sat-proof-debug2")
506
          << "SatProofManager::finalizeProof: " << link.first << "\n";
507
      // get resolution
508
      Node cur = link.first;
509
      std::shared_ptr<ProofNode> pfn = link.second;
510
      while (pfn->getRule() != PfRule::MACRO_RESOLUTION_TRUST)
511
      {
512
        Assert(pfn->getChildren().size() == 1
513
               && pfn->getChildren()[0]->getRule() == PfRule::ASSUME)
514
            << *link.second.get() << "\n"
515
            << *pfn.get();
516
        cur = pfn->getChildren()[0]->getResult();
517
        // retrieve justification of assumption in the links
518
        Assert(links.find(cur) != links.end());
519
        pfn = links[cur];
520
        // ignore it in the rest of the outside loop
521
        skip.insert(cur);
522
      }
523
      std::vector<Node> fassumps;
524
      expr::getFreeAssumptions(pfn.get(), fassumps);
525
      Trace("sat-proof-debug2") << push;
526
      for (const Node& fa : fassumps)
527
      {
528
        Trace("sat-proof-debug2") << "SatProofManager::finalizeProof:   - ";
529
        it = d_cnfStream->getTranslationCache().find(fa);
530
        if (it != d_cnfStream->getTranslationCache().end())
531
        {
532
          Trace("sat-proof-debug2") << it->second << "\n";
533
          continue;
534
        }
535
        // then it's a clause
536
        Assert(fa.getKind() == kind::OR);
537
        for (const Node& n : fa)
538
        {
539
          it = d_cnfStream->getTranslationCache().find(n);
540
          Assert(it != d_cnfStream->getTranslationCache().end());
541
          Trace("sat-proof-debug2") << it->second << " ";
542
        }
543
        Trace("sat-proof-debug2") << "\n";
544
      }
545
      Trace("sat-proof-debug2") << pop;
546
      Trace("sat-proof-debug2")
547
          << "SatProofManager::finalizeProof:  " << *pfn.get() << "\n=======\n";
548
      ;
549
    }
550
    Trace("sat-proof-debug2") << pop;
551
  }
552
  // We will resolve away of the literals l_1...l_n in inConflict. At this point
553
  // each ~l_i must be either explainable, the result of a previously saved
554
  // resolution chain, or an input. In account of it possibly being the first,
555
  // we call explainLit on each ~l_i while accumulating the children and
556
  // arguments for the resolution step to conclude false.
557
1898
  std::vector<Node> children{inConflictNode}, args;
558
1898
  std::unordered_set<TNode> premises;
559
4541
  for (unsigned i = 0, size = inConflict.size(); i < size; ++i)
560
  {
561
3592
    Assert(d_cnfStream->getNodeCache().find(inConflict[i])
562
           != d_cnfStream->getNodeCache().end());
563
7184
    std::unordered_set<TNode> childPremises;
564
3592
    explainLit(~inConflict[i], childPremises);
565
7184
    Node negatedLitNode = d_cnfStream->getNodeCache()[~inConflict[i]];
566
    // save to resolution chain premises / arguments
567
3592
    children.push_back(negatedLitNode);
568
7184
    Node litNode = d_cnfStream->getNodeCache()[inConflict[i]];
569
3592
    bool negated = inConflict[i].isNegated();
570
3592
    Assert(!negated || litNode.getKind() == kind::NOT);
571
    // note this is the opposite of what is done in addResolutionStep. This is
572
    // because here the clause, which contains the literal being analyzed, is
573
    // the first clause rather than the second
574
3592
    args.push_back(!negated ? d_true : d_false);
575
3592
    args.push_back(negated ? litNode[0] : litNode);
576
    // add child premises and the child itself
577
3592
    premises.insert(childPremises.begin(), childPremises.end());
578
3592
    premises.insert(negatedLitNode);
579
3592
    Trace("sat-proof") << "===========\n";
580
  }
581
949
  if (Trace.isOn("sat-proof"))
582
  {
583
    Trace("sat-proof") << "SatProofManager::finalizeProof: chain_res for false "
584
                          "with clauses:\n";
585
    for (unsigned i = 0, size = children.size(); i < size; ++i)
586
    {
587
      Trace("sat-proof") << "SatProofManager::finalizeProof:   " << children[i];
588
      if (i > 0)
589
      {
590
        Trace("sat-proof") << " [" << args[i - 1] << "]";
591
      }
592
      Trace("sat-proof") << "\n";
593
    }
594
  }
595
  // create step
596
949
  args.insert(args.begin(), d_false);
597
1898
  ProofStep ps(PfRule::MACRO_RESOLUTION_TRUST, children, args);
598
949
  d_resChainPg.addStep(d_false, ps);
599
  // not yet ready to check closedness because maybe only now we will justify
600
  // literals used in resolutions
601
949
  d_resChains.addLazyStep(d_false, &d_resChainPg);
602
  // Fix point justification of literals in leaves of the proof of false
603
  bool expanded;
604
1198
  do
605
  {
606
1198
    expanded = false;
607
1198
    Trace("sat-proof") << "expand assumptions to prove false\n";
608
2396
    std::shared_ptr<ProofNode> pfn = d_resChains.getProofFor(d_false);
609
1198
    Assert(pfn);
610
1198
    Trace("sat-proof-debug") << "sat proof of flase: " << *pfn.get() << "\n";
611
2396
    std::vector<Node> fassumps;
612
1198
    expr::getFreeAssumptions(pfn.get(), fassumps);
613
1198
    if (Trace.isOn("sat-proof"))
614
    {
615
      for (const Node& fa : fassumps)
616
      {
617
        Trace("sat-proof") << "- ";
618
        auto it = d_cnfStream->getTranslationCache().find(fa);
619
        if (it != d_cnfStream->getTranslationCache().end())
620
        {
621
          Trace("sat-proof") << it->second << "\n";
622
          Trace("sat-proof") << "- " << fa << "\n";
623
          continue;
624
        }
625
        // then it's a clause
626
        Assert(fa.getKind() == kind::OR);
627
        for (const Node& n : fa)
628
        {
629
          it = d_cnfStream->getTranslationCache().find(n);
630
          Assert(it != d_cnfStream->getTranslationCache().end());
631
          Trace("sat-proof") << it->second << " ";
632
        }
633
        Trace("sat-proof") << "\n";
634
        Trace("sat-proof") << "- " << fa << "\n";
635
      }
636
    }
637
638
    // for each assumption, see if it has a reason
639
108421
    for (const Node& fa : fassumps)
640
    {
641
      // ignore already processed assumptions
642
136116
      if (premises.count(fa))
643
      {
644
28893
        Trace("sat-proof") << "already processed assumption " << fa << "\n";
645
134498
        continue;
646
      }
647
      // ignore input assumptions. This is necessary to avoid rare collisions
648
      // between input clauses and literals that are equivalent at the node
649
      // level. In trying to justify the literal below if, it was previously
650
      // propagated (say, in a previous check-sat call that survived the
651
      // user-context changes) but no longer holds, then we may introduce a
652
      // bogus proof for it, rather than keeping it as an input.
653
155042
      if (d_assumptions.contains(fa))
654
      {
655
76712
        Trace("sat-proof") << "input assumption " << fa << "\n";
656
76712
        continue;
657
      }
658
      // ignore non-literals
659
1618
      auto it = d_cnfStream->getTranslationCache().find(fa);
660
1618
      if (it == d_cnfStream->getTranslationCache().end())
661
      {
662
        Trace("sat-proof") << "no lit assumption " << fa << "\n";
663
        premises.insert(fa);
664
        continue;
665
      }
666
3236
      Trace("sat-proof") << "lit assumption (" << it->second << "), " << fa
667
1618
                         << "\n";
668
      // mark another iteration for the loop, as some resolution link may be
669
      // connected because of the new justifications
670
1618
      expanded = true;
671
3236
      std::unordered_set<TNode> childPremises;
672
1618
      explainLit(it->second, childPremises);
673
      // add the premises used in the justification. We know they will have
674
      // been as expanded as possible
675
1618
      premises.insert(childPremises.begin(), childPremises.end());
676
      // add free assumption itself
677
1618
      premises.insert(fa);
678
    }
679
  } while (expanded);
680
  // now we should be able to close it
681
949
  if (options::proofEagerChecking())
682
  {
683
    std::vector<Node> assumptionsVec;
684
    for (const Node& a : d_assumptions)
685
    {
686
      assumptionsVec.push_back(a);
687
    }
688
    d_resChains.addLazyStep(d_false, &d_resChainPg, assumptionsVec);
689
  }
690
}
691
692
49
void SatProofManager::storeUnitConflict(Minisat::Lit inConflict)
693
{
694
49
  Assert(d_conflictLit == undefSatVariable);
695
49
  d_conflictLit = MinisatSatSolver::toSatLiteral(inConflict);
696
49
}
697
698
49
void SatProofManager::finalizeProof()
699
{
700
49
  Assert(d_conflictLit != undefSatVariable);
701
98
  Trace("sat-proof")
702
49
      << "SatProofManager::finalizeProof: conflicting (lazy) satLit: "
703
49
      << d_conflictLit << "\n";
704
49
  finalizeProof(getClauseNode(d_conflictLit), {d_conflictLit});
705
49
}
706
707
470
void SatProofManager::finalizeProof(Minisat::Lit inConflict, bool adding)
708
{
709
470
  SatLiteral satLit = MinisatSatSolver::toSatLiteral(inConflict);
710
940
  Trace("sat-proof") << "SatProofManager::finalizeProof: conflicting satLit: "
711
470
                     << satLit << "\n";
712
940
  Node clauseNode = getClauseNode(satLit);
713
470
  if (adding)
714
  {
715
470
    registerSatAssumptions({clauseNode});
716
  }
717
470
  finalizeProof(clauseNode, {satLit});
718
470
}
719
720
843
void SatProofManager::finalizeProof(const Minisat::Clause& inConflict,
721
                                    bool adding)
722
{
723
843
  if (Trace.isOn("sat-proof"))
724
  {
725
    Trace("sat-proof")
726
        << "SatProofManager::finalizeProof: conflicting clause: ";
727
    printClause(inConflict);
728
    Trace("sat-proof") << "\n";
729
  }
730
1686
  std::vector<SatLiteral> clause;
731
4329
  for (unsigned i = 0, size = inConflict.size(); i < size; ++i)
732
  {
733
3486
    clause.push_back(MinisatSatSolver::toSatLiteral(inConflict[i]));
734
  }
735
1686
  Node clauseNode = getClauseNode(inConflict);
736
843
  if (adding)
737
  {
738
19
    registerSatAssumptions({clauseNode});
739
  }
740
843
  finalizeProof(clauseNode, clause);
741
843
}
742
743
2811
std::shared_ptr<ProofNode> SatProofManager::getProof()
744
{
745
2811
  std::shared_ptr<ProofNode> pfn = d_resChains.getProofFor(d_false);
746
2811
  if (!pfn)
747
  {
748
852
    pfn = d_pnm->mkAssume(d_false);
749
  }
750
2811
  return pfn;
751
}
752
753
23803
void SatProofManager::registerSatLitAssumption(Minisat::Lit lit)
754
{
755
47606
  Trace("sat-proof") << "SatProofManager::registerSatLitAssumption: - "
756
47606
                     << getClauseNode(MinisatSatSolver::toSatLiteral(lit))
757
23803
                     << "\n";
758
23803
  d_assumptions.insert(getClauseNode(MinisatSatSolver::toSatLiteral(lit)));
759
23803
}
760
761
735571
void SatProofManager::registerSatAssumptions(const std::vector<Node>& assumps)
762
{
763
1471142
  for (const Node& a : assumps)
764
  {
765
1471142
    Trace("sat-proof") << "SatProofManager::registerSatAssumptions: - " << a
766
735571
                       << "\n";
767
735571
    d_assumptions.insert(a);
768
  }
769
735571
}
770
771
}  // namespace prop
772
29340
}  // namespace cvc5