1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Clark Barrett, Morgan Deters |
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 model class. |
14 |
|
*/ |
15 |
|
#include "theory/theory_model.h" |
16 |
|
|
17 |
|
#include "expr/node_algorithm.h" |
18 |
|
#include "options/quantifiers_options.h" |
19 |
|
#include "options/smt_options.h" |
20 |
|
#include "options/theory_options.h" |
21 |
|
#include "options/uf_options.h" |
22 |
|
#include "smt/env.h" |
23 |
|
#include "smt/smt_engine.h" |
24 |
|
#include "theory/rewriter.h" |
25 |
|
#include "theory/trust_substitutions.h" |
26 |
|
|
27 |
|
using namespace std; |
28 |
|
using namespace cvc5::kind; |
29 |
|
using namespace cvc5::context; |
30 |
|
|
31 |
|
namespace cvc5 { |
32 |
|
namespace theory { |
33 |
|
|
34 |
9459 |
TheoryModel::TheoryModel(Env& env, std::string name, bool enableFuncModels) |
35 |
|
: d_env(env), |
36 |
|
d_name(name), |
37 |
|
d_equalityEngine(nullptr), |
38 |
|
d_using_model_core(false), |
39 |
9459 |
d_enableFuncModels(enableFuncModels) |
40 |
|
{ |
41 |
|
// must use function models when ufHo is enabled |
42 |
9459 |
Assert(d_enableFuncModels || !options::ufHo()); |
43 |
9459 |
d_true = NodeManager::currentNM()->mkConst( true ); |
44 |
9459 |
d_false = NodeManager::currentNM()->mkConst( false ); |
45 |
9459 |
} |
46 |
|
|
47 |
18918 |
TheoryModel::~TheoryModel() {} |
48 |
|
|
49 |
9459 |
void TheoryModel::finishInit(eq::EqualityEngine* ee) |
50 |
|
{ |
51 |
9459 |
Assert(ee != nullptr); |
52 |
9459 |
d_equalityEngine = ee; |
53 |
|
// The kinds we are treating as function application in congruence |
54 |
9459 |
d_equalityEngine->addFunctionKind(kind::APPLY_UF, false, options::ufHo()); |
55 |
9459 |
d_equalityEngine->addFunctionKind(kind::HO_APPLY); |
56 |
9459 |
d_equalityEngine->addFunctionKind(kind::SELECT); |
57 |
|
// d_equalityEngine->addFunctionKind(kind::STORE); |
58 |
9459 |
d_equalityEngine->addFunctionKind(kind::APPLY_CONSTRUCTOR); |
59 |
9459 |
d_equalityEngine->addFunctionKind(kind::APPLY_SELECTOR_TOTAL); |
60 |
9459 |
d_equalityEngine->addFunctionKind(kind::APPLY_TESTER); |
61 |
|
// do not interpret APPLY_UF if we are not assigning function values |
62 |
9459 |
if (!d_enableFuncModels) |
63 |
|
{ |
64 |
|
setSemiEvaluatedKind(kind::APPLY_UF); |
65 |
|
} |
66 |
|
// Equal and not terms are not relevant terms. In other words, asserted |
67 |
|
// equalities and negations of predicates (as terms) do not need to be sent |
68 |
|
// to the model. Regardless, theories should send information to the model |
69 |
|
// that ensures that all assertions are satisfied. |
70 |
9459 |
setIrrelevantKind(EQUAL); |
71 |
9459 |
setIrrelevantKind(NOT); |
72 |
9459 |
} |
73 |
|
|
74 |
19608 |
void TheoryModel::reset(){ |
75 |
19608 |
d_modelCache.clear(); |
76 |
19608 |
d_comment_str.clear(); |
77 |
19608 |
d_sep_heap = Node::null(); |
78 |
19608 |
d_sep_nil_eq = Node::null(); |
79 |
19608 |
d_approximations.clear(); |
80 |
19608 |
d_approx_list.clear(); |
81 |
19608 |
d_reps.clear(); |
82 |
19608 |
d_assignExcSet.clear(); |
83 |
19608 |
d_aesMaster.clear(); |
84 |
19608 |
d_aesSlaves.clear(); |
85 |
19608 |
d_rep_set.clear(); |
86 |
19608 |
d_uf_terms.clear(); |
87 |
19608 |
d_ho_uf_terms.clear(); |
88 |
19608 |
d_uf_models.clear(); |
89 |
19608 |
d_using_model_core = false; |
90 |
19608 |
d_model_core.clear(); |
91 |
19608 |
} |
92 |
|
|
93 |
25 |
void TheoryModel::getComments(std::ostream& out) const { |
94 |
25 |
Trace("model-builder") << "get comments..." << std::endl; |
95 |
25 |
out << d_comment_str.str(); |
96 |
25 |
} |
97 |
|
|
98 |
7 |
void TheoryModel::setHeapModel( Node h, Node neq ) { |
99 |
7 |
d_sep_heap = h; |
100 |
7 |
d_sep_nil_eq = neq; |
101 |
7 |
} |
102 |
|
|
103 |
31 |
bool TheoryModel::getHeapModel(Node& h, Node& neq) const |
104 |
|
{ |
105 |
31 |
if (d_sep_heap.isNull() || d_sep_nil_eq.isNull()) |
106 |
|
{ |
107 |
25 |
return false; |
108 |
|
} |
109 |
6 |
h = d_sep_heap; |
110 |
6 |
neq = d_sep_nil_eq; |
111 |
6 |
return true; |
112 |
|
} |
113 |
|
|
114 |
4374 |
bool TheoryModel::hasApproximations() const { return !d_approx_list.empty(); } |
115 |
|
|
116 |
|
std::vector<std::pair<Node, Node> > TheoryModel::getApproximations() const |
117 |
|
{ |
118 |
|
return d_approx_list; |
119 |
|
} |
120 |
|
|
121 |
4 |
std::vector<Node> TheoryModel::getDomainElements(TypeNode tn) const |
122 |
|
{ |
123 |
|
// must be an uninterpreted sort |
124 |
4 |
Assert(tn.isSort()); |
125 |
8 |
std::vector<Node> elements; |
126 |
4 |
const std::vector<Node>* type_refs = d_rep_set.getTypeRepsOrNull(tn); |
127 |
4 |
if (type_refs == nullptr || type_refs->empty()) |
128 |
|
{ |
129 |
|
// This is called when t is a sort that does not occur in this model. |
130 |
|
// Sorts are always interpreted as non-empty, thus we add a single element. |
131 |
4 |
elements.push_back(tn.mkGroundTerm()); |
132 |
4 |
return elements; |
133 |
|
} |
134 |
|
return *type_refs; |
135 |
|
} |
136 |
|
|
137 |
224107 |
Node TheoryModel::getValue(TNode n) const |
138 |
|
{ |
139 |
|
//apply substitutions |
140 |
224107 |
Node nn = d_env.getTopLevelSubstitutions().apply(n); |
141 |
224107 |
Debug("model-getvalue-debug") << "[model-getvalue] getValue : substitute " << n << " to " << nn << std::endl; |
142 |
|
//get value in model |
143 |
224107 |
nn = getModelValue(nn); |
144 |
224107 |
if (nn.isNull()) return nn; |
145 |
453229 |
if(options::condenseFunctionValues() || nn.getKind() != kind::LAMBDA) { |
146 |
|
//normalize |
147 |
224107 |
nn = Rewriter::rewrite(nn); |
148 |
|
} |
149 |
448214 |
Debug("model-getvalue") << "[model-getvalue] getValue( " << n << " ): " << std::endl |
150 |
224107 |
<< "[model-getvalue] returning " << nn << std::endl; |
151 |
224107 |
return nn; |
152 |
|
} |
153 |
|
|
154 |
30 |
bool TheoryModel::isModelCoreSymbol(Node s) const |
155 |
|
{ |
156 |
30 |
if (!d_using_model_core) |
157 |
|
{ |
158 |
30 |
return true; |
159 |
|
} |
160 |
|
Assert(s.isVar() && s.getKind() != BOUND_VARIABLE); |
161 |
|
return d_model_core.find(s) != d_model_core.end(); |
162 |
|
} |
163 |
|
|
164 |
163 |
Cardinality TheoryModel::getCardinality(TypeNode tn) const |
165 |
|
{ |
166 |
|
//for now, we only handle cardinalities for uninterpreted sorts |
167 |
163 |
if (!tn.isSort()) |
168 |
|
{ |
169 |
|
Debug("model-getvalue-debug") |
170 |
|
<< "Get cardinality other sort, unknown." << std::endl; |
171 |
|
return Cardinality( CardinalityUnknown() ); |
172 |
|
} |
173 |
163 |
if (d_rep_set.hasType(tn)) |
174 |
|
{ |
175 |
326 |
Debug("model-getvalue-debug") |
176 |
163 |
<< "Get cardinality sort, #rep : " |
177 |
163 |
<< d_rep_set.getNumRepresentatives(tn) << std::endl; |
178 |
163 |
return Cardinality(d_rep_set.getNumRepresentatives(tn)); |
179 |
|
} |
180 |
|
Debug("model-getvalue-debug") |
181 |
|
<< "Get cardinality sort, unconstrained, return 1." << std::endl; |
182 |
|
return Cardinality(1); |
183 |
|
} |
184 |
|
|
185 |
1306922 |
Node TheoryModel::getModelValue(TNode n) const |
186 |
|
{ |
187 |
1306922 |
std::unordered_map<Node, Node>::iterator it = d_modelCache.find(n); |
188 |
1306922 |
if (it != d_modelCache.end()) { |
189 |
747365 |
return (*it).second; |
190 |
|
} |
191 |
559557 |
Debug("model-getvalue-debug") << "Get model value " << n << " ... "; |
192 |
559557 |
Debug("model-getvalue-debug") << d_equalityEngine->hasTerm(n) << std::endl; |
193 |
559557 |
Kind nk = n.getKind(); |
194 |
559557 |
if (n.isConst() || nk == BOUND_VARIABLE) |
195 |
|
{ |
196 |
30562 |
d_modelCache[n] = n; |
197 |
30562 |
return n; |
198 |
|
} |
199 |
|
|
200 |
1057990 |
Node ret = n; |
201 |
528995 |
NodeManager* nm = NodeManager::currentNM(); |
202 |
|
|
203 |
|
// If it has children, evaluate them. We do this even if n is an unevaluatable |
204 |
|
// or semi-unevaluatable operator. Notice this includes quantified |
205 |
|
// formulas. It may not be possible in general to evaluate bodies of |
206 |
|
// quantified formulas, because they have free variables. Regardless, we |
207 |
|
// may often be able to evaluate the body of a quantified formula to true, |
208 |
|
// e.g. forall x. P(x) where P = lambda x. true. |
209 |
528995 |
if (n.getNumChildren() > 0) |
210 |
|
{ |
211 |
877206 |
Debug("model-getvalue-debug") |
212 |
438603 |
<< "Get model value children " << n << std::endl; |
213 |
439029 |
std::vector<Node> children; |
214 |
438603 |
if (n.getKind() == APPLY_UF) |
215 |
|
{ |
216 |
18926 |
Node op = getModelValue(n.getOperator()); |
217 |
9463 |
Debug("model-getvalue-debug") << " operator : " << op << std::endl; |
218 |
9463 |
children.push_back(op); |
219 |
|
} |
220 |
429140 |
else if (n.getMetaKind() == kind::metakind::PARAMETERIZED) |
221 |
|
{ |
222 |
6051 |
children.push_back(n.getOperator()); |
223 |
|
} |
224 |
|
// evaluate the children |
225 |
1512585 |
for (size_t i = 0, nchild = n.getNumChildren(); i < nchild; ++i) |
226 |
|
{ |
227 |
1073982 |
if (n.isClosure() && i==0) |
228 |
|
{ |
229 |
|
// do not evaluate bound variable lists |
230 |
630 |
ret = n[0]; |
231 |
|
} |
232 |
|
else |
233 |
|
{ |
234 |
1073352 |
ret = getModelValue(n[i]); |
235 |
|
} |
236 |
2147964 |
Debug("model-getvalue-debug") |
237 |
1073982 |
<< " " << n << "[" << i << "] is " << ret << std::endl; |
238 |
1073982 |
children.push_back(ret); |
239 |
|
} |
240 |
438603 |
ret = nm->mkNode(n.getKind(), children); |
241 |
438603 |
Debug("model-getvalue-debug") << "ret (pre-rewrite): " << ret << std::endl; |
242 |
438603 |
ret = Rewriter::rewrite(ret); |
243 |
438603 |
Debug("model-getvalue-debug") << "ret (post-rewrite): " << ret << std::endl; |
244 |
|
// special cases |
245 |
438603 |
if (ret.getKind() == kind::CARDINALITY_CONSTRAINT) |
246 |
|
{ |
247 |
326 |
Debug("model-getvalue-debug") |
248 |
163 |
<< "get cardinality constraint " << ret[0].getType() << std::endl; |
249 |
489 |
ret = nm->mkConst(getCardinality(ret[0].getType()).getFiniteCardinality() |
250 |
652 |
<= ret[1].getConst<Rational>().getNumerator()); |
251 |
|
} |
252 |
438440 |
else if (ret.getKind() == kind::CARDINALITY_VALUE) |
253 |
|
{ |
254 |
|
Debug("model-getvalue-debug") |
255 |
|
<< "get cardinality value " << ret[0].getType() << std::endl; |
256 |
|
ret = nm->mkConst( |
257 |
|
Rational(getCardinality(ret[0].getType()).getFiniteCardinality())); |
258 |
|
} |
259 |
|
// if the value was constant, we return it. If it was non-constant, |
260 |
|
// we only return it if we an evaluated kind. This can occur if the |
261 |
|
// children of n failed to evaluate. |
262 |
1319778 |
if (ret.isConst() || ( |
263 |
442998 |
d_unevaluated_kinds.find(nk) == d_unevaluated_kinds.end() |
264 |
442572 |
&& d_semi_evaluated_kinds.find(nk) == d_semi_evaluated_kinds.end())) |
265 |
|
{ |
266 |
438177 |
d_modelCache[n] = ret; |
267 |
438177 |
return ret; |
268 |
|
} |
269 |
|
} |
270 |
|
// it might be approximate |
271 |
90818 |
std::map<Node, Node>::const_iterator ita = d_approximations.find(n); |
272 |
90818 |
if (ita != d_approximations.end()) |
273 |
|
{ |
274 |
|
// If the value of n is approximate based on predicate P(n), we return |
275 |
|
// witness z. P(z). |
276 |
4 |
Node v = nm->mkBoundVar(n.getType()); |
277 |
4 |
Node bvl = nm->mkNode(BOUND_VAR_LIST, v); |
278 |
4 |
Node answer = nm->mkNode(WITNESS, bvl, ita->second.substitute(n, v)); |
279 |
2 |
d_modelCache[n] = answer; |
280 |
2 |
return answer; |
281 |
|
} |
282 |
|
// must rewrite the term at this point |
283 |
90816 |
ret = Rewriter::rewrite(n); |
284 |
|
// return the representative of the term in the equality engine, if it exists |
285 |
181632 |
TypeNode t = ret.getType(); |
286 |
|
bool eeHasTerm; |
287 |
90816 |
if (!options::ufHo() && (t.isFunction() || t.isPredicate())) |
288 |
|
{ |
289 |
|
// functions are in the equality engine, but *not* as first-class members |
290 |
|
// when higher-order is disabled. In this case, we cannot query |
291 |
|
// representatives for functions since they are "internal" nodes according |
292 |
|
// to the equality engine despite hasTerm returning true. However, they are |
293 |
|
// first class members when higher-order is enabled. Hence, the special |
294 |
|
// case here. |
295 |
1047 |
eeHasTerm = false; |
296 |
|
} |
297 |
|
else |
298 |
|
{ |
299 |
89769 |
eeHasTerm = d_equalityEngine->hasTerm(ret); |
300 |
|
} |
301 |
90816 |
if (eeHasTerm) |
302 |
|
{ |
303 |
170818 |
Debug("model-getvalue-debug") |
304 |
85409 |
<< "get value from representative " << ret << "..." << std::endl; |
305 |
85409 |
ret = d_equalityEngine->getRepresentative(ret); |
306 |
85409 |
Assert(d_reps.find(ret) != d_reps.end()); |
307 |
85409 |
std::map<Node, Node>::const_iterator it2 = d_reps.find(ret); |
308 |
85409 |
if (it2 != d_reps.end()) |
309 |
|
{ |
310 |
85409 |
ret = it2->second; |
311 |
85409 |
d_modelCache[n] = ret; |
312 |
85409 |
return ret; |
313 |
|
} |
314 |
|
} |
315 |
|
|
316 |
|
// if we are a evaluated or semi-evaluated kind, return an arbitrary value |
317 |
|
// if we are not in the d_unevaluated_kinds map, we are evaluated |
318 |
5407 |
if (d_unevaluated_kinds.find(nk) == d_unevaluated_kinds.end()) |
319 |
|
{ |
320 |
5342 |
if (t.isFunction() || t.isPredicate()) |
321 |
|
{ |
322 |
1047 |
if (d_enableFuncModels) |
323 |
|
{ |
324 |
1047 |
std::map<Node, Node>::const_iterator entry = d_uf_models.find(n); |
325 |
1047 |
if (entry != d_uf_models.end()) |
326 |
|
{ |
327 |
|
// Existing function |
328 |
988 |
ret = entry->second; |
329 |
988 |
d_modelCache[n] = ret; |
330 |
988 |
return ret; |
331 |
|
} |
332 |
|
// Unknown function symbol: return LAMBDA x. c, where c is the first |
333 |
|
// constant in the enumeration of the range type |
334 |
118 |
vector<TypeNode> argTypes = t.getArgTypes(); |
335 |
118 |
vector<Node> args; |
336 |
165 |
for (unsigned i = 0, size = argTypes.size(); i < size; ++i) |
337 |
|
{ |
338 |
106 |
args.push_back(nm->mkBoundVar(argTypes[i])); |
339 |
|
} |
340 |
118 |
Node boundVarList = nm->mkNode(kind::BOUND_VAR_LIST, args); |
341 |
118 |
TypeEnumerator te(t.getRangeType()); |
342 |
59 |
ret = nm->mkNode(kind::LAMBDA, boundVarList, *te); |
343 |
|
} |
344 |
|
else |
345 |
|
{ |
346 |
|
// if func models not enabled, throw an error |
347 |
|
Unreachable(); |
348 |
|
} |
349 |
|
} |
350 |
4295 |
else if (!t.isFirstClass()) |
351 |
|
{ |
352 |
|
// this is the class for regular expressions |
353 |
|
// we simply invoke the rewriter on them |
354 |
17 |
ret = Rewriter::rewrite(ret); |
355 |
|
} |
356 |
|
else |
357 |
|
{ |
358 |
|
// Unknown term - return first enumerated value for this type |
359 |
8556 |
TypeEnumerator te(n.getType()); |
360 |
4278 |
ret = *te; |
361 |
|
} |
362 |
4354 |
d_modelCache[n] = ret; |
363 |
4354 |
return ret; |
364 |
|
} |
365 |
|
|
366 |
65 |
d_modelCache[n] = n; |
367 |
65 |
return n; |
368 |
|
} |
369 |
|
|
370 |
|
/** add term */ |
371 |
1217875 |
void TheoryModel::addTermInternal(TNode n) |
372 |
|
{ |
373 |
1217875 |
Assert(d_equalityEngine->hasTerm(n)); |
374 |
1217875 |
Trace("model-builder-debug2") << "TheoryModel::addTerm : " << n << std::endl; |
375 |
|
//must collect UF terms |
376 |
1217875 |
if (n.getKind()==APPLY_UF) { |
377 |
131994 |
Node op = n.getOperator(); |
378 |
65997 |
if( std::find( d_uf_terms[ op ].begin(), d_uf_terms[ op ].end(), n )==d_uf_terms[ op ].end() ){ |
379 |
65997 |
d_uf_terms[ op ].push_back( n ); |
380 |
65997 |
Trace("model-builder-fun") << "Add apply term " << n << std::endl; |
381 |
|
} |
382 |
1151878 |
}else if( n.getKind()==HO_APPLY ){ |
383 |
4854 |
Node op = n[0]; |
384 |
2427 |
if( std::find( d_ho_uf_terms[ op ].begin(), d_ho_uf_terms[ op ].end(), n )==d_ho_uf_terms[ op ].end() ){ |
385 |
2427 |
d_ho_uf_terms[ op ].push_back( n ); |
386 |
2427 |
Trace("model-builder-fun") << "Add ho apply term " << n << std::endl; |
387 |
|
} |
388 |
|
} |
389 |
|
// all functions must be included, marked as higher-order |
390 |
1217875 |
if( n.getType().isFunction() ){ |
391 |
1483 |
Trace("model-builder-fun") << "Add function variable (without term) " << n << std::endl; |
392 |
1483 |
if( d_uf_terms.find( n )==d_uf_terms.end() ){ |
393 |
1354 |
d_uf_terms[n].clear(); |
394 |
|
} |
395 |
1483 |
if( d_ho_uf_terms.find( n )==d_ho_uf_terms.end() ){ |
396 |
1237 |
d_ho_uf_terms[n].clear(); |
397 |
|
} |
398 |
|
} |
399 |
1217875 |
} |
400 |
|
|
401 |
|
/** assert equality */ |
402 |
610093 |
bool TheoryModel::assertEquality(TNode a, TNode b, bool polarity) |
403 |
|
{ |
404 |
610093 |
Assert(d_equalityEngine->consistent()); |
405 |
610093 |
if (a == b && polarity) { |
406 |
2234 |
return true; |
407 |
|
} |
408 |
607859 |
Trace("model-builder-assertions") << "(assert " << (polarity ? "(= " : "(not (= ") << a << " " << b << (polarity ? "));" : ")));") << endl; |
409 |
607859 |
d_equalityEngine->assertEquality( a.eqNode(b), polarity, Node::null() ); |
410 |
607859 |
return d_equalityEngine->consistent(); |
411 |
|
} |
412 |
|
|
413 |
|
/** assert predicate */ |
414 |
271939 |
bool TheoryModel::assertPredicate(TNode a, bool polarity) |
415 |
|
{ |
416 |
271939 |
Assert(d_equalityEngine->consistent()); |
417 |
607772 |
if ((a == d_true && polarity) || |
418 |
271939 |
(a == d_false && (!polarity))) { |
419 |
127788 |
return true; |
420 |
|
} |
421 |
144151 |
if (a.getKind() == EQUAL) { |
422 |
|
Trace("model-builder-assertions") << "(assert " << (polarity ? " " : "(not ") << a << (polarity ? ");" : "));") << endl; |
423 |
|
d_equalityEngine->assertEquality( a, polarity, Node::null() ); |
424 |
|
} else { |
425 |
144151 |
Trace("model-builder-assertions") << "(assert " << (polarity ? "" : "(not ") << a << (polarity ? ");" : "));") << endl; |
426 |
144151 |
d_equalityEngine->assertPredicate( a, polarity, Node::null() ); |
427 |
|
} |
428 |
144151 |
return d_equalityEngine->consistent(); |
429 |
|
} |
430 |
|
|
431 |
|
/** assert equality engine */ |
432 |
63894 |
bool TheoryModel::assertEqualityEngine(const eq::EqualityEngine* ee, |
433 |
|
const std::set<Node>* termSet) |
434 |
|
{ |
435 |
63894 |
Assert(d_equalityEngine->consistent()); |
436 |
63894 |
eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( ee ); |
437 |
3277118 |
for (; !eqcs_i.isFinished(); ++eqcs_i) { |
438 |
3213224 |
Node eqc = (*eqcs_i); |
439 |
1606612 |
bool predicate = false; |
440 |
1606612 |
bool predTrue = false; |
441 |
1606612 |
bool predFalse = false; |
442 |
1606612 |
Trace("model-builder-debug") << "...asserting terms in equivalence class " << eqc; |
443 |
1606612 |
if (eqc.getType().isBoolean()) { |
444 |
284156 |
predicate = true; |
445 |
284156 |
predTrue = ee->areEqual(eqc, d_true); |
446 |
284156 |
predFalse = ee->areEqual(eqc, d_false); |
447 |
284156 |
Trace("model-builder-debug") << ", pred = " << predTrue << "/" << predFalse; |
448 |
|
} |
449 |
1606612 |
Trace("model-builder-debug") << std::endl; |
450 |
1606612 |
eq::EqClassIterator eqc_i = eq::EqClassIterator(eqc, ee); |
451 |
1606612 |
bool first = true; |
452 |
3213224 |
Node rep; |
453 |
10016752 |
for (; !eqc_i.isFinished(); ++eqc_i) { |
454 |
5643144 |
Node n = *eqc_i; |
455 |
|
// notice that constants are always relevant |
456 |
11177136 |
if (termSet != nullptr && termSet->find(n) == termSet->end() |
457 |
7346217 |
&& !n.isConst()) |
458 |
|
{ |
459 |
5533992 |
Trace("model-builder-debug") |
460 |
2766996 |
<< "...skip node " << n << " in eqc " << eqc << std::endl; |
461 |
2766996 |
continue; |
462 |
|
} |
463 |
1438074 |
if (predicate) { |
464 |
189308 |
if (predTrue || predFalse) |
465 |
|
{ |
466 |
365580 |
if (!assertPredicate(n, predTrue)) |
467 |
|
{ |
468 |
|
return false; |
469 |
|
} |
470 |
|
} |
471 |
|
else { |
472 |
6518 |
if (first) { |
473 |
6518 |
rep = n; |
474 |
6518 |
first = false; |
475 |
|
} |
476 |
|
else { |
477 |
|
Node eq = (n).eqNode(rep); |
478 |
|
Trace("model-builder-assertions") |
479 |
|
<< "(assert " << eq << ");" << std::endl; |
480 |
|
d_equalityEngine->assertEquality(eq, true, Node::null()); |
481 |
|
if (!d_equalityEngine->consistent()) |
482 |
|
{ |
483 |
|
return false; |
484 |
|
} |
485 |
|
} |
486 |
|
} |
487 |
|
} else { |
488 |
1248766 |
if (first) { |
489 |
894066 |
rep = n; |
490 |
|
//add the term (this is specifically for the case of singleton equivalence classes) |
491 |
894066 |
if (rep.getType().isFirstClass()) |
492 |
|
{ |
493 |
893292 |
d_equalityEngine->addTerm( rep ); |
494 |
893292 |
Trace("model-builder-debug") << "Add term to ee within assertEqualityEngine: " << rep << std::endl; |
495 |
|
} |
496 |
894066 |
first = false; |
497 |
|
} |
498 |
|
else { |
499 |
354700 |
if (!assertEquality(n, rep, true)) |
500 |
|
{ |
501 |
|
return false; |
502 |
|
} |
503 |
|
} |
504 |
|
} |
505 |
|
} |
506 |
|
} |
507 |
63894 |
return true; |
508 |
|
} |
509 |
|
|
510 |
50683 |
void TheoryModel::assertSkeleton(TNode n) |
511 |
|
{ |
512 |
50683 |
Trace("model-builder-reps") << "Assert skeleton : " << n << std::endl; |
513 |
101366 |
Trace("model-builder-reps") << "...rep eqc is : " << getRepresentative(n) |
514 |
50683 |
<< std::endl; |
515 |
50683 |
d_reps[ n ] = n; |
516 |
50683 |
} |
517 |
|
|
518 |
8 |
void TheoryModel::setAssignmentExclusionSet(TNode n, |
519 |
|
const std::vector<Node>& eset) |
520 |
|
{ |
521 |
|
// should not be assigned yet |
522 |
8 |
Assert(d_assignExcSet.find(n) == d_assignExcSet.end()); |
523 |
16 |
Trace("model-builder-debug") |
524 |
8 |
<< "Exclude values of " << n << " : " << eset << std::endl; |
525 |
8 |
std::vector<Node>& aes = d_assignExcSet[n]; |
526 |
8 |
aes.insert(aes.end(), eset.begin(), eset.end()); |
527 |
8 |
} |
528 |
|
|
529 |
8 |
void TheoryModel::setAssignmentExclusionSetGroup( |
530 |
|
const std::vector<TNode>& group, const std::vector<Node>& eset) |
531 |
|
{ |
532 |
8 |
if (group.empty()) |
533 |
|
{ |
534 |
|
return; |
535 |
|
} |
536 |
|
// for efficiency, we store a single copy of eset and set a slave/master |
537 |
|
// relationship |
538 |
8 |
setAssignmentExclusionSet(group[0], eset); |
539 |
8 |
std::vector<Node>& gslaves = d_aesSlaves[group[0]]; |
540 |
43 |
for (unsigned i = 1, gsize = group.size(); i < gsize; ++i) |
541 |
|
{ |
542 |
70 |
Node gs = group[i]; |
543 |
|
// set master |
544 |
35 |
d_aesMaster[gs] = group[0]; |
545 |
|
// add to slaves |
546 |
35 |
gslaves.push_back(gs); |
547 |
|
} |
548 |
|
} |
549 |
|
|
550 |
530406 |
bool TheoryModel::getAssignmentExclusionSet(TNode n, |
551 |
|
std::vector<Node>& group, |
552 |
|
std::vector<Node>& eset) |
553 |
|
{ |
554 |
|
// does it have a master? |
555 |
530406 |
std::map<Node, Node>::iterator itm = d_aesMaster.find(n); |
556 |
530406 |
if (itm != d_aesMaster.end()) |
557 |
|
{ |
558 |
2 |
return getAssignmentExclusionSet(itm->second, group, eset); |
559 |
|
} |
560 |
530404 |
std::map<Node, std::vector<Node> >::iterator ita = d_assignExcSet.find(n); |
561 |
530404 |
if (ita == d_assignExcSet.end()) |
562 |
|
{ |
563 |
530396 |
return false; |
564 |
|
} |
565 |
8 |
eset.insert(eset.end(), ita->second.begin(), ita->second.end()); |
566 |
8 |
group.push_back(n); |
567 |
|
// does it have slaves? |
568 |
8 |
ita = d_aesSlaves.find(n); |
569 |
8 |
if (ita != d_aesSlaves.end()) |
570 |
|
{ |
571 |
8 |
group.insert(group.end(), ita->second.begin(), ita->second.end()); |
572 |
|
} |
573 |
8 |
return true; |
574 |
|
} |
575 |
|
|
576 |
15238 |
bool TheoryModel::hasAssignmentExclusionSets() const |
577 |
|
{ |
578 |
15238 |
return !d_assignExcSet.empty(); |
579 |
|
} |
580 |
|
|
581 |
35 |
void TheoryModel::recordApproximation(TNode n, TNode pred) |
582 |
|
{ |
583 |
70 |
Trace("model-builder-debug") |
584 |
35 |
<< "Record approximation : " << n << " satisfies the predicate " << pred |
585 |
35 |
<< std::endl; |
586 |
35 |
Assert(d_approximations.find(n) == d_approximations.end()); |
587 |
35 |
Assert(pred.getType().isBoolean()); |
588 |
35 |
d_approximations[n] = pred; |
589 |
35 |
d_approx_list.push_back(std::pair<Node, Node>(n, pred)); |
590 |
|
// model cache is invalid |
591 |
35 |
d_modelCache.clear(); |
592 |
35 |
} |
593 |
|
void TheoryModel::recordApproximation(TNode n, TNode pred, Node witness) |
594 |
|
{ |
595 |
|
Node predDisj = NodeManager::currentNM()->mkNode(OR, n.eqNode(witness), pred); |
596 |
|
recordApproximation(n, predDisj); |
597 |
|
} |
598 |
|
void TheoryModel::setUsingModelCore() |
599 |
|
{ |
600 |
|
d_using_model_core = true; |
601 |
|
d_model_core.clear(); |
602 |
|
} |
603 |
|
|
604 |
|
void TheoryModel::recordModelCoreSymbol(Node sym) { d_model_core.insert(sym); } |
605 |
|
|
606 |
101091 |
void TheoryModel::setUnevaluatedKind(Kind k) { d_unevaluated_kinds.insert(k); } |
607 |
|
|
608 |
18918 |
void TheoryModel::setSemiEvaluatedKind(Kind k) |
609 |
|
{ |
610 |
18918 |
d_semi_evaluated_kinds.insert(k); |
611 |
18918 |
} |
612 |
|
|
613 |
28377 |
void TheoryModel::setIrrelevantKind(Kind k) { d_irrKinds.insert(k); } |
614 |
|
|
615 |
2683203 |
const std::set<Kind>& TheoryModel::getIrrelevantKinds() const |
616 |
|
{ |
617 |
2683203 |
return d_irrKinds; |
618 |
|
} |
619 |
|
|
620 |
2950 |
bool TheoryModel::isLegalElimination(TNode x, TNode val) |
621 |
|
{ |
622 |
2950 |
return !expr::hasSubtermKinds(d_unevaluated_kinds, val); |
623 |
|
} |
624 |
|
|
625 |
20470 |
bool TheoryModel::hasTerm(TNode a) |
626 |
|
{ |
627 |
20470 |
return d_equalityEngine->hasTerm( a ); |
628 |
|
} |
629 |
|
|
630 |
563182 |
Node TheoryModel::getRepresentative(TNode a) |
631 |
|
{ |
632 |
563182 |
if( d_equalityEngine->hasTerm( a ) ){ |
633 |
1048420 |
Node r = d_equalityEngine->getRepresentative( a ); |
634 |
524210 |
if( d_reps.find( r )!=d_reps.end() ){ |
635 |
473175 |
return d_reps[ r ]; |
636 |
|
}else{ |
637 |
51035 |
return r; |
638 |
|
} |
639 |
|
}else{ |
640 |
38972 |
return a; |
641 |
|
} |
642 |
|
} |
643 |
|
|
644 |
2427 |
bool TheoryModel::areEqual(TNode a, TNode b) |
645 |
|
{ |
646 |
2427 |
if( a==b ){ |
647 |
2217 |
return true; |
648 |
210 |
}else if( d_equalityEngine->hasTerm( a ) && d_equalityEngine->hasTerm( b ) ){ |
649 |
210 |
return d_equalityEngine->areEqual( a, b ); |
650 |
|
}else{ |
651 |
|
return false; |
652 |
|
} |
653 |
|
} |
654 |
|
|
655 |
|
bool TheoryModel::areDisequal(TNode a, TNode b) |
656 |
|
{ |
657 |
|
if( d_equalityEngine->hasTerm( a ) && d_equalityEngine->hasTerm( b ) ){ |
658 |
|
return d_equalityEngine->areDisequal( a, b, false ); |
659 |
|
}else{ |
660 |
|
return false; |
661 |
|
} |
662 |
|
} |
663 |
|
|
664 |
7645 |
bool TheoryModel::hasUfTerms(Node f) const |
665 |
|
{ |
666 |
7645 |
return d_uf_terms.find(f) != d_uf_terms.end(); |
667 |
|
} |
668 |
|
|
669 |
6184 |
const std::vector<Node>& TheoryModel::getUfTerms(Node f) const |
670 |
|
{ |
671 |
6184 |
const auto it = d_uf_terms.find(f); |
672 |
6184 |
Assert(it != d_uf_terms.end()); |
673 |
6184 |
return it->second; |
674 |
|
} |
675 |
|
|
676 |
17172 |
bool TheoryModel::areFunctionValuesEnabled() const |
677 |
|
{ |
678 |
17172 |
return d_enableFuncModels; |
679 |
|
} |
680 |
|
|
681 |
13777 |
void TheoryModel::assignFunctionDefinition( Node f, Node f_def ) { |
682 |
13777 |
Trace("model-builder") << " Assigning function (" << f << ") to (" << f_def << ")" << endl; |
683 |
13777 |
Assert(d_uf_models.find(f) == d_uf_models.end()); |
684 |
|
|
685 |
13777 |
if( options::ufHo() ){ |
686 |
|
//we must rewrite the function value since the definition needs to be a constant value |
687 |
1117 |
f_def = Rewriter::rewrite( f_def ); |
688 |
2234 |
Trace("model-builder-debug") |
689 |
1117 |
<< "Model value (post-rewrite) : " << f_def << std::endl; |
690 |
1117 |
Assert(f_def.isConst()) << "Non-constant f_def: " << f_def; |
691 |
|
} |
692 |
|
|
693 |
|
// d_uf_models only stores models for variables |
694 |
13777 |
if( f.isVar() ){ |
695 |
13641 |
d_uf_models[f] = f_def; |
696 |
|
} |
697 |
|
|
698 |
13777 |
if (options::ufHo() && d_equalityEngine->hasTerm(f)) |
699 |
|
{ |
700 |
1117 |
Trace("model-builder-debug") << " ...function is first-class member of equality engine" << std::endl; |
701 |
|
// assign to representative if higher-order |
702 |
2234 |
Node r = d_equalityEngine->getRepresentative( f ); |
703 |
|
//always replace the representative, since it is initially assigned to itself |
704 |
1117 |
Trace("model-builder") << " Assign: Setting function rep " << r << " to " << f_def << endl; |
705 |
1117 |
d_reps[r] = f_def; |
706 |
|
// also assign to other assignable functions in the same equivalence class |
707 |
1117 |
eq::EqClassIterator eqc_i = eq::EqClassIterator(r,d_equalityEngine); |
708 |
4083 |
while( !eqc_i.isFinished() ) { |
709 |
2966 |
Node n = *eqc_i; |
710 |
|
// if an unassigned variable function |
711 |
1483 |
if( n.isVar() && d_uf_terms.find( n )!=d_uf_terms.end() && !hasAssignedFunctionDefinition( n ) ){ |
712 |
286 |
d_uf_models[n] = f_def; |
713 |
286 |
Trace("model-builder") << " Assigning function (" << n << ") to function definition of " << f << std::endl; |
714 |
|
} |
715 |
1483 |
++eqc_i; |
716 |
|
} |
717 |
1117 |
Trace("model-builder-debug") << " ...finished." << std::endl; |
718 |
|
} |
719 |
13777 |
} |
720 |
|
|
721 |
15238 |
std::vector< Node > TheoryModel::getFunctionsToAssign() { |
722 |
15238 |
std::vector< Node > funcs_to_assign; |
723 |
30476 |
std::map< Node, Node > func_to_rep; |
724 |
|
|
725 |
|
// collect functions |
726 |
27920 |
for( std::map< Node, std::vector< Node > >::iterator it = d_uf_terms.begin(); it != d_uf_terms.end(); ++it ){ |
727 |
25364 |
Node n = it->first; |
728 |
12682 |
Assert(!n.isNull()); |
729 |
|
// should not have been solved for in a substitution |
730 |
12682 |
Assert(d_env.getTopLevelSubstitutions().apply(n) == n); |
731 |
12682 |
if( !hasAssignedFunctionDefinition( n ) ){ |
732 |
6498 |
Trace("model-builder-fun-debug") << "Look at function : " << n << std::endl; |
733 |
6498 |
if( options::ufHo() ){ |
734 |
|
// if in higher-order mode, assign function definitions modulo equality |
735 |
2966 |
Node r = getRepresentative( n ); |
736 |
1483 |
std::map< Node, Node >::iterator itf = func_to_rep.find( r ); |
737 |
1483 |
if( itf==func_to_rep.end() ){ |
738 |
1117 |
func_to_rep[r] = n; |
739 |
1117 |
funcs_to_assign.push_back( n ); |
740 |
1117 |
Trace("model-builder-fun") << "Make function " << n; |
741 |
1117 |
Trace("model-builder-fun") << " the assignable function in its equivalence class." << std::endl; |
742 |
|
}else{ |
743 |
|
// must combine uf terms |
744 |
366 |
Trace("model-builder-fun") << "Copy " << it->second.size() << " uf terms"; |
745 |
366 |
d_uf_terms[itf->second].insert( d_uf_terms[itf->second].end(), it->second.begin(), it->second.end() ); |
746 |
366 |
std::map< Node, std::vector< Node > >::iterator ith = d_ho_uf_terms.find( n ); |
747 |
366 |
if( ith!=d_ho_uf_terms.end() ){ |
748 |
366 |
d_ho_uf_terms[itf->second].insert( d_ho_uf_terms[itf->second].end(), ith->second.begin(), ith->second.end() ); |
749 |
366 |
Trace("model-builder-fun") << " and " << ith->second.size() << " ho uf terms"; |
750 |
|
} |
751 |
366 |
Trace("model-builder-fun") << " from " << n << " to its assignable representative function " << itf->second << std::endl; |
752 |
366 |
it->second.clear(); |
753 |
|
} |
754 |
|
}else{ |
755 |
5015 |
Trace("model-builder-fun") << "Function to assign : " << n << std::endl; |
756 |
5015 |
funcs_to_assign.push_back( n ); |
757 |
|
} |
758 |
|
} |
759 |
|
} |
760 |
|
|
761 |
15238 |
Trace("model-builder-fun") << "return " << funcs_to_assign.size() << " functions to assign..." << std::endl; |
762 |
30476 |
return funcs_to_assign; |
763 |
|
} |
764 |
|
|
765 |
9459 |
const std::string& TheoryModel::getName() const { return d_name; } |
766 |
|
|
767 |
|
std::string TheoryModel::debugPrintModelEqc() const |
768 |
|
{ |
769 |
|
std::stringstream ss; |
770 |
|
ss << "--- Equivalence classes:" << std::endl; |
771 |
|
ss << d_equalityEngine->debugPrintEqc() << std::endl; |
772 |
|
ss << "--- Representative map: " << std::endl; |
773 |
|
for (const std::pair<const Node, Node>& r : d_reps) |
774 |
|
{ |
775 |
|
ss << r.first << " -> " << r.second << std::endl; |
776 |
|
} |
777 |
|
ss << "---" << std::endl; |
778 |
|
return ss.str(); |
779 |
|
} |
780 |
|
|
781 |
|
} // namespace theory |
782 |
257313 |
} // namespace cvc5 |