1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Haniel Barbosa, Mathias Preiner |
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 class for cegis with unification techniques. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "theory/quantifiers/sygus/cegis_unif.h" |
17 |
|
|
18 |
|
#include "expr/skolem_manager.h" |
19 |
|
#include "expr/sygus_datatype.h" |
20 |
|
#include "options/quantifiers_options.h" |
21 |
|
#include "printer/printer.h" |
22 |
|
#include "theory/datatypes/sygus_datatype_utils.h" |
23 |
|
#include "theory/quantifiers/sygus/sygus_unif_rl.h" |
24 |
|
#include "theory/quantifiers/sygus/synth_conjecture.h" |
25 |
|
#include "theory/quantifiers/sygus/term_database_sygus.h" |
26 |
|
|
27 |
|
using namespace cvc5::kind; |
28 |
|
|
29 |
|
namespace cvc5 { |
30 |
|
namespace theory { |
31 |
|
namespace quantifiers { |
32 |
|
|
33 |
1151 |
CegisUnif::CegisUnif(QuantifiersState& qs, |
34 |
|
QuantifiersInferenceManager& qim, |
35 |
|
TermDbSygus* tds, |
36 |
1151 |
SynthConjecture* p) |
37 |
1151 |
: Cegis(qim, tds, p), d_sygus_unif(p), d_u_enum_manager(qs, qim, tds, p) |
38 |
|
{ |
39 |
1151 |
} |
40 |
|
|
41 |
2302 |
CegisUnif::~CegisUnif() {} |
42 |
11 |
bool CegisUnif::processInitialize(Node conj, |
43 |
|
Node n, |
44 |
|
const std::vector<Node>& candidates) |
45 |
|
{ |
46 |
|
// list of strategy points for unification candidates |
47 |
22 |
std::vector<Node> unif_candidate_pts; |
48 |
|
// map from strategy points to their conditions |
49 |
22 |
std::map<Node, Node> pt_to_cond; |
50 |
|
// strategy lemmas for each strategy point |
51 |
22 |
std::map<Node, std::vector<Node>> strategy_lemmas; |
52 |
|
// Initialize strategies for all functions-to-synthesize |
53 |
|
// The role of non-unification enumerators is to be either the single solution |
54 |
|
// or part of a solution involving multiple enumerators. |
55 |
11 |
EnumeratorRole eroleNonUnif = candidates.size() == 1 |
56 |
11 |
? ROLE_ENUM_SINGLE_SOLUTION |
57 |
11 |
: ROLE_ENUM_MULTI_SOLUTION; |
58 |
25 |
for (const Node& f : candidates) |
59 |
|
{ |
60 |
|
// Init UNIF util for this candidate |
61 |
28 |
d_sygus_unif.initializeCandidate( |
62 |
14 |
d_tds, f, d_cand_to_strat_pt[f], strategy_lemmas); |
63 |
14 |
if (!d_sygus_unif.usingUnif(f)) |
64 |
|
{ |
65 |
3 |
Trace("cegis-unif") << "* non-unification candidate : " << f << std::endl; |
66 |
3 |
d_tds->registerEnumerator(f, f, d_parent, eroleNonUnif); |
67 |
3 |
d_non_unif_candidates.push_back(f); |
68 |
|
} |
69 |
|
else |
70 |
|
{ |
71 |
11 |
d_unif_candidates.push_back(f); |
72 |
22 |
Trace("cegis-unif") << "* unification candidate : " << f |
73 |
11 |
<< " with strategy points:" << std::endl; |
74 |
11 |
std::vector<Node>& enums = d_cand_to_strat_pt[f]; |
75 |
11 |
unif_candidate_pts.insert( |
76 |
22 |
unif_candidate_pts.end(), enums.begin(), enums.end()); |
77 |
|
// map strategy point to its condition in pt_to_cond |
78 |
22 |
for (const Node& e : enums) |
79 |
|
{ |
80 |
22 |
Node cond = d_sygus_unif.getConditionForEvaluationPoint(e); |
81 |
11 |
Assert(!cond.isNull()); |
82 |
22 |
Trace("cegis-unif") |
83 |
11 |
<< " " << e << " with condition : " << cond << std::endl; |
84 |
11 |
pt_to_cond[e] = cond; |
85 |
|
} |
86 |
|
} |
87 |
|
} |
88 |
|
// initialize the enumeration manager |
89 |
11 |
d_u_enum_manager.initialize(unif_candidate_pts, pt_to_cond, strategy_lemmas); |
90 |
22 |
return true; |
91 |
|
} |
92 |
|
|
93 |
408 |
void CegisUnif::getTermList(const std::vector<Node>& candidates, |
94 |
|
std::vector<Node>& enums) |
95 |
|
{ |
96 |
|
// Non-unif candidate are themselves the enumerators |
97 |
408 |
enums.insert( |
98 |
816 |
enums.end(), d_non_unif_candidates.begin(), d_non_unif_candidates.end()); |
99 |
816 |
for (const Node& c : d_unif_candidates) |
100 |
|
{ |
101 |
|
// Collect heads of candidates |
102 |
3482 |
for (const Node& hd : d_sygus_unif.getEvalPointHeads(c)) |
103 |
|
{ |
104 |
6148 |
Trace("cegis-unif-enum-debug") |
105 |
3074 |
<< "......cand " << c << " with enum hd " << hd << "\n"; |
106 |
3074 |
enums.push_back(hd); |
107 |
|
} |
108 |
|
// get unification enumerators |
109 |
816 |
for (const Node& e : d_cand_to_strat_pt[c]) |
110 |
|
{ |
111 |
1224 |
for (unsigned index = 0; index < 2; index++) |
112 |
|
{ |
113 |
1632 |
std::vector<Node> uenums; |
114 |
|
// get the current unification enumerators |
115 |
816 |
d_u_enum_manager.getEnumeratorsForStrategyPt(e, uenums, index); |
116 |
|
// get the model value of each enumerator |
117 |
816 |
enums.insert(enums.end(), uenums.begin(), uenums.end()); |
118 |
|
} |
119 |
|
} |
120 |
|
} |
121 |
408 |
} |
122 |
|
|
123 |
359 |
bool CegisUnif::getEnumValues(const std::vector<Node>& enums, |
124 |
|
const std::vector<Node>& enum_values, |
125 |
|
std::map<Node, std::vector<Node>>& unif_cenums, |
126 |
|
std::map<Node, std::vector<Node>>& unif_cvalues) |
127 |
|
{ |
128 |
359 |
NodeManager* nm = NodeManager::currentNM(); |
129 |
718 |
Node cost_lit = d_u_enum_manager.getAssertedLiteral(); |
130 |
718 |
std::map<Node, std::vector<Node>> unif_renums, unif_rvalues; |
131 |
|
// build model value map |
132 |
718 |
std::map<Node, Node> mvMap; |
133 |
4197 |
for (unsigned i = 0, size = enums.size(); i < size; i++) |
134 |
|
{ |
135 |
3838 |
mvMap[enums[i]] = enum_values[i]; |
136 |
|
} |
137 |
359 |
bool addedUnifEnumSymBreakLemma = false; |
138 |
|
// populate maps between unification enumerators and their model values |
139 |
718 |
for (const Node& c : d_unif_candidates) |
140 |
|
{ |
141 |
|
// for each decision tree strategy allocated for c (these are referenced |
142 |
|
// by strategy points in d_cand_to_strat_pt[c]) |
143 |
718 |
for (const Node& e : d_cand_to_strat_pt[c]) |
144 |
|
{ |
145 |
1077 |
for (unsigned index = 0; index < 2; index++) |
146 |
|
{ |
147 |
1436 |
std::vector<Node> es, vs; |
148 |
1436 |
Trace("cegis-unif") |
149 |
1436 |
<< " " << (index == 0 ? "Return values" : "Conditions") << " for " |
150 |
718 |
<< e << ":\n"; |
151 |
|
// get the current unification enumerators |
152 |
718 |
d_u_enum_manager.getEnumeratorsForStrategyPt(e, es, index); |
153 |
|
// set enums for condition enumerators |
154 |
718 |
if (index == 1) |
155 |
|
{ |
156 |
359 |
if (usingConditionPool()) |
157 |
|
{ |
158 |
|
Assert(es.size() == 1); |
159 |
|
// whether valueus exhausted |
160 |
|
if (mvMap.find(es[0]) == mvMap.end()) |
161 |
|
{ |
162 |
|
Trace("cegis-unif") << " " << es[0] << " -> N/A\n"; |
163 |
|
es.clear(); |
164 |
|
} |
165 |
|
} |
166 |
359 |
unif_cenums[e] = es; |
167 |
|
} |
168 |
|
// get the model value of each enumerator |
169 |
1701 |
for (const Node& eu : es) |
170 |
|
{ |
171 |
983 |
Assert(mvMap.find(eu) != mvMap.end()); |
172 |
1966 |
Node m_eu = mvMap[eu]; |
173 |
983 |
if (Trace.isOn("cegis-unif")) |
174 |
|
{ |
175 |
|
Trace("cegis-unif") << " " << eu << " -> "; |
176 |
|
TermDbSygus::toStreamSygus("cegis-unif", m_eu); |
177 |
|
Trace("cegis-unif") << "\n"; |
178 |
|
} |
179 |
983 |
vs.push_back(m_eu); |
180 |
|
} |
181 |
|
// set values for condition enumerators of e |
182 |
718 |
if (index == 1) |
183 |
|
{ |
184 |
359 |
unif_cvalues[e] = vs; |
185 |
|
} |
186 |
|
// inter-enumerator symmetry breaking for return values |
187 |
|
else |
188 |
|
{ |
189 |
|
// given a pool of unification enumerators eu_1, ..., eu_n, |
190 |
|
// CegisUnifEnumDecisionStrategy insists that size(eu_1) <= ... <= |
191 |
|
// size(eu_n). We additionally insist that M(eu_i) < M(eu_{i+1}) when |
192 |
|
// size(eu_i) = size(eu_{i+1}), where < is pointer comparison. |
193 |
|
// We enforce this below by adding symmetry breaking lemmas of the |
194 |
|
// form ~( eu_i = M(eu_i) ^ eu_{i+1} = M(eu_{i+1} ) ) |
195 |
|
// when applicable. |
196 |
|
// we only do this for return value enumerators, since condition |
197 |
|
// enumerators cannot be ordered (their order is based on the |
198 |
|
// seperation resolution scheme during model construction). |
199 |
645 |
for (unsigned j = 1, nenum = vs.size(); j < nenum; j++) |
200 |
|
{ |
201 |
595 |
Node prev_val = vs[j - 1]; |
202 |
595 |
Node curr_val = vs[j]; |
203 |
|
// compare the node values |
204 |
309 |
if (curr_val < prev_val) |
205 |
|
{ |
206 |
|
// must have the same size |
207 |
23 |
unsigned prev_size = datatypes::utils::getSygusTermSize(prev_val); |
208 |
23 |
unsigned curr_size = datatypes::utils::getSygusTermSize(curr_val); |
209 |
23 |
Assert(prev_size <= curr_size); |
210 |
23 |
if (curr_size == prev_size) |
211 |
|
{ |
212 |
|
Node slem = |
213 |
92 |
nm->mkNode( |
214 |
92 |
AND, es[j - 1].eqNode(vs[j - 1]), es[j].eqNode(vs[j])) |
215 |
46 |
.negate(); |
216 |
46 |
Trace("cegis-unif") |
217 |
|
<< "CegisUnif::lemma, inter-unif-enumerator " |
218 |
23 |
"symmetry breaking lemma : " |
219 |
23 |
<< slem << "\n"; |
220 |
23 |
d_qim.lemma( |
221 |
|
slem, InferenceId::QUANTIFIERS_SYGUS_UNIF_PI_INTER_ENUM_SB); |
222 |
23 |
addedUnifEnumSymBreakLemma = true; |
223 |
23 |
break; |
224 |
|
} |
225 |
|
} |
226 |
|
} |
227 |
|
} |
228 |
|
} |
229 |
|
} |
230 |
|
} |
231 |
718 |
return !addedUnifEnumSymBreakLemma; |
232 |
|
} |
233 |
|
|
234 |
915 |
bool CegisUnif::usingConditionPool() const |
235 |
|
{ |
236 |
915 |
return d_sygus_unif.usingConditionPool(); |
237 |
|
} |
238 |
|
|
239 |
226 |
void CegisUnif::setConditions( |
240 |
|
const std::map<Node, std::vector<Node>>& unif_cenums, |
241 |
|
const std::map<Node, std::vector<Node>>& unif_cvalues) |
242 |
|
{ |
243 |
452 |
Node cost_lit = d_u_enum_manager.getAssertedLiteral(); |
244 |
226 |
NodeManager* nm = NodeManager::currentNM(); |
245 |
|
// set the conditions |
246 |
452 |
for (const Node& c : d_unif_candidates) |
247 |
|
{ |
248 |
452 |
for (const Node& e : d_cand_to_strat_pt[c]) |
249 |
|
{ |
250 |
226 |
Assert(unif_cenums.find(e) != unif_cenums.end()); |
251 |
226 |
Assert(unif_cvalues.find(e) != unif_cvalues.end()); |
252 |
|
std::map<Node, std::vector<Node>>::const_iterator itc = |
253 |
226 |
unif_cenums.find(e); |
254 |
|
std::map<Node, std::vector<Node>>::const_iterator itv = |
255 |
226 |
unif_cvalues.find(e); |
256 |
226 |
d_sygus_unif.setConditions(e, cost_lit, itc->second, itv->second); |
257 |
|
// d_sygus_unif.setConditions(e, cost_lit, unif_cenums[e], |
258 |
|
// unif_cvalues[e]); if condition enumerator had value and it is being |
259 |
|
// passively generated, exclude this value |
260 |
226 |
if (usingConditionPool() && !itc->second.empty()) |
261 |
|
{ |
262 |
|
Node eu = itc->second[0]; |
263 |
|
Assert(d_tds->isEnumerator(eu)); |
264 |
|
Assert(!itv->second.empty()); |
265 |
|
if (d_tds->isPassiveEnumerator(eu)) |
266 |
|
{ |
267 |
|
Node g = d_tds->getActiveGuardForEnumerator(eu); |
268 |
|
Node exp_exc = d_tds->getExplain() |
269 |
|
->getExplanationForEquality(eu, itv->second[0]) |
270 |
|
.negate(); |
271 |
|
Node lem = nm->mkNode(OR, g.negate(), exp_exc); |
272 |
|
d_qim.addPendingLemma( |
273 |
|
lem, InferenceId::QUANTIFIERS_SYGUS_UNIF_PI_COND_EXCLUDE); |
274 |
|
} |
275 |
|
} |
276 |
|
} |
277 |
|
} |
278 |
226 |
} |
279 |
|
|
280 |
359 |
bool CegisUnif::processConstructCandidates(const std::vector<Node>& enums, |
281 |
|
const std::vector<Node>& enum_values, |
282 |
|
const std::vector<Node>& candidates, |
283 |
|
std::vector<Node>& candidate_values, |
284 |
|
bool satisfiedRl) |
285 |
|
{ |
286 |
359 |
if (d_unif_candidates.empty()) |
287 |
|
{ |
288 |
|
Assert(d_non_unif_candidates.size() == candidates.size()); |
289 |
|
return Cegis::processConstructCandidates( |
290 |
|
enums, enum_values, candidates, candidate_values, satisfiedRl); |
291 |
|
} |
292 |
359 |
if (Trace.isOn("cegis-unif")) |
293 |
|
{ |
294 |
|
for (const Node& c : d_unif_candidates) |
295 |
|
{ |
296 |
|
// Collect heads of candidates |
297 |
|
Trace("cegis-unif") << " Evaluation heads for " << c << " :\n"; |
298 |
|
for (const Node& hd : d_sygus_unif.getEvalPointHeads(c)) |
299 |
|
{ |
300 |
|
bool isUnit = false; |
301 |
|
// d_rl_eval_hds accumulates eval apps, so need to look at operators |
302 |
|
for (const Node& hd_unit : d_rl_eval_hds) |
303 |
|
{ |
304 |
|
if (hd == hd_unit[0]) |
305 |
|
{ |
306 |
|
isUnit = true; |
307 |
|
break; |
308 |
|
} |
309 |
|
} |
310 |
|
Trace("cegis-unif") << " " << hd << (isUnit ? "*" : "") << " -> "; |
311 |
|
Assert(std::find(enums.begin(), enums.end(), hd) != enums.end()); |
312 |
|
unsigned i = std::distance(enums.begin(), |
313 |
|
std::find(enums.begin(), enums.end(), hd)); |
314 |
|
Assert(i >= 0 && i < enum_values.size()); |
315 |
|
TermDbSygus::toStreamSygus("cegis-unif", enum_values[i]); |
316 |
|
Trace("cegis-unif") << "\n"; |
317 |
|
} |
318 |
|
} |
319 |
|
} |
320 |
|
// the unification enumerators for conditions and their model values |
321 |
718 |
std::map<Node, std::vector<Node>> unif_cenums; |
322 |
718 |
std::map<Node, std::vector<Node>> unif_cvalues; |
323 |
|
// we only proceed to solution building if we are not introducing symmetry |
324 |
|
// breaking lemmas between return values and if we have not previously |
325 |
|
// introduced return values refinement lemmas |
326 |
718 |
if (!getEnumValues(enums, enum_values, unif_cenums, unif_cvalues) |
327 |
359 |
|| !satisfiedRl) |
328 |
|
{ |
329 |
|
// if condition values are being indepedently enumerated, they should be |
330 |
|
// communicated to the decision tree strategies indepedently of we |
331 |
|
// proceeding to attempt solution building |
332 |
133 |
if (usingConditionPool()) |
333 |
|
{ |
334 |
|
setConditions(unif_cenums, unif_cvalues); |
335 |
|
} |
336 |
266 |
Trace("cegis-unif") << (!satisfiedRl |
337 |
266 |
? "..added refinement lemmas" |
338 |
133 |
: "..added unif enum symmetry breaking") |
339 |
133 |
<< "\n---CegisUnif Engine---\n"; |
340 |
|
// if we didn't satisfy the specification, there is no way to repair |
341 |
133 |
return false; |
342 |
|
} |
343 |
226 |
setConditions(unif_cenums, unif_cvalues); |
344 |
|
// build solutions (for unif candidates a divide-and-conquer approach is used) |
345 |
452 |
std::vector<Node> sols; |
346 |
452 |
std::vector<Node> lemmas; |
347 |
226 |
if (d_sygus_unif.constructSolution(sols, lemmas)) |
348 |
|
{ |
349 |
29 |
candidate_values.insert(candidate_values.end(), sols.begin(), sols.end()); |
350 |
29 |
if (Trace.isOn("cegis-unif")) |
351 |
|
{ |
352 |
|
Trace("cegis-unif") << "* Candidate solutions are:\n"; |
353 |
|
for (const Node& sol : sols) |
354 |
|
{ |
355 |
|
Trace("cegis-unif") |
356 |
|
<< "... " << d_tds->sygusToBuiltin(sol, sol.getType()) << "\n"; |
357 |
|
} |
358 |
|
Trace("cegis-unif") << "---CegisUnif Engine---\n"; |
359 |
|
} |
360 |
29 |
return true; |
361 |
|
} |
362 |
|
|
363 |
|
// TODO tie this to the lemma for getting a new condition value |
364 |
197 |
Assert(usingConditionPool() || !lemmas.empty()); |
365 |
394 |
for (const Node& lem : lemmas) |
366 |
|
{ |
367 |
394 |
Trace("cegis-unif-lemma") |
368 |
197 |
<< "CegisUnif::lemma, separation lemma : " << lem << "\n"; |
369 |
197 |
d_qim.lemma(lem, InferenceId::QUANTIFIERS_SYGUS_UNIF_PI_SEPARATION); |
370 |
|
} |
371 |
197 |
Trace("cegis-unif") << "..failed to separate heads\n---CegisUnif Engine---\n"; |
372 |
197 |
return false; |
373 |
|
} |
374 |
|
|
375 |
21 |
void CegisUnif::registerRefinementLemma(const std::vector<Node>& vars, Node lem) |
376 |
|
{ |
377 |
|
// Notify lemma to unification utility and get its purified form |
378 |
42 |
std::map<Node, std::vector<Node>> eval_pts; |
379 |
42 |
Node plem = d_sygus_unif.addRefLemma(lem, eval_pts); |
380 |
21 |
addRefinementLemma(plem); |
381 |
42 |
Trace("cegis-unif-lemma") |
382 |
21 |
<< "CegisUnif::lemma, refinement lemma : " << plem << "\n"; |
383 |
|
// Notify the enumeration manager if there are new evaluation points |
384 |
42 |
for (const std::pair<const Node, std::vector<Node>>& ep : eval_pts) |
385 |
|
{ |
386 |
21 |
Assert(d_cand_to_strat_pt.find(ep.first) != d_cand_to_strat_pt.end()); |
387 |
|
// Notify each strategy point of the respective candidate |
388 |
42 |
for (const Node& n : d_cand_to_strat_pt[ep.first]) |
389 |
|
{ |
390 |
21 |
d_u_enum_manager.registerEvalPts(ep.second, n); |
391 |
|
} |
392 |
|
} |
393 |
|
// Make the refinement lemma and add it to lems. This lemma is guarded by the |
394 |
|
// parent's guard, which has the semantics "this conjecture has a solution", |
395 |
|
// hence this lemma states: if the parent conjecture has a solution, it |
396 |
|
// satisfies the specification for the given concrete point. |
397 |
|
Node rlem = |
398 |
42 |
NodeManager::currentNM()->mkNode(OR, d_parent->getGuard().negate(), plem); |
399 |
21 |
d_qim.addPendingLemma(rlem, |
400 |
|
InferenceId::QUANTIFIERS_SYGUS_UNIF_PI_REFINEMENT); |
401 |
21 |
} |
402 |
|
|
403 |
1151 |
CegisUnifEnumDecisionStrategy::CegisUnifEnumDecisionStrategy( |
404 |
|
QuantifiersState& qs, |
405 |
|
QuantifiersInferenceManager& qim, |
406 |
|
TermDbSygus* tds, |
407 |
1151 |
SynthConjecture* parent) |
408 |
1151 |
: DecisionStrategyFmf(qs.getSatContext(), qs.getValuation()), |
409 |
|
d_qim(qim), |
410 |
|
d_tds(tds), |
411 |
2302 |
d_parent(parent) |
412 |
|
{ |
413 |
1151 |
d_initialized = false; |
414 |
1151 |
options::SygusUnifPiMode mode = options::sygusUnifPi(); |
415 |
1151 |
d_useCondPool = mode == options::SygusUnifPiMode::CENUM |
416 |
1151 |
|| mode == options::SygusUnifPiMode::CENUM_IGAIN; |
417 |
1151 |
} |
418 |
|
|
419 |
17 |
Node CegisUnifEnumDecisionStrategy::mkLiteral(unsigned n) |
420 |
|
{ |
421 |
17 |
NodeManager* nm = NodeManager::currentNM(); |
422 |
17 |
SkolemManager* sm = nm->getSkolemManager(); |
423 |
17 |
Node newLit = sm->mkDummySkolem("G_cost", nm->booleanType()); |
424 |
17 |
unsigned new_size = n + 1; |
425 |
|
|
426 |
|
// allocate an enumerator for each candidate |
427 |
34 |
for (std::pair<const Node, StrategyPtInfo>& ci : d_ce_info) |
428 |
|
{ |
429 |
34 |
Node c = ci.first; |
430 |
34 |
TypeNode ct = c.getType(); |
431 |
34 |
Node eu = sm->mkDummySkolem("eu", ct); |
432 |
34 |
Node ceu; |
433 |
17 |
if (!d_useCondPool && !ci.second.d_enums[0].empty()) |
434 |
|
{ |
435 |
|
// make a new conditional enumerator as well, starting the |
436 |
|
// second type around |
437 |
6 |
ceu = sm->mkDummySkolem("cu", ci.second.d_ce_type); |
438 |
|
} |
439 |
|
// register the new enumerators |
440 |
51 |
for (unsigned index = 0; index < 2; index++) |
441 |
|
{ |
442 |
57 |
Node e = index == 0 ? eu : ceu; |
443 |
34 |
if (e.isNull()) |
444 |
|
{ |
445 |
11 |
continue; |
446 |
|
} |
447 |
23 |
setUpEnumerator(e, ci.second, index); |
448 |
|
} |
449 |
|
} |
450 |
|
// register the evaluation points at the new value |
451 |
34 |
for (std::pair<const Node, StrategyPtInfo>& ci : d_ce_info) |
452 |
|
{ |
453 |
34 |
Node c = ci.first; |
454 |
37 |
for (const Node& ei : ci.second.d_eval_points) |
455 |
|
{ |
456 |
40 |
Trace("cegis-unif-enum") << "...increasing enum number for hd " << ei |
457 |
20 |
<< " to new size " << new_size << "\n"; |
458 |
20 |
registerEvalPtAtSize(c, ei, newLit, new_size); |
459 |
|
} |
460 |
|
} |
461 |
|
// enforce fairness between number of enumerators and enumerator size |
462 |
17 |
if (new_size > 1) |
463 |
|
{ |
464 |
|
// construct the "virtual enumerator" |
465 |
6 |
if (d_virtual_enum.isNull()) |
466 |
|
{ |
467 |
|
// we construct the default integer grammar with no variables, e.g.: |
468 |
|
// A -> 1 | A+A |
469 |
10 |
TypeNode intTn = nm->integerType(); |
470 |
|
// use a null variable list |
471 |
10 |
Node bvl; |
472 |
10 |
std::string veName("_virtual_enum_grammar"); |
473 |
10 |
SygusDatatype sdt(veName); |
474 |
10 |
TypeNode u = nm->mkSort(veName, NodeManager::SORT_FLAG_PLACEHOLDER); |
475 |
10 |
std::set<TypeNode> unresolvedTypes; |
476 |
5 |
unresolvedTypes.insert(u); |
477 |
10 |
std::vector<TypeNode> cargsEmpty; |
478 |
10 |
Node cr = nm->mkConst(Rational(1)); |
479 |
5 |
sdt.addConstructor(cr, "1", cargsEmpty); |
480 |
10 |
std::vector<TypeNode> cargsPlus; |
481 |
5 |
cargsPlus.push_back(u); |
482 |
5 |
cargsPlus.push_back(u); |
483 |
5 |
sdt.addConstructor(PLUS, cargsPlus); |
484 |
5 |
sdt.initializeDatatype(nm->integerType(), bvl, false, false); |
485 |
10 |
std::vector<DType> datatypes; |
486 |
5 |
datatypes.push_back(sdt.getDatatype()); |
487 |
|
std::vector<TypeNode> dtypes = nm->mkMutualDatatypeTypes( |
488 |
10 |
datatypes, unresolvedTypes, NodeManager::DATATYPE_FLAG_PLACEHOLDER); |
489 |
5 |
d_virtual_enum = sm->mkDummySkolem("_ve", dtypes[0]); |
490 |
10 |
d_tds->registerEnumerator( |
491 |
10 |
d_virtual_enum, Node::null(), d_parent, ROLE_ENUM_CONSTRAINED); |
492 |
|
} |
493 |
|
// if new_size is a power of two, then isPow2 returns log2(new_size)+1 |
494 |
|
// otherwise, this returns 0. In the case it returns 0, we don't care |
495 |
|
// since the floor( log2( i ) ) = floor( log2( i - 1 ) ) and we do not |
496 |
|
// increase our size bound. |
497 |
6 |
unsigned pow_two = Integer(new_size).isPow2(); |
498 |
6 |
if (pow_two > 0) |
499 |
|
{ |
500 |
10 |
Node size_ve = nm->mkNode(DT_SIZE, d_virtual_enum); |
501 |
|
Node fair_lemma = |
502 |
10 |
nm->mkNode(GEQ, size_ve, nm->mkConst(Rational(pow_two - 1))); |
503 |
5 |
fair_lemma = nm->mkNode(OR, newLit, fair_lemma); |
504 |
10 |
Trace("cegis-unif-enum-lemma") |
505 |
5 |
<< "CegisUnifEnum::lemma, fairness size:" << fair_lemma << "\n"; |
506 |
|
// this lemma relates the number of conditions we enumerate and the |
507 |
|
// maximum size of a term that is part of our solution. It is of the |
508 |
|
// form: |
509 |
|
// G_uq_i => size(ve) >= log_2( i-1 ) |
510 |
|
// In other words, if we use i conditions, then we allow terms in our |
511 |
|
// solution whose size is at most log_2(i-1). |
512 |
5 |
d_qim.lemma(fair_lemma, InferenceId::QUANTIFIERS_SYGUS_UNIF_PI_FAIR_SIZE); |
513 |
|
} |
514 |
|
} |
515 |
|
|
516 |
17 |
return newLit; |
517 |
|
} |
518 |
|
|
519 |
11 |
void CegisUnifEnumDecisionStrategy::initialize( |
520 |
|
const std::vector<Node>& es, |
521 |
|
const std::map<Node, Node>& e_to_cond, |
522 |
|
const std::map<Node, std::vector<Node>>& strategy_lemmas) |
523 |
|
{ |
524 |
11 |
Assert(!d_initialized); |
525 |
11 |
d_initialized = true; |
526 |
11 |
if (es.empty()) |
527 |
|
{ |
528 |
|
return; |
529 |
|
} |
530 |
|
// initialize type information for candidates |
531 |
11 |
NodeManager* nm = NodeManager::currentNM(); |
532 |
11 |
SkolemManager* sm = nm->getSkolemManager(); |
533 |
22 |
for (const Node& e : es) |
534 |
|
{ |
535 |
11 |
Trace("cegis-unif-enum-debug") << "...adding strategy point " << e << "\n"; |
536 |
|
// currently, we allocate the same enumerators for candidates of the same |
537 |
|
// type |
538 |
11 |
d_ce_info[e].d_pt = e; |
539 |
11 |
std::map<Node, Node>::const_iterator itcc = e_to_cond.find(e); |
540 |
11 |
Assert(itcc != e_to_cond.end()); |
541 |
22 |
Node cond = itcc->second; |
542 |
22 |
Trace("cegis-unif-enum-debug") |
543 |
11 |
<< "...its condition strategy point is " << cond << "\n"; |
544 |
11 |
d_ce_info[e].d_ce_type = cond.getType(); |
545 |
|
// initialize the symmetry breaking lemma templates |
546 |
33 |
for (unsigned index = 0; index < 2; index++) |
547 |
|
{ |
548 |
22 |
Assert(d_ce_info[e].d_sbt_lemma_tmpl[index].first.isNull()); |
549 |
42 |
Node sp = index == 0 ? e : cond; |
550 |
|
std::map<Node, std::vector<Node>>::const_iterator it = |
551 |
22 |
strategy_lemmas.find(sp); |
552 |
22 |
if (it == strategy_lemmas.end()) |
553 |
|
{ |
554 |
2 |
continue; |
555 |
|
} |
556 |
|
// collect lemmas for removing redundant ops for this candidate's type |
557 |
|
Node d_sbt_lemma = |
558 |
40 |
it->second.size() == 1 ? it->second[0] : nm->mkNode(AND, it->second); |
559 |
40 |
Trace("cegis-unif-enum-debug") |
560 |
20 |
<< "...adding lemma template to remove redundant operators for " << sp |
561 |
20 |
<< " --> lambda " << sp << ". " << d_sbt_lemma << "\n"; |
562 |
40 |
d_ce_info[e].d_sbt_lemma_tmpl[index] = |
563 |
60 |
std::pair<Node, Node>(d_sbt_lemma, sp); |
564 |
|
} |
565 |
|
} |
566 |
|
|
567 |
|
// register this strategy |
568 |
11 |
d_qim.getDecisionManager()->registerStrategy( |
569 |
|
DecisionManager::STRAT_QUANT_CEGIS_UNIF_NUM_ENUMS, this); |
570 |
|
|
571 |
|
// create single condition enumerator for each decision tree strategy |
572 |
11 |
if (d_useCondPool) |
573 |
|
{ |
574 |
|
// allocate a condition enumerator for each candidate |
575 |
|
for (std::pair<const Node, StrategyPtInfo>& ci : d_ce_info) |
576 |
|
{ |
577 |
|
Node ceu = sm->mkDummySkolem("cu", ci.second.d_ce_type); |
578 |
|
setUpEnumerator(ceu, ci.second, 1); |
579 |
|
} |
580 |
|
} |
581 |
|
} |
582 |
|
|
583 |
1534 |
void CegisUnifEnumDecisionStrategy::getEnumeratorsForStrategyPt( |
584 |
|
Node e, std::vector<Node>& es, unsigned index) const |
585 |
|
{ |
586 |
|
// the number of active enumerators is related to the current cost value |
587 |
1534 |
unsigned num_enums = 0; |
588 |
1534 |
bool has_num_enums = getAssertedLiteralIndex(num_enums); |
589 |
1534 |
AlwaysAssert(has_num_enums); |
590 |
1534 |
num_enums = num_enums + 1; |
591 |
1534 |
if (index == 1) |
592 |
|
{ |
593 |
|
// we always use (cost-1) conditions, or 1 if in the indepedent case |
594 |
767 |
num_enums = !d_useCondPool ? num_enums - 1 : 1; |
595 |
|
} |
596 |
1534 |
if (num_enums > 0) |
597 |
|
{ |
598 |
1376 |
std::map<Node, StrategyPtInfo>::const_iterator itc = d_ce_info.find(e); |
599 |
1376 |
Assert(itc != d_ce_info.end()); |
600 |
1376 |
Assert(num_enums <= itc->second.d_enums[index].size()); |
601 |
4128 |
es.insert(es.end(), |
602 |
1376 |
itc->second.d_enums[index].begin(), |
603 |
6880 |
itc->second.d_enums[index].begin() + num_enums); |
604 |
|
} |
605 |
1534 |
} |
606 |
|
|
607 |
23 |
void CegisUnifEnumDecisionStrategy::setUpEnumerator(Node e, |
608 |
|
StrategyPtInfo& si, |
609 |
|
unsigned index) |
610 |
|
{ |
611 |
23 |
NodeManager* nm = NodeManager::currentNM(); |
612 |
|
// instantiate template for removing redundant operators |
613 |
23 |
if (!si.d_sbt_lemma_tmpl[index].first.isNull()) |
614 |
|
{ |
615 |
46 |
Node templ = si.d_sbt_lemma_tmpl[index].first; |
616 |
46 |
TNode templ_var = si.d_sbt_lemma_tmpl[index].second; |
617 |
46 |
Node sym_break_red_ops = templ.substitute(templ_var, e); |
618 |
46 |
Trace("cegis-unif-enum-lemma") |
619 |
23 |
<< "CegisUnifEnum::lemma, remove redundant ops of " << e << " : " |
620 |
23 |
<< sym_break_red_ops << "\n"; |
621 |
23 |
d_qim.lemma(sym_break_red_ops, |
622 |
|
InferenceId::QUANTIFIERS_SYGUS_UNIF_PI_REM_OPS); |
623 |
|
} |
624 |
|
// symmetry breaking between enumerators |
625 |
23 |
if (!si.d_enums[index].empty() && index == 0) |
626 |
|
{ |
627 |
12 |
Node e_prev = si.d_enums[index].back(); |
628 |
12 |
Node size_e = nm->mkNode(DT_SIZE, e); |
629 |
12 |
Node size_e_prev = nm->mkNode(DT_SIZE, e_prev); |
630 |
12 |
Node sym_break = nm->mkNode(GEQ, size_e, size_e_prev); |
631 |
12 |
Trace("cegis-unif-enum-lemma") |
632 |
6 |
<< "CegisUnifEnum::lemma, enum sym break:" << sym_break << "\n"; |
633 |
6 |
d_qim.lemma(sym_break, InferenceId::QUANTIFIERS_SYGUS_UNIF_PI_ENUM_SB); |
634 |
|
} |
635 |
|
// register the enumerator |
636 |
23 |
si.d_enums[index].push_back(e); |
637 |
23 |
EnumeratorRole erole = ROLE_ENUM_CONSTRAINED; |
638 |
|
// if we are using a single independent enumerator for conditions, then we |
639 |
|
// allocate an active guard, and are eligible to use variable-agnostic |
640 |
|
// enumeration. |
641 |
23 |
if (d_useCondPool && index == 1) |
642 |
|
{ |
643 |
|
erole = ROLE_ENUM_POOL; |
644 |
|
} |
645 |
46 |
Trace("cegis-unif-enum") << "* Registering new enumerator " << e |
646 |
23 |
<< " to strategy point " << si.d_pt << "\n"; |
647 |
23 |
d_tds->registerEnumerator(e, si.d_pt, d_parent, erole); |
648 |
23 |
} |
649 |
|
|
650 |
21 |
void CegisUnifEnumDecisionStrategy::registerEvalPts( |
651 |
|
const std::vector<Node>& eis, Node e) |
652 |
|
{ |
653 |
|
// candidates of the same type are managed |
654 |
21 |
std::map<Node, StrategyPtInfo>::iterator it = d_ce_info.find(e); |
655 |
21 |
Assert(it != d_ce_info.end()); |
656 |
42 |
it->second.d_eval_points.insert( |
657 |
42 |
it->second.d_eval_points.end(), eis.begin(), eis.end()); |
658 |
|
// register at all already allocated sizes |
659 |
56 |
for (const Node& ei : eis) |
660 |
|
{ |
661 |
35 |
Assert(ei.getType() == e.getType()); |
662 |
89 |
for (unsigned j = 0, size = d_literals.size(); j < size; j++) |
663 |
|
{ |
664 |
108 |
Trace("cegis-unif-enum") << "...for cand " << e << " adding hd " << ei |
665 |
54 |
<< " at size " << j << "\n"; |
666 |
54 |
registerEvalPtAtSize(e, ei, d_literals[j], j + 1); |
667 |
|
} |
668 |
|
} |
669 |
21 |
} |
670 |
|
|
671 |
74 |
void CegisUnifEnumDecisionStrategy::registerEvalPtAtSize(Node e, |
672 |
|
Node ei, |
673 |
|
Node guq_lit, |
674 |
|
unsigned n) |
675 |
|
{ |
676 |
|
// must be equal to one of the first n enums |
677 |
74 |
std::map<Node, StrategyPtInfo>::iterator itc = d_ce_info.find(e); |
678 |
74 |
Assert(itc != d_ce_info.end()); |
679 |
74 |
Assert(itc->second.d_enums[0].size() >= n); |
680 |
148 |
std::vector<Node> disj; |
681 |
74 |
disj.push_back(guq_lit.negate()); |
682 |
192 |
for (unsigned i = 0; i < n; i++) |
683 |
|
{ |
684 |
118 |
disj.push_back(ei.eqNode(itc->second.d_enums[0][i])); |
685 |
|
} |
686 |
148 |
Node lem = NodeManager::currentNM()->mkNode(OR, disj); |
687 |
148 |
Trace("cegis-unif-enum-lemma") |
688 |
74 |
<< "CegisUnifEnum::lemma, domain:" << lem << "\n"; |
689 |
74 |
d_qim.lemma(lem, InferenceId::QUANTIFIERS_SYGUS_UNIF_PI_DOMAIN); |
690 |
74 |
} |
691 |
|
|
692 |
|
} // namespace quantifiers |
693 |
|
} // namespace theory |
694 |
29340 |
} // namespace cvc5 |