1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Haniel Barbosa, 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 |
|
* Utility for processing programming by examples synthesis conjectures. |
14 |
|
*/ |
15 |
|
#include "theory/quantifiers/sygus/sygus_pbe.h" |
16 |
|
|
17 |
|
#include "options/quantifiers_options.h" |
18 |
|
#include "theory/datatypes/sygus_datatype_utils.h" |
19 |
|
#include "theory/quantifiers/sygus/example_infer.h" |
20 |
|
#include "theory/quantifiers/sygus/sygus_unif_io.h" |
21 |
|
#include "theory/quantifiers/sygus/synth_conjecture.h" |
22 |
|
#include "theory/quantifiers/sygus/term_database_sygus.h" |
23 |
|
#include "theory/quantifiers/term_util.h" |
24 |
|
#include "util/random.h" |
25 |
|
|
26 |
|
using namespace cvc5; |
27 |
|
using namespace cvc5::kind; |
28 |
|
|
29 |
|
namespace cvc5 { |
30 |
|
namespace theory { |
31 |
|
namespace quantifiers { |
32 |
|
|
33 |
1233 |
SygusPbe::SygusPbe(Env& env, |
34 |
|
QuantifiersState& qs, |
35 |
|
QuantifiersInferenceManager& qim, |
36 |
|
TermDbSygus* tds, |
37 |
1233 |
SynthConjecture* p) |
38 |
1233 |
: SygusModule(env, qs, qim, tds, p) |
39 |
|
{ |
40 |
1233 |
d_true = NodeManager::currentNM()->mkConst(true); |
41 |
1233 |
d_false = NodeManager::currentNM()->mkConst(false); |
42 |
1233 |
d_is_pbe = false; |
43 |
1233 |
} |
44 |
|
|
45 |
2462 |
SygusPbe::~SygusPbe() {} |
46 |
|
|
47 |
240 |
bool SygusPbe::initialize(Node conj, |
48 |
|
Node n, |
49 |
|
const std::vector<Node>& candidates) |
50 |
|
{ |
51 |
240 |
Trace("sygus-pbe") << "Initialize PBE : " << n << std::endl; |
52 |
240 |
NodeManager* nm = NodeManager::currentNM(); |
53 |
|
|
54 |
240 |
if (!options::sygusUnifPbe()) |
55 |
|
{ |
56 |
|
// we are not doing unification |
57 |
31 |
return false; |
58 |
|
} |
59 |
|
|
60 |
|
// check if all candidates are valid examples |
61 |
209 |
ExampleInfer* ei = d_parent->getExampleInfer(); |
62 |
209 |
d_is_pbe = true; |
63 |
256 |
for (const Node& c : candidates) |
64 |
|
{ |
65 |
|
// if it has no examples or the output of the examples is invalid |
66 |
212 |
if (ei->getNumExamples(c) == 0 || !ei->hasExamplesOut(c)) |
67 |
|
{ |
68 |
165 |
d_is_pbe = false; |
69 |
165 |
return false; |
70 |
|
} |
71 |
|
} |
72 |
91 |
for (const Node& c : candidates) |
73 |
|
{ |
74 |
47 |
Assert(ei->hasExamples(c)); |
75 |
47 |
d_sygus_unif[c].reset(new SygusUnifIo(d_env, d_parent)); |
76 |
94 |
Trace("sygus-pbe") << "Initialize unif utility for " << c << "..." |
77 |
47 |
<< std::endl; |
78 |
94 |
std::map<Node, std::vector<Node>> strategy_lemmas; |
79 |
94 |
d_sygus_unif[c]->initializeCandidate( |
80 |
47 |
d_tds, c, d_candidate_to_enum[c], strategy_lemmas); |
81 |
47 |
Assert(!d_candidate_to_enum[c].empty()); |
82 |
94 |
Trace("sygus-pbe") << "Initialize " << d_candidate_to_enum[c].size() |
83 |
47 |
<< " enumerators for " << c << "..." << std::endl; |
84 |
|
// collect list per type of strategy points with strategy lemmas |
85 |
94 |
std::map<TypeNode, std::vector<Node>> tn_to_strategy_pt; |
86 |
101 |
for (const std::pair<const Node, std::vector<Node>>& p : strategy_lemmas) |
87 |
|
{ |
88 |
108 |
TypeNode tnsp = p.first.getType(); |
89 |
54 |
tn_to_strategy_pt[tnsp].push_back(p.first); |
90 |
|
} |
91 |
|
// initialize the enumerators |
92 |
110 |
for (const Node& e : d_candidate_to_enum[c]) |
93 |
|
{ |
94 |
126 |
TypeNode etn = e.getType(); |
95 |
63 |
d_tds->registerEnumerator(e, c, d_parent, ROLE_ENUM_POOL); |
96 |
63 |
d_enum_to_candidate[e] = c; |
97 |
126 |
TNode te = e; |
98 |
|
// initialize static symmetry breaking lemmas for it |
99 |
|
// we register only one "master" enumerator per type |
100 |
|
// thus, the strategy lemmas (which are for individual strategy points) |
101 |
|
// are applicable (disjunctively) to the master enumerator |
102 |
|
std::map<TypeNode, std::vector<Node>>::iterator itt = |
103 |
63 |
tn_to_strategy_pt.find(etn); |
104 |
63 |
if (itt != tn_to_strategy_pt.end()) |
105 |
|
{ |
106 |
72 |
std::vector<Node> disj; |
107 |
88 |
for (const Node& sp : itt->second) |
108 |
|
{ |
109 |
|
std::map<Node, std::vector<Node>>::iterator itsl = |
110 |
52 |
strategy_lemmas.find(sp); |
111 |
52 |
Assert(itsl != strategy_lemmas.end()); |
112 |
52 |
if (!itsl->second.empty()) |
113 |
|
{ |
114 |
104 |
TNode tsp = sp; |
115 |
98 |
Node lem = itsl->second.size() == 1 ? itsl->second[0] |
116 |
150 |
: nm->mkNode(AND, itsl->second); |
117 |
52 |
if (tsp != te) |
118 |
|
{ |
119 |
16 |
lem = lem.substitute(tsp, te); |
120 |
|
} |
121 |
52 |
if (std::find(disj.begin(), disj.end(), lem) == disj.end()) |
122 |
|
{ |
123 |
38 |
disj.push_back(lem); |
124 |
|
} |
125 |
|
} |
126 |
|
} |
127 |
|
// add its active guard |
128 |
72 |
Node ag = d_tds->getActiveGuardForEnumerator(e); |
129 |
36 |
Assert(!ag.isNull()); |
130 |
36 |
disj.push_back(ag.negate()); |
131 |
72 |
Node lem = disj.size() == 1 ? disj[0] : nm->mkNode(OR, disj); |
132 |
|
// Apply extended rewriting on the lemma. This helps utilities like |
133 |
|
// SygusEnumerator more easily recognize the shape of this lemma, e.g. |
134 |
|
// ( ~is-ite(x) or ( ~is-ite(x) ^ P ) ) --> ~is-ite(x). |
135 |
36 |
lem = extendedRewrite(lem); |
136 |
72 |
Trace("sygus-pbe") << " static redundant op lemma : " << lem |
137 |
36 |
<< std::endl; |
138 |
|
// Register as a symmetry breaking lemma with the term database. |
139 |
|
// This will either be processed via a lemma on the output channel |
140 |
|
// of the sygus extension of the datatypes solver, or internally |
141 |
|
// encoded as a constraint to an active enumerator. |
142 |
36 |
d_tds->registerSymBreakLemma(e, lem, etn, 0, false); |
143 |
|
} |
144 |
|
} |
145 |
|
} |
146 |
44 |
return true; |
147 |
|
} |
148 |
|
|
149 |
|
// ------------------------------------------- solution construction from enumeration |
150 |
|
|
151 |
3744 |
void SygusPbe::getTermList(const std::vector<Node>& candidates, |
152 |
|
std::vector<Node>& terms) |
153 |
|
{ |
154 |
7582 |
for( unsigned i=0; i<candidates.size(); i++ ){ |
155 |
7676 |
Node v = candidates[i]; |
156 |
|
std::map<Node, std::vector<Node> >::iterator it = |
157 |
3838 |
d_candidate_to_enum.find(v); |
158 |
3838 |
if (it != d_candidate_to_enum.end()) |
159 |
|
{ |
160 |
3838 |
terms.insert(terms.end(), it->second.begin(), it->second.end()); |
161 |
|
} |
162 |
|
} |
163 |
3744 |
} |
164 |
|
|
165 |
3744 |
bool SygusPbe::allowPartialModel() { return !options::sygusPbeMultiFair(); } |
166 |
|
|
167 |
517 |
bool SygusPbe::constructCandidates(const std::vector<Node>& enums, |
168 |
|
const std::vector<Node>& enum_values, |
169 |
|
const std::vector<Node>& candidates, |
170 |
|
std::vector<Node>& candidate_values) |
171 |
|
{ |
172 |
517 |
Assert(enums.size() == enum_values.size()); |
173 |
517 |
if( !enums.empty() ){ |
174 |
517 |
unsigned min_term_size = 0; |
175 |
517 |
Trace("sygus-pbe-enum") << "Register new enumerated values : " << std::endl; |
176 |
1034 |
std::vector<unsigned> szs; |
177 |
1243 |
for (unsigned i = 0, esize = enums.size(); i < esize; i++) |
178 |
|
{ |
179 |
726 |
Trace("sygus-pbe-enum") << " " << enums[i] << " -> "; |
180 |
726 |
TermDbSygus::toStreamSygus("sygus-pbe-enum", enum_values[i]); |
181 |
726 |
Trace("sygus-pbe-enum") << std::endl; |
182 |
726 |
if (!enum_values[i].isNull()) |
183 |
|
{ |
184 |
583 |
unsigned sz = datatypes::utils::getSygusTermSize(enum_values[i]); |
185 |
583 |
szs.push_back(sz); |
186 |
583 |
if (i == 0 || sz < min_term_size) |
187 |
|
{ |
188 |
487 |
min_term_size = sz; |
189 |
|
} |
190 |
|
} |
191 |
|
else |
192 |
|
{ |
193 |
143 |
szs.push_back(0); |
194 |
|
} |
195 |
|
} |
196 |
|
// Assume two enumerators of types T1 and T2. |
197 |
|
// If options::sygusPbeMultiFair() is true, |
198 |
|
// we ensure that all values of type T1 and size n are enumerated before |
199 |
|
// any term of type T2 of size n+d, and vice versa, where d is |
200 |
|
// set by options::sygusPbeMultiFairDiff(). If d is zero, then our |
201 |
|
// enumeration is such that all terms of T1 or T2 of size n are considered |
202 |
|
// before any term of size n+1. |
203 |
517 |
int diffAllow = options::sygusPbeMultiFairDiff(); |
204 |
1034 |
std::vector<unsigned> enum_consider; |
205 |
1243 |
for (unsigned i = 0, esize = enums.size(); i < esize; i++) |
206 |
|
{ |
207 |
726 |
if (!enum_values[i].isNull()) |
208 |
|
{ |
209 |
583 |
Assert(szs[i] >= min_term_size); |
210 |
583 |
int diff = szs[i] - min_term_size; |
211 |
583 |
if (!options::sygusPbeMultiFair() || diff <= diffAllow) |
212 |
|
{ |
213 |
583 |
enum_consider.push_back(i); |
214 |
|
} |
215 |
|
} |
216 |
|
} |
217 |
|
|
218 |
|
// only consider the enumerators that are at minimum size (for fairness) |
219 |
517 |
Trace("sygus-pbe-enum") << "...register " << enum_consider.size() << " / " << enums.size() << std::endl; |
220 |
517 |
NodeManager* nm = NodeManager::currentNM(); |
221 |
1100 |
for (unsigned i = 0, ecsize = enum_consider.size(); i < ecsize; i++) |
222 |
|
{ |
223 |
583 |
unsigned j = enum_consider[i]; |
224 |
1166 |
Node e = enums[j]; |
225 |
1166 |
Node v = enum_values[j]; |
226 |
583 |
Assert(d_enum_to_candidate.find(e) != d_enum_to_candidate.end()); |
227 |
1166 |
Node c = d_enum_to_candidate[e]; |
228 |
1166 |
std::vector<Node> enum_lems; |
229 |
583 |
d_sygus_unif[c]->notifyEnumeration(e, v, enum_lems); |
230 |
583 |
if (!enum_lems.empty()) |
231 |
|
{ |
232 |
|
// the lemmas must be guarded by the active guard of the enumerator |
233 |
14 |
Node g = d_tds->getActiveGuardForEnumerator(e); |
234 |
7 |
Assert(!g.isNull()); |
235 |
14 |
for (unsigned k = 0, size = enum_lems.size(); k < size; k++) |
236 |
|
{ |
237 |
14 |
Node lem = nm->mkNode(OR, g.negate(), enum_lems[k]); |
238 |
7 |
d_qim.addPendingLemma(lem, |
239 |
|
InferenceId::QUANTIFIERS_SYGUS_PBE_EXCLUDE); |
240 |
|
} |
241 |
|
} |
242 |
|
} |
243 |
|
} |
244 |
568 |
for( unsigned i=0; i<candidates.size(); i++ ){ |
245 |
576 |
Node c = candidates[i]; |
246 |
|
//build decision tree for candidate |
247 |
576 |
std::vector<Node> sol; |
248 |
576 |
std::vector<Node> lems; |
249 |
525 |
bool solSuccess = d_sygus_unif[c]->constructSolution(sol, lems); |
250 |
525 |
for (const Node& lem : lems) |
251 |
|
{ |
252 |
|
d_qim.addPendingLemma(lem, |
253 |
|
InferenceId::QUANTIFIERS_SYGUS_PBE_CONSTRUCT_SOL); |
254 |
|
} |
255 |
525 |
if (solSuccess) |
256 |
|
{ |
257 |
51 |
Assert(sol.size() == 1); |
258 |
51 |
candidate_values.push_back(sol[0]); |
259 |
|
} |
260 |
|
else |
261 |
|
{ |
262 |
474 |
return false; |
263 |
|
} |
264 |
|
} |
265 |
43 |
return true; |
266 |
|
} |
267 |
|
|
268 |
|
} |
269 |
|
} |
270 |
29574 |
} // namespace cvc5 |