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