GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/prop/proof_cnf_stream.cpp Lines: 580 619 93.7 %
Date: 2021-05-22 Branches: 1499 3332 45.0 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Haniel Barbosa, Andrew Reynolds, Tim King
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-producing CNF stream.
14
 */
15
16
#include "prop/proof_cnf_stream.h"
17
18
#include "options/smt_options.h"
19
#include "prop/minisat/minisat.h"
20
#include "theory/builtin/proof_checker.h"
21
22
namespace cvc5 {
23
namespace prop {
24
25
1197
ProofCnfStream::ProofCnfStream(context::UserContext* u,
26
                               CnfStream& cnfStream,
27
                               SatProofManager* satPM,
28
1197
                               ProofNodeManager* pnm)
29
    : d_cnfStream(cnfStream),
30
      d_satPM(satPM),
31
      d_proof(pnm, nullptr, u, "ProofCnfStream::LazyCDProof"),
32
1197
      d_blocked(u)
33
{
34
1197
}
35
36
889928
void ProofCnfStream::addBlocked(std::shared_ptr<ProofNode> pfn)
37
{
38
889928
  d_blocked.insert(pfn);
39
889928
}
40
41
1557994
bool ProofCnfStream::isBlocked(std::shared_ptr<ProofNode> pfn)
42
{
43
1557994
  return d_blocked.contains(pfn);
44
}
45
46
101941
std::shared_ptr<ProofNode> ProofCnfStream::getProofFor(Node f)
47
{
48
101941
  return d_proof.getProofFor(f);
49
}
50
51
1267271
bool ProofCnfStream::hasProofFor(Node f)
52
{
53
1267271
  return d_proof.hasStep(f) || d_proof.hasGenerator(f);
54
}
55
56
std::string ProofCnfStream::identify() const { return "ProofCnfStream"; }
57
58
377534
void ProofCnfStream::normalizeAndRegister(TNode clauseNode)
59
{
60
755068
  Node normClauseNode = d_psb.factorReorderElimDoubleNeg(clauseNode);
61
377534
  if (Trace.isOn("cnf") && normClauseNode != clauseNode)
62
  {
63
    Trace("cnf") << push
64
                 << "ProofCnfStream::normalizeAndRegister: steps to normalized "
65
                 << normClauseNode << "\n"
66
                 << pop;
67
  }
68
377534
  d_satPM->registerSatAssumptions({normClauseNode});
69
377534
}
70
71
90682
void ProofCnfStream::convertAndAssert(TNode node,
72
                                      bool negated,
73
                                      bool removable,
74
                                      ProofGenerator* pg)
75
{
76
181364
  Trace("cnf") << "ProofCnfStream::convertAndAssert(" << node
77
181364
               << ", negated = " << (negated ? "true" : "false")
78
90682
               << ", removable = " << (removable ? "true" : "false") << ")\n";
79
90682
  d_cnfStream.d_removable = removable;
80
90682
  if (pg)
81
  {
82
139446
    Trace("cnf") << "ProofCnfStream::convertAndAssert: pg: " << pg->identify()
83
69723
                 << "\n";
84
139446
    Node toJustify = negated ? node.notNode() : static_cast<Node>(node);
85
69723
    d_proof.addLazyStep(toJustify,
86
                        pg,
87
                        PfRule::ASSUME,
88
                        true,
89
                        "ProofCnfStream::convertAndAssert:cnf");
90
  }
91
90682
  convertAndAssert(node, negated);
92
  // process saved steps in buffer
93
90682
  const std::vector<std::pair<Node, ProofStep>>& steps = d_psb.getSteps();
94
1557100
  for (const std::pair<Node, ProofStep>& step : steps)
95
  {
96
1466418
    d_proof.addStep(step.first, step.second);
97
  }
98
90682
  d_psb.clear();
99
90682
}
100
101
166235
void ProofCnfStream::convertAndAssert(TNode node, bool negated)
102
{
103
332470
  Trace("cnf") << "ProofCnfStream::convertAndAssert(" << node
104
166235
               << ", negated = " << (negated ? "true" : "false") << ")\n";
105
166235
  switch (node.getKind())
106
  {
107
20824
    case kind::AND: convertAndAssertAnd(node, negated); break;
108
53417
    case kind::OR: convertAndAssertOr(node, negated); break;
109
12
    case kind::XOR: convertAndAssertXor(node, negated); break;
110
14061
    case kind::IMPLIES: convertAndAssertImplies(node, negated); break;
111
5096
    case kind::ITE: convertAndAssertIte(node, negated); break;
112
23421
    case kind::NOT:
113
    {
114
      // track double negation elimination
115
23421
      if (negated)
116
      {
117
627
        d_proof.addStep(node[0], PfRule::NOT_NOT_ELIM, {node.notNode()}, {});
118
1254
        Trace("cnf")
119
627
            << "ProofCnfStream::convertAndAssert: NOT_NOT_ELIM added norm "
120
627
            << node[0] << "\n";
121
      }
122
23421
      convertAndAssert(node[0], !negated);
123
23421
      break;
124
    }
125
31040
    case kind::EQUAL:
126
31040
      if (node[0].getType().isBoolean())
127
      {
128
2278
        convertAndAssertIff(node, negated);
129
2278
        break;
130
      }
131
      CVC5_FALLTHROUGH;
132
    default:
133
    {
134
      // negate
135
94252
      Node nnode = negated ? node.negate() : static_cast<Node>(node);
136
      // Atoms
137
47126
      SatLiteral lit = toCNF(node, negated);
138
47126
      bool added = d_cnfStream.assertClause(nnode, lit);
139
47126
      if (negated && added && nnode != node.notNode())
140
      {
141
        // track double negation elimination
142
        //    (not (not n))
143
        //   -------------- NOT_NOT_ELIM
144
        //        n
145
        d_proof.addStep(nnode, PfRule::NOT_NOT_ELIM, {node.notNode()}, {});
146
        Trace("cnf")
147
            << "ProofCnfStream::convertAndAssert: NOT_NOT_ELIM added norm "
148
            << nnode << "\n";
149
      }
150
47126
      if (added)
151
      {
152
        // note that we do not need to do the normalization here since this is
153
        // not a clause and double negation is tracked in a dedicated manner
154
        // above
155
28040
        d_satPM->registerSatAssumptions({nnode});
156
47126
      }
157
    }
158
  }
159
166235
}
160
161
20824
void ProofCnfStream::convertAndAssertAnd(TNode node, bool negated)
162
{
163
41648
  Trace("cnf") << "ProofCnfStream::convertAndAssertAnd(" << node
164
20824
               << ", negated = " << (negated ? "true" : "false") << ")\n";
165
20824
  Assert(node.getKind() == kind::AND);
166
20824
  if (!negated)
167
  {
168
    // If the node is a conjunction, we handle each conjunct separately
169
2356
    NodeManager* nm = NodeManager::currentNM();
170
53373
    for (unsigned i = 0, size = node.getNumChildren(); i < size; ++i)
171
    {
172
      // Create a proof step for each n_i
173
102034
      Node iNode = nm->mkConst<Rational>(i);
174
51017
      d_proof.addStep(node[i], PfRule::AND_ELIM, {node}, {iNode});
175
102034
      Trace("cnf") << "ProofCnfStream::convertAndAssertAnd: AND_ELIM " << i
176
51017
                   << " added norm " << node[i] << "\n";
177
51017
      convertAndAssert(node[i], false);
178
    }
179
  }
180
  else
181
  {
182
    // If the node is a disjunction, we construct a clause and assert it
183
18468
    unsigned i, size = node.getNumChildren();
184
36936
    SatClause clause(size);
185
196591
    for (i = 0; i < size; ++i)
186
    {
187
178123
      clause[i] = toCNF(node[i], true);
188
    }
189
18468
    bool added = d_cnfStream.assertClause(node.negate(), clause);
190
    // register proof step
191
18468
    if (added)
192
    {
193
36858
      std::vector<Node> disjuncts;
194
196414
      for (i = 0; i < size; ++i)
195
      {
196
177985
        disjuncts.push_back(node[i].notNode());
197
      }
198
36858
      Node clauseNode = NodeManager::currentNM()->mkNode(kind::OR, disjuncts);
199
18429
      d_proof.addStep(clauseNode, PfRule::NOT_AND, {node.notNode()}, {});
200
36858
      Trace("cnf") << "ProofCnfStream::convertAndAssertAnd: NOT_AND added "
201
18429
                   << clauseNode << "\n";
202
18429
      normalizeAndRegister(clauseNode);
203
    }
204
  }
205
20824
}
206
207
53417
void ProofCnfStream::convertAndAssertOr(TNode node, bool negated)
208
{
209
106834
  Trace("cnf") << "ProofCnfStream::convertAndAssertOr(" << node
210
53417
               << ", negated = " << (negated ? "true" : "false") << ")\n";
211
53417
  Assert(node.getKind() == kind::OR);
212
53417
  if (!negated)
213
  {
214
    // If the node is a disjunction, we construct a clause and assert it
215
53318
    unsigned size = node.getNumChildren();
216
106636
    SatClause clause(size);
217
216396
    for (unsigned i = 0; i < size; ++i)
218
    {
219
163078
      clause[i] = toCNF(node[i], false);
220
    }
221
53318
    normalizeAndRegister(node);
222
53318
    d_cnfStream.assertClause(node, clause);
223
  }
224
  else
225
  {
226
    // If the node is a negated disjunction, we handle it as a conjunction of
227
    // the negated arguments
228
99
    NodeManager* nm = NodeManager::currentNM();
229
928
    for (unsigned i = 0, size = node.getNumChildren(); i < size; ++i)
230
    {
231
      // Create a proof step for each (not n_i)
232
1658
      Node iNode = nm->mkConst<Rational>(i);
233
4145
      d_proof.addStep(
234
3316
          node[i].notNode(), PfRule::NOT_OR_ELIM, {node.notNode()}, {iNode});
235
1658
      Trace("cnf") << "ProofCnfStream::convertAndAssertOr: NOT_OR_ELIM " << i
236
829
                   << " added norm  " << node[i].notNode() << "\n";
237
829
      convertAndAssert(node[i], true);
238
    }
239
  }
240
53417
}
241
242
12
void ProofCnfStream::convertAndAssertXor(TNode node, bool negated)
243
{
244
24
  Trace("cnf") << "ProofCnfStream::convertAndAssertXor(" << node
245
12
               << ", negated = " << (negated ? "true" : "false") << ")\n";
246
12
  if (!negated)
247
  {
248
    // p XOR q
249
12
    SatLiteral p = toCNF(node[0], false);
250
12
    SatLiteral q = toCNF(node[1], false);
251
    bool added;
252
12
    NodeManager* nm = NodeManager::currentNM();
253
    // Construct the clause (~p v ~q)
254
24
    SatClause clause1(2);
255
12
    clause1[0] = ~p;
256
12
    clause1[1] = ~q;
257
12
    added = d_cnfStream.assertClause(node, clause1);
258
12
    if (added)
259
    {
260
      Node clauseNode =
261
22
          nm->mkNode(kind::OR, node[0].notNode(), node[1].notNode());
262
11
      d_proof.addStep(clauseNode, PfRule::XOR_ELIM2, {node}, {});
263
22
      Trace("cnf") << "ProofCnfStream::convertAndAssertXor: XOR_ELIM2 added "
264
11
                   << clauseNode << "\n";
265
11
      normalizeAndRegister(clauseNode);
266
    }
267
    // Construct the clause (p v q)
268
24
    SatClause clause2(2);
269
12
    clause2[0] = p;
270
12
    clause2[1] = q;
271
12
    added = d_cnfStream.assertClause(node, clause2);
272
12
    if (added)
273
    {
274
22
      Node clauseNode = nm->mkNode(kind::OR, node[0], node[1]);
275
11
      d_proof.addStep(clauseNode, PfRule::XOR_ELIM1, {node}, {});
276
22
      Trace("cnf") << "ProofCnfStream::convertAndAssertXor: XOR_ELIM1 added "
277
11
                   << clauseNode << "\n";
278
11
      normalizeAndRegister(clauseNode);
279
    }
280
  }
281
  else
282
  {
283
    // ~(p XOR q) is the same as p <=> q
284
    SatLiteral p = toCNF(node[0], false);
285
    SatLiteral q = toCNF(node[1], false);
286
    bool added;
287
    NodeManager* nm = NodeManager::currentNM();
288
    // Construct the clause ~p v q
289
    SatClause clause1(2);
290
    clause1[0] = ~p;
291
    clause1[1] = q;
292
    added = d_cnfStream.assertClause(node.negate(), clause1);
293
    if (added)
294
    {
295
      Node clauseNode = nm->mkNode(kind::OR, node[0].notNode(), node[1]);
296
      d_proof.addStep(clauseNode, PfRule::NOT_XOR_ELIM2, {node.notNode()}, {});
297
      Trace("cnf")
298
          << "ProofCnfStream::convertAndAssertXor: NOT_XOR_ELIM2 added "
299
          << clauseNode << "\n";
300
      normalizeAndRegister(clauseNode);
301
    }
302
    // Construct the clause ~q v p
303
    SatClause clause2(2);
304
    clause2[0] = p;
305
    clause2[1] = ~q;
306
    added = d_cnfStream.assertClause(node.negate(), clause2);
307
    if (added)
308
    {
309
      Node clauseNode = nm->mkNode(kind::OR, node[0], node[1].notNode());
310
      d_proof.addStep(clauseNode, PfRule::NOT_XOR_ELIM1, {node.notNode()}, {});
311
      Trace("cnf")
312
          << "ProofCnfStream::convertAndAssertXor: NOT_XOR_ELIM1 added "
313
          << clauseNode << "\n";
314
      normalizeAndRegister(clauseNode);
315
    }
316
  }
317
12
}
318
319
2278
void ProofCnfStream::convertAndAssertIff(TNode node, bool negated)
320
{
321
4556
  Trace("cnf") << "ProofCnfStream::convertAndAssertIff(" << node
322
2278
               << ", negated = " << (negated ? "true" : "false") << ")\n";
323
2278
  if (!negated)
324
  {
325
    // p <=> q
326
2247
    Trace("cnf") << push;
327
2247
    SatLiteral p = toCNF(node[0], false);
328
2247
    SatLiteral q = toCNF(node[1], false);
329
2247
    Trace("cnf") << pop;
330
    bool added;
331
2247
    NodeManager* nm = NodeManager::currentNM();
332
    // Construct the clauses ~p v q
333
4494
    SatClause clause1(2);
334
2247
    clause1[0] = ~p;
335
2247
    clause1[1] = q;
336
2247
    added = d_cnfStream.assertClause(node, clause1);
337
2247
    if (added)
338
    {
339
3580
      Node clauseNode = nm->mkNode(kind::OR, node[0].notNode(), node[1]);
340
1790
      d_proof.addStep(clauseNode, PfRule::EQUIV_ELIM1, {node}, {});
341
3580
      Trace("cnf") << "ProofCnfStream::convertAndAssertIff: EQUIV_ELIM1 added "
342
1790
                   << clauseNode << "\n";
343
1790
      normalizeAndRegister(clauseNode);
344
    }
345
    // Construct the clauses ~q v p
346
4494
    SatClause clause2(2);
347
2247
    clause2[0] = p;
348
2247
    clause2[1] = ~q;
349
2247
    added = d_cnfStream.assertClause(node, clause2);
350
2247
    if (added)
351
    {
352
4382
      Node clauseNode = nm->mkNode(kind::OR, node[0], node[1].notNode());
353
2191
      d_proof.addStep(clauseNode, PfRule::EQUIV_ELIM2, {node}, {});
354
4382
      Trace("cnf") << "ProofCnfStream::convertAndAssertIff: EQUIV_ELIM2 added "
355
2191
                   << clauseNode << "\n";
356
2191
      normalizeAndRegister(clauseNode);
357
    }
358
  }
359
  else
360
  {
361
    // ~(p <=> q) is the same as p XOR q
362
31
    Trace("cnf") << push;
363
31
    SatLiteral p = toCNF(node[0], false);
364
31
    SatLiteral q = toCNF(node[1], false);
365
31
    Trace("cnf") << pop;
366
    bool added;
367
31
    NodeManager* nm = NodeManager::currentNM();
368
    // Construct the clauses ~p v ~q
369
62
    SatClause clause1(2);
370
31
    clause1[0] = ~p;
371
31
    clause1[1] = ~q;
372
31
    added = d_cnfStream.assertClause(node.negate(), clause1);
373
31
    if (added)
374
    {
375
      Node clauseNode =
376
60
          nm->mkNode(kind::OR, node[0].notNode(), node[1].notNode());
377
60
      d_proof.addStep(
378
30
          clauseNode, PfRule::NOT_EQUIV_ELIM2, {node.notNode()}, {});
379
60
      Trace("cnf")
380
30
          << "ProofCnfStream::convertAndAssertIff: NOT_EQUIV_ELIM2 added "
381
30
          << clauseNode << "\n";
382
30
      normalizeAndRegister(clauseNode);
383
    }
384
    // Construct the clauses q v p
385
62
    SatClause clause2(2);
386
31
    clause2[0] = p;
387
31
    clause2[1] = q;
388
31
    added = d_cnfStream.assertClause(node.negate(), clause2);
389
31
    if (added)
390
    {
391
58
      Node clauseNode = nm->mkNode(kind::OR, node[0], node[1]);
392
58
      d_proof.addStep(
393
29
          clauseNode, PfRule::NOT_EQUIV_ELIM1, {node.notNode()}, {});
394
58
      Trace("cnf")
395
29
          << "ProofCnfStream::convertAndAssertIff: NOT_EQUIV_ELIM1 added "
396
29
          << clauseNode << "\n";
397
29
      normalizeAndRegister(clauseNode);
398
    }
399
  }
400
2278
}
401
402
14061
void ProofCnfStream::convertAndAssertImplies(TNode node, bool negated)
403
{
404
28122
  Trace("cnf") << "ProofCnfStream::convertAndAssertImplies(" << node
405
14061
               << ", negated = " << (negated ? "true" : "false") << ")\n";
406
14061
  if (!negated)
407
  {
408
    // ~p v q
409
13918
    SatLiteral p = toCNF(node[0], false);
410
13918
    SatLiteral q = toCNF(node[1], false);
411
    // Construct the clause ~p || q
412
27836
    SatClause clause(2);
413
13918
    clause[0] = ~p;
414
13918
    clause[1] = q;
415
13918
    bool added = d_cnfStream.assertClause(node, clause);
416
13918
    if (added)
417
    {
418
      Node clauseNode = NodeManager::currentNM()->mkNode(
419
27038
          kind::OR, node[0].notNode(), node[1]);
420
13519
      d_proof.addStep(clauseNode, PfRule::IMPLIES_ELIM, {node}, {});
421
27038
      Trace("cnf")
422
13519
          << "ProofCnfStream::convertAndAssertImplies: IMPLIES_ELIM added "
423
13519
          << clauseNode << "\n";
424
13519
      normalizeAndRegister(clauseNode);
425
    }
426
  }
427
  else
428
  {
429
    // ~(p => q) is the same as p ^ ~q
430
    // process p
431
143
    convertAndAssert(node[0], false);
432
143
    d_proof.addStep(node[0], PfRule::NOT_IMPLIES_ELIM1, {node.notNode()}, {});
433
286
    Trace("cnf")
434
143
        << "ProofCnfStream::convertAndAssertImplies: NOT_IMPLIES_ELIM1 added "
435
143
        << node[0] << "\n";
436
    // process ~q
437
143
    convertAndAssert(node[1], true);
438
572
    d_proof.addStep(
439
429
        node[1].notNode(), PfRule::NOT_IMPLIES_ELIM2, {node.notNode()}, {});
440
286
    Trace("cnf")
441
143
        << "ProofCnfStream::convertAndAssertImplies: NOT_IMPLIES_ELIM2 added "
442
143
        << node[1].notNode() << "\n";
443
  }
444
14061
}
445
446
5096
void ProofCnfStream::convertAndAssertIte(TNode node, bool negated)
447
{
448
10192
  Trace("cnf") << "ProofCnfStream::convertAndAssertIte(" << node
449
5096
               << ", negated = " << (negated ? "true" : "false") << ")\n";
450
  // ITE(p, q, r)
451
5096
  SatLiteral p = toCNF(node[0], false);
452
5096
  SatLiteral q = toCNF(node[1], negated);
453
5096
  SatLiteral r = toCNF(node[2], negated);
454
  bool added;
455
5096
  NodeManager* nm = NodeManager::currentNM();
456
  // Construct the clauses:
457
  // (~p v q) and (p v r)
458
  //
459
  // Note that below q and r can be used directly because whether they are
460
  // negated has been push to the literal definitions above
461
10192
  Node nnode = negated ? node.negate() : static_cast<Node>(node);
462
  // (~p v q)
463
10192
  SatClause clause1(2);
464
5096
  clause1[0] = ~p;
465
5096
  clause1[1] = q;
466
5096
  added = d_cnfStream.assertClause(nnode, clause1);
467
5096
  if (added)
468
  {
469
    // redo the negation here to avoid silent double negation elimination
470
4758
    if (!negated)
471
    {
472
9382
      Node clauseNode = nm->mkNode(kind::OR, node[0].notNode(), node[1]);
473
4691
      d_proof.addStep(clauseNode, PfRule::ITE_ELIM1, {node}, {});
474
9382
      Trace("cnf") << "ProofCnfStream::convertAndAssertIte: ITE_ELIM1 added "
475
4691
                   << clauseNode << "\n";
476
4691
      normalizeAndRegister(clauseNode);
477
    }
478
    else
479
    {
480
      Node clauseNode =
481
134
          nm->mkNode(kind::OR, node[0].notNode(), node[1].notNode());
482
67
      d_proof.addStep(clauseNode, PfRule::NOT_ITE_ELIM1, {node.notNode()}, {});
483
134
      Trace("cnf")
484
67
          << "ProofCnfStream::convertAndAssertIte: NOT_ITE_ELIM1 added "
485
67
          << clauseNode << "\n";
486
67
      normalizeAndRegister(clauseNode);
487
    }
488
  }
489
  // (p v r)
490
10192
  SatClause clause2(2);
491
5096
  clause2[0] = p;
492
5096
  clause2[1] = r;
493
5096
  added = d_cnfStream.assertClause(nnode, clause2);
494
5096
  if (added)
495
  {
496
    // redo the negation here to avoid silent double negation elimination
497
5004
    if (!negated)
498
    {
499
9874
      Node clauseNode = nm->mkNode(kind::OR, node[0], node[2]);
500
4937
      d_proof.addStep(clauseNode, PfRule::ITE_ELIM2, {node}, {});
501
9874
      Trace("cnf") << "ProofCnfStream::convertAndAssertIte: ITE_ELIM2 added "
502
4937
                   << clauseNode << "\n";
503
4937
      normalizeAndRegister(clauseNode);
504
    }
505
    else
506
    {
507
134
      Node clauseNode = nm->mkNode(kind::OR, node[0], node[2].notNode());
508
67
      d_proof.addStep(clauseNode, PfRule::NOT_ITE_ELIM2, {node.notNode()}, {});
509
134
      Trace("cnf")
510
67
          << "ProofCnfStream::convertAndAssertIte: NOT_ITE_ELIM2 added "
511
67
          << clauseNode << "\n";
512
67
      normalizeAndRegister(clauseNode);
513
    }
514
  }
515
5096
}
516
517
21449
void ProofCnfStream::convertPropagation(theory::TrustNode trn)
518
{
519
42898
  Node proven = trn.getProven();
520
42898
  Trace("cnf") << "ProofCnfStream::convertPropagation: proven explanation"
521
21449
               << proven << "\n";
522
  // If we are not producing proofs in the theory engine there is no need to
523
  // keep track in d_proof of the clausification. We still need however to let
524
  // the SAT proof manager know that this clause is an assumption.
525
21449
  bool proofLogging = trn.getGenerator() != nullptr;
526
21449
  if (proofLogging)
527
  {
528
21449
    Assert(trn.getGenerator()->getProofFor(proven)->isClosed());
529
42898
    Trace("cnf-steps") << proven << " by explainPropagation "
530
21449
                       << trn.identifyGenerator() << std::endl;
531
21449
    d_proof.addLazyStep(proven,
532
                        trn.getGenerator(),
533
                        PfRule::ASSUME,
534
                        true,
535
                        "ProofCnfStream::convertPropagation");
536
  }
537
  // since the propagation is added directly to the SAT solver via theoryProxy,
538
  // do the transformation of the lemma E1 ^ ... ^ En => P into CNF here
539
21449
  NodeManager* nm = NodeManager::currentNM();
540
42898
  Node clauseImpliesElim;
541
21449
  if (proofLogging)
542
  {
543
21449
    clauseImpliesElim = nm->mkNode(kind::OR, proven[0].notNode(), proven[1]);
544
42898
    Trace("cnf") << "ProofCnfStream::convertPropagation: adding "
545
42898
                 << PfRule::IMPLIES_ELIM << " rule to conclude "
546
21449
                 << clauseImpliesElim << "\n";
547
21449
    d_proof.addStep(clauseImpliesElim, PfRule::IMPLIES_ELIM, {proven}, {});
548
  }
549
42898
  Node clauseExp;
550
  // need to eliminate AND
551
21449
  if (proven[0].getKind() == kind::AND)
552
  {
553
40366
    std::vector<Node> disjunctsAndNeg{proven[0]};
554
40366
    std::vector<Node> disjunctsRes;
555
183111
    for (unsigned i = 0, size = proven[0].getNumChildren(); i < size; ++i)
556
    {
557
162928
      disjunctsAndNeg.push_back(proven[0][i].notNode());
558
162928
      disjunctsRes.push_back(proven[0][i].notNode());
559
    }
560
20183
    disjunctsRes.push_back(proven[1]);
561
20183
    clauseExp = nm->mkNode(kind::OR, disjunctsRes);
562
20183
    if (proofLogging)
563
    {
564
      // add proof steps to convert into clause
565
40366
      Node clauseAndNeg = nm->mkNode(kind::OR, disjunctsAndNeg);
566
20183
      d_proof.addStep(clauseAndNeg, PfRule::CNF_AND_NEG, {}, {proven[0]});
567
100915
      d_proof.addStep(clauseExp,
568
                      PfRule::RESOLUTION,
569
                      {clauseAndNeg, clauseImpliesElim},
570
80732
                      {nm->mkConst(true), proven[0]});
571
    }
572
  }
573
  else
574
  {
575
1266
    clauseExp = nm->mkNode(kind::OR, proven[0].notNode(), proven[1]);
576
  }
577
21449
  normalizeAndRegister(clauseExp);
578
  // consume steps
579
21449
  if (proofLogging)
580
  {
581
21449
    const std::vector<std::pair<Node, ProofStep>>& steps = d_psb.getSteps();
582
237146
    for (const std::pair<Node, ProofStep>& step : steps)
583
    {
584
215697
      d_proof.addStep(step.first, step.second);
585
    }
586
21449
    d_psb.clear();
587
  }
588
21449
}
589
590
18920
void ProofCnfStream::ensureLiteral(TNode n)
591
{
592
18920
  Trace("cnf") << "ProofCnfStream::ensureLiteral(" << n << ")\n";
593
18920
  if (d_cnfStream.hasLiteral(n))
594
  {
595
18147
    d_cnfStream.ensureMappingForLiteral(n);
596
18147
    return;
597
  }
598
  // remove top level negation. We don't need to track this because it's a
599
  // literal.
600
773
  n = n.getKind() == kind::NOT ? n[0] : n;
601
773
  if (theory::Theory::theoryOf(n) == theory::THEORY_BOOL && !n.isVar())
602
  {
603
    // These are not removable
604
2
    d_cnfStream.d_removable = false;
605
2
    SatLiteral lit = toCNF(n, false);
606
    // Store backward-mappings
607
    // These may already exist
608
2
    d_cnfStream.d_literalToNodeMap.insert_safe(lit, n);
609
2
    d_cnfStream.d_literalToNodeMap.insert_safe(~lit, n.notNode());
610
  }
611
  else
612
  {
613
771
    d_cnfStream.convertAtom(n);
614
  }
615
}
616
617
675287
SatLiteral ProofCnfStream::toCNF(TNode node, bool negated)
618
{
619
1350574
  Trace("cnf") << "toCNF(" << node
620
675287
               << ", negated = " << (negated ? "true" : "false") << ")\n";
621
675287
  SatLiteral lit;
622
  // If the node has already has a literal, return it (maybe negated)
623
675287
  if (d_cnfStream.hasLiteral(node))
624
  {
625
518993
    Trace("cnf") << "toCNF(): already translated\n";
626
518993
    lit = d_cnfStream.getLiteral(node);
627
    // Return the (maybe negated) literal
628
518993
    return !negated ? lit : ~lit;
629
  }
630
631
  // Handle each Boolean operator case
632
156294
  switch (node.getKind())
633
  {
634
22014
    case kind::AND: lit = handleAnd(node); break;
635
14168
    case kind::OR: lit = handleOr(node); break;
636
173
    case kind::XOR: lit = handleXor(node); break;
637
1943
    case kind::IMPLIES: lit = handleImplies(node); break;
638
3862
    case kind::ITE: lit = handleIte(node); break;
639
20566
    case kind::NOT: lit = ~toCNF(node[0]); break;
640
46973
    case kind::EQUAL:
641
139125
      lit = node[0].getType().isBoolean() ? handleIff(node)
642
92152
                                          : d_cnfStream.convertAtom(node);
643
46973
      break;
644
46595
    default:
645
    {
646
46595
      lit = d_cnfStream.convertAtom(node);
647
    }
648
46595
    break;
649
  }
650
  // Return the (maybe negated) literal
651
156294
  return !negated ? lit : ~lit;
652
}
653
654
22014
SatLiteral ProofCnfStream::handleAnd(TNode node)
655
{
656
22014
  Assert(!d_cnfStream.hasLiteral(node)) << "Atom already mapped!";
657
22014
  Assert(node.getKind() == kind::AND) << "Expecting an AND expression!";
658
22014
  Assert(node.getNumChildren() > 1) << "Expecting more than 1 child!";
659
22014
  Assert(!d_cnfStream.d_removable)
660
      << "Removable clauses cannot contain Boolean structure";
661
22014
  Trace("cnf") << "ProofCnfStream::handleAnd(" << node << ")\n";
662
  // Number of children
663
22014
  unsigned size = node.getNumChildren();
664
  // Transform all the children first (remembering the negation)
665
44028
  SatClause clause(size + 1);
666
180706
  for (unsigned i = 0; i < size; ++i)
667
  {
668
158692
    Trace("cnf") << push;
669
158692
    clause[i] = ~toCNF(node[i]);
670
158692
    Trace("cnf") << pop;
671
  }
672
  // Create literal for the node
673
22014
  SatLiteral lit = d_cnfStream.newLiteral(node);
674
  bool added;
675
22014
  NodeManager* nm = NodeManager::currentNM();
676
  // lit -> (a_1 & a_2 & a_3 & ... & a_n)
677
  // ~lit | (a_1 & a_2 & a_3 & ... & a_n)
678
  // (~lit | a_1) & (~lit | a_2) & ... & (~lit | a_n)
679
180706
  for (unsigned i = 0; i < size; ++i)
680
  {
681
158692
    Trace("cnf") << push;
682
158692
    added = d_cnfStream.assertClause(node.negate(), ~lit, ~clause[i]);
683
158692
    Trace("cnf") << pop;
684
158692
    if (added)
685
    {
686
304792
      Node clauseNode = nm->mkNode(kind::OR, node.notNode(), node[i]);
687
304792
      Node iNode = nm->mkConst<Rational>(i);
688
152396
      d_proof.addStep(clauseNode, PfRule::CNF_AND_POS, {}, {node, iNode});
689
304792
      Trace("cnf") << "ProofCnfStream::handleAnd: CNF_AND_POS " << i
690
152396
                   << " added " << clauseNode << "\n";
691
152396
      normalizeAndRegister(clauseNode);
692
    }
693
  }
694
  // lit <- (a_1 & a_2 & a_3 & ... a_n)
695
  // lit | ~(a_1 & a_2 & a_3 & ... & a_n)
696
  // lit | ~a_1 | ~a_2 | ~a_3 | ... | ~a_n
697
22014
  clause[size] = lit;
698
  // This needs to go last, as the clause might get modified by the SAT solver
699
22014
  Trace("cnf") << push;
700
22014
  added = d_cnfStream.assertClause(node, clause);
701
22014
  Trace("cnf") << pop;
702
22014
  if (added)
703
  {
704
42466
    std::vector<Node> disjuncts{node};
705
177160
    for (unsigned i = 0; i < size; ++i)
706
    {
707
155927
      disjuncts.push_back(node[i].notNode());
708
    }
709
42466
    Node clauseNode = nm->mkNode(kind::OR, disjuncts);
710
21233
    d_proof.addStep(clauseNode, PfRule::CNF_AND_NEG, {}, {node});
711
42466
    Trace("cnf") << "ProofCnfStream::handleAnd: CNF_AND_NEG added "
712
21233
                 << clauseNode << "\n";
713
21233
    normalizeAndRegister(clauseNode);
714
  }
715
44028
  return lit;
716
}
717
718
14168
SatLiteral ProofCnfStream::handleOr(TNode node)
719
{
720
14168
  Assert(!d_cnfStream.hasLiteral(node)) << "Atom already mapped!";
721
14168
  Assert(node.getKind() == kind::OR) << "Expecting an OR expression!";
722
14168
  Assert(node.getNumChildren() > 1) << "Expecting more then 1 child!";
723
14168
  Assert(!d_cnfStream.d_removable)
724
      << "Removable clauses can not contain Boolean structure";
725
14168
  Trace("cnf") << "ProofCnfStream::handleOr(" << node << ")\n";
726
  // Number of children
727
14168
  unsigned size = node.getNumChildren();
728
  // Transform all the children first
729
28336
  SatClause clause(size + 1);
730
54758
  for (unsigned i = 0; i < size; ++i)
731
  {
732
40590
    clause[i] = toCNF(node[i]);
733
  }
734
  // Create literal for the node
735
14168
  SatLiteral lit = d_cnfStream.newLiteral(node);
736
  bool added;
737
14168
  NodeManager* nm = NodeManager::currentNM();
738
  // lit <- (a_1 | a_2 | a_3 | ... | a_n)
739
  // lit | ~(a_1 | a_2 | a_3 | ... | a_n)
740
  // (lit | ~a_1) & (lit | ~a_2) & (lit & ~a_3) & ... & (lit & ~a_n)
741
54758
  for (unsigned i = 0; i < size; ++i)
742
  {
743
40590
    added = d_cnfStream.assertClause(node, lit, ~clause[i]);
744
40590
    if (added)
745
    {
746
71602
      Node clauseNode = nm->mkNode(kind::OR, node, node[i].notNode());
747
71602
      Node iNode = nm->mkConst<Rational>(i);
748
35801
      d_proof.addStep(clauseNode, PfRule::CNF_OR_NEG, {}, {node, iNode});
749
71602
      Trace("cnf") << "ProofCnfStream::handleOr: CNF_OR_NEG " << i << " added "
750
35801
                   << clauseNode << "\n";
751
35801
      normalizeAndRegister(clauseNode);
752
    }
753
  }
754
  // lit -> (a_1 | a_2 | a_3 | ... | a_n)
755
  // ~lit | a_1 | a_2 | a_3 | ... | a_n
756
14168
  clause[size] = ~lit;
757
  // This needs to go last, as the clause might get modified by the SAT solver
758
14168
  added = d_cnfStream.assertClause(node.negate(), clause);
759
14168
  if (added)
760
  {
761
26772
    std::vector<Node> disjuncts{node.notNode()};
762
51313
    for (unsigned i = 0; i < size; ++i)
763
    {
764
37927
      disjuncts.push_back(node[i]);
765
    }
766
26772
    Node clauseNode = nm->mkNode(kind::OR, disjuncts);
767
13386
    d_proof.addStep(clauseNode, PfRule::CNF_OR_POS, {}, {node});
768
26772
    Trace("cnf") << "ProofCnfStream::handleOr: CNF_OR_POS added " << clauseNode
769
13386
                 << "\n";
770
13386
    normalizeAndRegister(clauseNode);
771
  }
772
28336
  return lit;
773
}
774
775
173
SatLiteral ProofCnfStream::handleXor(TNode node)
776
{
777
173
  Assert(!d_cnfStream.hasLiteral(node)) << "Atom already mapped!";
778
173
  Assert(node.getKind() == kind::XOR) << "Expecting an XOR expression!";
779
173
  Assert(node.getNumChildren() == 2) << "Expecting exactly 2 children!";
780
173
  Assert(!d_cnfStream.d_removable)
781
      << "Removable clauses can not contain Boolean structure";
782
173
  Trace("cnf") << "ProofCnfStream::handleXor(" << node << ")\n";
783
173
  SatLiteral a = toCNF(node[0]);
784
173
  SatLiteral b = toCNF(node[1]);
785
173
  SatLiteral lit = d_cnfStream.newLiteral(node);
786
  bool added;
787
173
  added = d_cnfStream.assertClause(node.negate(), a, b, ~lit);
788
173
  if (added)
789
  {
790
    Node clauseNode = NodeManager::currentNM()->mkNode(
791
312
        kind::OR, node.notNode(), node[0], node[1]);
792
156
    d_proof.addStep(clauseNode, PfRule::CNF_XOR_POS1, {}, {node});
793
312
    Trace("cnf") << "ProofCnfStream::handleXor: CNF_XOR_POS1 added "
794
156
                 << clauseNode << "\n";
795
156
    normalizeAndRegister(clauseNode);
796
  }
797
173
  added = d_cnfStream.assertClause(node.negate(), ~a, ~b, ~lit);
798
173
  if (added)
799
  {
800
    Node clauseNode = NodeManager::currentNM()->mkNode(
801
318
        kind::OR, node.notNode(), node[0].notNode(), node[1].notNode());
802
159
    d_proof.addStep(clauseNode, PfRule::CNF_XOR_POS2, {}, {node});
803
318
    Trace("cnf") << "ProofCnfStream::handleXor: CNF_XOR_POS2 added "
804
159
                 << clauseNode << "\n";
805
159
    normalizeAndRegister(clauseNode);
806
  }
807
173
  added = d_cnfStream.assertClause(node, a, ~b, lit);
808
173
  if (added)
809
  {
810
    Node clauseNode = NodeManager::currentNM()->mkNode(
811
316
        kind::OR, node, node[0], node[1].notNode());
812
158
    d_proof.addStep(clauseNode, PfRule::CNF_XOR_NEG2, {}, {node});
813
316
    Trace("cnf") << "ProofCnfStream::handleXor: CNF_XOR_NEG2 added "
814
158
                 << clauseNode << "\n";
815
158
    normalizeAndRegister(clauseNode);
816
  }
817
173
  added = d_cnfStream.assertClause(node, ~a, b, lit);
818
173
  if (added)
819
  {
820
    Node clauseNode = NodeManager::currentNM()->mkNode(
821
314
        kind::OR, node, node[0].notNode(), node[1]);
822
157
    d_proof.addStep(clauseNode, PfRule::CNF_XOR_NEG1, {}, {node});
823
314
    Trace("cnf") << "ProofCnfStream::handleXor: CNF_XOR_NEG1 added "
824
157
                 << clauseNode << "\n";
825
157
    normalizeAndRegister(clauseNode);
826
  }
827
173
  return lit;
828
}
829
830
1794
SatLiteral ProofCnfStream::handleIff(TNode node)
831
{
832
1794
  Assert(!d_cnfStream.hasLiteral(node)) << "Atom already mapped!";
833
1794
  Assert(node.getKind() == kind::EQUAL) << "Expecting an EQUAL expression!";
834
1794
  Assert(node.getNumChildren() == 2) << "Expecting exactly 2 children!";
835
1794
  Trace("cnf") << "handleIff(" << node << ")\n";
836
  // Convert the children to CNF
837
1794
  SatLiteral a = toCNF(node[0]);
838
1794
  SatLiteral b = toCNF(node[1]);
839
  // Create literal for the node
840
1794
  SatLiteral lit = d_cnfStream.newLiteral(node);
841
  bool added;
842
1794
  NodeManager* nm = NodeManager::currentNM();
843
  // lit -> ((a-> b) & (b->a))
844
  // ~lit | ((~a | b) & (~b | a))
845
  // (~a | b | ~lit) & (~b | a | ~lit)
846
1794
  added = d_cnfStream.assertClause(node.negate(), ~a, b, ~lit);
847
1794
  if (added)
848
  {
849
    Node clauseNode =
850
3140
        nm->mkNode(kind::OR, node.notNode(), node[0].notNode(), node[1]);
851
1570
    d_proof.addStep(clauseNode, PfRule::CNF_EQUIV_POS1, {}, {node});
852
3140
    Trace("cnf") << "ProofCnfStream::handleIff: CNF_EQUIV_POS1 added "
853
1570
                 << clauseNode << "\n";
854
1570
    normalizeAndRegister(clauseNode);
855
  }
856
1794
  added = d_cnfStream.assertClause(node.negate(), a, ~b, ~lit);
857
1794
  if (added)
858
  {
859
    Node clauseNode =
860
2314
        nm->mkNode(kind::OR, node.notNode(), node[0], node[1].notNode());
861
1157
    d_proof.addStep(clauseNode, PfRule::CNF_EQUIV_POS2, {}, {node});
862
2314
    Trace("cnf") << "ProofCnfStream::handleIff: CNF_EQUIV_POS2 added "
863
1157
                 << clauseNode << "\n";
864
1157
    normalizeAndRegister(clauseNode);
865
  }
866
  // (a<->b) -> lit
867
  // ~((a & b) | (~a & ~b)) | lit
868
  // (~(a & b)) & (~(~a & ~b)) | lit
869
  // ((~a | ~b) & (a | b)) | lit
870
  // (~a | ~b | lit) & (a | b | lit)
871
1794
  added = d_cnfStream.assertClause(node, ~a, ~b, lit);
872
1794
  if (added)
873
  {
874
    Node clauseNode =
875
3394
        nm->mkNode(kind::OR, node, node[0].notNode(), node[1].notNode());
876
1697
    d_proof.addStep(clauseNode, PfRule::CNF_EQUIV_NEG2, {}, {node});
877
3394
    Trace("cnf") << "ProofCnfStream::handleIff: CNF_EQUIV_NEG2 added "
878
1697
                 << clauseNode << "\n";
879
1697
    normalizeAndRegister(clauseNode);
880
  }
881
1794
  added = d_cnfStream.assertClause(node, a, b, lit);
882
1794
  if (added)
883
  {
884
2062
    Node clauseNode = nm->mkNode(kind::OR, node, node[0], node[1]);
885
1031
    d_proof.addStep(clauseNode, PfRule::CNF_EQUIV_NEG1, {}, {node});
886
2062
    Trace("cnf") << "ProofCnfStream::handleIff: CNF_EQUIV_NEG1 added "
887
1031
                 << clauseNode << "\n";
888
1031
    normalizeAndRegister(clauseNode);
889
  }
890
1794
  return lit;
891
}
892
893
1943
SatLiteral ProofCnfStream::handleImplies(TNode node)
894
{
895
1943
  Assert(!d_cnfStream.hasLiteral(node)) << "Atom already mapped!";
896
1943
  Assert(node.getKind() == kind::IMPLIES) << "Expecting an IMPLIES expression!";
897
1943
  Assert(node.getNumChildren() == 2) << "Expecting exactly 2 children!";
898
1943
  Assert(!d_cnfStream.d_removable)
899
      << "Removable clauses can not contain Boolean structure";
900
1943
  Trace("cnf") << "ProofCnfStream::handleImplies(" << node << ")\n";
901
  // Convert the children to cnf
902
1943
  SatLiteral a = toCNF(node[0]);
903
1943
  SatLiteral b = toCNF(node[1]);
904
1943
  SatLiteral lit = d_cnfStream.newLiteral(node);
905
  bool added;
906
1943
  NodeManager* nm = NodeManager::currentNM();
907
  // lit -> (a->b)
908
  // ~lit | ~ a | b
909
1943
  added = d_cnfStream.assertClause(node.negate(), ~lit, ~a, b);
910
1943
  if (added)
911
  {
912
    Node clauseNode =
913
3866
        nm->mkNode(kind::OR, node.notNode(), node[0].notNode(), node[1]);
914
1933
    d_proof.addStep(clauseNode, PfRule::CNF_IMPLIES_POS, {}, {node});
915
3866
    Trace("cnf") << "ProofCnfStream::handleImplies: CNF_IMPLIES_POS added "
916
1933
                 << clauseNode << "\n";
917
1933
    normalizeAndRegister(clauseNode);
918
  }
919
  // (a->b) -> lit
920
  // ~(~a | b) | lit
921
  // (a | l) & (~b | l)
922
1943
  added = d_cnfStream.assertClause(node, a, lit);
923
1943
  if (added)
924
  {
925
3874
    Node clauseNode = nm->mkNode(kind::OR, node, node[0]);
926
1937
    d_proof.addStep(clauseNode, PfRule::CNF_IMPLIES_NEG1, {}, {node});
927
3874
    Trace("cnf") << "ProofCnfStream::handleImplies: CNF_IMPLIES_NEG1 added "
928
1937
                 << clauseNode << "\n";
929
1937
    normalizeAndRegister(clauseNode);
930
  }
931
1943
  added = d_cnfStream.assertClause(node, ~b, lit);
932
1943
  if (added)
933
  {
934
3882
    Node clauseNode = nm->mkNode(kind::OR, node, node[1].notNode());
935
1941
    d_proof.addStep(clauseNode, PfRule::CNF_IMPLIES_NEG2, {}, {node});
936
3882
    Trace("cnf") << "ProofCnfStream::handleImplies: CNF_IMPLIES_NEG2 added "
937
1941
                 << clauseNode << "\n";
938
1941
    normalizeAndRegister(clauseNode);
939
  }
940
1943
  return lit;
941
}
942
943
3862
SatLiteral ProofCnfStream::handleIte(TNode node)
944
{
945
3862
  Assert(!d_cnfStream.hasLiteral(node)) << "Atom already mapped!";
946
3862
  Assert(node.getKind() == kind::ITE);
947
3862
  Assert(node.getNumChildren() == 3);
948
3862
  Assert(!d_cnfStream.d_removable)
949
      << "Removable clauses can not contain Boolean structure";
950
7724
  Trace("cnf") << "handleIte(" << node[0] << " " << node[1] << " " << node[2]
951
3862
               << ")\n";
952
3862
  SatLiteral condLit = toCNF(node[0]);
953
3862
  SatLiteral thenLit = toCNF(node[1]);
954
3862
  SatLiteral elseLit = toCNF(node[2]);
955
  // create literal to the node
956
3862
  SatLiteral lit = d_cnfStream.newLiteral(node);
957
  bool added;
958
3862
  NodeManager* nm = NodeManager::currentNM();
959
  // If ITE is true then one of the branches is true and the condition
960
  // implies which one
961
  // lit -> (ite b t e)
962
  // lit -> (t | e) & (b -> t) & (!b -> e)
963
  // lit -> (t | e) & (!b | t) & (b | e)
964
  // (!lit | t | e) & (!lit | !b | t) & (!lit | b | e)
965
3862
  added = d_cnfStream.assertClause(node.negate(), ~lit, thenLit, elseLit);
966
3862
  if (added)
967
  {
968
7670
    Node clauseNode = nm->mkNode(kind::OR, node.notNode(), node[1], node[2]);
969
3835
    d_proof.addStep(clauseNode, PfRule::CNF_ITE_POS3, {}, {node});
970
7670
    Trace("cnf") << "ProofCnfStream::handleIte: CNF_ITE_POS3 added "
971
3835
                 << clauseNode << "\n";
972
3835
    normalizeAndRegister(clauseNode);
973
  }
974
3862
  added = d_cnfStream.assertClause(node.negate(), ~lit, ~condLit, thenLit);
975
3862
  if (added)
976
  {
977
    Node clauseNode =
978
7588
        nm->mkNode(kind::OR, node.notNode(), node[0].notNode(), node[1]);
979
3794
    d_proof.addStep(clauseNode, PfRule::CNF_ITE_POS1, {}, {node});
980
7588
    Trace("cnf") << "ProofCnfStream::handleIte: CNF_ITE_POS1 added "
981
3794
                 << clauseNode << "\n";
982
3794
    normalizeAndRegister(clauseNode);
983
  }
984
3862
  added = d_cnfStream.assertClause(node.negate(), ~lit, condLit, elseLit);
985
3862
  if (added)
986
  {
987
7028
    Node clauseNode = nm->mkNode(kind::OR, node.notNode(), node[0], node[2]);
988
3514
    d_proof.addStep(clauseNode, PfRule::CNF_ITE_POS2, {}, {node});
989
7028
    Trace("cnf") << "ProofCnfStream::handleIte: CNF_ITE_POS2 added "
990
3514
                 << clauseNode << "\n";
991
3514
    normalizeAndRegister(clauseNode);
992
  }
993
  // If ITE is false then one of the branches is false and the condition
994
  // implies which one
995
  // !lit -> !(ite b t e)
996
  // !lit -> (!t | !e) & (b -> !t) & (!b -> !e)
997
  // !lit -> (!t | !e) & (!b | !t) & (b | !e)
998
  // (lit | !t | !e) & (lit | !b | !t) & (lit | b | !e)
999
3862
  added = d_cnfStream.assertClause(node, lit, ~thenLit, ~elseLit);
1000
3862
  if (added)
1001
  {
1002
    Node clauseNode =
1003
7676
        nm->mkNode(kind::OR, node, node[1].notNode(), node[2].notNode());
1004
3838
    d_proof.addStep(clauseNode, PfRule::CNF_ITE_NEG3, {}, {node});
1005
7676
    Trace("cnf") << "ProofCnfStream::handleIte: CNF_ITE_NEG3 added "
1006
3838
                 << clauseNode << "\n";
1007
3838
    normalizeAndRegister(clauseNode);
1008
  }
1009
3862
  added = d_cnfStream.assertClause(node, lit, ~condLit, ~thenLit);
1010
3862
  if (added)
1011
  {
1012
    Node clauseNode =
1013
7580
        nm->mkNode(kind::OR, node, node[0].notNode(), node[1].notNode());
1014
3790
    d_proof.addStep(clauseNode, PfRule::CNF_ITE_NEG1, {}, {node});
1015
7580
    Trace("cnf") << "ProofCnfStream::handleIte: CNF_ITE_NEG1 added "
1016
3790
                 << clauseNode << "\n";
1017
3790
    normalizeAndRegister(clauseNode);
1018
  }
1019
3862
  added = d_cnfStream.assertClause(node, lit, condLit, ~elseLit);
1020
3862
  if (added)
1021
  {
1022
7024
    Node clauseNode = nm->mkNode(kind::OR, node, node[0], node[2].notNode());
1023
3512
    d_proof.addStep(clauseNode, PfRule::CNF_ITE_NEG2, {}, {node});
1024
7024
    Trace("cnf") << "ProofCnfStream::handleIte: CNF_ITE_NEG2 added "
1025
3512
                 << clauseNode << "\n";
1026
3512
    normalizeAndRegister(clauseNode);
1027
  }
1028
3862
  return lit;
1029
}
1030
1031
}  // namespace prop
1032
28191
}  // namespace cvc5