GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/quantifiers/quantifiers_macros.cpp Lines: 134 144 93.1 %
Date: 2021-11-07 Branches: 292 654 44.6 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Yoni Zohar, 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
 * Utility for quantifiers macro definitions.
14
 */
15
16
#include "theory/quantifiers/quantifiers_macros.h"
17
18
#include "expr/node_algorithm.h"
19
#include "expr/skolem_manager.h"
20
#include "options/quantifiers_options.h"
21
#include "theory/arith/arith_msum.h"
22
#include "theory/quantifiers/ematching/pattern_term_selector.h"
23
#include "theory/quantifiers/quantifiers_registry.h"
24
#include "theory/quantifiers/term_database.h"
25
#include "theory/quantifiers/term_util.h"
26
#include "theory/rewriter.h"
27
28
using namespace cvc5::kind;
29
30
namespace cvc5 {
31
namespace theory {
32
namespace quantifiers {
33
34
24
QuantifiersMacros::QuantifiersMacros(QuantifiersRegistry& qr) : d_qreg(qr) {}
35
36
36
Node QuantifiersMacros::solve(Node lit, bool reqGround)
37
{
38
36
  Trace("macros-debug") << "QuantifiersMacros::solve " << lit << std::endl;
39
36
  if (lit.getKind() != FORALL)
40
  {
41
4
    return Node::null();
42
  }
43
64
  Node body = lit[1];
44
32
  bool pol = body.getKind() != NOT;
45
64
  Node n = pol ? body : body[0];
46
32
  NodeManager* nm = NodeManager::currentNM();
47
32
  if (n.getKind() == APPLY_UF)
48
  {
49
    // predicate case
50
4
    if (isBoundVarApplyUf(n))
51
    {
52
8
      Node op = n.getOperator();
53
8
      Node n_def = nm->mkConst(pol);
54
8
      Node fdef = solveEq(n, n_def);
55
4
      Assert(!fdef.isNull());
56
4
      return returnMacro(fdef, lit);
57
    }
58
  }
59
28
  else if (pol && n.getKind() == EQUAL)
60
  {
61
    // literal case
62
22
    Trace("macros-debug") << "Check macro literal : " << n << std::endl;
63
30
    std::map<Node, bool> visited;
64
30
    std::vector<Node> candidates;
65
66
    for (const Node& nc : n)
66
    {
67
44
      getMacroCandidates(nc, candidates, visited);
68
    }
69
22
    for (const Node& m : candidates)
70
    {
71
14
      Node op = m.getOperator();
72
14
      Trace("macros-debug") << "Check macro candidate : " << m << std::endl;
73
      // get definition and condition
74
14
      Node n_def = solveInEquality(m, n);  // definition for the macro
75
14
      if (n_def.isNull())
76
      {
77
        continue;
78
      }
79
28
      Trace("macros-debug")
80
14
          << m << " is possible macro in " << lit << std::endl;
81
28
      Trace("macros-debug")
82
14
          << "  corresponding definition is : " << n_def << std::endl;
83
14
      visited.clear();
84
      // cannot contain a defined operator
85
14
      if (!containsBadOp(n_def, op, reqGround))
86
      {
87
28
        Trace("macros-debug")
88
14
            << "...does not contain bad (recursive) operator." << std::endl;
89
        // must be ground UF term if mode is GROUND_UF
90
28
        if (options::macrosQuantMode() != options::MacrosQuantMode::GROUND_UF
91
28
            || preservesTriggerVariables(lit, n_def))
92
        {
93
28
          Trace("macros-debug")
94
14
              << "...respects ground-uf constraint." << std::endl;
95
14
          Node fdef = solveEq(m, n_def);
96
14
          if (!fdef.isNull())
97
          {
98
14
            return returnMacro(fdef, lit);
99
          }
100
        }
101
      }
102
    }
103
  }
104
14
  return Node::null();
105
}
106
107
14
bool QuantifiersMacros::containsBadOp(Node n, Node op, bool reqGround)
108
{
109
28
  std::unordered_set<TNode> visited;
110
14
  std::unordered_set<TNode>::iterator it;
111
28
  std::vector<TNode> visit;
112
28
  TNode cur;
113
14
  visit.push_back(n);
114
14
  do
115
  {
116
28
    cur = visit.back();
117
28
    visit.pop_back();
118
28
    it = visited.find(cur);
119
28
    if (it == visited.end())
120
    {
121
28
      visited.insert(cur);
122
28
      if (cur.isClosure() && reqGround)
123
      {
124
        return true;
125
      }
126
28
      else if (cur == op)
127
      {
128
        return true;
129
      }
130
28
      else if (cur.hasOperator() && cur.getOperator() == op)
131
      {
132
        return true;
133
      }
134
28
      visit.insert(visit.end(), cur.begin(), cur.end());
135
    }
136
28
  } while (!visit.empty());
137
14
  return false;
138
}
139
140
12
bool QuantifiersMacros::preservesTriggerVariables(Node q, Node n)
141
{
142
12
  Assert(q.getKind() == FORALL) << "Expected quantified formula, got " << q;
143
24
  Node icn = d_qreg.substituteBoundVariablesToInstConstants(n, q);
144
12
  Trace("macros-debug2") << "Get free variables in " << icn << std::endl;
145
24
  std::vector<Node> var;
146
12
  quantifiers::TermUtil::computeInstConstContainsForQuant(q, icn, var);
147
12
  Trace("macros-debug2") << "Get trigger variables for " << icn << std::endl;
148
24
  std::vector<Node> trigger_var;
149
12
  inst::PatternTermSelector::getTriggerVariables(icn, q, trigger_var);
150
12
  Trace("macros-debug2") << "Done." << std::endl;
151
  // only if all variables are also trigger variables
152
24
  return trigger_var.size() >= var.size();
153
}
154
155
30
bool QuantifiersMacros::isBoundVarApplyUf(Node n)
156
{
157
30
  Assert(n.getKind() == APPLY_UF);
158
60
  TypeNode tno = n.getOperator().getType();
159
60
  std::map<Node, bool> vars;
160
  // allow if a vector of unique variables of the same type as UF arguments
161
52
  for (size_t i = 0, nchild = n.getNumChildren(); i < nchild; i++)
162
  {
163
32
    if (n[i].getKind() != BOUND_VARIABLE)
164
    {
165
8
      return false;
166
    }
167
24
    if (n[i].getType() != tno[i])
168
    {
169
2
      return false;
170
    }
171
22
    if (vars.find(n[i]) == vars.end())
172
    {
173
22
      vars[n[i]] = true;
174
    }
175
    else
176
    {
177
      return false;
178
    }
179
  }
180
20
  return true;
181
}
182
183
50
void QuantifiersMacros::getMacroCandidates(Node n,
184
                                           std::vector<Node>& candidates,
185
                                           std::map<Node, bool>& visited)
186
{
187
50
  if (visited.find(n) == visited.end())
188
  {
189
50
    visited[n] = true;
190
50
    if (n.getKind() == APPLY_UF)
191
    {
192
26
      if (isBoundVarApplyUf(n))
193
      {
194
16
        candidates.push_back(n);
195
      }
196
    }
197
24
    else if (n.getKind() == PLUS)
198
    {
199
6
      for (size_t i = 0; i < n.getNumChildren(); i++)
200
      {
201
4
        getMacroCandidates(n[i], candidates, visited);
202
      }
203
    }
204
22
    else if (n.getKind() == MULT)
205
    {
206
      // if the LHS is a constant
207
2
      if (n.getNumChildren() == 2 && n[0].isConst())
208
      {
209
2
        getMacroCandidates(n[1], candidates, visited);
210
      }
211
    }
212
20
    else if (n.getKind() == NOT)
213
    {
214
      getMacroCandidates(n[0], candidates, visited);
215
    }
216
  }
217
50
}
218
219
14
Node QuantifiersMacros::solveInEquality(Node n, Node lit)
220
{
221
14
  if (lit.getKind() == EQUAL)
222
  {
223
    // return the opposite side of the equality if defined that way
224
22
    for (int i = 0; i < 2; i++)
225
    {
226
18
      if (lit[i] == n)
227
      {
228
24
        return lit[i == 0 ? 1 : 0];
229
      }
230
8
      else if (lit[i].getKind() == NOT && lit[i][0] == n)
231
      {
232
        return lit[i == 0 ? 1 : 0].negate();
233
      }
234
    }
235
4
    std::map<Node, Node> msum;
236
4
    if (ArithMSum::getMonomialSumLit(lit, msum))
237
    {
238
4
      Node veq_c;
239
4
      Node val;
240
4
      int res = ArithMSum::isolate(n, msum, veq_c, val, EQUAL);
241
4
      if (res != 0 && veq_c.isNull())
242
      {
243
4
        return val;
244
      }
245
    }
246
  }
247
  Trace("macros-debug") << "Cannot find for " << lit << " " << n << std::endl;
248
  return Node::null();
249
}
250
251
18
Node QuantifiersMacros::solveEq(Node n, Node ndef)
252
{
253
18
  Assert(n.getKind() == APPLY_UF);
254
18
  NodeManager* nm = NodeManager::currentNM();
255
18
  Trace("macros-debug") << "Add macro eq for " << n << std::endl;
256
18
  Trace("macros-debug") << "  def: " << ndef << std::endl;
257
36
  std::vector<Node> vars;
258
36
  std::vector<Node> fvars;
259
38
  for (const Node& nc : n)
260
  {
261
20
    vars.push_back(nc);
262
40
    Node v = nm->mkBoundVar(nc.getType());
263
20
    fvars.push_back(v);
264
  }
265
  Node fdef =
266
36
      ndef.substitute(vars.begin(), vars.end(), fvars.begin(), fvars.end());
267
18
  fdef = nm->mkNode(LAMBDA, nm->mkNode(BOUND_VAR_LIST, fvars), fdef);
268
  // If the definition has a free variable, it is malformed. This can happen
269
  // if the right hand side of a macro definition contains a variable not
270
  // contained in the left hand side
271
18
  if (expr::hasFreeVar(fdef))
272
  {
273
    return Node::null();
274
  }
275
36
  TNode op = n.getOperator();
276
36
  TNode fdeft = fdef;
277
18
  Assert(op.getType().isComparableTo(fdef.getType()));
278
18
  return op.eqNode(fdef);
279
}
280
281
18
Node QuantifiersMacros::returnMacro(Node fdef, Node lit) const
282
{
283
36
  Trace("macros") << "* Inferred macro " << fdef << " from " << lit
284
18
                  << std::endl;
285
18
  return fdef;
286
}
287
288
}  // namespace quantifiers
289
}  // namespace theory
290
31137
}  // namespace cvc5