GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/proof/proof_node_algorithm.cpp Lines: 85 109 78.0 %
Date: 2021-09-07 Branches: 86 204 42.2 %

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 proof node algorithm utilities.
14
 */
15
16
#include "proof/proof_node_algorithm.h"
17
18
#include "proof/proof_node.h"
19
20
namespace cvc5 {
21
namespace expr {
22
23
84872
void getFreeAssumptions(ProofNode* pn, std::vector<Node>& assump)
24
{
25
169744
  std::map<Node, std::vector<std::shared_ptr<ProofNode>>> amap;
26
  std::shared_ptr<ProofNode> spn = std::make_shared<ProofNode>(
27
169744
      pn->getRule(), pn->getChildren(), pn->getArguments());
28
84872
  getFreeAssumptionsMap(spn, amap);
29
114208
  for (const std::pair<const Node, std::vector<std::shared_ptr<ProofNode>>>& p :
30
84872
       amap)
31
  {
32
114208
    assump.push_back(p.first);
33
  }
34
84872
}
35
36
391635
void getFreeAssumptionsMap(
37
    std::shared_ptr<ProofNode> pn,
38
    std::map<Node, std::vector<std::shared_ptr<ProofNode>>>& amap)
39
{
40
  // proof should not be cyclic
41
  // visited set false after preorder traversal, true after postorder traversal
42
783270
  std::unordered_map<ProofNode*, bool> visited;
43
391635
  std::unordered_map<ProofNode*, bool>::iterator it;
44
783270
  std::vector<std::shared_ptr<ProofNode>> visit;
45
783270
  std::vector<std::shared_ptr<ProofNode>> traversing;
46
  // Maps a bound assumption to the number of bindings it is under
47
  // e.g. in (SCOPE (SCOPE (ASSUME x) (x y)) (y)), y would be mapped to 2 at
48
  // (ASSUME x), and x would be mapped to 1.
49
  //
50
  // This map is used to track which nodes are in scope while traversing the
51
  // DAG. The in-scope assumptions are keys in the map. They're removed when
52
  // their binding count drops to zero. Let's annotate the above example to
53
  // serve as an illustration:
54
  //
55
  //   (SCOPE0 (SCOPE1 (ASSUME x) (x y)) (y))
56
  //
57
  // This is how the map changes during the traversal:
58
  //   after  previsiting SCOPE0: { y: 1 }
59
  //   after  previsiting SCOPE1: { y: 2, x: 1 }
60
  //   at                 ASSUME: { y: 2, x: 1 } (so x is in scope!)
61
  //   after postvisiting SCOPE1: { y: 1 }
62
  //   after postvisiting SCOPE2: {}
63
  //
64
783270
  std::unordered_map<Node, uint32_t> scopeDepth;
65
783270
  std::shared_ptr<ProofNode> cur;
66
391635
  visit.push_back(pn);
67
20414239
  do
68
  {
69
20805874
    cur = visit.back();
70
20805874
    visit.pop_back();
71
20805874
    it = visited.find(cur.get());
72
20805874
    const std::vector<Node>& cargs = cur->getArguments();
73
20805874
    if (it == visited.end())
74
    {
75
10221003
      PfRule id = cur->getRule();
76
10221003
      if (id == PfRule::ASSUME)
77
      {
78
3874195
        visited[cur.get()] = true;
79
3874195
        Assert(cargs.size() == 1);
80
7748390
        Node f = cargs[0];
81
3874195
        if (!scopeDepth.count(f))
82
        {
83
3090285
          amap[f].push_back(cur);
84
        }
85
      }
86
      else
87
      {
88
6346808
        if (id == PfRule::SCOPE)
89
        {
90
          // mark that its arguments are bound in the current scope
91
998582
          for (const Node& a : cargs)
92
          {
93
819851
            scopeDepth[a] += 1;
94
          }
95
          // will need to unbind the variables below
96
        }
97
        // The following loop cannot be merged with the loop above because the
98
        // same subproof
99
6346808
        visited[cur.get()] = false;
100
6346808
        visit.push_back(cur);
101
6346808
        traversing.push_back(cur);
102
6346808
        const std::vector<std::shared_ptr<ProofNode>>& cs = cur->getChildren();
103
20414239
        for (const std::shared_ptr<ProofNode>& cp : cs)
104
        {
105
42202293
          if (std::find(traversing.begin(), traversing.end(), cp)
106
42202293
              != traversing.end())
107
          {
108
            Unhandled() << "getFreeAssumptionsMap: cyclic proof! (use "
109
                           "--proof-eager-checking)"
110
                        << std::endl;
111
          }
112
14067431
          visit.push_back(cp);
113
        }
114
      }
115
    }
116
10584871
    else if (!it->second)
117
    {
118
6346808
      Assert(!traversing.empty());
119
6346808
      traversing.pop_back();
120
6346808
      visited[cur.get()] = true;
121
6346808
      if (cur->getRule() == PfRule::SCOPE)
122
      {
123
        // unbind its assumptions
124
998582
        for (const Node& a : cargs)
125
        {
126
819851
          auto scopeCt = scopeDepth.find(a);
127
819851
          Assert(scopeCt != scopeDepth.end());
128
819851
          scopeCt->second -= 1;
129
819851
          if (scopeCt->second == 0)
130
          {
131
749518
            scopeDepth.erase(scopeCt);
132
          }
133
        }
134
      }
135
    }
136
20805874
  } while (!visit.empty());
137
391635
}
138
139
2267391
bool containsAssumption(const ProofNode* pn,
140
                        std::unordered_map<const ProofNode*, bool>& caMap)
141
{
142
4534782
  std::unordered_map<const ProofNode*, bool> visited;
143
2267391
  std::unordered_map<const ProofNode*, bool>::iterator it;
144
4534782
  std::vector<const ProofNode*> visit;
145
2267391
  visit.push_back(pn);
146
2267391
  bool foundAssumption = false;
147
  const ProofNode* cur;
148
18756351
  while (!visit.empty())
149
  {
150
8244480
    cur = visit.back();
151
8244480
    visit.pop_back();
152
    // have we already computed?
153
8244480
    it = caMap.find(cur);
154
12216873
    if (it != caMap.end())
155
    {
156
      // if cached, we set found assumption to true if applicable and continue
157
3972393
      if (it->second)
158
      {
159
2558985
        foundAssumption = true;
160
      }
161
3972393
      continue;
162
    }
163
4272087
    it = visited.find(cur);
164
4272087
    if (it == visited.end())
165
    {
166
2267391
      PfRule r = cur->getRule();
167
2267391
      if (r == PfRule::ASSUME)
168
      {
169
262695
        visited[cur] = true;
170
262695
        caMap[cur] = true;
171
262695
        foundAssumption = true;
172
      }
173
2004696
      else if (!foundAssumption)
174
      {
175
        // if we haven't found an assumption yet, recurse. Otherwise, we will
176
        // not bother computing whether this subproof contains an assumption
177
        // since we know its parent already contains one by another child.
178
2004696
        visited[cur] = false;
179
2004696
        visit.push_back(cur);
180
        const std::vector<std::shared_ptr<ProofNode>>& children =
181
2004696
            cur->getChildren();
182
5977089
        for (const std::shared_ptr<ProofNode>& cp : children)
183
        {
184
3972393
          visit.push_back(cp.get());
185
        }
186
      }
187
    }
188
2004696
    else if (!it->second)
189
    {
190
2004696
      visited[cur] = true;
191
      // we contain an assumption if we've found an assumption in a child
192
2004696
      caMap[cur] = foundAssumption;
193
    }
194
  }
195
4534782
  return caMap[cur];
196
}
197
198
bool containsAssumption(const ProofNode* pn)
199
{
200
  std::unordered_map<const ProofNode*, bool> caMap;
201
  return containsAssumption(pn, caMap);
202
}
203
204
bool containsSubproof(ProofNode* pn, ProofNode* pnc)
205
{
206
  std::unordered_set<const ProofNode*> visited;
207
  return containsSubproof(pn, pnc, visited);
208
}
209
210
bool containsSubproof(ProofNode* pn,
211
                      ProofNode* pnc,
212
                      std::unordered_set<const ProofNode*>& visited)
213
{
214
  std::unordered_map<const ProofNode*, bool>::iterator it;
215
  std::vector<const ProofNode*> visit;
216
  visit.push_back(pn);
217
  const ProofNode* cur;
218
  while (!visit.empty())
219
  {
220
    cur = visit.back();
221
    visit.pop_back();
222
    if (visited.find(cur) == visited.end())
223
    {
224
      visited.insert(cur);
225
      if (cur == pnc)
226
      {
227
        return true;
228
      }
229
      const std::vector<std::shared_ptr<ProofNode>>& children =
230
          cur->getChildren();
231
      for (const std::shared_ptr<ProofNode>& cp : children)
232
      {
233
        visit.push_back(cp.get());
234
      }
235
    }
236
  }
237
  return false;
238
}
239
240
}  // namespace expr
241
29502
}  // namespace cvc5