1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Mathias Preiner, Andres Noetzli |
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 ceg_bv_instantiator |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "theory/quantifiers/cegqi/ceg_bv_instantiator.h" |
17 |
|
|
18 |
|
#include <stack> |
19 |
|
#include "expr/skolem_manager.h" |
20 |
|
#include "options/quantifiers_options.h" |
21 |
|
#include "theory/bv/theory_bv_utils.h" |
22 |
|
#include "theory/quantifiers/cegqi/ceg_bv_instantiator_utils.h" |
23 |
|
#include "theory/rewriter.h" |
24 |
|
#include "util/bitvector.h" |
25 |
|
#include "util/random.h" |
26 |
|
|
27 |
|
using namespace std; |
28 |
|
using namespace cvc5::kind; |
29 |
|
|
30 |
|
namespace cvc5 { |
31 |
|
namespace theory { |
32 |
|
namespace quantifiers { |
33 |
|
|
34 |
|
// this class can be used to query the model value through the CegInstaniator |
35 |
|
// class |
36 |
|
class CegInstantiatorBvInverterQuery : public BvInverterQuery |
37 |
|
{ |
38 |
|
public: |
39 |
4397 |
CegInstantiatorBvInverterQuery(CegInstantiator* ci) |
40 |
4397 |
: BvInverterQuery(), d_ci(ci) |
41 |
|
{ |
42 |
4397 |
} |
43 |
4397 |
~CegInstantiatorBvInverterQuery() {} |
44 |
|
/** return the model value of n */ |
45 |
|
Node getModelValue(Node n) override { return d_ci->getModelValue(n); } |
46 |
|
/** get bound variable of type tn */ |
47 |
1133 |
Node getBoundVariable(TypeNode tn) override |
48 |
|
{ |
49 |
1133 |
return d_ci->getBoundVariable(tn); |
50 |
|
} |
51 |
|
|
52 |
|
protected: |
53 |
|
// pointer to class that is able to query model values |
54 |
|
CegInstantiator* d_ci; |
55 |
|
}; |
56 |
|
|
57 |
1110 |
BvInstantiator::BvInstantiator(TypeNode tn, BvInverter* inv) |
58 |
1110 |
: Instantiator(tn), d_inverter(inv), d_inst_id_counter(0) |
59 |
|
{ |
60 |
|
// The inverter utility d_inverter is global to all BvInstantiator classes. |
61 |
|
// This must be global since we need to: |
62 |
|
// * process Skolem functions properly across multiple variables within the |
63 |
|
// same quantifier |
64 |
|
// * cache Skolem variables uniformly across multiple quantified formulas |
65 |
1110 |
} |
66 |
|
|
67 |
2220 |
BvInstantiator::~BvInstantiator() {} |
68 |
5977 |
void BvInstantiator::reset(CegInstantiator* ci, |
69 |
|
SolvedForm& sf, |
70 |
|
Node pv, |
71 |
|
CegInstEffort effort) |
72 |
|
{ |
73 |
5977 |
d_inst_id_counter = 0; |
74 |
5977 |
d_var_to_inst_id.clear(); |
75 |
5977 |
d_inst_id_to_term.clear(); |
76 |
5977 |
d_inst_id_to_alit.clear(); |
77 |
5977 |
d_var_to_curr_inst_id.clear(); |
78 |
5977 |
d_alit_to_model_slack.clear(); |
79 |
5977 |
} |
80 |
|
|
81 |
4741 |
void BvInstantiator::processLiteral(CegInstantiator* ci, |
82 |
|
SolvedForm& sf, |
83 |
|
Node pv, |
84 |
|
Node lit, |
85 |
|
Node alit, |
86 |
|
CegInstEffort effort) |
87 |
|
{ |
88 |
4741 |
Assert(d_inverter != NULL); |
89 |
|
// find path to pv |
90 |
9482 |
std::vector<unsigned> path; |
91 |
9482 |
Node sv = d_inverter->getSolveVariable(pv.getType()); |
92 |
9482 |
Node pvs = ci->getModelValue(pv); |
93 |
4741 |
Trace("cegqi-bv") << "Get path to " << pv << " : " << lit << std::endl; |
94 |
|
Node slit = |
95 |
9482 |
d_inverter->getPathToPv(lit, pv, sv, pvs, path, options::cegqiBvSolveNl()); |
96 |
4741 |
if (!slit.isNull()) |
97 |
|
{ |
98 |
8794 |
CegInstantiatorBvInverterQuery m(ci); |
99 |
4397 |
unsigned iid = d_inst_id_counter; |
100 |
4397 |
Trace("cegqi-bv") << "Solve lit to bv inverter : " << slit << std::endl; |
101 |
8794 |
Node inst = d_inverter->solveBvLit(sv, slit, path, &m); |
102 |
4397 |
if (!inst.isNull()) |
103 |
|
{ |
104 |
3377 |
inst = Rewriter::rewrite(inst); |
105 |
3377 |
if (inst.isConst() || !ci->hasNestedQuantification()) |
106 |
|
{ |
107 |
3357 |
Trace("cegqi-bv") << "...solved form is " << inst << std::endl; |
108 |
|
// store information for id and increment |
109 |
3357 |
d_var_to_inst_id[pv].push_back(iid); |
110 |
3357 |
d_inst_id_to_term[iid] = inst; |
111 |
3357 |
d_inst_id_to_alit[iid] = alit; |
112 |
3357 |
d_inst_id_counter++; |
113 |
|
} |
114 |
|
} |
115 |
|
else |
116 |
|
{ |
117 |
1020 |
Trace("cegqi-bv") << "...failed to solve." << std::endl; |
118 |
|
} |
119 |
|
} |
120 |
|
else |
121 |
|
{ |
122 |
344 |
Trace("cegqi-bv") << "...no path." << std::endl; |
123 |
|
} |
124 |
4741 |
} |
125 |
|
|
126 |
5167 |
bool BvInstantiator::hasProcessAssertion(CegInstantiator* ci, |
127 |
|
SolvedForm& sf, |
128 |
|
Node pv, |
129 |
|
CegInstEffort effort) |
130 |
|
{ |
131 |
5167 |
return true; |
132 |
|
} |
133 |
|
|
134 |
27385 |
Node BvInstantiator::hasProcessAssertion(CegInstantiator* ci, |
135 |
|
SolvedForm& sf, |
136 |
|
Node pv, |
137 |
|
Node lit, |
138 |
|
CegInstEffort effort) |
139 |
|
{ |
140 |
27385 |
if (effort == CEG_INST_EFFORT_FULL) |
141 |
|
{ |
142 |
|
// always use model values at full effort |
143 |
9181 |
return Node::null(); |
144 |
|
} |
145 |
36408 |
Node atom = lit.getKind() == NOT ? lit[0] : lit; |
146 |
18204 |
bool pol = lit.getKind() != NOT; |
147 |
18204 |
Kind k = atom.getKind(); |
148 |
18204 |
if (k != EQUAL && k != BITVECTOR_ULT && k != BITVECTOR_SLT) |
149 |
|
{ |
150 |
|
// others are unhandled |
151 |
2424 |
return Node::null(); |
152 |
|
} |
153 |
15780 |
else if (!atom[0].getType().isBitVector()) |
154 |
|
{ |
155 |
|
return Node::null(); |
156 |
|
} |
157 |
31560 |
else if (options::cegqiBvIneqMode() == options::CegqiBvIneqMode::KEEP |
158 |
15780 |
|| (pol && k == EQUAL)) |
159 |
|
{ |
160 |
6987 |
return lit; |
161 |
|
} |
162 |
8793 |
NodeManager* nm = NodeManager::currentNM(); |
163 |
17586 |
Node s = atom[0]; |
164 |
17586 |
Node t = atom[1]; |
165 |
|
|
166 |
17586 |
Node sm = ci->getModelValue(s); |
167 |
17586 |
Node tm = ci->getModelValue(t); |
168 |
8793 |
Assert(!sm.isNull() && sm.isConst()); |
169 |
8793 |
Assert(!tm.isNull() && tm.isConst()); |
170 |
8793 |
Trace("cegqi-bv") << "Model value: " << std::endl; |
171 |
17586 |
Trace("cegqi-bv") << " " << s << " " << k << " " << t << " is " |
172 |
8793 |
<< std::endl; |
173 |
8793 |
Trace("cegqi-bv") << " " << sm << " <> " << tm << std::endl; |
174 |
|
|
175 |
17586 |
Node ret; |
176 |
8793 |
if (options::cegqiBvIneqMode() == options::CegqiBvIneqMode::EQ_SLACK) |
177 |
|
{ |
178 |
|
// if using slack, we convert constraints to a positive equality based on |
179 |
|
// the current model M, e.g.: |
180 |
|
// (not) s ~ t ---> s = t + ( s^M - t^M ) |
181 |
|
if (sm != tm) |
182 |
|
{ |
183 |
|
Node slack = Rewriter::rewrite(nm->mkNode(BITVECTOR_SUB, sm, tm)); |
184 |
|
Assert(slack.isConst()); |
185 |
|
// remember the slack value for the asserted literal |
186 |
|
d_alit_to_model_slack[lit] = slack; |
187 |
|
ret = nm->mkNode(EQUAL, s, nm->mkNode(BITVECTOR_ADD, t, slack)); |
188 |
|
Trace("cegqi-bv") << "Slack is " << slack << std::endl; |
189 |
|
} |
190 |
|
else |
191 |
|
{ |
192 |
|
ret = s.eqNode(t); |
193 |
|
} |
194 |
|
} |
195 |
|
else |
196 |
|
{ |
197 |
|
// turn disequality into an inequality |
198 |
|
// e.g. s != t becomes s < t or t < s |
199 |
8793 |
if (k == EQUAL) |
200 |
|
{ |
201 |
3168 |
if (Random::getRandom().pickWithProb(0.5)) |
202 |
|
{ |
203 |
1481 |
std::swap(s, t); |
204 |
|
} |
205 |
3168 |
pol = true; |
206 |
|
} |
207 |
|
// otherwise, we optimistically solve for the boundary point of an |
208 |
|
// inequality, for example: |
209 |
|
// for s < t, we solve s+1 = t |
210 |
|
// for ~( s < t ), we solve s = t |
211 |
|
// notice that this equality does not necessarily hold in the model, and |
212 |
|
// hence the corresponding instantiation strategy is not guaranteed to be |
213 |
|
// monotonic. |
214 |
8793 |
if (!pol) |
215 |
|
{ |
216 |
3685 |
ret = s.eqNode(t); |
217 |
|
} |
218 |
|
else |
219 |
|
{ |
220 |
10216 |
Node bv_one = bv::utils::mkOne(bv::utils::getSize(s)); |
221 |
5108 |
ret = nm->mkNode(BITVECTOR_ADD, s, bv_one).eqNode(t); |
222 |
|
} |
223 |
|
} |
224 |
8793 |
Trace("cegqi-bv") << "Process " << lit << " as " << ret << std::endl; |
225 |
8793 |
return ret; |
226 |
|
} |
227 |
|
|
228 |
5699 |
bool BvInstantiator::processAssertion(CegInstantiator* ci, |
229 |
|
SolvedForm& sf, |
230 |
|
Node pv, |
231 |
|
Node lit, |
232 |
|
Node alit, |
233 |
|
CegInstEffort effort) |
234 |
|
{ |
235 |
|
// if option enabled, use approach for word-level inversion for BV |
236 |
|
// instantiation |
237 |
5699 |
if (options::cegqiBv()) |
238 |
|
{ |
239 |
|
// get the best rewritten form of lit for solving for pv |
240 |
|
// this should remove instances of non-invertible operators, and |
241 |
|
// "linearize" lit with respect to pv as much as possible |
242 |
9482 |
Node rlit = rewriteAssertionForSolvePv(ci, pv, lit); |
243 |
4741 |
if (Trace.isOn("cegqi-bv")) |
244 |
|
{ |
245 |
|
Trace("cegqi-bv") << "BvInstantiator::processAssertion : solve " << pv |
246 |
|
<< " in " << lit << std::endl; |
247 |
|
if (lit != rlit) |
248 |
|
{ |
249 |
|
Trace("cegqi-bv") << "...rewritten to " << rlit << std::endl; |
250 |
|
} |
251 |
|
} |
252 |
4741 |
if (!rlit.isNull()) |
253 |
|
{ |
254 |
4741 |
processLiteral(ci, sf, pv, rlit, alit, effort); |
255 |
|
} |
256 |
|
} |
257 |
5699 |
return false; |
258 |
|
} |
259 |
|
|
260 |
3699 |
bool BvInstantiator::useModelValue(CegInstantiator* ci, |
261 |
|
SolvedForm& sf, |
262 |
|
Node pv, |
263 |
|
CegInstEffort effort) |
264 |
|
{ |
265 |
3699 |
return effort < CEG_INST_EFFORT_FULL || options::cegqiFullEffort(); |
266 |
|
} |
267 |
|
|
268 |
5167 |
bool BvInstantiator::processAssertions(CegInstantiator* ci, |
269 |
|
SolvedForm& sf, |
270 |
|
Node pv, |
271 |
|
CegInstEffort effort) |
272 |
|
{ |
273 |
|
std::unordered_map<Node, std::vector<unsigned>>::iterator iti = |
274 |
5167 |
d_var_to_inst_id.find(pv); |
275 |
5167 |
if (iti == d_var_to_inst_id.end()) |
276 |
|
{ |
277 |
|
// no bounds |
278 |
2889 |
return false; |
279 |
|
} |
280 |
4556 |
Trace("cegqi-bv") << "BvInstantiator::processAssertions for " << pv |
281 |
2278 |
<< std::endl; |
282 |
|
// if interleaving, do not do inversion half the time |
283 |
2278 |
if (options::cegqiBvInterleaveValue() && Random::getRandom().pickWithProb(0.5)) |
284 |
|
{ |
285 |
|
Trace("cegqi-bv") << "...do not do instantiation for " << pv |
286 |
|
<< " (skip, based on heuristic)" << std::endl; |
287 |
|
} |
288 |
2278 |
bool firstVar = sf.empty(); |
289 |
|
// get inst id list |
290 |
2278 |
if (Trace.isOn("cegqi-bv")) |
291 |
|
{ |
292 |
|
Trace("cegqi-bv") << " " << iti->second.size() |
293 |
|
<< " candidate instantiations for " << pv << " : " |
294 |
|
<< std::endl; |
295 |
|
if (firstVar) |
296 |
|
{ |
297 |
|
Trace("cegqi-bv") << " ...this is the first variable" << std::endl; |
298 |
|
} |
299 |
|
} |
300 |
|
// until we have a model-preserving selection function for BV, this must |
301 |
|
// be heuristic (we only pick one literal) |
302 |
|
// hence we randomize the list |
303 |
|
// this helps robustness, since picking the same literal every time may |
304 |
|
// lead to a stream of value instantiations, whereas with randomization |
305 |
|
// we may find an invertible literal that leads to a useful instantiation. |
306 |
2278 |
std::shuffle(iti->second.begin(), iti->second.end(), Random::getRandom()); |
307 |
|
|
308 |
2278 |
if (Trace.isOn("cegqi-bv")) |
309 |
|
{ |
310 |
|
for (unsigned j = 0, size = iti->second.size(); j < size; j++) |
311 |
|
{ |
312 |
|
unsigned inst_id = iti->second[j]; |
313 |
|
Assert(d_inst_id_to_term.find(inst_id) != d_inst_id_to_term.end()); |
314 |
|
Node inst_term = d_inst_id_to_term[inst_id]; |
315 |
|
Assert(d_inst_id_to_alit.find(inst_id) != d_inst_id_to_alit.end()); |
316 |
|
Node alit = d_inst_id_to_alit[inst_id]; |
317 |
|
|
318 |
|
// get the slack value introduced for the asserted literal |
319 |
|
Node curr_slack_val; |
320 |
|
std::unordered_map<Node, Node>::iterator itms = |
321 |
|
d_alit_to_model_slack.find(alit); |
322 |
|
if (itms != d_alit_to_model_slack.end()) |
323 |
|
{ |
324 |
|
curr_slack_val = itms->second; |
325 |
|
} |
326 |
|
|
327 |
|
// debug printing |
328 |
|
Trace("cegqi-bv") << " [" << j << "] : "; |
329 |
|
Trace("cegqi-bv") << inst_term << std::endl; |
330 |
|
if (!curr_slack_val.isNull()) |
331 |
|
{ |
332 |
|
Trace("cegqi-bv") << " ...with slack value : " << curr_slack_val |
333 |
|
<< std::endl; |
334 |
|
} |
335 |
|
Trace("cegqi-bv-debug") << " ...from : " << alit << std::endl; |
336 |
|
Trace("cegqi-bv") << std::endl; |
337 |
|
} |
338 |
|
} |
339 |
|
|
340 |
|
// Now, try all instantiation ids we want to try |
341 |
|
// Typically we try only one, otherwise worst-case performance |
342 |
|
// for constructing instantiations is exponential on the number of |
343 |
|
// variables in this quantifier prefix. |
344 |
2278 |
bool ret = false; |
345 |
2278 |
bool tryMultipleInst = firstVar && options::cegqiMultiInst(); |
346 |
2278 |
bool revertOnSuccess = tryMultipleInst; |
347 |
2278 |
for (unsigned j = 0, size = iti->second.size(); j < size; j++) |
348 |
|
{ |
349 |
2278 |
unsigned inst_id = iti->second[j]; |
350 |
2278 |
Assert(d_inst_id_to_term.find(inst_id) != d_inst_id_to_term.end()); |
351 |
2278 |
Node inst_term = d_inst_id_to_term[inst_id]; |
352 |
2278 |
Node alit = d_inst_id_to_alit[inst_id]; |
353 |
|
// try instantiation pv -> inst_term |
354 |
2278 |
TermProperties pv_prop_bv; |
355 |
2278 |
Trace("cegqi-bv") << "*** try " << pv << " -> " << inst_term << std::endl; |
356 |
2278 |
d_var_to_curr_inst_id[pv] = inst_id; |
357 |
2278 |
ci->markSolved(alit); |
358 |
2278 |
if (ci->constructInstantiationInc( |
359 |
|
pv, inst_term, pv_prop_bv, sf, revertOnSuccess)) |
360 |
|
{ |
361 |
1087 |
ret = true; |
362 |
|
} |
363 |
2278 |
ci->markSolved(alit, false); |
364 |
|
// we are done unless we try multiple instances |
365 |
2278 |
if (!tryMultipleInst) |
366 |
|
{ |
367 |
2278 |
break; |
368 |
|
} |
369 |
|
} |
370 |
2278 |
if (ret) |
371 |
|
{ |
372 |
1087 |
return true; |
373 |
|
} |
374 |
1191 |
Trace("cegqi-bv") << "...failed to add instantiation for " << pv << std::endl; |
375 |
1191 |
d_var_to_curr_inst_id.erase(pv); |
376 |
|
|
377 |
1191 |
return false; |
378 |
|
} |
379 |
|
|
380 |
4741 |
Node BvInstantiator::rewriteAssertionForSolvePv(CegInstantiator* ci, |
381 |
|
Node pv, |
382 |
|
Node lit) |
383 |
|
{ |
384 |
|
// result of rewriting the visited term |
385 |
9482 |
std::stack<std::unordered_map<TNode, Node>> visited; |
386 |
4741 |
visited.push(std::unordered_map<TNode, Node>()); |
387 |
|
// whether the visited term contains pv |
388 |
9482 |
std::unordered_map<Node, bool> visited_contains_pv; |
389 |
4741 |
std::unordered_map<TNode, Node>::iterator it; |
390 |
9482 |
std::unordered_map<TNode, Node> curr_subs; |
391 |
9482 |
std::stack<std::stack<TNode> > visit; |
392 |
9482 |
TNode cur; |
393 |
4741 |
visit.push(std::stack<TNode>()); |
394 |
4741 |
visit.top().push(lit); |
395 |
86340 |
do |
396 |
|
{ |
397 |
91081 |
cur = visit.top().top(); |
398 |
91081 |
visit.top().pop(); |
399 |
91081 |
it = visited.top().find(cur); |
400 |
|
|
401 |
91081 |
if (it == visited.top().end()) |
402 |
|
{ |
403 |
38647 |
std::unordered_map<TNode, Node>::iterator itc = curr_subs.find(cur); |
404 |
38647 |
if (itc != curr_subs.end()) |
405 |
|
{ |
406 |
82 |
visited.top()[cur] = itc->second; |
407 |
|
} |
408 |
|
else |
409 |
|
{ |
410 |
38565 |
if (cur.getKind() == WITNESS) |
411 |
|
{ |
412 |
|
// must replace variables of choice functions |
413 |
|
// with new variables to avoid variable |
414 |
|
// capture when considering substitutions |
415 |
|
// with multiple literals. |
416 |
164 |
Node bv = ci->getBoundVariable(cur[0][0].getType()); |
417 |
|
// should not have captured variables |
418 |
82 |
Assert(curr_subs.find(cur[0][0]) == curr_subs.end()); |
419 |
82 |
curr_subs[cur[0][0]] = bv; |
420 |
|
// we cannot cache the results of subterms |
421 |
|
// of this witness expression since we are |
422 |
|
// now in the context { cur[0][0] -> bv }, |
423 |
|
// hence we push a context here |
424 |
82 |
visited.push(std::unordered_map<TNode, Node>()); |
425 |
82 |
visit.push(std::stack<TNode>()); |
426 |
|
} |
427 |
38565 |
visited.top()[cur] = Node::null(); |
428 |
38565 |
visit.top().push(cur); |
429 |
86340 |
for (unsigned i = 0; i < cur.getNumChildren(); i++) |
430 |
|
{ |
431 |
47775 |
visit.top().push(cur[i]); |
432 |
|
} |
433 |
|
} |
434 |
|
} |
435 |
52434 |
else if (it->second.isNull()) |
436 |
|
{ |
437 |
77130 |
Node ret; |
438 |
38565 |
bool childChanged = false; |
439 |
77130 |
std::vector<Node> children; |
440 |
38565 |
if (cur.getMetaKind() == kind::metakind::PARAMETERIZED) |
441 |
|
{ |
442 |
3365 |
children.push_back(cur.getOperator()); |
443 |
|
} |
444 |
38565 |
bool contains_pv = (cur == pv); |
445 |
86340 |
for (unsigned i = 0; i < cur.getNumChildren(); i++) |
446 |
|
{ |
447 |
47775 |
it = visited.top().find(cur[i]); |
448 |
47775 |
Assert(it != visited.top().end()); |
449 |
47775 |
Assert(!it->second.isNull()); |
450 |
47775 |
childChanged = childChanged || cur[i] != it->second; |
451 |
47775 |
children.push_back(it->second); |
452 |
47775 |
contains_pv = contains_pv || visited_contains_pv[cur[i]]; |
453 |
|
} |
454 |
|
// careful that rewrites above do not affect whether this term contains pv |
455 |
38565 |
visited_contains_pv[cur] = contains_pv; |
456 |
|
|
457 |
|
// rewrite the term |
458 |
38565 |
ret = rewriteTermForSolvePv(pv, cur, children, visited_contains_pv); |
459 |
|
|
460 |
|
// return original if the above function does not produce a result |
461 |
38565 |
if (ret.isNull()) |
462 |
|
{ |
463 |
37703 |
if (childChanged) |
464 |
|
{ |
465 |
1596 |
ret = NodeManager::currentNM()->mkNode(cur.getKind(), children); |
466 |
|
} |
467 |
|
else |
468 |
|
{ |
469 |
36107 |
ret = cur; |
470 |
|
} |
471 |
|
} |
472 |
|
|
473 |
|
/* We need to update contains_pv also for rewritten nodes, since |
474 |
|
* the normalizePv* functions rely on the information if pv occurs in a |
475 |
|
* rewritten node or not. */ |
476 |
38565 |
if (ret != cur) |
477 |
|
{ |
478 |
1991 |
contains_pv = (ret == pv); |
479 |
6413 |
for (unsigned i = 0, size = ret.getNumChildren(); i < size; ++i) |
480 |
|
{ |
481 |
4422 |
contains_pv = contains_pv || visited_contains_pv[ret[i]]; |
482 |
|
} |
483 |
1991 |
visited_contains_pv[ret] = contains_pv; |
484 |
|
} |
485 |
|
|
486 |
|
// if was witness, pop context |
487 |
38565 |
if (cur.getKind() == WITNESS) |
488 |
|
{ |
489 |
82 |
Assert(curr_subs.find(cur[0][0]) != curr_subs.end()); |
490 |
82 |
curr_subs.erase(cur[0][0]); |
491 |
82 |
visited.pop(); |
492 |
82 |
visit.pop(); |
493 |
82 |
Assert(visited.size() == visit.size()); |
494 |
82 |
Assert(!visit.empty()); |
495 |
|
} |
496 |
|
|
497 |
38565 |
visited.top()[cur] = ret; |
498 |
|
} |
499 |
91081 |
} while (!visit.top().empty()); |
500 |
4741 |
Assert(visited.size() == 1); |
501 |
4741 |
Assert(visited.top().find(lit) != visited.top().end()); |
502 |
4741 |
Assert(!visited.top().find(lit)->second.isNull()); |
503 |
|
|
504 |
4741 |
Node result = visited.top()[lit]; |
505 |
|
|
506 |
4741 |
if (Trace.isOn("cegqi-bv-nl")) |
507 |
|
{ |
508 |
|
std::vector<TNode> trace_visit; |
509 |
|
std::unordered_set<TNode> trace_visited; |
510 |
|
|
511 |
|
trace_visit.push_back(result); |
512 |
|
do |
513 |
|
{ |
514 |
|
cur = trace_visit.back(); |
515 |
|
trace_visit.pop_back(); |
516 |
|
|
517 |
|
if (trace_visited.find(cur) == trace_visited.end()) |
518 |
|
{ |
519 |
|
trace_visited.insert(cur); |
520 |
|
trace_visit.insert(trace_visit.end(), cur.begin(), cur.end()); |
521 |
|
} |
522 |
|
else if (cur == pv) |
523 |
|
{ |
524 |
|
Trace("cegqi-bv-nl") |
525 |
|
<< "NONLINEAR LITERAL for " << pv << " : " << lit << std::endl; |
526 |
|
} |
527 |
|
} while (!trace_visit.empty()); |
528 |
|
} |
529 |
|
|
530 |
9482 |
return result; |
531 |
|
} |
532 |
|
|
533 |
38565 |
Node BvInstantiator::rewriteTermForSolvePv( |
534 |
|
Node pv, |
535 |
|
Node n, |
536 |
|
std::vector<Node>& children, |
537 |
|
std::unordered_map<Node, bool>& contains_pv) |
538 |
|
{ |
539 |
38565 |
NodeManager* nm = NodeManager::currentNM(); |
540 |
|
|
541 |
|
// [1] rewrite cases of non-invertible operators |
542 |
|
|
543 |
38565 |
if (n.getKind() == EQUAL) |
544 |
|
{ |
545 |
14447 |
TNode lhs = children[0]; |
546 |
14447 |
TNode rhs = children[1]; |
547 |
|
|
548 |
|
/* rewrite: x * x = x -> x < 2 */ |
549 |
14633 |
if ((lhs == pv && rhs.getKind() == BITVECTOR_MULT && rhs[0] == pv |
550 |
7232 |
&& rhs[1] == pv) |
551 |
28932 |
|| (rhs == pv && lhs.getKind() == BITVECTOR_MULT && lhs[0] == pv |
552 |
7240 |
&& lhs[1] == pv)) |
553 |
|
{ |
554 |
|
return nm->mkNode( |
555 |
|
BITVECTOR_ULT, |
556 |
|
pv, |
557 |
4 |
bv::utils::mkConst(BitVector(bv::utils::getSize(pv), Integer(2)))); |
558 |
|
} |
559 |
|
|
560 |
7228 |
if (options::cegqiBvLinearize() && contains_pv[lhs] && contains_pv[rhs]) |
561 |
|
{ |
562 |
26 |
Node result = utils::normalizePvEqual(pv, children, contains_pv); |
563 |
13 |
if (!result.isNull()) |
564 |
|
{ |
565 |
8 |
Trace("cegqi-bv-nl") |
566 |
4 |
<< "Normalize " << n << " to " << result << std::endl; |
567 |
|
} |
568 |
|
else |
569 |
|
{ |
570 |
18 |
Trace("cegqi-bv-nl") |
571 |
9 |
<< "Nonlinear " << n.getKind() << " " << n << std::endl; |
572 |
|
} |
573 |
13 |
return result; |
574 |
|
} |
575 |
|
} |
576 |
31333 |
else if (n.getKind() == BITVECTOR_MULT || n.getKind() == BITVECTOR_ADD) |
577 |
|
{ |
578 |
3607 |
if (options::cegqiBvLinearize() && contains_pv[n]) |
579 |
|
{ |
580 |
2444 |
Node result; |
581 |
1649 |
if (n.getKind() == BITVECTOR_MULT) |
582 |
|
{ |
583 |
278 |
result = utils::normalizePvMult(pv, children, contains_pv); |
584 |
|
} |
585 |
|
else |
586 |
|
{ |
587 |
1371 |
result = utils::normalizePvPlus(pv, children, contains_pv); |
588 |
|
} |
589 |
1649 |
if (!result.isNull()) |
590 |
|
{ |
591 |
1708 |
Trace("cegqi-bv-nl") |
592 |
854 |
<< "Normalize " << n << " to " << result << std::endl; |
593 |
854 |
return result; |
594 |
|
} |
595 |
|
else |
596 |
|
{ |
597 |
1590 |
Trace("cegqi-bv-nl") |
598 |
795 |
<< "Nonlinear " << n.getKind() << " " << n << std::endl; |
599 |
|
} |
600 |
|
} |
601 |
|
} |
602 |
|
|
603 |
|
// [2] try to rewrite non-linear literals -> linear literals |
604 |
|
|
605 |
37694 |
return Node::null(); |
606 |
|
} |
607 |
|
|
608 |
|
/** sort bv extract interval |
609 |
|
* |
610 |
|
* This sorts lists of bitvector extract terms where |
611 |
|
* ((_ extract i1 i2) t) < ((_ extract j1 j2) t) |
612 |
|
* if i1>j1 or i1=j1 and i2>j2. |
613 |
|
*/ |
614 |
|
struct SortBvExtractInterval |
615 |
|
{ |
616 |
81 |
bool operator()(Node i, Node j) |
617 |
|
{ |
618 |
81 |
Assert(i.getKind() == BITVECTOR_EXTRACT); |
619 |
81 |
Assert(j.getKind() == BITVECTOR_EXTRACT); |
620 |
81 |
BitVectorExtract ie = i.getOperator().getConst<BitVectorExtract>(); |
621 |
81 |
BitVectorExtract je = j.getOperator().getConst<BitVectorExtract>(); |
622 |
81 |
if (ie.d_high > je.d_high) |
623 |
|
{ |
624 |
79 |
return true; |
625 |
|
} |
626 |
2 |
else if (ie.d_high == je.d_high) |
627 |
|
{ |
628 |
|
Assert(ie.d_low != je.d_low); |
629 |
|
return ie.d_low > je.d_low; |
630 |
|
} |
631 |
2 |
return false; |
632 |
|
} |
633 |
|
}; |
634 |
|
|
635 |
710 |
void BvInstantiatorPreprocess::registerCounterexampleLemma( |
636 |
|
Node lem, std::vector<Node>& ceVars, std::vector<Node>& auxLems) |
637 |
|
{ |
638 |
|
// new variables |
639 |
1420 |
std::vector<Node> vars; |
640 |
|
// new lemmas |
641 |
1420 |
std::vector<Node> new_lems; |
642 |
|
|
643 |
710 |
if (options::cegqiBvRmExtract()) |
644 |
|
{ |
645 |
710 |
NodeManager* nm = NodeManager::currentNM(); |
646 |
710 |
SkolemManager* sm = nm->getSkolemManager(); |
647 |
710 |
Trace("cegqi-bv-pp") << "-----remove extracts..." << std::endl; |
648 |
|
// map from terms to bitvector extracts applied to that term |
649 |
1420 |
std::map<Node, std::vector<Node> > extract_map; |
650 |
1420 |
std::unordered_set<TNode> visited; |
651 |
710 |
Trace("cegqi-bv-pp-debug2") << "Register ce lemma " << lem << std::endl; |
652 |
710 |
collectExtracts(lem, extract_map, visited); |
653 |
813 |
for (std::pair<const Node, std::vector<Node> >& es : extract_map) |
654 |
|
{ |
655 |
|
// sort based on the extract start position |
656 |
103 |
std::vector<Node>& curr_vec = es.second; |
657 |
|
|
658 |
|
SortBvExtractInterval sbei; |
659 |
103 |
std::sort(curr_vec.begin(), curr_vec.end(), sbei); |
660 |
|
|
661 |
103 |
unsigned width = es.first.getType().getBitVectorSize(); |
662 |
|
|
663 |
|
// list of points b such that: |
664 |
|
// b>0 and we must start a segment at (b-1) or b==0 |
665 |
206 |
std::vector<unsigned> boundaries; |
666 |
103 |
boundaries.push_back(width); |
667 |
103 |
boundaries.push_back(0); |
668 |
|
|
669 |
103 |
Trace("cegqi-bv-pp") << "For term " << es.first << " : " << std::endl; |
670 |
286 |
for (unsigned i = 0, size = curr_vec.size(); i < size; i++) |
671 |
|
{ |
672 |
183 |
Trace("cegqi-bv-pp") << " " << i << " : " << curr_vec[i] << std::endl; |
673 |
|
BitVectorExtract e = |
674 |
183 |
curr_vec[i].getOperator().getConst<BitVectorExtract>(); |
675 |
549 |
if (std::find(boundaries.begin(), boundaries.end(), e.d_high + 1) |
676 |
549 |
== boundaries.end()) |
677 |
|
{ |
678 |
23 |
boundaries.push_back(e.d_high + 1); |
679 |
|
} |
680 |
549 |
if (std::find(boundaries.begin(), boundaries.end(), e.d_low) |
681 |
549 |
== boundaries.end()) |
682 |
|
{ |
683 |
82 |
boundaries.push_back(e.d_low); |
684 |
|
} |
685 |
|
} |
686 |
103 |
std::sort(boundaries.rbegin(), boundaries.rend()); |
687 |
|
|
688 |
|
// make the extract variables |
689 |
206 |
std::vector<Node> children; |
690 |
311 |
for (unsigned i = 1; i < boundaries.size(); i++) |
691 |
|
{ |
692 |
208 |
Assert(boundaries[i - 1] > 0); |
693 |
|
Node ex = bv::utils::mkExtract( |
694 |
416 |
es.first, boundaries[i - 1] - 1, boundaries[i]); |
695 |
|
Node var = |
696 |
|
sm->mkDummySkolem("ek", |
697 |
416 |
ex.getType(), |
698 |
832 |
"variable to represent disjoint extract region"); |
699 |
208 |
children.push_back(var); |
700 |
208 |
vars.push_back(var); |
701 |
|
} |
702 |
|
|
703 |
206 |
Node conc = nm->mkNode(kind::BITVECTOR_CONCAT, children); |
704 |
103 |
Assert(conc.getType() == es.first.getType()); |
705 |
206 |
Node eq_lem = conc.eqNode(es.first); |
706 |
103 |
Trace("cegqi-bv-pp") << "Introduced : " << eq_lem << std::endl; |
707 |
103 |
new_lems.push_back(eq_lem); |
708 |
206 |
Trace("cegqi-bv-pp") << "...finished processing extracts for term " |
709 |
103 |
<< es.first << std::endl; |
710 |
|
} |
711 |
710 |
Trace("cegqi-bv-pp") << "-----done remove extracts" << std::endl; |
712 |
|
} |
713 |
|
|
714 |
710 |
if (!vars.empty()) |
715 |
|
{ |
716 |
|
// could try applying subs -> vars here |
717 |
|
// in practice, this led to worse performance |
718 |
|
|
719 |
114 |
Trace("cegqi-bv-pp") << "Adding " << new_lems.size() << " lemmas..." |
720 |
57 |
<< std::endl; |
721 |
57 |
auxLems.insert(auxLems.end(), new_lems.begin(), new_lems.end()); |
722 |
114 |
Trace("cegqi-bv-pp") << "Adding " << vars.size() << " variables..." |
723 |
57 |
<< std::endl; |
724 |
57 |
ceVars.insert(ceVars.end(), vars.begin(), vars.end()); |
725 |
|
} |
726 |
710 |
} |
727 |
|
|
728 |
710 |
void BvInstantiatorPreprocess::collectExtracts( |
729 |
|
Node lem, |
730 |
|
std::map<Node, std::vector<Node>>& extract_map, |
731 |
|
std::unordered_set<TNode>& visited) |
732 |
|
{ |
733 |
1420 |
std::vector<TNode> visit; |
734 |
1420 |
TNode cur; |
735 |
710 |
visit.push_back(lem); |
736 |
8520 |
do |
737 |
|
{ |
738 |
9230 |
cur = visit.back(); |
739 |
9230 |
visit.pop_back(); |
740 |
9230 |
if (visited.find(cur) == visited.end()) |
741 |
|
{ |
742 |
7996 |
visited.insert(cur); |
743 |
7996 |
if (cur.getKind() != FORALL) |
744 |
|
{ |
745 |
7986 |
if (cur.getKind() == BITVECTOR_EXTRACT) |
746 |
|
{ |
747 |
243 |
if (cur[0].getKind() == INST_CONSTANT) |
748 |
|
{ |
749 |
183 |
extract_map[cur[0]].push_back(cur); |
750 |
|
} |
751 |
|
} |
752 |
|
|
753 |
16506 |
for (const Node& nc : cur) |
754 |
|
{ |
755 |
8520 |
visit.push_back(nc); |
756 |
|
} |
757 |
|
} |
758 |
|
} |
759 |
9230 |
} while (!visit.empty()); |
760 |
710 |
} |
761 |
|
|
762 |
|
} // namespace quantifiers |
763 |
|
} // namespace theory |
764 |
29502 |
} // namespace cvc5 |