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