GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/quantifiers/sygus/sygus_eval_unfold.cpp Lines: 172 183 94.0 %
Date: 2021-11-07 Branches: 366 918 39.9 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Mathias Preiner, 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 sygus_eval_unfold.
14
 */
15
16
#include "theory/quantifiers/sygus/sygus_eval_unfold.h"
17
18
#include "expr/dtype_cons.h"
19
#include "expr/sygus_datatype.h"
20
#include "options/quantifiers_options.h"
21
#include "theory/datatypes/sygus_datatype_utils.h"
22
#include "theory/quantifiers/sygus/term_database_sygus.h"
23
#include "theory/rewriter.h"
24
25
using namespace std;
26
using namespace cvc5::kind;
27
using namespace cvc5::context;
28
29
namespace cvc5 {
30
namespace theory {
31
namespace quantifiers {
32
33
1900
SygusEvalUnfold::SygusEvalUnfold(Env& env, TermDbSygus* tds)
34
1900
    : EnvObj(env), d_tds(tds)
35
{
36
1900
}
37
38
327019
void SygusEvalUnfold::registerEvalTerm(Node n)
39
{
40
327019
  Assert(options::sygusEvalUnfold());
41
  // is this a sygus evaluation function application?
42
327019
  if (n.getKind() != DT_SYGUS_EVAL)
43
  {
44
644356
    return;
45
  }
46
7160
  if (d_eval_processed.find(n) != d_eval_processed.end())
47
  {
48
4638
    return;
49
  }
50
5044
  Trace("sygus-eval-unfold")
51
2522
      << "SygusEvalUnfold: register eval term : " << n << std::endl;
52
2522
  d_eval_processed.insert(n);
53
5044
  TypeNode tn = n[0].getType();
54
  // since n[0] is an evaluation head, we know tn is a sygus datatype
55
2522
  Assert(tn.isDatatype());
56
2522
  const DType& dt = tn.getDType();
57
2522
  Assert(dt.isSygus());
58
2522
  if (n[0].getKind() == APPLY_CONSTRUCTOR)
59
  {
60
    // constructors should be unfolded and reduced already
61
    Assert(false);
62
    return;
63
  }
64
  // register this evaluation term with its head
65
2522
  d_evals[n[0]].push_back(n);
66
5044
  Node var_list = dt.getSygusVarList();
67
2522
  d_eval_args[n[0]].push_back(std::vector<Node>());
68
9613
  for (unsigned j = 1, size = n.getNumChildren(); j < size; j++)
69
  {
70
7091
    d_eval_args[n[0]].back().push_back(n[j]);
71
  }
72
5044
  Node a = TermDbSygus::getAnchor(n[0]);
73
2522
  d_subterms[a].insert(n[0]);
74
}
75
76
10986
void SygusEvalUnfold::registerModelValue(Node a,
77
                                         Node v,
78
                                         std::vector<Node>& terms,
79
                                         std::vector<Node>& vals,
80
                                         std::vector<Node>& exps)
81
{
82
10986
  std::map<Node, std::unordered_set<Node> >::iterator its = d_subterms.find(a);
83
10986
  if (its == d_subterms.end())
84
  {
85
3452
    return;
86
  }
87
7534
  NodeManager* nm = NodeManager::currentNM();
88
7534
  SygusExplain* sy_exp = d_tds->getExplain();
89
15068
  Trace("sygus-eval-unfold")
90
15068
      << "SygusEvalUnfold: " << a << ", has " << its->second.size()
91
7534
      << " registered subterms." << std::endl;
92
26154
  for (const Node& n : its->second)
93
  {
94
18620
    Trace("sygus-eval-unfold-debug") << "...process : " << n << std::endl;
95
    std::map<Node, std::vector<std::vector<Node> > >::iterator it =
96
18620
        d_eval_args.find(n);
97
18620
    if (it != d_eval_args.end() && !it->second.empty())
98
    {
99
37240
      TNode at = a;
100
37240
      TNode vt = v;
101
37240
      Node vn = n.substitute(at, vt);
102
18620
      vn = rewrite(vn);
103
18620
      unsigned start = d_node_mv_args_proc[n][vn];
104
      // get explanation in terms of testers
105
37240
      std::vector<Node> antec_exp;
106
18620
      sy_exp->getExplanationForEquality(n, vn, antec_exp);
107
      Node antec =
108
37240
          antec_exp.size() == 1 ? antec_exp[0] : nm->mkNode(AND, antec_exp);
109
      // Node antec = n.eqNode( vn );
110
37240
      TypeNode tn = n.getType();
111
      // Check if the sygus type has any symbolic constructors. This will
112
      // impact how the unfolding is computed below.
113
18620
      SygusTypeInfo& sti = d_tds->getTypeInfo(tn);
114
18620
      bool hasSymCons = sti.hasSubtermSymbolicCons();
115
      // n occurs as an evaluation head, thus it has sygus datatype type
116
18620
      Assert(tn.isDatatype());
117
18620
      const DType& dt = tn.getDType();
118
18620
      Assert(dt.isSygus());
119
37240
      Trace("sygus-eval-unfold")
120
18620
          << "SygusEvalUnfold: Register model value : " << vn << " for " << n
121
18620
          << std::endl;
122
18620
      unsigned curr_size = it->second.size();
123
37240
      Trace("sygus-eval-unfold")
124
18620
          << "...it has " << curr_size << " evaluations, already processed "
125
18620
          << start << "." << std::endl;
126
37240
      Node bTerm = d_tds->sygusToBuiltin(vn, tn);
127
18620
      Trace("sygus-eval-unfold") << "Built-in term : " << bTerm << std::endl;
128
37240
      std::vector<Node> vars;
129
37240
      Node var_list = dt.getSygusVarList();
130
73023
      for (const Node& var : var_list)
131
      {
132
54403
        vars.push_back(var);
133
      }
134
      // evaluation children
135
37240
      std::vector<Node> eval_children;
136
18620
      eval_children.push_back(n);
137
      // for each evaluation
138
44704
      for (unsigned i = start; i < curr_size; i++)
139
      {
140
52168
        Node res;
141
52168
        Node expn;
142
        // should we unfold?
143
26084
        bool do_unfold = false;
144
26084
        if (options::sygusEvalUnfoldBool())
145
        {
146
52168
          Node bTermUse = bTerm;
147
26084
          if (bTerm.getKind() == APPLY_UF)
148
          {
149
            // if the builtin term is non-beta-reduced application of lambda,
150
            // we look at the body of the lambda.
151
            Node bTermOp = bTerm.getOperator();
152
            if (bTermOp.getKind() == LAMBDA)
153
            {
154
              bTermUse = bTermOp[0];
155
            }
156
          }
157
26084
          if (bTermUse.getKind() == ITE || bTermUse.getType().isBoolean())
158
          {
159
7095
            do_unfold = true;
160
          }
161
        }
162
26084
        if (do_unfold || hasSymCons)
163
        {
164
          // note that this is replicated for different values
165
16334
          std::map<Node, Node> vtm;
166
16334
          std::vector<Node> exp;
167
8167
          vtm[n] = vn;
168
8167
          eval_children.insert(
169
16334
              eval_children.end(), it->second[i].begin(), it->second[i].end());
170
16334
          Node eval_fun = nm->mkNode(DT_SYGUS_EVAL, eval_children);
171
8167
          eval_children.resize(1);
172
          // If we explicitly asked to unfold, we use single step, otherwise
173
          // we use multi step.
174
8167
          res = unfold(eval_fun, vtm, exp, true, !do_unfold);
175
8167
          Trace("sygus-eval-unfold") << "Unfold returns " << res << std::endl;
176
16334
          expn = exp.size() == 1 ? exp[0] : nm->mkNode(AND, exp);
177
        }
178
        else
179
        {
180
35834
          EvalSygusInvarianceTest esit;
181
17917
          eval_children.insert(
182
35834
              eval_children.end(), it->second[i].begin(), it->second[i].end());
183
35834
          Node conj = nm->mkNode(DT_SYGUS_EVAL, eval_children);
184
17917
          eval_children[0] = vn;
185
35834
          Node eval_fun = nm->mkNode(DT_SYGUS_EVAL, eval_children);
186
17917
          res = d_tds->rewriteNode(eval_fun);
187
35834
          Trace("sygus-eval-unfold")
188
17917
              << "Evaluate with unfolding returns " << res << std::endl;
189
17917
          esit.init(conj, n, res);
190
17917
          eval_children.resize(1);
191
17917
          eval_children[0] = n;
192
193
          // evaluate with minimal explanation
194
35834
          std::vector<Node> mexp;
195
17917
          sy_exp->getExplanationFor(n, vn, mexp, esit);
196
17917
          Assert(!mexp.empty());
197
17917
          expn = mexp.size() == 1 ? mexp[0] : nm->mkNode(AND, mexp);
198
        }
199
26084
        Assert(!res.isNull());
200
26084
        terms.push_back(d_evals[n][i]);
201
26084
        vals.push_back(res);
202
26084
        exps.push_back(expn);
203
52168
        Trace("sygus-eval-unfold")
204
26084
            << "Conclude : " << d_evals[n][i] << " == " << res << std::endl;
205
26084
        Trace("sygus-eval-unfold") << "   from " << expn << std::endl;
206
      }
207
18620
      d_node_mv_args_proc[n][vn] = curr_size;
208
    }
209
  }
210
}
211
212
10727
Node SygusEvalUnfold::unfold(Node en,
213
                             std::map<Node, Node>& vtm,
214
                             std::vector<Node>& exp,
215
                             bool track_exp,
216
                             bool doRec)
217
{
218
10727
  if (en.getKind() != DT_SYGUS_EVAL)
219
  {
220
    Assert(en.isConst());
221
    return en;
222
  }
223
21454
  Trace("sygus-eval-unfold-debug")
224
10727
      << "Unfold : " << en << ", track exp is " << track_exp << ", doRec is "
225
10727
      << doRec << std::endl;
226
21454
  Node ev = en[0];
227
10727
  if (track_exp)
228
  {
229
10727
    std::map<Node, Node>::iterator itv = vtm.find(en[0]);
230
10727
    Assert(itv != vtm.end());
231
10727
    if (itv != vtm.end())
232
    {
233
10727
      ev = itv->second;
234
    }
235
10727
    Assert(en[0].getType() == ev.getType());
236
10727
    Assert(ev.isConst());
237
  }
238
21454
  Trace("sygus-eval-unfold-debug")
239
10727
      << "Unfold model value is : " << ev << std::endl;
240
10727
  AlwaysAssert(ev.getKind() == APPLY_CONSTRUCTOR);
241
21454
  std::vector<Node> args;
242
35693
  for (unsigned i = 1, nchild = en.getNumChildren(); i < nchild; i++)
243
  {
244
24966
    args.push_back(en[i]);
245
  }
246
247
21454
  TypeNode headType = en[0].getType();
248
10727
  NodeManager* nm = NodeManager::currentNM();
249
10727
  const DType& dt = headType.getDType();
250
10727
  unsigned i = datatypes::utils::indexOf(ev.getOperator());
251
10727
  if (track_exp)
252
  {
253
    // explanation
254
21454
    Node ee = nm->mkNode(APPLY_TESTER, dt[i].getTester(), en[0]);
255
10727
    if (std::find(exp.begin(), exp.end(), ee) == exp.end())
256
    {
257
10727
      exp.push_back(ee);
258
    }
259
  }
260
  // if we are a symbolic constructor, unfolding returns the subterm itself
261
21454
  Node sop = dt[i].getSygusOp();
262
10727
  if (sop.getAttribute(SygusAnyConstAttribute()))
263
  {
264
1980
    Trace("sygus-eval-unfold-debug")
265
990
        << "...it is an any-constant constructor" << std::endl;
266
990
    Assert(dt[i].getNumArgs() == 1);
267
    // If the argument to evaluate is itself concrete, then we use its
268
    // argument; otherwise we return its selector.
269
990
    if (en[0].getKind() == APPLY_CONSTRUCTOR)
270
    {
271
      Trace("sygus-eval-unfold-debug")
272
          << "...return (from constructor) " << en[0][0] << std::endl;
273
      return en[0][0];
274
    }
275
    else
276
    {
277
      Node ret = nm->mkNode(
278
1980
          APPLY_SELECTOR_TOTAL, dt[i].getSelectorInternal(headType, 0), en[0]);
279
1980
      Trace("sygus-eval-unfold-debug")
280
990
          << "...return (from constructor) " << ret << std::endl;
281
990
      return ret;
282
    }
283
  }
284
285
9737
  Assert(!dt.isParametric());
286
19474
  std::map<int, Node> pre;
287
26634
  for (unsigned j = 0, nargs = dt[i].getNumArgs(); j < nargs; j++)
288
  {
289
33794
    std::vector<Node> cc;
290
33794
    Node s;
291
    // get the j^th subfield of en
292
16897
    if (en[0].getKind() == APPLY_CONSTRUCTOR)
293
    {
294
      // if it is a concrete constructor application, as an optimization,
295
      // just return the argument
296
      s = en[0][j];
297
    }
298
    else
299
    {
300
50691
      s = nm->mkNode(
301
33794
          APPLY_SELECTOR_TOTAL, dt[i].getSelectorInternal(headType, j), en[0]);
302
    }
303
16897
    cc.push_back(s);
304
16897
    if (track_exp)
305
    {
306
      // update vtm map
307
16897
      vtm[s] = ev[j];
308
    }
309
16897
    cc.insert(cc.end(), args.begin(), args.end());
310
33794
    Node argj = nm->mkNode(DT_SYGUS_EVAL, cc);
311
16897
    if (doRec)
312
    {
313
2560
      Trace("sygus-eval-unfold-debug") << "Recurse on " << s << std::endl;
314
      // evaluate recursively
315
2560
      argj = unfold(argj, vtm, exp, track_exp, doRec);
316
    }
317
16897
    pre[j] = argj;
318
  }
319
19474
  Node ret = d_tds->mkGeneric(dt, i, pre);
320
  // apply the appropriate substitution to ret
321
9737
  ret = datatypes::utils::applySygusArgs(dt, sop, ret, args);
322
19474
  Trace("sygus-eval-unfold-debug")
323
9737
      << "Applied sygus args : " << ret << std::endl;
324
  // rewrite
325
9737
  ret = rewrite(ret);
326
9737
  Trace("sygus-eval-unfold-debug") << "Rewritten : " << ret << std::endl;
327
9737
  return ret;
328
}
329
330
}  // namespace quantifiers
331
}  // namespace theory
332
31137
}  // namespace cvc5