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/cardinality_constraint.h" |
18 |
|
#include "expr/node_algorithm.h" |
19 |
|
#include "options/quantifiers_options.h" |
20 |
|
#include "options/smt_options.h" |
21 |
|
#include "options/theory_options.h" |
22 |
|
#include "options/uf_options.h" |
23 |
|
#include "smt/env.h" |
24 |
|
#include "smt/solver_engine.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 |
15272 |
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 |
15272 |
d_enableFuncModels(enableFuncModels) |
41 |
|
{ |
42 |
|
// must use function models when ufHo is enabled |
43 |
15272 |
Assert(d_enableFuncModels || !logicInfo().isHigherOrder()); |
44 |
15272 |
d_true = NodeManager::currentNM()->mkConst( true ); |
45 |
15272 |
d_false = NodeManager::currentNM()->mkConst( false ); |
46 |
15272 |
} |
47 |
|
|
48 |
30534 |
TheoryModel::~TheoryModel() {} |
49 |
|
|
50 |
15272 |
void TheoryModel::finishInit(eq::EqualityEngine* ee) |
51 |
|
{ |
52 |
15272 |
Assert(ee != nullptr); |
53 |
15272 |
d_equalityEngine = ee; |
54 |
|
// The kinds we are treating as function application in congruence |
55 |
30544 |
d_equalityEngine->addFunctionKind( |
56 |
15272 |
kind::APPLY_UF, false, logicInfo().isHigherOrder()); |
57 |
15272 |
d_equalityEngine->addFunctionKind(kind::HO_APPLY); |
58 |
15272 |
d_equalityEngine->addFunctionKind(kind::SELECT); |
59 |
|
// d_equalityEngine->addFunctionKind(kind::STORE); |
60 |
15272 |
d_equalityEngine->addFunctionKind(kind::APPLY_CONSTRUCTOR); |
61 |
15272 |
d_equalityEngine->addFunctionKind(kind::APPLY_SELECTOR_TOTAL); |
62 |
15272 |
d_equalityEngine->addFunctionKind(kind::APPLY_TESTER); |
63 |
|
// do not interpret APPLY_UF if we are not assigning function values |
64 |
15272 |
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 |
15272 |
setIrrelevantKind(EQUAL); |
73 |
15272 |
setIrrelevantKind(NOT); |
74 |
15272 |
} |
75 |
|
|
76 |
29800 |
void TheoryModel::reset(){ |
77 |
29800 |
d_modelCache.clear(); |
78 |
29800 |
d_sep_heap = Node::null(); |
79 |
29800 |
d_sep_nil_eq = Node::null(); |
80 |
29800 |
d_approximations.clear(); |
81 |
29800 |
d_approx_list.clear(); |
82 |
29800 |
d_reps.clear(); |
83 |
29800 |
d_assignExcSet.clear(); |
84 |
29800 |
d_aesMaster.clear(); |
85 |
29800 |
d_aesSlaves.clear(); |
86 |
29800 |
d_rep_set.clear(); |
87 |
29800 |
d_uf_terms.clear(); |
88 |
29800 |
d_ho_uf_terms.clear(); |
89 |
29800 |
d_uf_models.clear(); |
90 |
29800 |
d_using_model_core = false; |
91 |
29800 |
d_model_core.clear(); |
92 |
29800 |
} |
93 |
|
|
94 |
6 |
void TheoryModel::setHeapModel( Node h, Node neq ) { |
95 |
6 |
d_sep_heap = h; |
96 |
6 |
d_sep_nil_eq = neq; |
97 |
6 |
} |
98 |
|
|
99 |
3871 |
bool TheoryModel::getHeapModel(Node& h, Node& neq) const |
100 |
|
{ |
101 |
3871 |
if (d_sep_heap.isNull() || d_sep_nil_eq.isNull()) |
102 |
|
{ |
103 |
3865 |
return false; |
104 |
|
} |
105 |
6 |
h = d_sep_heap; |
106 |
6 |
neq = d_sep_nil_eq; |
107 |
6 |
return true; |
108 |
|
} |
109 |
|
|
110 |
12874 |
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 |
18 |
std::vector<Node> TheoryModel::getDomainElements(TypeNode tn) const |
118 |
|
{ |
119 |
|
// must be an uninterpreted sort |
120 |
18 |
Assert(tn.isSort()); |
121 |
36 |
std::vector<Node> elements; |
122 |
18 |
const std::vector<Node>* type_refs = d_rep_set.getTypeRepsOrNull(tn); |
123 |
18 |
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 |
14 |
return *type_refs; |
131 |
|
} |
132 |
|
|
133 |
311577 |
Node TheoryModel::getValue(TNode n) const |
134 |
|
{ |
135 |
|
//apply substitutions |
136 |
311577 |
Node nn = d_env.getTopLevelSubstitutions().apply(n); |
137 |
311577 |
Debug("model-getvalue-debug") << "[model-getvalue] getValue : substitute " << n << " to " << nn << std::endl; |
138 |
|
//get value in model |
139 |
311577 |
nn = getModelValue(nn); |
140 |
311577 |
if (nn.isNull()) |
141 |
|
{ |
142 |
|
return nn; |
143 |
|
} |
144 |
311577 |
else if (nn.getKind() == kind::LAMBDA) |
145 |
|
{ |
146 |
59 |
if (options().theory.condenseFunctionValues) |
147 |
|
{ |
148 |
|
// normalize the body. Do not normalize the entire node, which |
149 |
|
// involves array normalization. |
150 |
59 |
NodeManager* nm = NodeManager::currentNM(); |
151 |
59 |
nn = nm->mkNode(kind::LAMBDA, nn[0], rewrite(nn[1])); |
152 |
|
} |
153 |
|
} |
154 |
|
else |
155 |
|
{ |
156 |
|
//normalize |
157 |
311518 |
nn = rewrite(nn); |
158 |
|
} |
159 |
623154 |
Debug("model-getvalue") << "[model-getvalue] getValue( " << n << " ): " << std::endl |
160 |
311577 |
<< "[model-getvalue] returning " << nn << std::endl; |
161 |
311577 |
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 |
|
Cardinality TheoryModel::getCardinality(TypeNode tn) const |
175 |
|
{ |
176 |
|
//for now, we only handle cardinalities for uninterpreted sorts |
177 |
|
if (!tn.isSort()) |
178 |
|
{ |
179 |
|
Debug("model-getvalue-debug") |
180 |
|
<< "Get cardinality other sort, unknown." << std::endl; |
181 |
|
return Cardinality( CardinalityUnknown() ); |
182 |
|
} |
183 |
|
if (d_rep_set.hasType(tn)) |
184 |
|
{ |
185 |
|
Debug("model-getvalue-debug") |
186 |
|
<< "Get cardinality sort, #rep : " |
187 |
|
<< d_rep_set.getNumRepresentatives(tn) << std::endl; |
188 |
|
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 |
1025727 |
Node TheoryModel::getModelValue(TNode n) const |
196 |
|
{ |
197 |
1025727 |
std::unordered_map<Node, Node>::iterator it = d_modelCache.find(n); |
198 |
1025727 |
if (it != d_modelCache.end()) { |
199 |
544876 |
return (*it).second; |
200 |
|
} |
201 |
480851 |
Debug("model-getvalue-debug") << "Get model value " << n << " ... "; |
202 |
480851 |
Debug("model-getvalue-debug") << d_equalityEngine->hasTerm(n) << std::endl; |
203 |
480851 |
Kind nk = n.getKind(); |
204 |
480851 |
if (n.isConst() || nk == BOUND_VARIABLE) |
205 |
|
{ |
206 |
34484 |
d_modelCache[n] = n; |
207 |
34484 |
return n; |
208 |
|
} |
209 |
|
|
210 |
892734 |
Node ret = n; |
211 |
446367 |
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 |
446367 |
if (n.getNumChildren() > 0) |
220 |
|
{ |
221 |
683586 |
Debug("model-getvalue-debug") |
222 |
341793 |
<< "Get model value children " << n << std::endl; |
223 |
342155 |
std::vector<Node> children; |
224 |
341793 |
if (n.getKind() == APPLY_UF) |
225 |
|
{ |
226 |
19586 |
Node op = getModelValue(n.getOperator()); |
227 |
9793 |
Debug("model-getvalue-debug") << " operator : " << op << std::endl; |
228 |
9793 |
children.push_back(op); |
229 |
|
} |
230 |
332000 |
else if (n.getMetaKind() == kind::metakind::PARAMETERIZED) |
231 |
|
{ |
232 |
6146 |
children.push_back(n.getOperator()); |
233 |
|
} |
234 |
|
// evaluate the children |
235 |
1046838 |
for (size_t i = 0, nchild = n.getNumChildren(); i < nchild; ++i) |
236 |
|
{ |
237 |
705045 |
if (n.isClosure() && i==0) |
238 |
|
{ |
239 |
|
// do not evaluate bound variable lists |
240 |
688 |
ret = n[0]; |
241 |
|
} |
242 |
|
else |
243 |
|
{ |
244 |
704357 |
ret = getModelValue(n[i]); |
245 |
|
} |
246 |
1410090 |
Debug("model-getvalue-debug") |
247 |
705045 |
<< " " << n << "[" << i << "] is " << ret << std::endl; |
248 |
705045 |
children.push_back(ret); |
249 |
|
} |
250 |
341793 |
ret = nm->mkNode(n.getKind(), children); |
251 |
341793 |
Debug("model-getvalue-debug") << "ret (pre-rewrite): " << ret << std::endl; |
252 |
341793 |
ret = rewrite(ret); |
253 |
341793 |
Debug("model-getvalue-debug") << "ret (post-rewrite): " << ret << std::endl; |
254 |
|
// special cases |
255 |
341793 |
if (ret.getKind() == kind::CARDINALITY_CONSTRAINT) |
256 |
|
{ |
257 |
|
const CardinalityConstraint& cc = |
258 |
|
ret.getOperator().getConst<CardinalityConstraint>(); |
259 |
|
Debug("model-getvalue-debug") |
260 |
|
<< "get cardinality constraint " << cc.getType() << std::endl; |
261 |
|
ret = nm->mkConst(getCardinality(cc.getType()).getFiniteCardinality() |
262 |
|
<= cc.getUpperBound()); |
263 |
|
} |
264 |
|
// if the value was constant, we return it. If it was non-constant, |
265 |
|
// we only return it if we an evaluated kind. This can occur if the |
266 |
|
// children of n failed to evaluate. |
267 |
1030526 |
if (ret.isConst() || ( |
268 |
347302 |
d_unevaluated_kinds.find(nk) == d_unevaluated_kinds.end() |
269 |
346940 |
&& d_semi_evaluated_kinds.find(nk) == d_semi_evaluated_kinds.end())) |
270 |
|
{ |
271 |
341431 |
d_modelCache[n] = ret; |
272 |
341431 |
return ret; |
273 |
|
} |
274 |
|
} |
275 |
|
// it might be approximate |
276 |
104936 |
std::map<Node, Node>::const_iterator ita = d_approximations.find(n); |
277 |
104936 |
if (ita != d_approximations.end()) |
278 |
|
{ |
279 |
|
// If the value of n is approximate based on predicate P(n), we return |
280 |
|
// witness z. P(z). |
281 |
4 |
Node v = nm->mkBoundVar(n.getType()); |
282 |
4 |
Node bvl = nm->mkNode(BOUND_VAR_LIST, v); |
283 |
4 |
Node answer = nm->mkNode(WITNESS, bvl, ita->second.substitute(n, v)); |
284 |
2 |
d_modelCache[n] = answer; |
285 |
2 |
return answer; |
286 |
|
} |
287 |
|
// must rewrite the term at this point |
288 |
104934 |
ret = rewrite(n); |
289 |
|
// return the representative of the term in the equality engine, if it exists |
290 |
209868 |
TypeNode t = ret.getType(); |
291 |
|
bool eeHasTerm; |
292 |
104934 |
if (!logicInfo().isHigherOrder() && (t.isFunction() || t.isPredicate())) |
293 |
|
{ |
294 |
|
// functions are in the equality engine, but *not* as first-class members |
295 |
|
// when higher-order is disabled. In this case, we cannot query |
296 |
|
// representatives for functions since they are "internal" nodes according |
297 |
|
// to the equality engine despite hasTerm returning true. However, they are |
298 |
|
// first class members when higher-order is enabled. Hence, the special |
299 |
|
// case here. |
300 |
1136 |
eeHasTerm = false; |
301 |
|
} |
302 |
|
else |
303 |
|
{ |
304 |
103798 |
eeHasTerm = d_equalityEngine->hasTerm(ret); |
305 |
|
} |
306 |
104934 |
if (eeHasTerm) |
307 |
|
{ |
308 |
192164 |
Debug("model-getvalue-debug") |
309 |
96082 |
<< "get value from representative " << ret << "..." << std::endl; |
310 |
96082 |
ret = d_equalityEngine->getRepresentative(ret); |
311 |
96082 |
Assert(d_reps.find(ret) != d_reps.end()); |
312 |
96082 |
std::map<Node, Node>::const_iterator it2 = d_reps.find(ret); |
313 |
96082 |
if (it2 != d_reps.end()) |
314 |
|
{ |
315 |
96082 |
ret = it2->second; |
316 |
96082 |
d_modelCache[n] = ret; |
317 |
96082 |
return ret; |
318 |
|
} |
319 |
|
} |
320 |
|
|
321 |
|
// if we are a evaluated or semi-evaluated kind, return an arbitrary value |
322 |
|
// if we are not in the d_unevaluated_kinds map, we are evaluated |
323 |
8852 |
if (d_unevaluated_kinds.find(nk) == d_unevaluated_kinds.end()) |
324 |
|
{ |
325 |
8787 |
if (t.isFunction() || t.isPredicate()) |
326 |
|
{ |
327 |
1137 |
if (d_enableFuncModels) |
328 |
|
{ |
329 |
1137 |
std::map<Node, Node>::const_iterator entry = d_uf_models.find(n); |
330 |
1137 |
if (entry != d_uf_models.end()) |
331 |
|
{ |
332 |
|
// Existing function |
333 |
1091 |
ret = entry->second; |
334 |
1091 |
d_modelCache[n] = ret; |
335 |
1091 |
return ret; |
336 |
|
} |
337 |
|
// Unknown function symbol: return LAMBDA x. c, where c is the first |
338 |
|
// constant in the enumeration of the range type |
339 |
92 |
vector<TypeNode> argTypes = t.getArgTypes(); |
340 |
92 |
vector<Node> args; |
341 |
125 |
for (unsigned i = 0, size = argTypes.size(); i < size; ++i) |
342 |
|
{ |
343 |
79 |
args.push_back(nm->mkBoundVar(argTypes[i])); |
344 |
|
} |
345 |
92 |
Node boundVarList = nm->mkNode(kind::BOUND_VAR_LIST, args); |
346 |
92 |
TypeEnumerator te(t.getRangeType()); |
347 |
46 |
ret = nm->mkNode(kind::LAMBDA, boundVarList, *te); |
348 |
|
} |
349 |
|
else |
350 |
|
{ |
351 |
|
// if func models not enabled, throw an error |
352 |
|
Unreachable(); |
353 |
|
} |
354 |
|
} |
355 |
7650 |
else if (!t.isFirstClass()) |
356 |
|
{ |
357 |
|
// this is the class for regular expressions |
358 |
|
// we simply invoke the rewriter on them |
359 |
24 |
ret = rewrite(ret); |
360 |
|
} |
361 |
|
else |
362 |
|
{ |
363 |
|
// Unknown term - return first enumerated value for this type |
364 |
15252 |
TypeEnumerator te(n.getType()); |
365 |
7626 |
ret = *te; |
366 |
|
} |
367 |
7696 |
d_modelCache[n] = ret; |
368 |
7696 |
return ret; |
369 |
|
} |
370 |
|
|
371 |
65 |
d_modelCache[n] = n; |
372 |
65 |
return n; |
373 |
|
} |
374 |
|
|
375 |
|
/** add term */ |
376 |
2036629 |
void TheoryModel::addTermInternal(TNode n) |
377 |
|
{ |
378 |
2036629 |
Assert(d_equalityEngine->hasTerm(n)); |
379 |
2036629 |
Trace("model-builder-debug2") << "TheoryModel::addTerm : " << n << std::endl; |
380 |
|
//must collect UF terms |
381 |
2036629 |
if (n.getKind()==APPLY_UF) { |
382 |
106896 |
Node op = n.getOperator(); |
383 |
53448 |
if( std::find( d_uf_terms[ op ].begin(), d_uf_terms[ op ].end(), n )==d_uf_terms[ op ].end() ){ |
384 |
53448 |
d_uf_terms[ op ].push_back( n ); |
385 |
53448 |
Trace("model-builder-fun") << "Add apply term " << n << std::endl; |
386 |
|
} |
387 |
1983181 |
}else if( n.getKind()==HO_APPLY ){ |
388 |
2490 |
Node op = n[0]; |
389 |
1245 |
if( std::find( d_ho_uf_terms[ op ].begin(), d_ho_uf_terms[ op ].end(), n )==d_ho_uf_terms[ op ].end() ){ |
390 |
1245 |
d_ho_uf_terms[ op ].push_back( n ); |
391 |
1245 |
Trace("model-builder-fun") << "Add ho apply term " << n << std::endl; |
392 |
|
} |
393 |
|
} |
394 |
|
// all functions must be included, marked as higher-order |
395 |
2036629 |
if( n.getType().isFunction() ){ |
396 |
516 |
Trace("model-builder-fun") << "Add function variable (without term) " << n << std::endl; |
397 |
516 |
if( d_uf_terms.find( n )==d_uf_terms.end() ){ |
398 |
379 |
d_uf_terms[n].clear(); |
399 |
|
} |
400 |
516 |
if( d_ho_uf_terms.find( n )==d_ho_uf_terms.end() ){ |
401 |
246 |
d_ho_uf_terms[n].clear(); |
402 |
|
} |
403 |
|
} |
404 |
2036629 |
} |
405 |
|
|
406 |
|
/** assert equality */ |
407 |
1202184 |
bool TheoryModel::assertEquality(TNode a, TNode b, bool polarity) |
408 |
|
{ |
409 |
1202184 |
Assert(d_equalityEngine->consistent()); |
410 |
1202184 |
if (a == b && polarity) { |
411 |
10727 |
return true; |
412 |
|
} |
413 |
1191457 |
Trace("model-builder-assertions") << "(assert " << (polarity ? "(= " : "(not (= ") << a << " " << b << (polarity ? "));" : ")));") << endl; |
414 |
1191457 |
d_equalityEngine->assertEquality( a.eqNode(b), polarity, Node::null() ); |
415 |
1191457 |
return d_equalityEngine->consistent(); |
416 |
|
} |
417 |
|
|
418 |
|
/** assert predicate */ |
419 |
341442 |
bool TheoryModel::assertPredicate(TNode a, bool polarity) |
420 |
|
{ |
421 |
341442 |
Assert(d_equalityEngine->consistent()); |
422 |
729554 |
if ((a == d_true && polarity) || |
423 |
341442 |
(a == d_false && (!polarity))) { |
424 |
93340 |
return true; |
425 |
|
} |
426 |
248102 |
if (a.getKind() == EQUAL) { |
427 |
|
Trace("model-builder-assertions") << "(assert " << (polarity ? " " : "(not ") << a << (polarity ? ");" : "));") << endl; |
428 |
|
d_equalityEngine->assertEquality( a, polarity, Node::null() ); |
429 |
|
} else { |
430 |
248102 |
Trace("model-builder-assertions") << "(assert " << (polarity ? "" : "(not ") << a << (polarity ? ");" : "));") << endl; |
431 |
248102 |
d_equalityEngine->assertPredicate( a, polarity, Node::null() ); |
432 |
|
} |
433 |
248102 |
return d_equalityEngine->consistent(); |
434 |
|
} |
435 |
|
|
436 |
|
/** assert equality engine */ |
437 |
46670 |
bool TheoryModel::assertEqualityEngine(const eq::EqualityEngine* ee, |
438 |
|
const std::set<Node>* termSet) |
439 |
|
{ |
440 |
46670 |
Assert(d_equalityEngine->consistent()); |
441 |
46670 |
eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( ee ); |
442 |
5206738 |
for (; !eqcs_i.isFinished(); ++eqcs_i) { |
443 |
5160068 |
Node eqc = (*eqcs_i); |
444 |
2580034 |
bool predicate = false; |
445 |
2580034 |
bool predTrue = false; |
446 |
2580034 |
bool predFalse = false; |
447 |
2580034 |
Trace("model-builder-debug") << "...asserting terms in equivalence class " << eqc; |
448 |
2580034 |
if (eqc.getType().isBoolean()) { |
449 |
418121 |
predicate = true; |
450 |
418121 |
predTrue = ee->areEqual(eqc, d_true); |
451 |
418121 |
predFalse = ee->areEqual(eqc, d_false); |
452 |
418121 |
Trace("model-builder-debug") << ", pred = " << predTrue << "/" << predFalse; |
453 |
|
} |
454 |
2580034 |
Trace("model-builder-debug") << std::endl; |
455 |
2580034 |
eq::EqClassIterator eqc_i = eq::EqClassIterator(eqc, ee); |
456 |
2580034 |
bool first = true; |
457 |
5160068 |
Node rep; |
458 |
19457926 |
for (; !eqc_i.isFinished(); ++eqc_i) { |
459 |
10817276 |
Node n = *eqc_i; |
460 |
|
// notice that constants are always relevant |
461 |
22938508 |
if (termSet != nullptr && termSet->find(n) == termSet->end() |
462 |
14978966 |
&& !n.isConst()) |
463 |
|
{ |
464 |
12121232 |
Trace("model-builder-debug") |
465 |
6060616 |
<< "...skip node " << n << " in eqc " << eqc << std::endl; |
466 |
6060616 |
continue; |
467 |
|
} |
468 |
2378330 |
if (predicate) { |
469 |
247924 |
if (predTrue || predFalse) |
470 |
|
{ |
471 |
495824 |
if (!assertPredicate(n, predTrue)) |
472 |
|
{ |
473 |
|
return false; |
474 |
|
} |
475 |
|
} |
476 |
|
else { |
477 |
12 |
if (first) { |
478 |
12 |
rep = n; |
479 |
12 |
first = false; |
480 |
|
} |
481 |
|
else { |
482 |
|
Node eq = (n).eqNode(rep); |
483 |
|
Trace("model-builder-assertions") |
484 |
|
<< "(assert " << eq << ");" << std::endl; |
485 |
|
d_equalityEngine->assertEquality(eq, true, Node::null()); |
486 |
|
if (!d_equalityEngine->consistent()) |
487 |
|
{ |
488 |
|
return false; |
489 |
|
} |
490 |
|
} |
491 |
|
} |
492 |
|
} else { |
493 |
2130406 |
if (first) { |
494 |
1437857 |
rep = n; |
495 |
|
//add the term (this is specifically for the case of singleton equivalence classes) |
496 |
1437857 |
if (rep.getType().isFirstClass()) |
497 |
|
{ |
498 |
1435993 |
d_equalityEngine->addTerm( rep ); |
499 |
1435993 |
Trace("model-builder-debug") << "Add term to ee within assertEqualityEngine: " << rep << std::endl; |
500 |
|
} |
501 |
1437857 |
first = false; |
502 |
|
} |
503 |
|
else { |
504 |
692549 |
if (!assertEquality(n, rep, true)) |
505 |
|
{ |
506 |
|
return false; |
507 |
|
} |
508 |
|
} |
509 |
|
} |
510 |
|
} |
511 |
|
} |
512 |
46670 |
return true; |
513 |
|
} |
514 |
|
|
515 |
104855 |
void TheoryModel::assertSkeleton(TNode n) |
516 |
|
{ |
517 |
104855 |
Trace("model-builder-reps") << "Assert skeleton : " << n << std::endl; |
518 |
209710 |
Trace("model-builder-reps") << "...rep eqc is : " << getRepresentative(n) |
519 |
104855 |
<< std::endl; |
520 |
104855 |
d_reps[ n ] = n; |
521 |
104855 |
} |
522 |
|
|
523 |
10 |
void TheoryModel::setAssignmentExclusionSet(TNode n, |
524 |
|
const std::vector<Node>& eset) |
525 |
|
{ |
526 |
|
// should not be assigned yet |
527 |
10 |
Assert(d_assignExcSet.find(n) == d_assignExcSet.end()); |
528 |
20 |
Trace("model-builder-debug") |
529 |
10 |
<< "Exclude values of " << n << " : " << eset << std::endl; |
530 |
10 |
std::vector<Node>& aes = d_assignExcSet[n]; |
531 |
10 |
aes.insert(aes.end(), eset.begin(), eset.end()); |
532 |
10 |
} |
533 |
|
|
534 |
10 |
void TheoryModel::setAssignmentExclusionSetGroup( |
535 |
|
const std::vector<TNode>& group, const std::vector<Node>& eset) |
536 |
|
{ |
537 |
10 |
if (group.empty()) |
538 |
|
{ |
539 |
|
return; |
540 |
|
} |
541 |
|
// for efficiency, we store a single copy of eset and set a slave/master |
542 |
|
// relationship |
543 |
10 |
setAssignmentExclusionSet(group[0], eset); |
544 |
10 |
std::vector<Node>& gslaves = d_aesSlaves[group[0]]; |
545 |
45 |
for (unsigned i = 1, gsize = group.size(); i < gsize; ++i) |
546 |
|
{ |
547 |
70 |
Node gs = group[i]; |
548 |
|
// set master |
549 |
35 |
d_aesMaster[gs] = group[0]; |
550 |
|
// add to slaves |
551 |
35 |
gslaves.push_back(gs); |
552 |
|
} |
553 |
|
} |
554 |
|
|
555 |
993153 |
bool TheoryModel::getAssignmentExclusionSet(TNode n, |
556 |
|
std::vector<Node>& group, |
557 |
|
std::vector<Node>& eset) |
558 |
|
{ |
559 |
|
// does it have a master? |
560 |
993153 |
std::map<Node, Node>::iterator itm = d_aesMaster.find(n); |
561 |
993153 |
if (itm != d_aesMaster.end()) |
562 |
|
{ |
563 |
|
return getAssignmentExclusionSet(itm->second, group, eset); |
564 |
|
} |
565 |
993153 |
std::map<Node, std::vector<Node> >::iterator ita = d_assignExcSet.find(n); |
566 |
993153 |
if (ita == d_assignExcSet.end()) |
567 |
|
{ |
568 |
993143 |
return false; |
569 |
|
} |
570 |
10 |
eset.insert(eset.end(), ita->second.begin(), ita->second.end()); |
571 |
10 |
group.push_back(n); |
572 |
|
// does it have slaves? |
573 |
10 |
ita = d_aesSlaves.find(n); |
574 |
10 |
if (ita != d_aesSlaves.end()) |
575 |
|
{ |
576 |
10 |
group.insert(group.end(), ita->second.begin(), ita->second.end()); |
577 |
|
} |
578 |
10 |
return true; |
579 |
|
} |
580 |
|
|
581 |
24995 |
bool TheoryModel::hasAssignmentExclusionSets() const |
582 |
|
{ |
583 |
24995 |
return !d_assignExcSet.empty(); |
584 |
|
} |
585 |
|
|
586 |
30 |
void TheoryModel::recordApproximation(TNode n, TNode pred) |
587 |
|
{ |
588 |
60 |
Trace("model-builder-debug") |
589 |
30 |
<< "Record approximation : " << n << " satisfies the predicate " << pred |
590 |
30 |
<< std::endl; |
591 |
30 |
Assert(d_approximations.find(n) == d_approximations.end()); |
592 |
30 |
Assert(pred.getType().isBoolean()); |
593 |
30 |
d_approximations[n] = pred; |
594 |
30 |
d_approx_list.push_back(std::pair<Node, Node>(n, pred)); |
595 |
|
// model cache is invalid |
596 |
30 |
d_modelCache.clear(); |
597 |
30 |
} |
598 |
|
void TheoryModel::recordApproximation(TNode n, TNode pred, Node witness) |
599 |
|
{ |
600 |
|
Node predDisj = NodeManager::currentNM()->mkNode(OR, n.eqNode(witness), pred); |
601 |
|
recordApproximation(n, predDisj); |
602 |
|
} |
603 |
8 |
bool TheoryModel::isUsingModelCore() const { return d_using_model_core; } |
604 |
2 |
void TheoryModel::setUsingModelCore() |
605 |
|
{ |
606 |
2 |
d_using_model_core = true; |
607 |
2 |
d_model_core.clear(); |
608 |
2 |
} |
609 |
|
|
610 |
4 |
void TheoryModel::recordModelCoreSymbol(Node sym) { d_model_core.insert(sym); } |
611 |
|
|
612 |
171628 |
void TheoryModel::setUnevaluatedKind(Kind k) { d_unevaluated_kinds.insert(k); } |
613 |
|
|
614 |
30544 |
void TheoryModel::setSemiEvaluatedKind(Kind k) |
615 |
|
{ |
616 |
30544 |
d_semi_evaluated_kinds.insert(k); |
617 |
30544 |
} |
618 |
|
|
619 |
45816 |
void TheoryModel::setIrrelevantKind(Kind k) { d_irrKinds.insert(k); } |
620 |
|
|
621 |
17646663 |
const std::set<Kind>& TheoryModel::getIrrelevantKinds() const |
622 |
|
{ |
623 |
17646663 |
return d_irrKinds; |
624 |
|
} |
625 |
|
|
626 |
11182 |
bool TheoryModel::isLegalElimination(TNode x, TNode val) |
627 |
|
{ |
628 |
11182 |
return !expr::hasSubtermKinds(d_unevaluated_kinds, val); |
629 |
|
} |
630 |
|
|
631 |
23149 |
bool TheoryModel::hasTerm(TNode a) |
632 |
|
{ |
633 |
23149 |
return d_equalityEngine->hasTerm( a ); |
634 |
|
} |
635 |
|
|
636 |
590295 |
Node TheoryModel::getRepresentative(TNode a) |
637 |
|
{ |
638 |
590295 |
if( d_equalityEngine->hasTerm( a ) ){ |
639 |
1120314 |
Node r = d_equalityEngine->getRepresentative( a ); |
640 |
560157 |
if( d_reps.find( r )!=d_reps.end() ){ |
641 |
454889 |
return d_reps[ r ]; |
642 |
|
}else{ |
643 |
105268 |
return r; |
644 |
|
} |
645 |
|
}else{ |
646 |
30138 |
return a; |
647 |
|
} |
648 |
|
} |
649 |
|
|
650 |
1207 |
bool TheoryModel::areEqual(TNode a, TNode b) |
651 |
|
{ |
652 |
1207 |
if( a==b ){ |
653 |
771 |
return true; |
654 |
436 |
}else if( d_equalityEngine->hasTerm( a ) && d_equalityEngine->hasTerm( b ) ){ |
655 |
436 |
return d_equalityEngine->areEqual( a, b ); |
656 |
|
}else{ |
657 |
|
return false; |
658 |
|
} |
659 |
|
} |
660 |
|
|
661 |
|
bool TheoryModel::areDisequal(TNode a, TNode b) |
662 |
|
{ |
663 |
|
if( d_equalityEngine->hasTerm( a ) && d_equalityEngine->hasTerm( b ) ){ |
664 |
|
return d_equalityEngine->areDisequal( a, b, false ); |
665 |
|
}else{ |
666 |
|
return false; |
667 |
|
} |
668 |
|
} |
669 |
|
|
670 |
7313 |
bool TheoryModel::hasUfTerms(Node f) const |
671 |
|
{ |
672 |
7313 |
return d_uf_terms.find(f) != d_uf_terms.end(); |
673 |
|
} |
674 |
|
|
675 |
5544 |
const std::vector<Node>& TheoryModel::getUfTerms(Node f) const |
676 |
|
{ |
677 |
5544 |
const auto it = d_uf_terms.find(f); |
678 |
5544 |
Assert(it != d_uf_terms.end()); |
679 |
5544 |
return it->second; |
680 |
|
} |
681 |
|
|
682 |
27122 |
bool TheoryModel::areFunctionValuesEnabled() const |
683 |
|
{ |
684 |
27122 |
return d_enableFuncModels; |
685 |
|
} |
686 |
|
|
687 |
12575 |
void TheoryModel::assignFunctionDefinition( Node f, Node f_def ) { |
688 |
12575 |
Trace("model-builder") << " Assigning function (" << f << ") to (" << f_def << ")" << endl; |
689 |
12575 |
Assert(d_uf_models.find(f) == d_uf_models.end()); |
690 |
|
|
691 |
12575 |
if (logicInfo().isHigherOrder()) |
692 |
|
{ |
693 |
|
//we must rewrite the function value since the definition needs to be a constant value |
694 |
388 |
f_def = rewrite(f_def); |
695 |
776 |
Trace("model-builder-debug") |
696 |
388 |
<< "Model value (post-rewrite) : " << f_def << std::endl; |
697 |
388 |
Assert(f_def.isConst()) << "Non-constant f_def: " << f_def; |
698 |
|
} |
699 |
|
|
700 |
|
// d_uf_models only stores models for variables |
701 |
12575 |
if( f.isVar() ){ |
702 |
12428 |
d_uf_models[f] = f_def; |
703 |
|
} |
704 |
|
|
705 |
12575 |
if (logicInfo().isHigherOrder() && d_equalityEngine->hasTerm(f)) |
706 |
|
{ |
707 |
380 |
Trace("model-builder-debug") << " ...function is first-class member of equality engine" << std::endl; |
708 |
|
// assign to representative if higher-order |
709 |
760 |
Node r = d_equalityEngine->getRepresentative( f ); |
710 |
|
//always replace the representative, since it is initially assigned to itself |
711 |
380 |
Trace("model-builder") << " Assign: Setting function rep " << r << " to " << f_def << endl; |
712 |
380 |
d_reps[r] = f_def; |
713 |
|
// also assign to other assignable functions in the same equivalence class |
714 |
380 |
eq::EqClassIterator eqc_i = eq::EqClassIterator(r,d_equalityEngine); |
715 |
1412 |
while( !eqc_i.isFinished() ) { |
716 |
1032 |
Node n = *eqc_i; |
717 |
|
// if an unassigned variable function |
718 |
516 |
if( n.isVar() && d_uf_terms.find( n )!=d_uf_terms.end() && !hasAssignedFunctionDefinition( n ) ){ |
719 |
39 |
d_uf_models[n] = f_def; |
720 |
39 |
Trace("model-builder") << " Assigning function (" << n << ") to function definition of " << f << std::endl; |
721 |
|
} |
722 |
516 |
++eqc_i; |
723 |
|
} |
724 |
380 |
Trace("model-builder-debug") << " ...finished." << std::endl; |
725 |
|
} |
726 |
12575 |
} |
727 |
|
|
728 |
24995 |
std::vector< Node > TheoryModel::getFunctionsToAssign() { |
729 |
24995 |
std::vector< Node > funcs_to_assign; |
730 |
49990 |
std::map< Node, Node > func_to_rep; |
731 |
|
|
732 |
|
// collect functions |
733 |
35937 |
for( std::map< Node, std::vector< Node > >::iterator it = d_uf_terms.begin(); it != d_uf_terms.end(); ++it ){ |
734 |
21884 |
Node n = it->first; |
735 |
10942 |
Assert(!n.isNull()); |
736 |
|
// should not have been solved for in a substitution |
737 |
10942 |
Assert(d_env.getTopLevelSubstitutions().apply(n) == n); |
738 |
10942 |
if( !hasAssignedFunctionDefinition( n ) ){ |
739 |
5398 |
Trace("model-builder-fun-debug") << "Look at function : " << n << std::endl; |
740 |
5398 |
if (logicInfo().isHigherOrder()) |
741 |
|
{ |
742 |
|
// if in higher-order mode, assign function definitions modulo equality |
743 |
1004 |
Node r = getRepresentative( n ); |
744 |
502 |
std::map< Node, Node >::iterator itf = func_to_rep.find( r ); |
745 |
502 |
if( itf==func_to_rep.end() ){ |
746 |
366 |
func_to_rep[r] = n; |
747 |
366 |
funcs_to_assign.push_back( n ); |
748 |
366 |
Trace("model-builder-fun") << "Make function " << n; |
749 |
366 |
Trace("model-builder-fun") << " the assignable function in its equivalence class." << std::endl; |
750 |
|
}else{ |
751 |
|
// must combine uf terms |
752 |
136 |
Trace("model-builder-fun") << "Copy " << it->second.size() << " uf terms"; |
753 |
136 |
d_uf_terms[itf->second].insert( d_uf_terms[itf->second].end(), it->second.begin(), it->second.end() ); |
754 |
136 |
std::map< Node, std::vector< Node > >::iterator ith = d_ho_uf_terms.find( n ); |
755 |
136 |
if( ith!=d_ho_uf_terms.end() ){ |
756 |
136 |
d_ho_uf_terms[itf->second].insert( d_ho_uf_terms[itf->second].end(), ith->second.begin(), ith->second.end() ); |
757 |
136 |
Trace("model-builder-fun") << " and " << ith->second.size() << " ho uf terms"; |
758 |
|
} |
759 |
136 |
Trace("model-builder-fun") << " from " << n << " to its assignable representative function " << itf->second << std::endl; |
760 |
136 |
it->second.clear(); |
761 |
|
} |
762 |
|
}else{ |
763 |
4896 |
Trace("model-builder-fun") << "Function to assign : " << n << std::endl; |
764 |
4896 |
funcs_to_assign.push_back( n ); |
765 |
|
} |
766 |
|
} |
767 |
|
} |
768 |
|
|
769 |
24995 |
Trace("model-builder-fun") << "return " << funcs_to_assign.size() << " functions to assign..." << std::endl; |
770 |
49990 |
return funcs_to_assign; |
771 |
|
} |
772 |
|
|
773 |
15272 |
const std::string& TheoryModel::getName() const { return d_name; } |
774 |
|
|
775 |
|
std::string TheoryModel::debugPrintModelEqc() const |
776 |
|
{ |
777 |
|
std::stringstream ss; |
778 |
|
ss << "--- Equivalence classes:" << std::endl; |
779 |
|
ss << d_equalityEngine->debugPrintEqc() << std::endl; |
780 |
|
ss << "--- Representative map: " << std::endl; |
781 |
|
for (const std::pair<const Node, Node>& r : d_reps) |
782 |
|
{ |
783 |
|
ss << r.first << " -> " << r.second << std::endl; |
784 |
|
} |
785 |
|
ss << "---" << std::endl; |
786 |
|
return ss.str(); |
787 |
|
} |
788 |
|
|
789 |
|
} // namespace theory |
790 |
31137 |
} // namespace cvc5 |