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