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/quantifiers/sygus/sygus_unif_rl.h" |
23 |
|
#include "theory/quantifiers/sygus/synth_conjecture.h" |
24 |
|
#include "theory/quantifiers/sygus/term_database_sygus.h" |
25 |
|
|
26 |
|
using namespace cvc5::kind; |
27 |
|
|
28 |
|
namespace cvc5 { |
29 |
|
namespace theory { |
30 |
|
namespace quantifiers { |
31 |
|
|
32 |
1543 |
CegisUnif::CegisUnif(QuantifiersState& qs, |
33 |
|
QuantifiersInferenceManager& qim, |
34 |
|
TermDbSygus* tds, |
35 |
1543 |
SynthConjecture* p) |
36 |
1543 |
: Cegis(qim, tds, p), d_sygus_unif(p), d_u_enum_manager(qs, qim, tds, p) |
37 |
|
{ |
38 |
1543 |
} |
39 |
|
|
40 |
3086 |
CegisUnif::~CegisUnif() {} |
41 |
9 |
bool CegisUnif::processInitialize(Node conj, |
42 |
|
Node n, |
43 |
|
const std::vector<Node>& candidates, |
44 |
|
std::vector<Node>& lemmas) |
45 |
|
{ |
46 |
|
// list of strategy points for unification candidates |
47 |
18 |
std::vector<Node> unif_candidate_pts; |
48 |
|
// map from strategy points to their conditions |
49 |
18 |
std::map<Node, Node> pt_to_cond; |
50 |
|
// strategy lemmas for each strategy point |
51 |
18 |
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 |
9 |
EnumeratorRole eroleNonUnif = candidates.size() == 1 |
56 |
9 |
? ROLE_ENUM_SINGLE_SOLUTION |
57 |
9 |
: ROLE_ENUM_MULTI_SOLUTION; |
58 |
21 |
for (const Node& f : candidates) |
59 |
|
{ |
60 |
|
// Init UNIF util for this candidate |
61 |
24 |
d_sygus_unif.initializeCandidate( |
62 |
12 |
d_tds, f, d_cand_to_strat_pt[f], strategy_lemmas); |
63 |
12 |
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 |
9 |
d_unif_candidates.push_back(f); |
72 |
18 |
Trace("cegis-unif") << "* unification candidate : " << f |
73 |
9 |
<< " with strategy points:" << std::endl; |
74 |
9 |
std::vector<Node>& enums = d_cand_to_strat_pt[f]; |
75 |
9 |
unif_candidate_pts.insert( |
76 |
18 |
unif_candidate_pts.end(), enums.begin(), enums.end()); |
77 |
|
// map strategy point to its condition in pt_to_cond |
78 |
18 |
for (const Node& e : enums) |
79 |
|
{ |
80 |
18 |
Node cond = d_sygus_unif.getConditionForEvaluationPoint(e); |
81 |
9 |
Assert(!cond.isNull()); |
82 |
18 |
Trace("cegis-unif") |
83 |
9 |
<< " " << e << " with condition : " << cond << std::endl; |
84 |
9 |
pt_to_cond[e] = cond; |
85 |
|
} |
86 |
|
} |
87 |
|
} |
88 |
|
// initialize the enumeration manager |
89 |
9 |
d_u_enum_manager.initialize(unif_candidate_pts, pt_to_cond, strategy_lemmas); |
90 |
18 |
return true; |
91 |
|
} |
92 |
|
|
93 |
325 |
void CegisUnif::getTermList(const std::vector<Node>& candidates, |
94 |
|
std::vector<Node>& enums) |
95 |
|
{ |
96 |
|
// Non-unif candidate are themselves the enumerators |
97 |
325 |
enums.insert( |
98 |
650 |
enums.end(), d_non_unif_candidates.begin(), d_non_unif_candidates.end()); |
99 |
650 |
for (const Node& c : d_unif_candidates) |
100 |
|
{ |
101 |
|
// Collect heads of candidates |
102 |
1620 |
for (const Node& hd : d_sygus_unif.getEvalPointHeads(c)) |
103 |
|
{ |
104 |
2590 |
Trace("cegis-unif-enum-debug") |
105 |
1295 |
<< "......cand " << c << " with enum hd " << hd << "\n"; |
106 |
1295 |
enums.push_back(hd); |
107 |
|
} |
108 |
|
// get unification enumerators |
109 |
650 |
for (const Node& e : d_cand_to_strat_pt[c]) |
110 |
|
{ |
111 |
975 |
for (unsigned index = 0; index < 2; index++) |
112 |
|
{ |
113 |
1300 |
std::vector<Node> uenums; |
114 |
|
// get the current unification enumerators |
115 |
650 |
d_u_enum_manager.getEnumeratorsForStrategyPt(e, uenums, index); |
116 |
|
// get the model value of each enumerator |
117 |
650 |
enums.insert(enums.end(), uenums.begin(), uenums.end()); |
118 |
|
} |
119 |
|
} |
120 |
|
} |
121 |
325 |
} |
122 |
|
|
123 |
254 |
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 |
|
std::vector<Node>& lems) |
128 |
|
{ |
129 |
254 |
NodeManager* nm = NodeManager::currentNM(); |
130 |
508 |
Node cost_lit = d_u_enum_manager.getAssertedLiteral(); |
131 |
508 |
std::map<Node, std::vector<Node>> unif_renums, unif_rvalues; |
132 |
|
// build model value map |
133 |
508 |
std::map<Node, Node> mvMap; |
134 |
1926 |
for (unsigned i = 0, size = enums.size(); i < size; i++) |
135 |
|
{ |
136 |
1672 |
mvMap[enums[i]] = enum_values[i]; |
137 |
|
} |
138 |
254 |
bool addedUnifEnumSymBreakLemma = false; |
139 |
|
// populate maps between unification enumerators and their model values |
140 |
508 |
for (const Node& c : d_unif_candidates) |
141 |
|
{ |
142 |
|
// for each decision tree strategy allocated for c (these are referenced |
143 |
|
// by strategy points in d_cand_to_strat_pt[c]) |
144 |
508 |
for (const Node& e : d_cand_to_strat_pt[c]) |
145 |
|
{ |
146 |
762 |
for (unsigned index = 0; index < 2; index++) |
147 |
|
{ |
148 |
1016 |
std::vector<Node> es, vs; |
149 |
1016 |
Trace("cegis-unif") |
150 |
1016 |
<< " " << (index == 0 ? "Return values" : "Conditions") << " for " |
151 |
508 |
<< e << ":\n"; |
152 |
|
// get the current unification enumerators |
153 |
508 |
d_u_enum_manager.getEnumeratorsForStrategyPt(e, es, index); |
154 |
|
// set enums for condition enumerators |
155 |
508 |
if (index == 1) |
156 |
|
{ |
157 |
254 |
if (usingConditionPool()) |
158 |
|
{ |
159 |
|
Assert(es.size() == 1); |
160 |
|
// whether valueus exhausted |
161 |
|
if (mvMap.find(es[0]) == mvMap.end()) |
162 |
|
{ |
163 |
|
Trace("cegis-unif") << " " << es[0] << " -> N/A\n"; |
164 |
|
es.clear(); |
165 |
|
} |
166 |
|
} |
167 |
254 |
unif_cenums[e] = es; |
168 |
|
} |
169 |
|
// get the model value of each enumerator |
170 |
1140 |
for (const Node& eu : es) |
171 |
|
{ |
172 |
632 |
Assert(mvMap.find(eu) != mvMap.end()); |
173 |
1264 |
Node m_eu = mvMap[eu]; |
174 |
632 |
if (Trace.isOn("cegis-unif")) |
175 |
|
{ |
176 |
|
Trace("cegis-unif") << " " << eu << " -> "; |
177 |
|
TermDbSygus::toStreamSygus("cegis-unif", m_eu); |
178 |
|
Trace("cegis-unif") << "\n"; |
179 |
|
} |
180 |
632 |
vs.push_back(m_eu); |
181 |
|
} |
182 |
|
// set values for condition enumerators of e |
183 |
508 |
if (index == 1) |
184 |
|
{ |
185 |
254 |
unif_cvalues[e] = vs; |
186 |
|
} |
187 |
|
// inter-enumerator symmetry breaking for return values |
188 |
|
else |
189 |
|
{ |
190 |
|
// given a pool of unification enumerators eu_1, ..., eu_n, |
191 |
|
// CegisUnifEnumDecisionStrategy insists that size(eu_1) <= ... <= |
192 |
|
// size(eu_n). We additionally insist that M(eu_i) < M(eu_{i+1}) when |
193 |
|
// size(eu_i) = size(eu_{i+1}), where < is pointer comparison. |
194 |
|
// We enforce this below by adding symmetry breaking lemmas of the |
195 |
|
// form ~( eu_i = M(eu_i) ^ eu_{i+1} = M(eu_{i+1} ) ) |
196 |
|
// when applicable. |
197 |
|
// we only do this for return value enumerators, since condition |
198 |
|
// enumerators cannot be ordered (their order is based on the |
199 |
|
// seperation resolution scheme during model construction). |
200 |
422 |
for (unsigned j = 1, nenum = vs.size(); j < nenum; j++) |
201 |
|
{ |
202 |
356 |
Node prev_val = vs[j - 1]; |
203 |
356 |
Node curr_val = vs[j]; |
204 |
|
// compare the node values |
205 |
188 |
if (curr_val < prev_val) |
206 |
|
{ |
207 |
|
// must have the same size |
208 |
20 |
unsigned prev_size = d_tds->getSygusTermSize(prev_val); |
209 |
20 |
unsigned curr_size = d_tds->getSygusTermSize(curr_val); |
210 |
20 |
Assert(prev_size <= curr_size); |
211 |
20 |
if (curr_size == prev_size) |
212 |
|
{ |
213 |
|
Node slem = |
214 |
80 |
nm->mkNode( |
215 |
80 |
AND, es[j - 1].eqNode(vs[j - 1]), es[j].eqNode(vs[j])) |
216 |
40 |
.negate(); |
217 |
40 |
Trace("cegis-unif") |
218 |
|
<< "CegisUnif::lemma, inter-unif-enumerator " |
219 |
20 |
"symmetry breaking lemma : " |
220 |
20 |
<< slem << "\n"; |
221 |
20 |
d_qim.lemma(slem, InferenceId::UNKNOWN); |
222 |
20 |
addedUnifEnumSymBreakLemma = true; |
223 |
20 |
break; |
224 |
|
} |
225 |
|
} |
226 |
|
} |
227 |
|
} |
228 |
|
} |
229 |
|
} |
230 |
|
} |
231 |
508 |
return !addedUnifEnumSymBreakLemma; |
232 |
|
} |
233 |
|
|
234 |
631 |
bool CegisUnif::usingConditionPool() const |
235 |
|
{ |
236 |
631 |
return d_sygus_unif.usingConditionPool(); |
237 |
|
} |
238 |
|
|
239 |
141 |
void CegisUnif::setConditions( |
240 |
|
const std::map<Node, std::vector<Node>>& unif_cenums, |
241 |
|
const std::map<Node, std::vector<Node>>& unif_cvalues, |
242 |
|
std::vector<Node>& lems) |
243 |
|
{ |
244 |
282 |
Node cost_lit = d_u_enum_manager.getAssertedLiteral(); |
245 |
141 |
NodeManager* nm = NodeManager::currentNM(); |
246 |
|
// set the conditions |
247 |
282 |
for (const Node& c : d_unif_candidates) |
248 |
|
{ |
249 |
282 |
for (const Node& e : d_cand_to_strat_pt[c]) |
250 |
|
{ |
251 |
141 |
Assert(unif_cenums.find(e) != unif_cenums.end()); |
252 |
141 |
Assert(unif_cvalues.find(e) != unif_cvalues.end()); |
253 |
|
std::map<Node, std::vector<Node>>::const_iterator itc = |
254 |
141 |
unif_cenums.find(e); |
255 |
|
std::map<Node, std::vector<Node>>::const_iterator itv = |
256 |
141 |
unif_cvalues.find(e); |
257 |
141 |
d_sygus_unif.setConditions(e, cost_lit, itc->second, itv->second); |
258 |
|
// d_sygus_unif.setConditions(e, cost_lit, unif_cenums[e], |
259 |
|
// unif_cvalues[e]); if condition enumerator had value and it is being |
260 |
|
// passively generated, exclude this value |
261 |
141 |
if (usingConditionPool() && !itc->second.empty()) |
262 |
|
{ |
263 |
|
Node eu = itc->second[0]; |
264 |
|
Assert(d_tds->isEnumerator(eu)); |
265 |
|
Assert(!itv->second.empty()); |
266 |
|
if (d_tds->isPassiveEnumerator(eu)) |
267 |
|
{ |
268 |
|
Node g = d_tds->getActiveGuardForEnumerator(eu); |
269 |
|
Node exp_exc = d_tds->getExplain() |
270 |
|
->getExplanationForEquality(eu, itv->second[0]) |
271 |
|
.negate(); |
272 |
|
lems.push_back(nm->mkNode(OR, g.negate(), exp_exc)); |
273 |
|
} |
274 |
|
} |
275 |
|
} |
276 |
|
} |
277 |
141 |
} |
278 |
|
|
279 |
254 |
bool CegisUnif::processConstructCandidates(const std::vector<Node>& enums, |
280 |
|
const std::vector<Node>& enum_values, |
281 |
|
const std::vector<Node>& candidates, |
282 |
|
std::vector<Node>& candidate_values, |
283 |
|
bool satisfiedRl, |
284 |
|
std::vector<Node>& lems) |
285 |
|
{ |
286 |
254 |
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, lems); |
291 |
|
} |
292 |
254 |
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 |
508 |
std::map<Node, std::vector<Node>> unif_cenums; |
322 |
508 |
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 |
508 |
if (!getEnumValues(enums, enum_values, unif_cenums, unif_cvalues, lems) |
327 |
254 |
|| !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 |
113 |
if (usingConditionPool()) |
333 |
|
{ |
334 |
|
setConditions(unif_cenums, unif_cvalues, lems); |
335 |
|
} |
336 |
226 |
Trace("cegis-unif") << (!satisfiedRl |
337 |
226 |
? "..added refinement lemmas" |
338 |
113 |
: "..added unif enum symmetry breaking") |
339 |
113 |
<< "\n---CegisUnif Engine---\n"; |
340 |
|
// if we didn't satisfy the specification, there is no way to repair |
341 |
113 |
return false; |
342 |
|
} |
343 |
141 |
setConditions(unif_cenums, unif_cvalues, lems); |
344 |
|
// build solutions (for unif candidates a divide-and-conquer approach is used) |
345 |
282 |
std::vector<Node> sols; |
346 |
282 |
std::vector<Node> lemmas; |
347 |
141 |
if (d_sygus_unif.constructSolution(sols, lemmas)) |
348 |
|
{ |
349 |
18 |
candidate_values.insert(candidate_values.end(), sols.begin(), sols.end()); |
350 |
18 |
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 |
18 |
return true; |
361 |
|
} |
362 |
|
|
363 |
|
// TODO tie this to the lemma for getting a new condition value |
364 |
123 |
Assert(usingConditionPool() || !lemmas.empty()); |
365 |
246 |
for (const Node& lem : lemmas) |
366 |
|
{ |
367 |
246 |
Trace("cegis-unif-lemma") |
368 |
123 |
<< "CegisUnif::lemma, separation lemma : " << lem << "\n"; |
369 |
123 |
d_qim.lemma(lem, InferenceId::UNKNOWN); |
370 |
|
} |
371 |
123 |
Trace("cegis-unif") << "..failed to separate heads\n---CegisUnif Engine---\n"; |
372 |
123 |
return false; |
373 |
|
} |
374 |
|
|
375 |
11 |
void CegisUnif::registerRefinementLemma(const std::vector<Node>& vars, |
376 |
|
Node lem, |
377 |
|
std::vector<Node>& lems) |
378 |
|
{ |
379 |
|
// Notify lemma to unification utility and get its purified form |
380 |
22 |
std::map<Node, std::vector<Node>> eval_pts; |
381 |
22 |
Node plem = d_sygus_unif.addRefLemma(lem, eval_pts); |
382 |
11 |
addRefinementLemma(plem); |
383 |
22 |
Trace("cegis-unif-lemma") |
384 |
11 |
<< "CegisUnif::lemma, refinement lemma : " << plem << "\n"; |
385 |
|
// Notify the enumeration manager if there are new evaluation points |
386 |
22 |
for (const std::pair<const Node, std::vector<Node>>& ep : eval_pts) |
387 |
|
{ |
388 |
11 |
Assert(d_cand_to_strat_pt.find(ep.first) != d_cand_to_strat_pt.end()); |
389 |
|
// Notify each strategy point of the respective candidate |
390 |
22 |
for (const Node& n : d_cand_to_strat_pt[ep.first]) |
391 |
|
{ |
392 |
11 |
d_u_enum_manager.registerEvalPts(ep.second, n); |
393 |
|
} |
394 |
|
} |
395 |
|
// Make the refinement lemma and add it to lems. This lemma is guarded by the |
396 |
|
// parent's guard, which has the semantics "this conjecture has a solution", |
397 |
|
// hence this lemma states: if the parent conjecture has a solution, it |
398 |
|
// satisfies the specification for the given concrete point. |
399 |
33 |
lems.push_back(NodeManager::currentNM()->mkNode( |
400 |
22 |
OR, d_parent->getGuard().negate(), plem)); |
401 |
11 |
} |
402 |
|
|
403 |
1543 |
CegisUnifEnumDecisionStrategy::CegisUnifEnumDecisionStrategy( |
404 |
|
QuantifiersState& qs, |
405 |
|
QuantifiersInferenceManager& qim, |
406 |
|
TermDbSygus* tds, |
407 |
1543 |
SynthConjecture* parent) |
408 |
1543 |
: DecisionStrategyFmf(qs.getSatContext(), qs.getValuation()), |
409 |
|
d_qim(qim), |
410 |
|
d_tds(tds), |
411 |
3086 |
d_parent(parent) |
412 |
|
{ |
413 |
1543 |
d_initialized = false; |
414 |
1543 |
options::SygusUnifPiMode mode = options::sygusUnifPi(); |
415 |
1543 |
d_useCondPool = mode == options::SygusUnifPiMode::CENUM |
416 |
1543 |
|| mode == options::SygusUnifPiMode::CENUM_IGAIN; |
417 |
1543 |
} |
418 |
|
|
419 |
14 |
Node CegisUnifEnumDecisionStrategy::mkLiteral(unsigned n) |
420 |
|
{ |
421 |
14 |
NodeManager* nm = NodeManager::currentNM(); |
422 |
14 |
SkolemManager* sm = nm->getSkolemManager(); |
423 |
14 |
Node newLit = sm->mkDummySkolem("G_cost", nm->booleanType()); |
424 |
14 |
unsigned new_size = n + 1; |
425 |
|
|
426 |
|
// allocate an enumerator for each candidate |
427 |
28 |
for (std::pair<const Node, StrategyPtInfo>& ci : d_ce_info) |
428 |
|
{ |
429 |
28 |
Node c = ci.first; |
430 |
28 |
TypeNode ct = c.getType(); |
431 |
28 |
Node eu = sm->mkDummySkolem("eu", ct); |
432 |
28 |
Node ceu; |
433 |
14 |
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 |
5 |
ceu = sm->mkDummySkolem("cu", ci.second.d_ce_type); |
438 |
|
} |
439 |
|
// register the new enumerators |
440 |
42 |
for (unsigned index = 0; index < 2; index++) |
441 |
|
{ |
442 |
47 |
Node e = index == 0 ? eu : ceu; |
443 |
28 |
if (e.isNull()) |
444 |
|
{ |
445 |
9 |
continue; |
446 |
|
} |
447 |
19 |
setUpEnumerator(e, ci.second, index); |
448 |
|
} |
449 |
|
} |
450 |
|
// register the evaluation points at the new value |
451 |
28 |
for (std::pair<const Node, StrategyPtInfo>& ci : d_ce_info) |
452 |
|
{ |
453 |
28 |
Node c = ci.first; |
454 |
31 |
for (const Node& ei : ci.second.d_eval_points) |
455 |
|
{ |
456 |
34 |
Trace("cegis-unif-enum") << "...increasing enum number for hd " << ei |
457 |
17 |
<< " to new size " << new_size << "\n"; |
458 |
17 |
registerEvalPtAtSize(c, ei, newLit, new_size); |
459 |
|
} |
460 |
|
} |
461 |
|
// enforce fairness between number of enumerators and enumerator size |
462 |
14 |
if (new_size > 1) |
463 |
|
{ |
464 |
|
// construct the "virtual enumerator" |
465 |
5 |
if (d_virtual_enum.isNull()) |
466 |
|
{ |
467 |
|
// we construct the default integer grammar with no variables, e.g.: |
468 |
|
// A -> 1 | A+A |
469 |
8 |
TypeNode intTn = nm->integerType(); |
470 |
|
// use a null variable list |
471 |
8 |
Node bvl; |
472 |
8 |
std::string veName("_virtual_enum_grammar"); |
473 |
8 |
SygusDatatype sdt(veName); |
474 |
8 |
TypeNode u = nm->mkSort(veName, NodeManager::SORT_FLAG_PLACEHOLDER); |
475 |
8 |
std::set<TypeNode> unresolvedTypes; |
476 |
4 |
unresolvedTypes.insert(u); |
477 |
8 |
std::vector<TypeNode> cargsEmpty; |
478 |
8 |
Node cr = nm->mkConst(Rational(1)); |
479 |
4 |
sdt.addConstructor(cr, "1", cargsEmpty); |
480 |
8 |
std::vector<TypeNode> cargsPlus; |
481 |
4 |
cargsPlus.push_back(u); |
482 |
4 |
cargsPlus.push_back(u); |
483 |
4 |
sdt.addConstructor(PLUS, cargsPlus); |
484 |
4 |
sdt.initializeDatatype(nm->integerType(), bvl, false, false); |
485 |
8 |
std::vector<DType> datatypes; |
486 |
4 |
datatypes.push_back(sdt.getDatatype()); |
487 |
|
std::vector<TypeNode> dtypes = nm->mkMutualDatatypeTypes( |
488 |
8 |
datatypes, unresolvedTypes, NodeManager::DATATYPE_FLAG_PLACEHOLDER); |
489 |
4 |
d_virtual_enum = sm->mkDummySkolem("_ve", dtypes[0]); |
490 |
8 |
d_tds->registerEnumerator( |
491 |
8 |
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 |
5 |
unsigned pow_two = Integer(new_size).isPow2(); |
498 |
5 |
if (pow_two > 0) |
499 |
|
{ |
500 |
8 |
Node size_ve = nm->mkNode(DT_SIZE, d_virtual_enum); |
501 |
|
Node fair_lemma = |
502 |
8 |
nm->mkNode(GEQ, size_ve, nm->mkConst(Rational(pow_two - 1))); |
503 |
4 |
fair_lemma = nm->mkNode(OR, newLit, fair_lemma); |
504 |
8 |
Trace("cegis-unif-enum-lemma") |
505 |
4 |
<< "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 |
4 |
d_qim.lemma(fair_lemma, InferenceId::UNKNOWN); |
513 |
|
} |
514 |
|
} |
515 |
|
|
516 |
14 |
return newLit; |
517 |
|
} |
518 |
|
|
519 |
9 |
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 |
9 |
Assert(!d_initialized); |
525 |
9 |
d_initialized = true; |
526 |
9 |
if (es.empty()) |
527 |
|
{ |
528 |
|
return; |
529 |
|
} |
530 |
|
// initialize type information for candidates |
531 |
9 |
NodeManager* nm = NodeManager::currentNM(); |
532 |
9 |
SkolemManager* sm = nm->getSkolemManager(); |
533 |
18 |
for (const Node& e : es) |
534 |
|
{ |
535 |
9 |
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 |
9 |
d_ce_info[e].d_pt = e; |
539 |
9 |
std::map<Node, Node>::const_iterator itcc = e_to_cond.find(e); |
540 |
9 |
Assert(itcc != e_to_cond.end()); |
541 |
18 |
Node cond = itcc->second; |
542 |
18 |
Trace("cegis-unif-enum-debug") |
543 |
9 |
<< "...its condition strategy point is " << cond << "\n"; |
544 |
9 |
d_ce_info[e].d_ce_type = cond.getType(); |
545 |
|
// initialize the symmetry breaking lemma templates |
546 |
27 |
for (unsigned index = 0; index < 2; index++) |
547 |
|
{ |
548 |
18 |
Assert(d_ce_info[e].d_sbt_lemma_tmpl[index].first.isNull()); |
549 |
34 |
Node sp = index == 0 ? e : cond; |
550 |
|
std::map<Node, std::vector<Node>>::const_iterator it = |
551 |
18 |
strategy_lemmas.find(sp); |
552 |
18 |
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 |
32 |
it->second.size() == 1 ? it->second[0] : nm->mkNode(AND, it->second); |
559 |
32 |
Trace("cegis-unif-enum-debug") |
560 |
16 |
<< "...adding lemma template to remove redundant operators for " << sp |
561 |
16 |
<< " --> lambda " << sp << ". " << d_sbt_lemma << "\n"; |
562 |
32 |
d_ce_info[e].d_sbt_lemma_tmpl[index] = |
563 |
48 |
std::pair<Node, Node>(d_sbt_lemma, sp); |
564 |
|
} |
565 |
|
} |
566 |
|
|
567 |
|
// register this strategy |
568 |
9 |
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 |
9 |
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 |
1158 |
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 |
1158 |
unsigned num_enums = 0; |
588 |
1158 |
bool has_num_enums = getAssertedLiteralIndex(num_enums); |
589 |
1158 |
AlwaysAssert(has_num_enums); |
590 |
1158 |
num_enums = num_enums + 1; |
591 |
1158 |
if (index == 1) |
592 |
|
{ |
593 |
|
// we always use (cost-1) conditions, or 1 if in the indepedent case |
594 |
579 |
num_enums = !d_useCondPool ? num_enums - 1 : 1; |
595 |
|
} |
596 |
1158 |
if (num_enums > 0) |
597 |
|
{ |
598 |
1002 |
std::map<Node, StrategyPtInfo>::const_iterator itc = d_ce_info.find(e); |
599 |
1002 |
Assert(itc != d_ce_info.end()); |
600 |
1002 |
Assert(num_enums <= itc->second.d_enums[index].size()); |
601 |
3006 |
es.insert(es.end(), |
602 |
1002 |
itc->second.d_enums[index].begin(), |
603 |
5010 |
itc->second.d_enums[index].begin() + num_enums); |
604 |
|
} |
605 |
1158 |
} |
606 |
|
|
607 |
19 |
void CegisUnifEnumDecisionStrategy::setUpEnumerator(Node e, |
608 |
|
StrategyPtInfo& si, |
609 |
|
unsigned index) |
610 |
|
{ |
611 |
19 |
NodeManager* nm = NodeManager::currentNM(); |
612 |
|
// instantiate template for removing redundant operators |
613 |
19 |
if (!si.d_sbt_lemma_tmpl[index].first.isNull()) |
614 |
|
{ |
615 |
38 |
Node templ = si.d_sbt_lemma_tmpl[index].first; |
616 |
38 |
TNode templ_var = si.d_sbt_lemma_tmpl[index].second; |
617 |
38 |
Node sym_break_red_ops = templ.substitute(templ_var, e); |
618 |
38 |
Trace("cegis-unif-enum-lemma") |
619 |
19 |
<< "CegisUnifEnum::lemma, remove redundant ops of " << e << " : " |
620 |
19 |
<< sym_break_red_ops << "\n"; |
621 |
19 |
d_qim.lemma(sym_break_red_ops, InferenceId::UNKNOWN); |
622 |
|
} |
623 |
|
// symmetry breaking between enumerators |
624 |
19 |
if (!si.d_enums[index].empty() && index == 0) |
625 |
|
{ |
626 |
10 |
Node e_prev = si.d_enums[index].back(); |
627 |
10 |
Node size_e = nm->mkNode(DT_SIZE, e); |
628 |
10 |
Node size_e_prev = nm->mkNode(DT_SIZE, e_prev); |
629 |
10 |
Node sym_break = nm->mkNode(GEQ, size_e, size_e_prev); |
630 |
10 |
Trace("cegis-unif-enum-lemma") |
631 |
5 |
<< "CegisUnifEnum::lemma, enum sym break:" << sym_break << "\n"; |
632 |
5 |
d_qim.lemma(sym_break, InferenceId::UNKNOWN); |
633 |
|
} |
634 |
|
// register the enumerator |
635 |
19 |
si.d_enums[index].push_back(e); |
636 |
19 |
EnumeratorRole erole = ROLE_ENUM_CONSTRAINED; |
637 |
|
// if we are using a single independent enumerator for conditions, then we |
638 |
|
// allocate an active guard, and are eligible to use variable-agnostic |
639 |
|
// enumeration. |
640 |
19 |
if (d_useCondPool && index == 1) |
641 |
|
{ |
642 |
|
erole = ROLE_ENUM_POOL; |
643 |
|
} |
644 |
38 |
Trace("cegis-unif-enum") << "* Registering new enumerator " << e |
645 |
19 |
<< " to strategy point " << si.d_pt << "\n"; |
646 |
19 |
d_tds->registerEnumerator(e, si.d_pt, d_parent, erole); |
647 |
19 |
} |
648 |
|
|
649 |
11 |
void CegisUnifEnumDecisionStrategy::registerEvalPts( |
650 |
|
const std::vector<Node>& eis, Node e) |
651 |
|
{ |
652 |
|
// candidates of the same type are managed |
653 |
11 |
std::map<Node, StrategyPtInfo>::iterator it = d_ce_info.find(e); |
654 |
11 |
Assert(it != d_ce_info.end()); |
655 |
22 |
it->second.d_eval_points.insert( |
656 |
22 |
it->second.d_eval_points.end(), eis.begin(), eis.end()); |
657 |
|
// register at all already allocated sizes |
658 |
30 |
for (const Node& ei : eis) |
659 |
|
{ |
660 |
19 |
Assert(ei.getType() == e.getType()); |
661 |
42 |
for (unsigned j = 0, size = d_literals.size(); j < size; j++) |
662 |
|
{ |
663 |
46 |
Trace("cegis-unif-enum") << "...for cand " << e << " adding hd " << ei |
664 |
23 |
<< " at size " << j << "\n"; |
665 |
23 |
registerEvalPtAtSize(e, ei, d_literals[j], j + 1); |
666 |
|
} |
667 |
|
} |
668 |
11 |
} |
669 |
|
|
670 |
40 |
void CegisUnifEnumDecisionStrategy::registerEvalPtAtSize(Node e, |
671 |
|
Node ei, |
672 |
|
Node guq_lit, |
673 |
|
unsigned n) |
674 |
|
{ |
675 |
|
// must be equal to one of the first n enums |
676 |
40 |
std::map<Node, StrategyPtInfo>::iterator itc = d_ce_info.find(e); |
677 |
40 |
Assert(itc != d_ce_info.end()); |
678 |
40 |
Assert(itc->second.d_enums[0].size() >= n); |
679 |
80 |
std::vector<Node> disj; |
680 |
40 |
disj.push_back(guq_lit.negate()); |
681 |
105 |
for (unsigned i = 0; i < n; i++) |
682 |
|
{ |
683 |
65 |
disj.push_back(ei.eqNode(itc->second.d_enums[0][i])); |
684 |
|
} |
685 |
80 |
Node lem = NodeManager::currentNM()->mkNode(OR, disj); |
686 |
80 |
Trace("cegis-unif-enum-lemma") |
687 |
40 |
<< "CegisUnifEnum::lemma, domain:" << lem << "\n"; |
688 |
40 |
d_qim.lemma(lem, InferenceId::UNKNOWN); |
689 |
40 |
} |
690 |
|
|
691 |
|
} // namespace quantifiers |
692 |
|
} // namespace theory |
693 |
27735 |
} // namespace cvc5 |