GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/quantifiers/candidate_rewrite_database.cpp Lines: 101 130 77.7 %
Date: 2021-11-07 Branches: 159 478 33.3 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Aina Niemetz, Andres Noetzli
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 candidate_rewrite_database.
14
 */
15
16
#include "theory/quantifiers/candidate_rewrite_database.h"
17
18
#include "options/base_options.h"
19
#include "printer/printer.h"
20
#include "smt/smt_statistics_registry.h"
21
#include "smt/solver_engine.h"
22
#include "smt/solver_engine_scope.h"
23
#include "theory/datatypes/sygus_datatype_utils.h"
24
#include "theory/quantifiers/sygus/term_database_sygus.h"
25
#include "theory/quantifiers/term_util.h"
26
#include "theory/rewriter.h"
27
28
using namespace std;
29
using namespace cvc5::kind;
30
using namespace cvc5::context;
31
32
namespace cvc5 {
33
namespace theory {
34
namespace quantifiers {
35
36
71
CandidateRewriteDatabase::CandidateRewriteDatabase(
37
71
    Env& env, bool doCheck, bool rewAccel, bool silent, bool filterPairs)
38
    : ExprMiner(env),
39
      d_tds(nullptr),
40
      d_useExtRewriter(false),
41
      d_doCheck(doCheck),
42
      d_rewAccel(rewAccel),
43
      d_silent(silent),
44
      d_filterPairs(filterPairs),
45
      d_using_sygus(false),
46
71
      d_crewrite_filter(env)
47
{
48
71
}
49
57
void CandidateRewriteDatabase::initialize(const std::vector<Node>& vars,
50
                                          SygusSampler* ss)
51
{
52
57
  Assert(ss != nullptr);
53
57
  d_candidate = Node::null();
54
57
  d_using_sygus = false;
55
57
  d_tds = nullptr;
56
57
  d_useExtRewriter = false;
57
57
  if (d_filterPairs)
58
  {
59
    d_crewrite_filter.initialize(ss, nullptr, false);
60
  }
61
57
  ExprMiner::initialize(vars, ss);
62
57
}
63
64
8
void CandidateRewriteDatabase::initializeSygus(const std::vector<Node>& vars,
65
                                               TermDbSygus* tds,
66
                                               Node f,
67
                                               SygusSampler* ss)
68
{
69
8
  Assert(ss != nullptr);
70
8
  d_candidate = f;
71
8
  d_using_sygus = true;
72
8
  d_tds = tds;
73
8
  d_useExtRewriter = false;
74
8
  if (d_filterPairs)
75
  {
76
8
    d_crewrite_filter.initialize(ss, d_tds, d_using_sygus);
77
  }
78
8
  ExprMiner::initialize(vars, ss);
79
8
}
80
81
2171
Node CandidateRewriteDatabase::addTerm(Node sol,
82
                                       bool rec,
83
                                       std::ostream& out,
84
                                       bool& rew_print)
85
{
86
  // have we added this term before?
87
2171
  std::unordered_map<Node, Node>::iterator itac = d_add_term_cache.find(sol);
88
2171
  if (itac != d_add_term_cache.end())
89
  {
90
57
    return itac->second;
91
  }
92
93
2114
  if (rec)
94
  {
95
    // if recursive, we first add all subterms
96
    for (const Node& solc : sol)
97
    {
98
      // whether a candidate rewrite is printed for any subterm is irrelevant
99
      bool rew_printc = false;
100
      addTerm(solc, rec, out, rew_printc);
101
    }
102
  }
103
  // register the term
104
2114
  bool is_unique_term = true;
105
4228
  Node eq_sol = d_sampler->registerTerm(sol);
106
  // eq_sol is a candidate solution that is equivalent to sol
107
2114
  if (eq_sol != sol)
108
  {
109
446
    is_unique_term = false;
110
    // should we filter the pair?
111
446
    if (!d_filterPairs || !d_crewrite_filter.filterPair(sol, eq_sol))
112
    {
113
      // get the actual term
114
596
      Node solb = sol;
115
596
      Node eq_solb = eq_sol;
116
298
      if (d_using_sygus)
117
      {
118
162
        Assert(d_tds != nullptr);
119
162
        solb = d_tds->sygusToBuiltin(sol);
120
162
        eq_solb = d_tds->sygusToBuiltin(eq_sol);
121
      }
122
      // get the rewritten form
123
596
      Node solbr;
124
596
      Node eq_solr;
125
298
      if (d_useExtRewriter)
126
      {
127
162
        solbr = extendedRewrite(solb);
128
162
        eq_solr = extendedRewrite(eq_solb);
129
      }
130
      else
131
      {
132
136
        solbr = rewrite(solb);
133
136
        eq_solr = rewrite(eq_solb);
134
      }
135
298
      bool verified = false;
136
298
      Trace("rr-check") << "Check candidate rewrite..." << std::endl;
137
      // verify it if applicable
138
298
      if (d_doCheck)
139
      {
140
508
        Node crr = solbr.eqNode(eq_solr).negate();
141
254
        Trace("rr-check") << "Check candidate rewrite : " << crr << std::endl;
142
143
        // Notice we don't set produce-models. rrChecker takes the same
144
        // options as the SolverEngine we belong to, where we ensure that
145
        // produce-models is set.
146
508
        std::unique_ptr<SolverEngine> rrChecker;
147
254
        initializeChecker(rrChecker, crr);
148
508
        Result r = rrChecker->checkSat();
149
254
        Trace("rr-check") << "...result : " << r << std::endl;
150
254
        if (r.asSatisfiabilityResult().isSat() == Result::SAT)
151
        {
152
135
          Trace("rr-check") << "...rewrite does not hold for: " << std::endl;
153
135
          is_unique_term = true;
154
270
          std::vector<Node> vars;
155
135
          d_sampler->getVariables(vars);
156
270
          std::vector<Node> pt;
157
463
          for (const Node& v : vars)
158
          {
159
656
            Node val;
160
656
            Node refv = v;
161
            // if a bound variable, map to the skolem we introduce before
162
            // looking up the model value
163
328
            if (v.getKind() == BOUND_VARIABLE)
164
            {
165
328
              std::map<Node, Node>::iterator itf = d_fv_to_skolem.find(v);
166
328
              if (itf == d_fv_to_skolem.end())
167
              {
168
                // not in conjecture, can use arbitrary value
169
                val = v.getType().mkGroundTerm();
170
              }
171
              else
172
              {
173
                // get the model value of its skolem
174
328
                refv = itf->second;
175
              }
176
            }
177
328
            if (val.isNull())
178
            {
179
328
              Assert(!refv.isNull() && refv.getKind() != BOUND_VARIABLE);
180
328
              val = rrChecker->getValue(refv);
181
            }
182
328
            Trace("rr-check") << "  " << v << " -> " << val << std::endl;
183
328
            pt.push_back(val);
184
          }
185
135
          d_sampler->addSamplePoint(pt);
186
          // add the solution again
187
          // by construction of the above point, we should be unique now
188
135
          eq_sol = d_sampler->registerTerm(sol);
189
135
          Assert(eq_sol == sol);
190
        }
191
        else
192
        {
193
119
          verified = !r.asSatisfiabilityResult().isUnknown();
194
        }
195
      }
196
      else
197
      {
198
        // just insist that constants are not relevant pairs
199
44
        if (solb.isConst() && eq_solb.isConst())
200
        {
201
          is_unique_term = true;
202
          eq_sol = sol;
203
        }
204
      }
205
298
      if (!is_unique_term)
206
      {
207
        // register this as a relevant pair (helps filtering)
208
163
        if (d_filterPairs)
209
        {
210
146
          d_crewrite_filter.registerRelevantPair(sol, eq_sol);
211
        }
212
        // The analog of terms sol and eq_sol are equivalent under
213
        // sample points but do not rewrite to the same term. Hence,
214
        // this indicates a candidate rewrite.
215
163
        if (!d_silent)
216
        {
217
146
          out << "(" << (verified ? "" : "candidate-") << "rewrite ";
218
146
          if (d_using_sygus)
219
          {
220
146
            TermDbSygus::toStreamSygus(out, sol);
221
146
            out << " ";
222
146
            TermDbSygus::toStreamSygus(out, eq_sol);
223
          }
224
          else
225
          {
226
            out << sol << " " << eq_sol;
227
          }
228
146
          out << ")" << std::endl;
229
        }
230
        // we count this as printed, despite not literally printing it
231
163
        rew_print = true;
232
        // debugging information
233
163
        if (Trace.isOn("sygus-rr-debug"))
234
        {
235
          Trace("sygus-rr-debug") << "; candidate #1 ext-rewrites to: " << solbr
236
                                  << std::endl;
237
          Trace("sygus-rr-debug")
238
              << "; candidate #2 ext-rewrites to: " << eq_solr << std::endl;
239
        }
240
163
        if (d_rewAccel && d_using_sygus)
241
        {
242
          Assert(d_tds != nullptr);
243
          // Add a symmetry breaking clause that excludes the larger
244
          // of sol and eq_sol. This effectively states that we no longer
245
          // wish to enumerate any term that contains sol (resp. eq_sol)
246
          // as a subterm.
247
          Node exc_sol = sol;
248
          unsigned sz = datatypes::utils::getSygusTermSize(sol);
249
          unsigned eqsz = datatypes::utils::getSygusTermSize(eq_sol);
250
          if (eqsz > sz)
251
          {
252
            sz = eqsz;
253
            exc_sol = eq_sol;
254
          }
255
          TypeNode ptn = d_candidate.getType();
256
          Node x = d_tds->getFreeVar(ptn, 0);
257
          Node lem = d_tds->getExplain()->getExplanationForEquality(x, exc_sol);
258
          lem = lem.negate();
259
          Trace("sygus-rr-sb") << "Symmetry breaking lemma : " << lem
260
                               << std::endl;
261
          d_tds->registerSymBreakLemma(d_candidate, lem, ptn, sz);
262
        }
263
      }
264
    }
265
    // We count this as a rewrite if we did not explicitly rule it out.
266
    // The value of is_unique_term is false iff this call resulted in a rewrite.
267
    // Notice that when --sygus-rr-synth-check is enabled,
268
    // statistics on number of candidate rewrite rules is
269
    // an accurate count of (#enumerated_terms-#unique_terms) only if
270
    // the option sygus-rr-synth-filter-order is disabled. The reason
271
    // is that the sygus sampler may reason that a candidate rewrite
272
    // rule is not useful since its variables are unordered, whereby
273
    // it discards it as a redundant candidate rewrite rule before
274
    // checking its correctness.
275
  }
276
2114
  d_add_term_cache[sol] = eq_sol;
277
2114
  return eq_sol;
278
}
279
280
794
Node CandidateRewriteDatabase::addTerm(Node sol, bool rec, std::ostream& out)
281
{
282
794
  bool rew_print = false;
283
794
  return addTerm(sol, rec, out, rew_print);
284
}
285
bool CandidateRewriteDatabase::addTerm(Node sol, std::ostream& out)
286
{
287
  Node rsol = addTerm(sol, false, out);
288
  return sol == rsol;
289
}
290
291
8
void CandidateRewriteDatabase::setSilent(bool flag) { d_silent = flag; }
292
293
8
void CandidateRewriteDatabase::enableExtendedRewriter()
294
{
295
8
  d_useExtRewriter = true;
296
8
}
297
298
}  // namespace quantifiers
299
}  // namespace theory
300
31137
}  // namespace cvc5