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