GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/prop/sat_proof_manager.cpp Lines: 314 435 72.2 %
Date: 2021-11-07 Branches: 627 2086 30.1 %

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
5381
SatProofManager::SatProofManager(Minisat::Solver* solver,
28
                                 CnfStream* cnfStream,
29
                                 context::UserContext* userContext,
30
5381
                                 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
5381
      d_conflictLit(undefSatVariable)
38
{
39
5381
  d_true = NodeManager::currentNM()->mkConst(true);
40
5381
  d_false = NodeManager::currentNM()->mkConst(false);
41
5381
}
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
93949
Node SatProofManager::getClauseNode(SatLiteral satLit)
53
{
54
93949
  Assert(d_cnfStream->getNodeCache().find(satLit)
55
         != d_cnfStream->getNodeCache().end())
56
      << "SatProofManager::getClauseNode: literal " << satLit
57
      << " undefined.\n";
58
93949
  return d_cnfStream->getNodeCache()[satLit];
59
}
60
61
451408
Node SatProofManager::getClauseNode(const Minisat::Clause& clause)
62
{
63
902816
  std::vector<Node> clauseNodes;
64
2546826
  for (unsigned i = 0, size = clause.size(); i < size; ++i)
65
  {
66
2095418
    SatLiteral satLit = MinisatSatSolver::toSatLiteral(clause[i]);
67
2095418
    Assert(d_cnfStream->getNodeCache().find(satLit)
68
           != d_cnfStream->getNodeCache().end())
69
        << "SatProofManager::getClauseNode: literal " << satLit
70
        << " undefined\n";
71
2095418
    clauseNodes.push_back(d_cnfStream->getNodeCache()[satLit]);
72
  }
73
  // order children by node id
74
451408
  std::sort(clauseNodes.begin(), clauseNodes.end());
75
902816
  return NodeManager::currentNM()->mkNode(kind::OR, clauseNodes);
76
}
77
78
21899
void SatProofManager::startResChain(const Minisat::Clause& start)
79
{
80
21899
  if (Trace.isOn("sat-proof"))
81
  {
82
    Trace("sat-proof") << "SatProofManager::startResChain: ";
83
    printClause(start);
84
    Trace("sat-proof") << "\n";
85
  }
86
21899
  d_resLinks.emplace_back(getClauseNode(start), Node::null(), true);
87
21899
}
88
89
146985
void SatProofManager::addResolutionStep(Minisat::Lit lit, bool redundant)
90
{
91
146985
  SatLiteral satLit = MinisatSatSolver::toSatLiteral(lit);
92
293970
  Node litNode = d_cnfStream->getNodeCache()[satLit];
93
146985
  bool negated = satLit.isNegated();
94
146985
  Assert(!negated || litNode.getKind() == kind::NOT);
95
146985
  if (!redundant)
96
  {
97
236576
    Trace("sat-proof") << "SatProofManager::addResolutionStep: {"
98
236576
                       << satLit.isNegated() << "} [" << satLit << "] "
99
118288
                       << ~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
236576
    d_resLinks.emplace_back(d_cnfStream->getNodeCache()[~satLit],
104
236576
                            negated ? litNode[0] : litNode,
105
354864
                            !satLit.isNegated());
106
  }
107
  else
108
  {
109
57394
    Trace("sat-proof") << "SatProofManager::addResolutionStep: redundant lit "
110
28697
                       << satLit << " stored\n";
111
28697
    d_redundantLits.push_back(satLit);
112
  }
113
146985
}
114
115
271914
void SatProofManager::addResolutionStep(const Minisat::Clause& clause,
116
                                        Minisat::Lit lit)
117
{
118
271914
  SatLiteral satLit = MinisatSatSolver::toSatLiteral(lit);
119
543828
  Node litNode = d_cnfStream->getNodeCache()[satLit];
120
271914
  bool negated = satLit.isNegated();
121
271914
  Assert(!negated || litNode.getKind() == kind::NOT);
122
543828
  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
271914
  d_resLinks.emplace_back(clauseNode, negated ? litNode[0] : litNode, negated);
128
271914
  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
271914
}
137
138
3419
void SatProofManager::endResChain(Minisat::Lit lit)
139
{
140
3419
  SatLiteral satLit = MinisatSatSolver::toSatLiteral(lit);
141
6838
  Trace("sat-proof") << "SatProofManager::endResChain: chain_res for "
142
3419
                     << satLit;
143
3419
  endResChain(getClauseNode(satLit), {satLit});
144
3419
}
145
146
18480
void SatProofManager::endResChain(const Minisat::Clause& clause)
147
{
148
18480
  if (Trace.isOn("sat-proof"))
149
  {
150
    Trace("sat-proof") << "SatProofManager::endResChain: chain_res for ";
151
    printClause(clause);
152
  }
153
36960
  std::set<SatLiteral> clauseLits;
154
232387
  for (unsigned i = 0, size = clause.size(); i < size; ++i)
155
  {
156
213907
    clauseLits.insert(MinisatSatSolver::toSatLiteral(clause[i]));
157
  }
158
18480
  endResChain(getClauseNode(clause), clauseLits);
159
18480
}
160
161
21899
void SatProofManager::endResChain(Node conclusion,
162
                                  const std::set<SatLiteral>& conclusionLits)
163
{
164
21899
  Trace("sat-proof") << ", " << conclusion << "\n";
165
21899
  if (d_resChains.hasGenerator(conclusion))
166
  {
167
754
    Trace("sat-proof")
168
377
        << "SatProofManager::endResChain: skip repeated proof of " << conclusion
169
377
        << "\n";
170
    // clearing
171
377
    d_resLinks.clear();
172
377
    d_redundantLits.clear();
173
377
    return;
174
  }
175
  // first process redundant literals
176
42906
  std::set<SatLiteral> visited;
177
21522
  unsigned pos = d_resLinks.size();
178
49676
  for (SatLiteral satLit : d_redundantLits)
179
  {
180
28154
    processRedundantLit(satLit, conclusionLits, visited, pos);
181
  }
182
21522
  d_redundantLits.clear();
183
  // build resolution chain
184
  // the conclusion is stored already in the arguments because of the
185
  // possibility of reordering
186
42906
  std::vector<Node> children, args{conclusion};
187
519209
  for (unsigned i = 0, size = d_resLinks.size(); i < size; ++i)
188
  {
189
995374
    Node clause, pivot;
190
    bool posFirst;
191
497687
    std::tie(clause, pivot, posFirst) = d_resLinks[i];
192
497687
    children.push_back(clause);
193
497687
    Trace("sat-proof") << "SatProofManager::endResChain:   ";
194
497687
    if (i > 0)
195
    {
196
952330
      Trace("sat-proof") << "{" << posFirst << "} ["
197
476165
                         << 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
995374
    if (clause.getKind() == kind::OR
203
1493061
        && !(pivot.getKind() == kind::NOT && pivot[0].getKind() == kind::OR
204
497687
             && pivot[0] == clause))
205
    {
206
1998060
      for (unsigned j = 0, sizeJ = clause.getNumChildren(); j < sizeJ; ++j)
207
      {
208
1640748
        Trace("sat-proof") << d_cnfStream->getTranslationCache()[clause[j]];
209
1640748
        if (j < sizeJ - 1)
210
        {
211
1283436
          Trace("sat-proof") << ", ";
212
        }
213
      }
214
    }
215
    else
216
    {
217
280750
      Assert(d_cnfStream->getTranslationCache().find(clause)
218
             != d_cnfStream->getTranslationCache().end())
219
140375
          << "clause node " << clause
220
140375
          << " treated as unit has no literal. Pivot is " << pivot << "\n";
221
140375
      Trace("sat-proof") << d_cnfStream->getTranslationCache()[clause];
222
    }
223
497687
    Trace("sat-proof") << " : ";
224
497687
    if (i > 0)
225
    {
226
476165
      args.push_back(posFirst ? d_true : d_false);
227
476165
      args.push_back(pivot);
228
476165
      Trace("sat-proof") << "{" << posFirst << "} [" << pivot << "] ";
229
    }
230
497687
    Trace("sat-proof") << clause << "\n";
231
  }
232
  // clearing
233
21522
  d_resLinks.clear();
234
  // whether no-op
235
21522
  if (children.size() == 1)
236
  {
237
6
    Trace("sat-proof") << "SatProofManager::endResChain: no-op. The conclusion "
238
3
                       << conclusion << " is set-equal to premise "
239
3
                       << children[0] << "\n";
240
3
    return;
241
  }
242
  // whether trivial cycle
243
518798
  for (const Node& child : children)
244
  {
245
497414
    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
42768
  ProofStep ps(PfRule::MACRO_RESOLUTION_TRUST, children, args);
259
  // note that we must tell the proof generator to overwrite if repeated
260
21384
  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
21384
  d_resChains.addLazyStep(conclusion, &d_resChainPg);
264
}
265
266
121255
void SatProofManager::processRedundantLit(
267
    SatLiteral lit,
268
    const std::set<SatLiteral>& conclusionLits,
269
    std::set<SatLiteral>& visited,
270
    unsigned pos)
271
{
272
242510
  Trace("sat-proof") << push
273
121255
                     << "SatProofManager::processRedundantLit: Lit: " << lit
274
121255
                     << "\n";
275
121255
  if (visited.count(lit))
276
  {
277
26807
    Trace("sat-proof") << "already visited\n" << pop;
278
78371
    return;
279
  }
280
  Minisat::Solver::TCRef reasonRef =
281
94448
      d_solver->reason(Minisat::var(MinisatSatSolver::toMinisatLit(lit)));
282
94448
  if (reasonRef == Minisat::Solver::TCRef_Undef)
283
  {
284
49514
    Trace("sat-proof") << "unit, add link to lit " << lit << " at pos: " << pos
285
24757
                       << "\n"
286
24757
                       << pop;
287
24757
    visited.insert(lit);
288
49514
    Node litNode = d_cnfStream->getNodeCache()[lit];
289
24757
    bool negated = lit.isNegated();
290
24757
    Assert(!negated || litNode.getKind() == kind::NOT);
291
292
74271
    d_resLinks.emplace(d_resLinks.begin() + pos,
293
49514
                       d_cnfStream->getNodeCache()[~lit],
294
49514
                       negated ? litNode[0] : litNode,
295
123785
                       !negated);
296
24757
    return;
297
  }
298
69691
  Assert(reasonRef >= 0 && reasonRef < d_solver->ca.size())
299
      << "reasonRef " << reasonRef << " and d_satSolver->ca.size() "
300
      << d_solver->ca.size() << "\n";
301
69691
  const Minisat::Clause& reason = d_solver->ca[reasonRef];
302
69691
  if (Trace.isOn("sat-proof"))
303
  {
304
    Trace("sat-proof") << "reason: ";
305
    printClause(reason);
306
    Trace("sat-proof") << "\n";
307
  }
308
  // Since processRedundantLit calls can reallocate memory in the SAT solver due
309
  // to explaining stuff, we directly get the literals and the clause node here
310
139382
  std::vector<SatLiteral> toProcess;
311
219589
  for (unsigned i = 1, size = reason.size(); i < size; ++i)
312
  {
313
149898
    toProcess.push_back(MinisatSatSolver::toSatLiteral(reason[i]));
314
  }
315
139382
  Node clauseNode = getClauseNode(reason);
316
    // check if redundant literals in the reason. The first literal is the one we
317
  // will be eliminating, so we check the others
318
219589
  for (unsigned i = 0, size = toProcess.size(); i < size; ++i)
319
  {
320
149898
    SatLiteral satLit = toProcess[i];
321
    // if literal does not occur in the conclusion we process it as well
322
149898
    if (!conclusionLits.count(satLit))
323
    {
324
93101
      processRedundantLit(satLit, conclusionLits, visited, pos);
325
    }
326
  }
327
69691
  Assert(!visited.count(lit));
328
69691
  visited.insert(lit);
329
139382
  Trace("sat-proof") << "clause, add link to lit " << lit << " at pos: " << pos
330
69691
                     << "\n"
331
69691
                     << pop;
332
  // add the step before steps for children. Note that the step is with the
333
  // reason, not only with ~lit, since the learned clause is built under the
334
  // assumption that the redundant literal is removed via the resolution with
335
  // the explanation of its negation
336
139382
  Node litNode = d_cnfStream->getNodeCache()[lit];
337
69691
  bool negated = lit.isNegated();
338
69691
  Assert(!negated || litNode.getKind() == kind::NOT);
339
209073
  d_resLinks.emplace(d_resLinks.begin() + pos,
340
                     clauseNode,
341
139382
                     negated ? litNode[0] : litNode,
342
278764
                     !negated);
343
}
344
345
36754
void SatProofManager::explainLit(SatLiteral lit,
346
                                 std::unordered_set<TNode>& premises)
347
{
348
36754
  Trace("sat-proof") << push << "SatProofManager::explainLit: Lit: " << lit;
349
55998
  Node litNode = getClauseNode(lit);
350
36754
  Trace("sat-proof") << " [" << litNode << "]\n";
351
  // We don't need to explain nodes who are inputs. Note that it's *necessary*
352
  // to avoid attempting such explanations because they can introduce cycles at
353
  // the node level. For example, if a literal l depends on an input clause C
354
  // but a literal l', node-equivalent to C, depends on l, we may have a cycle
355
  // when building the overall SAT proof.
356
36754
  if (d_assumptions.contains(litNode))
357
  {
358
12076
    Trace("sat-proof")
359
6038
        << "SatProofManager::explainLit: input assumption, ABORT\n";
360
6038
    return;
361
  }
362
  // We don't need to explain nodes who already have proofs.
363
  //
364
  // Note that if we had two literals for (= a b) and (= b a) and we had already
365
  // a proof for (= a b) this test would return true for (= b a), which could
366
  // lead to open proof. However we should never have two literals like this in
367
  // the SAT solver since they'd be rewritten to the same one
368
30716
  if (d_resChainPg.hasProofFor(litNode))
369
  {
370
22944
    Trace("sat-proof") << "SatProofManager::explainLit: already justified "
371
11472
                       << lit << ", ABORT\n"
372
11472
                       << pop;
373
11472
    return;
374
  }
375
  Minisat::Solver::TCRef reasonRef =
376
19244
      d_solver->reason(Minisat::var(MinisatSatSolver::toMinisatLit(lit)));
377
19244
  if (reasonRef == Minisat::Solver::TCRef_Undef)
378
  {
379
    Trace("sat-proof") << "SatProofManager::explainLit: no SAT reason\n" << pop;
380
    return;
381
  }
382
19244
  Assert(reasonRef >= 0 && reasonRef < d_solver->ca.size())
383
      << "reasonRef " << reasonRef << " and d_satSolver->ca.size() "
384
      << d_solver->ca.size() << "\n";
385
19244
  const Minisat::Clause& reason = d_solver->ca[reasonRef];
386
19244
  unsigned size = reason.size();
387
19244
  if (Trace.isOn("sat-proof"))
388
  {
389
    Trace("sat-proof") << "SatProofManager::explainLit: with clause: ";
390
    printClause(reason);
391
    Trace("sat-proof") << "\n";
392
  }
393
#ifdef CVC5_ASSERTIONS
394
  // pedantically check that the negation of the literal to explain *does not*
395
  // occur in the reason, otherwise we will loop forever
396
68075
  for (unsigned i = 0; i < size; ++i)
397
  {
398
48831
    AlwaysAssert(~MinisatSatSolver::toSatLiteral(reason[i]) != lit)
399
        << "cyclic justification\n";
400
  }
401
#endif
402
  // add the reason clause first
403
38488
  std::vector<Node> children{getClauseNode(reason)}, args;
404
  // save in the premises
405
19244
  premises.insert(children.back());
406
  // Since explainLit calls can reallocate memory in the
407
  // SAT solver, we directly get the literals we need to explain so we no
408
  // longer depend on the reference to reason
409
38488
  std::vector<Node> toExplain{children.back().begin(), children.back().end()};
410
19244
  Trace("sat-proof") << push;
411
68075
  for (unsigned i = 0; i < size; ++i)
412
  {
413
#ifdef CVC5_ASSERTIONS
414
    // pedantically make sure that the reason stays the same
415
48831
    const Minisat::Clause& reloadedReason = d_solver->ca[reasonRef];
416
48831
    AlwaysAssert(size == static_cast<unsigned>(reloadedReason.size()));
417
48831
    AlwaysAssert(children[0] == getClauseNode(reloadedReason));
418
#endif
419
48831
    SatLiteral currLit = d_cnfStream->getTranslationCache()[toExplain[i]];
420
    // ignore the lit we are trying to explain...
421
48831
    if (currLit == lit)
422
    {
423
19244
      continue;
424
    }
425
59174
    std::unordered_set<TNode> childPremises;
426
29587
    explainLit(~currLit, childPremises);
427
    // save to resolution chain premises / arguments
428
29587
    Assert(d_cnfStream->getNodeCache().find(currLit)
429
           != d_cnfStream->getNodeCache().end());
430
29587
    children.push_back(d_cnfStream->getNodeCache()[~currLit]);
431
59174
    Node currLitNode = d_cnfStream->getNodeCache()[currLit];
432
29587
    bool negated = currLit.isNegated();
433
29587
    Assert(!negated || currLitNode.getKind() == kind::NOT);
434
    // note this is the opposite of what is done in addResolutionStep. This is
435
    // because here the clause, which contains the literal being analyzed, is
436
    // the first clause rather than the second
437
29587
    args.push_back(!negated ? d_true : d_false);
438
29587
    args.push_back(negated ? currLitNode[0] : currLitNode);
439
    // add child premises and the child itself
440
29587
    premises.insert(childPremises.begin(), childPremises.end());
441
29587
    premises.insert(d_cnfStream->getNodeCache()[~currLit]);
442
  }
443
19244
  if (Trace.isOn("sat-proof"))
444
  {
445
    Trace("sat-proof") << pop << "SatProofManager::explainLit: chain_res for "
446
                       << lit << ", " << litNode << " with clauses:\n";
447
    for (unsigned i = 0, csize = children.size(); i < csize; ++i)
448
    {
449
      Trace("sat-proof") << "SatProofManager::explainLit:   " << children[i];
450
      if (i > 0)
451
      {
452
        Trace("sat-proof") << " [" << args[i - 1] << "]";
453
      }
454
      Trace("sat-proof") << "\n";
455
    }
456
  }
457
  // if justification of children contains the expected conclusion, avoid the
458
  // cyclic proof by aborting.
459
19244
  if (premises.count(litNode))
460
  {
461
    Trace("sat-proof") << "SatProofManager::explainLit: CYCLIC PROOF of " << lit
462
                       << " [" << litNode << "], ABORT\n"
463
                       << pop;
464
    return;
465
  }
466
19244
  Trace("sat-proof") << pop;
467
  // create step
468
19244
  args.insert(args.begin(), litNode);
469
38488
  ProofStep ps(PfRule::MACRO_RESOLUTION_TRUST, children, args);
470
19244
  d_resChainPg.addStep(litNode, ps);
471
  // the premises in the limit of the justification may correspond to other
472
  // links in the chain which have, themselves, literals yet to be justified. So
473
  // we are not ready yet to check closedness w.r.t. CNF transformation of the
474
  // preprocessed assertions
475
19244
  d_resChains.addLazyStep(litNode, &d_resChainPg);
476
}
477
478
3795
void SatProofManager::finalizeProof(Node inConflictNode,
479
                                    const std::vector<SatLiteral>& inConflict)
480
{
481
7590
  Trace("sat-proof")
482
3795
      << "SatProofManager::finalizeProof: conflicting clause node: "
483
3795
      << inConflictNode << "\n";
484
  // nothing to do
485
3795
  if (inConflictNode == d_false)
486
  {
487
2194
    return;
488
  }
489
1601
  if (Trace.isOn("sat-proof-debug2"))
490
  {
491
    Trace("sat-proof-debug2")
492
        << push << "SatProofManager::finalizeProof: saved proofs in chain:\n";
493
    std::map<Node, std::shared_ptr<ProofNode>> links = d_resChains.getLinks();
494
    std::unordered_set<Node> skip;
495
    for (const std::pair<const Node, std::shared_ptr<ProofNode>>& link : links)
496
    {
497
      if (skip.count(link.first))
498
      {
499
        continue;
500
      }
501
      auto it = d_cnfStream->getTranslationCache().find(link.first);
502
      if (it != d_cnfStream->getTranslationCache().end())
503
      {
504
        Trace("sat-proof-debug2")
505
            << "SatProofManager::finalizeProof:  " << it->second;
506
      }
507
      // a refl step added due to double elim negation, ignore
508
      else if (link.second->getRule() == PfRule::REFL)
509
      {
510
        continue;
511
      }
512
      // a clause
513
      else
514
      {
515
        Trace("sat-proof-debug2") << "SatProofManager::finalizeProof:";
516
        Assert(link.first.getKind() == kind::OR) << link.first;
517
        for (const Node& n : link.first)
518
        {
519
          it = d_cnfStream->getTranslationCache().find(n);
520
          Assert(it != d_cnfStream->getTranslationCache().end());
521
          Trace("sat-proof-debug2") << it->second << " ";
522
        }
523
      }
524
      Trace("sat-proof-debug2") << "\n";
525
      Trace("sat-proof-debug2")
526
          << "SatProofManager::finalizeProof: " << link.first << "\n";
527
      // get resolution
528
      Node cur = link.first;
529
      std::shared_ptr<ProofNode> pfn = link.second;
530
      while (pfn->getRule() != PfRule::MACRO_RESOLUTION_TRUST)
531
      {
532
        Assert(pfn->getChildren().size() == 1
533
               && pfn->getChildren()[0]->getRule() == PfRule::ASSUME)
534
            << *link.second.get() << "\n"
535
            << *pfn.get();
536
        cur = pfn->getChildren()[0]->getResult();
537
        // retrieve justification of assumption in the links
538
        Assert(links.find(cur) != links.end());
539
        pfn = links[cur];
540
        // ignore it in the rest of the outside loop
541
        skip.insert(cur);
542
      }
543
      std::vector<Node> fassumps;
544
      expr::getFreeAssumptions(pfn.get(), fassumps);
545
      Trace("sat-proof-debug2") << push;
546
      for (const Node& fa : fassumps)
547
      {
548
        Trace("sat-proof-debug2") << "SatProofManager::finalizeProof:   - ";
549
        it = d_cnfStream->getTranslationCache().find(fa);
550
        if (it != d_cnfStream->getTranslationCache().end())
551
        {
552
          Trace("sat-proof-debug2") << it->second << "\n";
553
          continue;
554
        }
555
        // then it's a clause
556
        Assert(fa.getKind() == kind::OR);
557
        for (const Node& n : fa)
558
        {
559
          it = d_cnfStream->getTranslationCache().find(n);
560
          Assert(it != d_cnfStream->getTranslationCache().end());
561
          Trace("sat-proof-debug2") << it->second << " ";
562
        }
563
        Trace("sat-proof-debug2") << "\n";
564
      }
565
      Trace("sat-proof-debug2") << pop;
566
      Trace("sat-proof-debug2")
567
          << "SatProofManager::finalizeProof:  " << *pfn.get() << "\n=======\n";
568
      ;
569
    }
570
    Trace("sat-proof-debug2") << pop;
571
  }
572
  // We will resolve away of the literals l_1...l_n in inConflict. At this point
573
  // each ~l_i must be either explainable, the result of a previously saved
574
  // resolution chain, or an input. In account of it possibly being the first,
575
  // we call explainLit on each ~l_i while accumulating the children and
576
  // arguments for the resolution step to conclude false.
577
3202
  std::vector<Node> children{inConflictNode}, args;
578
3202
  std::unordered_set<TNode> premises;
579
7201
  for (unsigned i = 0, size = inConflict.size(); i < size; ++i)
580
  {
581
5600
    Assert(d_cnfStream->getNodeCache().find(inConflict[i])
582
           != d_cnfStream->getNodeCache().end());
583
11200
    std::unordered_set<TNode> childPremises;
584
5600
    explainLit(~inConflict[i], childPremises);
585
11200
    Node negatedLitNode = d_cnfStream->getNodeCache()[~inConflict[i]];
586
    // save to resolution chain premises / arguments
587
5600
    children.push_back(negatedLitNode);
588
11200
    Node litNode = d_cnfStream->getNodeCache()[inConflict[i]];
589
5600
    bool negated = inConflict[i].isNegated();
590
5600
    Assert(!negated || litNode.getKind() == kind::NOT);
591
    // note this is the opposite of what is done in addResolutionStep. This is
592
    // because here the clause, which contains the literal being analyzed, is
593
    // the first clause rather than the second
594
5600
    args.push_back(!negated ? d_true : d_false);
595
5600
    args.push_back(negated ? litNode[0] : litNode);
596
    // add child premises and the child itself
597
5600
    premises.insert(childPremises.begin(), childPremises.end());
598
5600
    premises.insert(negatedLitNode);
599
5600
    Trace("sat-proof") << "===========\n";
600
  }
601
1601
  if (Trace.isOn("sat-proof"))
602
  {
603
    Trace("sat-proof") << "SatProofManager::finalizeProof: chain_res for false "
604
                          "with clauses:\n";
605
    for (unsigned i = 0, size = children.size(); i < size; ++i)
606
    {
607
      Trace("sat-proof") << "SatProofManager::finalizeProof:   " << children[i];
608
      if (i > 0)
609
      {
610
        Trace("sat-proof") << " [" << args[i - 1] << "]";
611
      }
612
      Trace("sat-proof") << "\n";
613
    }
614
  }
615
  // create step
616
1601
  args.insert(args.begin(), d_false);
617
3202
  ProofStep ps(PfRule::MACRO_RESOLUTION_TRUST, children, args);
618
1601
  d_resChainPg.addStep(d_false, ps);
619
  // not yet ready to check closedness because maybe only now we will justify
620
  // literals used in resolutions
621
1601
  d_resChains.addLazyStep(d_false, &d_resChainPg);
622
  // Fix point justification of literals in leaves of the proof of false
623
  bool expanded;
624
1867
  do
625
  {
626
1867
    expanded = false;
627
1867
    Trace("sat-proof") << "expand assumptions to prove false\n";
628
3734
    std::shared_ptr<ProofNode> pfn = d_resChains.getProofFor(d_false);
629
1867
    Assert(pfn);
630
1867
    Trace("sat-proof-debug") << "sat proof of flase: " << *pfn.get() << "\n";
631
3734
    std::vector<Node> fassumps;
632
1867
    expr::getFreeAssumptions(pfn.get(), fassumps);
633
1867
    if (Trace.isOn("sat-proof"))
634
    {
635
      for (const Node& fa : fassumps)
636
      {
637
        auto it = d_cnfStream->getTranslationCache().find(fa);
638
        if (it != d_cnfStream->getTranslationCache().end())
639
        {
640
          Trace("sat-proof") << "- " << it->second << "\n";
641
          Trace("sat-proof") << "  - " << fa << "\n";
642
          continue;
643
        }
644
        // then it's a clause
645
        std::stringstream ss;
646
        Assert(fa.getKind() == kind::OR);
647
        for (const Node& n : fa)
648
        {
649
          it = d_cnfStream->getTranslationCache().find(n);
650
          Assert(it != d_cnfStream->getTranslationCache().end());
651
          ss << it->second << " ";
652
        }
653
        Trace("sat-proof") << "- " << ss.str() << "\n";
654
        Trace("sat-proof") << "  - " << fa << "\n";
655
      }
656
    }
657
658
    // for each assumption, see if it has a reason
659
100403
    for (const Node& fa : fassumps)
660
    {
661
      // ignore already processed assumptions
662
129547
      if (premises.count(fa))
663
      {
664
31011
        Trace("sat-proof") << "already processed assumption " << fa << "\n";
665
127980
        continue;
666
      }
667
      // ignore input assumptions. This is necessary to avoid rare collisions
668
      // between input clauses and literals that are equivalent at the node
669
      // level. In trying to justify the literal below, if it was previously
670
      // propagated (say, in a previous check-sat call that survived the
671
      // user-context changes) but no longer holds, then we may introduce a
672
      // bogus proof for it, rather than keeping it as an input.
673
133483
      if (d_assumptions.contains(fa))
674
      {
675
65958
        Trace("sat-proof") << "input assumption " << fa << "\n";
676
65958
        continue;
677
      }
678
      // ignore non-literals
679
1567
      auto it = d_cnfStream->getTranslationCache().find(fa);
680
1567
      if (it == d_cnfStream->getTranslationCache().end())
681
      {
682
        Trace("sat-proof") << "no lit assumption " << fa << "\n";
683
        premises.insert(fa);
684
        continue;
685
      }
686
3134
      Trace("sat-proof") << "lit assumption (" << it->second << "), " << fa
687
1567
                         << "\n";
688
      // mark another iteration for the loop, as some resolution link may be
689
      // connected because of the new justifications
690
1567
      expanded = true;
691
3134
      std::unordered_set<TNode> childPremises;
692
1567
      explainLit(it->second, childPremises);
693
      // add the premises used in the justification. We know they will have
694
      // been as expanded as possible
695
1567
      premises.insert(childPremises.begin(), childPremises.end());
696
      // add free assumption itself
697
1567
      premises.insert(fa);
698
    }
699
  } while (expanded);
700
  // now we should be able to close it
701
1601
  if (options::proofCheck() == options::ProofCheckMode::EAGER)
702
  {
703
6
    std::vector<Node> assumptionsVec;
704
20
    for (const Node& a : d_assumptions)
705
    {
706
17
      assumptionsVec.push_back(a);
707
    }
708
3
    d_resChains.addLazyStep(d_false, &d_resChainPg, assumptionsVec);
709
  }
710
}
711
712
56
void SatProofManager::storeUnitConflict(Minisat::Lit inConflict)
713
{
714
56
  Assert(d_conflictLit == undefSatVariable);
715
56
  d_conflictLit = MinisatSatSolver::toSatLiteral(inConflict);
716
56
}
717
718
56
void SatProofManager::finalizeProof()
719
{
720
56
  Assert(d_conflictLit != undefSatVariable);
721
112
  Trace("sat-proof")
722
56
      << "SatProofManager::finalizeProof: conflicting (lazy) satLit: "
723
56
      << d_conflictLit << "\n";
724
56
  finalizeProof(getClauseNode(d_conflictLit), {d_conflictLit});
725
  // reset since if in incremental mode this may be used again
726
56
  d_conflictLit = undefSatVariable;
727
56
}
728
729
2390
void SatProofManager::finalizeProof(Minisat::Lit inConflict, bool adding)
730
{
731
2390
  SatLiteral satLit = MinisatSatSolver::toSatLiteral(inConflict);
732
4780
  Trace("sat-proof") << "SatProofManager::finalizeProof: conflicting satLit: "
733
2390
                     << satLit << "\n";
734
4780
  Node clauseNode = getClauseNode(satLit);
735
2390
  if (adding)
736
  {
737
2390
    registerSatAssumptions({clauseNode});
738
  }
739
2390
  finalizeProof(clauseNode, {satLit});
740
2390
}
741
742
1349
void SatProofManager::finalizeProof(const Minisat::Clause& inConflict,
743
                                    bool adding)
744
{
745
1349
  if (Trace.isOn("sat-proof"))
746
  {
747
    Trace("sat-proof")
748
        << "SatProofManager::finalizeProof: conflicting clause: ";
749
    printClause(inConflict);
750
    Trace("sat-proof") << "\n";
751
  }
752
2698
  std::vector<SatLiteral> clause;
753
6697
  for (unsigned i = 0, size = inConflict.size(); i < size; ++i)
754
  {
755
5348
    clause.push_back(MinisatSatSolver::toSatLiteral(inConflict[i]));
756
  }
757
2698
  Node clauseNode = getClauseNode(inConflict);
758
1349
  if (adding)
759
  {
760
28
    registerSatAssumptions({clauseNode});
761
  }
762
1349
  finalizeProof(clauseNode, clause);
763
1349
}
764
765
10064
std::shared_ptr<ProofNode> SatProofManager::getProof()
766
{
767
10064
  std::shared_ptr<ProofNode> pfn = d_resChains.getProofFor(d_false);
768
10064
  if (!pfn)
769
  {
770
6172
    pfn = d_pnm->mkAssume(d_false);
771
  }
772
10064
  return pfn;
773
}
774
775
25665
void SatProofManager::registerSatLitAssumption(Minisat::Lit lit)
776
{
777
51330
  Trace("sat-proof") << "SatProofManager::registerSatLitAssumption: - "
778
51330
                     << getClauseNode(MinisatSatSolver::toSatLiteral(lit))
779
25665
                     << "\n";
780
25665
  d_assumptions.insert(getClauseNode(MinisatSatSolver::toSatLiteral(lit)));
781
25665
}
782
783
654575
void SatProofManager::registerSatAssumptions(const std::vector<Node>& assumps)
784
{
785
1309150
  for (const Node& a : assumps)
786
  {
787
1309150
    Trace("sat-proof") << "SatProofManager::registerSatAssumptions: - " << a
788
654575
                       << "\n";
789
654575
    d_assumptions.insert(a);
790
  }
791
654575
}
792
793
}  // namespace prop
794
31137
}  // namespace cvc5