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