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