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