GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/quantifiers/quantifiers_macros.cpp Lines: 133 143 93.0 %
Date: 2021-09-29 Branches: 288 626 46.0 %

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