GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/expr/proof.cpp Lines: 170 212 80.2 %
Date: 2021-05-22 Branches: 391 922 42.4 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Aina Niemetz
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 proof.
14
 */
15
16
#include "expr/proof.h"
17
18
#include "expr/proof_checker.h"
19
#include "expr/proof_node.h"
20
#include "expr/proof_node_manager.h"
21
22
using namespace cvc5::kind;
23
24
namespace cvc5 {
25
26
1765516
CDProof::CDProof(ProofNodeManager* pnm,
27
                 context::Context* c,
28
                 std::string name,
29
1765516
                 bool autoSymm)
30
    : d_manager(pnm),
31
      d_context(),
32
      d_nodes(c ? c : &d_context),
33
      d_name(name),
34
1765516
      d_autoSymm(autoSymm)
35
{
36
1765516
}
37
38
1766708
CDProof::~CDProof() {}
39
40
2839759
std::shared_ptr<ProofNode> CDProof::getProofFor(Node fact)
41
{
42
5679518
  std::shared_ptr<ProofNode> pf = getProofSymm(fact);
43
2839759
  if (pf != nullptr)
44
  {
45
1976941
    return pf;
46
  }
47
  // add as assumption
48
1725636
  std::vector<Node> pargs = {fact};
49
1725636
  std::vector<std::shared_ptr<ProofNode>> passume;
50
  std::shared_ptr<ProofNode> pfa =
51
1725636
      d_manager->mkNode(PfRule::ASSUME, passume, pargs, fact);
52
862818
  d_nodes.insert(fact, pfa);
53
862818
  return pfa;
54
}
55
56
45457846
std::shared_ptr<ProofNode> CDProof::getProof(Node fact) const
57
{
58
45457846
  NodeProofNodeMap::iterator it = d_nodes.find(fact);
59
45457846
  if (it != d_nodes.end())
60
  {
61
28100717
    return (*it).second;
62
  }
63
17357129
  return nullptr;
64
}
65
66
19383226
std::shared_ptr<ProofNode> CDProof::getProofSymm(Node fact)
67
{
68
19383226
  Trace("cdproof") << "CDProof::getProofSymm: " << fact << std::endl;
69
38766452
  std::shared_ptr<ProofNode> pf = getProof(fact);
70
19383226
  if (pf != nullptr && !isAssumption(pf.get()))
71
  {
72
7441470
    Trace("cdproof") << "...existing non-assume " << pf->getRule() << std::endl;
73
7441470
    return pf;
74
  }
75
11941756
  else if (!d_autoSymm)
76
  {
77
    Trace("cdproof") << "...not auto considering symmetry" << std::endl;
78
    return pf;
79
  }
80
23883512
  Node symFact = getSymmFact(fact);
81
11941756
  if (symFact.isNull())
82
  {
83
7867040
    Trace("cdproof") << "...no possible symm" << std::endl;
84
    // no symmetry possible, return original proof (possibly assumption)
85
7867040
    return pf;
86
  }
87
  // See if a proof exists for the opposite direction, if so, add the step.
88
  // Notice that SYMM is also disallowed.
89
8149432
  std::shared_ptr<ProofNode> pfs = getProof(symFact);
90
4074716
  if (pfs != nullptr)
91
  {
92
    // The symmetric fact exists, and the current one either does not, or is
93
    // an assumption. We make a new proof that applies SYMM to pfs.
94
764147
    std::vector<std::shared_ptr<ProofNode>> pschild;
95
590377
    pschild.push_back(pfs);
96
764147
    std::vector<Node> args;
97
590377
    if (pf == nullptr)
98
    {
99
416607
      Trace("cdproof") << "...fresh make symm" << std::endl;
100
      std::shared_ptr<ProofNode> psym =
101
833214
          d_manager->mkNode(PfRule::SYMM, pschild, args, fact);
102
416607
      Assert(psym != nullptr);
103
416607
      d_nodes.insert(fact, psym);
104
416607
      return psym;
105
    }
106
173770
    else if (!isAssumption(pfs.get()))
107
    {
108
      // if its not an assumption, make the connection
109
10
      Trace("cdproof") << "...update symm" << std::endl;
110
      // update pf
111
10
      bool sret = d_manager->updateNode(pf.get(), PfRule::SYMM, pschild, args);
112
10
      AlwaysAssert(sret);
113
    }
114
  }
115
  else
116
  {
117
6968678
    Trace("cdproof") << "...no symm, return "
118
3484339
                     << (pf == nullptr ? "null" : "non-null") << std::endl;
119
  }
120
  // return original proof (possibly assumption)
121
3658109
  return pf;
122
}
123
124
4985013
bool CDProof::addStep(Node expected,
125
                      PfRule id,
126
                      const std::vector<Node>& children,
127
                      const std::vector<Node>& args,
128
                      bool ensureChildren,
129
                      CDPOverwrite opolicy)
130
{
131
9970026
  Trace("cdproof") << "CDProof::addStep: " << identify() << " : " << id << " "
132
4985013
                   << expected << ", ensureChildren = " << ensureChildren
133
4985013
                   << ", overwrite policy = " << opolicy << std::endl;
134
9970026
  Trace("cdproof-debug") << "CDProof::addStep: " << identify()
135
4985013
                         << " : children: " << children << "\n";
136
9970026
  Trace("cdproof-debug") << "CDProof::addStep: " << identify()
137
4985013
                         << " : args: " << args << "\n";
138
  // We must always provide expected to this method
139
4985013
  Assert(!expected.isNull());
140
141
9970026
  std::shared_ptr<ProofNode> pprev = getProofSymm(expected);
142
4985013
  if (pprev != nullptr)
143
  {
144
1514509
    if (!shouldOverwrite(pprev.get(), id, opolicy))
145
    {
146
      // we should not overwrite the current step
147
1460625
      Trace("cdproof") << "...success, no overwrite" << std::endl;
148
1460625
      return true;
149
    }
150
107768
    Trace("cdproof") << "existing proof " << pprev->getRule()
151
53884
                     << ", overwrite..." << std::endl;
152
    // we will overwrite the existing proof node by updating its contents below
153
  }
154
  // collect the child proofs, for each premise
155
7048776
  std::vector<std::shared_ptr<ProofNode>> pchildren;
156
12052158
  for (const Node& c : children)
157
  {
158
8527770
    Trace("cdproof") << "- get child " << c << std::endl;
159
17055540
    std::shared_ptr<ProofNode> pc = getProofSymm(c);
160
8527770
    if (pc == nullptr)
161
    {
162
3559861
      if (ensureChildren)
163
      {
164
        // failed to get a proof for a child, fail
165
        Trace("cdproof") << "...fail, no child" << std::endl;
166
        return false;
167
      }
168
3559861
      Trace("cdproof") << "--- add assume" << std::endl;
169
      // otherwise, we initialize it as an assumption
170
7119722
      std::vector<Node> pcargs = {c};
171
7119722
      std::vector<std::shared_ptr<ProofNode>> pcassume;
172
3559861
      pc = d_manager->mkNode(PfRule::ASSUME, pcassume, pcargs, c);
173
      // assumptions never fail to check
174
3559861
      Assert(pc != nullptr);
175
3559861
      d_nodes.insert(c, pc);
176
    }
177
8527770
    pchildren.push_back(pc);
178
  }
179
180
  // the user may have provided SYMM of an assumption
181
3524388
  if (id == PfRule::SYMM)
182
  {
183
36
    Assert(pchildren.size() == 1);
184
36
    if (isAssumption(pchildren[0].get()))
185
    {
186
      // the step we are constructing is a (symmetric fact of an) assumption, so
187
      // there is no use adding it to the proof.
188
36
      return true;
189
    }
190
  }
191
192
3524352
  bool ret = true;
193
  // create or update it
194
7048704
  std::shared_ptr<ProofNode> pthis;
195
3524352
  if (pprev == nullptr)
196
  {
197
3470468
    Trace("cdproof") << "  new node " << expected << "..." << std::endl;
198
3470468
    pthis = d_manager->mkNode(id, pchildren, args, expected);
199
3470468
    if (pthis == nullptr)
200
    {
201
      // failed to construct the node, perhaps due to a proof checking failure
202
      Trace("cdproof") << "...fail, proof checking" << std::endl;
203
      return false;
204
    }
205
3470468
    d_nodes.insert(expected, pthis);
206
  }
207
  else
208
  {
209
53884
    Trace("cdproof") << "  update node " << expected << "..." << std::endl;
210
    // update its value
211
53884
    pthis = pprev;
212
    // We return the value of updateNode here. This means this method may return
213
    // false if this call failed, regardless of whether we already have a proof
214
    // step for expected.
215
53884
    ret = d_manager->updateNode(pthis.get(), id, pchildren, args);
216
  }
217
3524352
  if (ret)
218
  {
219
    // the result of the proof node should be expected
220
3524352
    Assert(pthis->getResult() == expected);
221
222
    // notify new proof
223
3524352
    notifyNewProof(expected);
224
  }
225
226
3524352
  Trace("cdproof") << "...return " << ret << std::endl;
227
3524352
  return ret;
228
}
229
230
6553259
void CDProof::notifyNewProof(Node expected)
231
{
232
6553259
  if (!d_autoSymm)
233
  {
234
    return;
235
  }
236
  // ensure SYMM proof is also linked to an existing proof, if it is an
237
  // assumption.
238
13106518
  Node symExpected = getSymmFact(expected);
239
6553259
  if (!symExpected.isNull())
240
  {
241
2241621
    Trace("cdproof") << "  check connect symmetry " << symExpected << std::endl;
242
    // if it exists, we may need to update it
243
4483242
    std::shared_ptr<ProofNode> pfs = getProof(symExpected);
244
2241621
    if (pfs != nullptr)
245
    {
246
1777
      Trace("cdproof") << "  connect via getProofSymm method..." << std::endl;
247
      // call the get function with symmetry, which will do the update
248
1777
      std::shared_ptr<ProofNode> pfss = getProofSymm(symExpected);
249
    }
250
    else
251
    {
252
2239844
      Trace("cdproof") << "  no connect" << std::endl;
253
    }
254
  }
255
}
256
257
1977763
bool CDProof::addStep(Node expected,
258
                      const ProofStep& step,
259
                      bool ensureChildren,
260
                      CDPOverwrite opolicy)
261
{
262
5933289
  return addStep(expected,
263
1977763
                 step.d_rule,
264
                 step.d_children,
265
                 step.d_args,
266
                 ensureChildren,
267
3955526
                 opolicy);
268
}
269
270
17131
bool CDProof::addSteps(const ProofStepBuffer& psb,
271
                       bool ensureChildren,
272
                       CDPOverwrite opolicy)
273
{
274
17131
  const std::vector<std::pair<Node, ProofStep>>& steps = psb.getSteps();
275
151834
  for (const std::pair<Node, ProofStep>& ps : steps)
276
  {
277
134703
    if (!addStep(ps.first, ps.second, ensureChildren, opolicy))
278
    {
279
      return false;
280
    }
281
  }
282
17131
  return true;
283
}
284
285
3028907
bool CDProof::addProof(std::shared_ptr<ProofNode> pn,
286
                       CDPOverwrite opolicy,
287
                       bool doCopy)
288
{
289
3028907
  if (!doCopy)
290
  {
291
    // If we aren't doing a deep copy, we either store pn or link its top
292
    // node into the existing pointer
293
6057814
    Node curFact = pn->getResult();
294
6057814
    std::shared_ptr<ProofNode> cur = getProofSymm(curFact);
295
3028907
    if (cur == nullptr)
296
    {
297
      // Assert that the checker of this class agrees with (the externally
298
      // provided) pn. This ensures that if pn was checked by a different
299
      // checker than the one of the manager in this class, then it is double
300
      // checked here, so that this class maintains the invariant that all of
301
      // its nodes in d_nodes have been checked by the underlying checker.
302
2434001
      Assert(d_manager->getChecker() == nullptr
303
             || d_manager->getChecker()->check(pn.get(), curFact) == curFact);
304
      // just store the proof for fact
305
2434001
      d_nodes.insert(curFact, pn);
306
    }
307
594906
    else if (shouldOverwrite(cur.get(), pn->getRule(), opolicy))
308
    {
309
      // We update cur to have the structure of the top node of pn. Notice that
310
      // the interface to update this node will ensure that the proof apf is a
311
      // proof of the assumption. If it does not, then pn was wrong.
312
      if (!d_manager->updateNode(
313
              cur.get(), pn->getRule(), pn->getChildren(), pn->getArguments()))
314
      {
315
        return false;
316
      }
317
    }
318
    // also need to connect via SYMM if necessary
319
3028907
    notifyNewProof(curFact);
320
3028907
    return true;
321
  }
322
  std::unordered_map<ProofNode*, bool> visited;
323
  std::unordered_map<ProofNode*, bool>::iterator it;
324
  std::vector<ProofNode*> visit;
325
  ProofNode* cur;
326
  Node curFact;
327
  visit.push_back(pn.get());
328
  bool retValue = true;
329
  do
330
  {
331
    cur = visit.back();
332
    curFact = cur->getResult();
333
    visit.pop_back();
334
    it = visited.find(cur);
335
    if (it == visited.end())
336
    {
337
      // visit the children
338
      visited[cur] = false;
339
      visit.push_back(cur);
340
      const std::vector<std::shared_ptr<ProofNode>>& cs = cur->getChildren();
341
      for (const std::shared_ptr<ProofNode>& c : cs)
342
      {
343
        visit.push_back(c.get());
344
      }
345
    }
346
    else if (!it->second)
347
    {
348
      // we always call addStep, which may or may not overwrite the
349
      // current step
350
      std::vector<Node> pexp;
351
      const std::vector<std::shared_ptr<ProofNode>>& cs = cur->getChildren();
352
      for (const std::shared_ptr<ProofNode>& c : cs)
353
      {
354
        Assert(!c->getResult().isNull());
355
        pexp.push_back(c->getResult());
356
      }
357
      // can ensure children at this point
358
      bool res = addStep(
359
          curFact, cur->getRule(), pexp, cur->getArguments(), true, opolicy);
360
      // should always succeed
361
      Assert(res);
362
      retValue = retValue && res;
363
      visited[cur] = true;
364
    }
365
  } while (!visit.empty());
366
367
  return retValue;
368
}
369
370
1443366
bool CDProof::hasStep(Node fact)
371
{
372
2886732
  std::shared_ptr<ProofNode> pf = getProof(fact);
373
1443366
  if (pf != nullptr && !isAssumption(pf.get()))
374
  {
375
1049017
    return true;
376
  }
377
394349
  else if (!d_autoSymm)
378
  {
379
    return false;
380
  }
381
788698
  Node symFact = getSymmFact(fact);
382
394349
  if (symFact.isNull())
383
  {
384
378329
    return false;
385
  }
386
16020
  pf = getProof(symFact);
387
16020
  if (pf != nullptr && !isAssumption(pf.get()))
388
  {
389
    return true;
390
  }
391
16020
  return false;
392
}
393
394
94370
ProofNodeManager* CDProof::getManager() const { return d_manager; }
395
396
2109415
bool CDProof::shouldOverwrite(ProofNode* pn, PfRule newId, CDPOverwrite opol)
397
{
398
2109415
  Assert(pn != nullptr);
399
  // we overwrite only if opol is CDPOverwrite::ALWAYS, or if
400
  // opol is CDPOverwrite::ASSUME_ONLY and the previously
401
  // provided proof pn was an assumption and the currently provided step is not
402
  return opol == CDPOverwrite::ALWAYS
403
2163299
         || (opol == CDPOverwrite::ASSUME_ONLY && isAssumption(pn)
404
2565277
             && newId != PfRule::ASSUME);
405
}
406
407
11971673
bool CDProof::isAssumption(ProofNode* pn)
408
{
409
11971673
  PfRule rule = pn->getRule();
410
11971673
  if (rule == PfRule::ASSUME)
411
  {
412
1593938
    return true;
413
  }
414
10377735
  else if (rule == PfRule::SYMM)
415
  {
416
349065
    const std::vector<std::shared_ptr<ProofNode>>& pc = pn->getChildren();
417
349065
    Assert(pc.size() == 1);
418
349065
    return pc[0]->getRule() == PfRule::ASSUME;
419
  }
420
10028670
  return false;
421
}
422
423
981903
bool CDProof::isSame(TNode f, TNode g)
424
{
425
981903
  if (f == g)
426
  {
427
548636
    return true;
428
  }
429
433267
  Kind fk = f.getKind();
430
433267
  Kind gk = g.getKind();
431
433267
  if (fk == EQUAL && gk == EQUAL && f[0] == g[1] && f[1] == g[0])
432
  {
433
    // symmetric equality
434
164687
    return true;
435
  }
436
314474
  if (fk == NOT && gk == NOT && f[0].getKind() == EQUAL
437
289999
      && g[0].getKind() == EQUAL && f[0][0] == g[0][1] && f[0][1] == g[0][0])
438
  {
439
    // symmetric disequality
440
2055
    return true;
441
  }
442
266525
  return false;
443
}
444
445
20761271
Node CDProof::getSymmFact(TNode f)
446
{
447
20761271
  bool polarity = f.getKind() != NOT;
448
41522542
  TNode fatom = polarity ? f : f[0];
449
20761271
  if (fatom.getKind() != EQUAL || fatom[0] == fatom[1])
450
  {
451
13275250
    return Node::null();
452
  }
453
14972042
  Node symFact = fatom[1].eqNode(fatom[0]);
454
7486021
  return polarity ? symFact : symFact.notNode();
455
}
456
457
16726588
std::string CDProof::identify() const { return d_name; }
458
459
28194
}  // namespace cvc5