GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/expr/proof_node_updater.cpp Lines: 98 127 77.2 %
Date: 2021-05-22 Branches: 130 352 36.9 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Haniel Barbosa
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 a utility for updating proof nodes.
14
 */
15
16
#include "expr/proof_node_updater.h"
17
18
#include "expr/lazy_proof.h"
19
#include "expr/proof_ensure_closed.h"
20
#include "expr/proof_node_algorithm.h"
21
#include "expr/proof_node_manager.h"
22
23
namespace cvc5 {
24
25
8397
ProofNodeUpdaterCallback::ProofNodeUpdaterCallback() {}
26
8397
ProofNodeUpdaterCallback::~ProofNodeUpdaterCallback() {}
27
28
bool ProofNodeUpdaterCallback::update(Node res,
29
                                      PfRule id,
30
                                      const std::vector<Node>& children,
31
                                      const std::vector<Node>& args,
32
                                      CDProof* cdp,
33
                                      bool& continueUpdate)
34
{
35
  return false;
36
}
37
38
9898
ProofNodeUpdater::ProofNodeUpdater(ProofNodeManager* pnm,
39
                                   ProofNodeUpdaterCallback& cb,
40
                                   bool mergeSubproofs,
41
9898
                                   bool autoSym)
42
    : d_pnm(pnm),
43
      d_cb(cb),
44
      d_debugFreeAssumps(false),
45
      d_mergeSubproofs(mergeSubproofs),
46
9898
      d_autoSym(autoSym)
47
{
48
9898
}
49
50
8078
void ProofNodeUpdater::process(std::shared_ptr<ProofNode> pf)
51
{
52
8078
  if (d_debugFreeAssumps)
53
  {
54
    if (Trace.isOn("pfnu-debug"))
55
    {
56
      Trace("pfnu-debug2") << "Initial proof: " << *pf.get() << std::endl;
57
      Trace("pfnu-debug") << "ProofNodeUpdater::process" << std::endl;
58
      Trace("pfnu-debug") << "Expected free assumptions: " << std::endl;
59
      for (const Node& fa : d_freeAssumps)
60
      {
61
        Trace("pfnu-debug") << "- " << fa << std::endl;
62
      }
63
      std::vector<Node> assump;
64
      expr::getFreeAssumptions(pf.get(), assump);
65
      Trace("pfnu-debug") << "Current free assumptions: " << std::endl;
66
      for (const Node& fa : assump)
67
      {
68
        Trace("pfnu-debug") << "- " << fa << std::endl;
69
      }
70
    }
71
  }
72
16156
  std::vector<std::shared_ptr<ProofNode>> traversing;
73
8078
  processInternal(pf, d_freeAssumps, traversing);
74
8078
}
75
76
96960
void ProofNodeUpdater::processInternal(
77
    std::shared_ptr<ProofNode> pf,
78
    const std::vector<Node>& fa,
79
    std::vector<std::shared_ptr<ProofNode>>& traversing)
80
{
81
96960
  Trace("pf-process") << "ProofNodeUpdater::process" << std::endl;
82
193920
  std::unordered_map<std::shared_ptr<ProofNode>, bool> visited;
83
96960
  std::unordered_map<std::shared_ptr<ProofNode>, bool>::iterator it;
84
193920
  std::vector<std::shared_ptr<ProofNode>> visit;
85
193920
  std::shared_ptr<ProofNode> cur;
86
96960
  visit.push_back(pf);
87
96960
  std::map<Node, std::shared_ptr<ProofNode>>::iterator itc;
88
  // A cache from formulas to proof nodes that are in the current scope.
89
  // Notice that we make a fresh recursive call to process if the current
90
  // rule is SCOPE below.
91
193920
  std::map<Node, std::shared_ptr<ProofNode>> resCache;
92
193920
  Node res;
93
17396962
  do
94
  {
95
17493922
    cur = visit.back();
96
17493922
    visit.pop_back();
97
17493922
    it = visited.find(cur);
98
17493922
    res = cur->getResult();
99
17493922
    if (it == visited.end())
100
    {
101
7068454
      if (d_mergeSubproofs)
102
      {
103
2755232
        itc = resCache.find(res);
104
3301371
        if (itc != resCache.end())
105
        {
106
          // already have a proof, merge it into this one
107
546139
          visited[cur] = true;
108
546139
          d_pnm->updateNode(cur.get(), itc->second.get());
109
2071088
          continue;
110
        }
111
      }
112
      // run update to a fixed point
113
6522315
      bool continueUpdate = true;
114
6743987
      while (runUpdate(cur, fa, continueUpdate) && continueUpdate)
115
      {
116
110836
        Trace("pf-process-debug") << "...updated proof." << std::endl;
117
      }
118
6522315
      visited[cur] = !continueUpdate;
119
7412243
      if (!continueUpdate)
120
      {
121
        // no further changes should be made to cur according to the callback
122
1779856
        Trace("pf-process-debug")
123
889928
            << "...marked to not continue update." << std::endl;
124
889928
        runFinalize(cur, fa, resCache);
125
889928
        continue;
126
      }
127
5632387
      traversing.push_back(cur);
128
5632387
      visit.push_back(cur);
129
      // If we are not the top-level proof, we were a scope, or became a scope
130
      // after updating, we do a separate recursive call to this method. This
131
      // allows us to properly track the assumptions in scope, which is
132
      // important for example to merge or to determine updates based on free
133
      // assumptions.
134
5632387
      if (cur->getRule() == PfRule::SCOPE && cur != pf)
135
      {
136
177764
        std::vector<Node> nfa;
137
88882
        nfa.insert(nfa.end(), fa.begin(), fa.end());
138
88882
        const std::vector<Node>& args = cur->getArguments();
139
88882
        nfa.insert(nfa.end(), args.begin(), args.end());
140
88882
        Trace("pfnu-debug2") << "Process new scope with " << args << std::endl;
141
        // Process in new call separately
142
88882
        processInternal(cur, nfa, traversing);
143
88882
        continue;
144
      }
145
5543505
      const std::vector<std::shared_ptr<ProofNode>>& ccp = cur->getChildren();
146
      // now, process children
147
17308080
      for (const std::shared_ptr<ProofNode>& cp : ccp)
148
      {
149
35293725
        if (std::find(traversing.begin(), traversing.end(), cp)
150
35293725
            != traversing.end())
151
        {
152
          Unhandled()
153
              << "ProofNodeUpdater::processInternal: cyclic proof! (use "
154
                 "--proof-eager-checking)"
155
              << std::endl;
156
        }
157
11764575
        visit.push_back(cp);
158
      }
159
    }
160
10425468
    else if (!it->second)
161
    {
162
5632387
      Assert(!traversing.empty());
163
5632387
      traversing.pop_back();
164
5632387
      visited[cur] = true;
165
      // finalize the node
166
5632387
      runFinalize(cur, fa, resCache);
167
    }
168
17493922
  } while (!visit.empty());
169
96960
  Trace("pf-process") << "ProofNodeUpdater::process: finished" << std::endl;
170
96960
}
171
172
6633151
bool ProofNodeUpdater::runUpdate(std::shared_ptr<ProofNode> cur,
173
                                 const std::vector<Node>& fa,
174
                                 bool& continueUpdate)
175
{
176
  // should it be updated?
177
6633151
  if (!d_cb.shouldUpdate(cur, fa, continueUpdate))
178
  {
179
5360988
    return false;
180
  }
181
1272163
  PfRule id = cur->getRule();
182
  // use CDProof to open a scope for which the callback updates
183
2544326
  CDProof cpf(d_pnm, nullptr, "ProofNodeUpdater::CDProof", d_autoSym);
184
1272163
  const std::vector<std::shared_ptr<ProofNode>>& cc = cur->getChildren();
185
2544326
  std::vector<Node> ccn;
186
2439659
  for (const std::shared_ptr<ProofNode>& cp : cc)
187
  {
188
2334992
    Node cpres = cp->getResult();
189
1167496
    ccn.push_back(cpres);
190
    // store in the proof
191
1167496
    cpf.addProof(cp);
192
  }
193
2544326
  Node res = cur->getResult();
194
2544326
  Trace("pf-process-debug")
195
1272163
      << "Updating (" << cur->getRule() << "): " << res << std::endl;
196
  // only if the callback updated the node
197
1272163
  if (d_cb.update(res, id, ccn, cur->getArguments(), &cpf, continueUpdate))
198
  {
199
2001528
    std::shared_ptr<ProofNode> npn = cpf.getProofFor(res);
200
2001528
    std::vector<Node> fullFa;
201
1000764
    if (d_debugFreeAssumps)
202
    {
203
      expr::getFreeAssumptions(cur.get(), fullFa);
204
      Trace("pfnu-debug") << "Original proof : " << *cur << std::endl;
205
    }
206
    // then, update the original proof node based on this one
207
1000764
    Trace("pf-process-debug") << "Update node..." << std::endl;
208
1000764
    d_pnm->updateNode(cur.get(), npn.get());
209
1000764
    Trace("pf-process-debug") << "...update node finished." << std::endl;
210
1000764
    if (d_debugFreeAssumps)
211
    {
212
      fullFa.insert(fullFa.end(), fa.begin(), fa.end());
213
      // We have that npn is a node we occurring the final updated version of
214
      // the proof. We can now debug based on the expected set of free
215
      // assumptions.
216
      Trace("pfnu-debug") << "Ensure update closed..." << std::endl;
217
      pfnEnsureClosedWrt(
218
          npn.get(), fullFa, "pfnu-debug", "ProofNodeUpdater:postupdate");
219
    }
220
1000764
    Trace("pf-process-debug") << "..finished" << std::endl;
221
1000764
    return true;
222
  }
223
271399
  Trace("pf-process-debug") << "..finished" << std::endl;
224
271399
  return false;
225
}
226
227
6522315
void ProofNodeUpdater::runFinalize(
228
    std::shared_ptr<ProofNode> cur,
229
    const std::vector<Node>& fa,
230
    std::map<Node, std::shared_ptr<ProofNode>>& resCache)
231
{
232
6522315
  if (d_mergeSubproofs)
233
  {
234
4418186
    Node res = cur->getResult();
235
    // cache result if we are merging subproofs
236
2209093
    resCache[res] = cur;
237
  }
238
6522315
  if (d_debugFreeAssumps)
239
  {
240
    // We have that npn is a node we occurring the final updated version of
241
    // the proof. We can now debug based on the expected set of free
242
    // assumptions.
243
    Trace("pfnu-debug") << "Ensure update closed..." << std::endl;
244
    pfnEnsureClosedWrt(
245
        cur.get(), fa, "pfnu-debug", "ProofNodeUpdater:finalize");
246
  }
247
6522315
}
248
249
void ProofNodeUpdater::setDebugFreeAssumptions(
250
    const std::vector<Node>& freeAssumps)
251
{
252
  d_freeAssumps.clear();
253
  d_freeAssumps.insert(
254
      d_freeAssumps.end(), freeAssumps.begin(), freeAssumps.end());
255
  d_debugFreeAssumps = true;
256
}
257
258
28194
}  // namespace cvc5