1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Clark Barrett, Andres Noetzli |
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 theory model buidler class. |
14 |
|
*/ |
15 |
|
#include "theory/theory_model_builder.h" |
16 |
|
|
17 |
|
#include "expr/dtype.h" |
18 |
|
#include "expr/dtype_cons.h" |
19 |
|
#include "expr/uninterpreted_constant.h" |
20 |
|
#include "options/quantifiers_options.h" |
21 |
|
#include "options/smt_options.h" |
22 |
|
#include "options/theory_options.h" |
23 |
|
#include "options/uf_options.h" |
24 |
|
#include "smt/env.h" |
25 |
|
#include "theory/rewriter.h" |
26 |
|
#include "theory/uf/theory_uf_model.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 |
13041 |
TheoryEngineModelBuilder::TheoryEngineModelBuilder(Env& env) : EnvObj(env) {} |
36 |
|
|
37 |
10 |
void TheoryEngineModelBuilder::Assigner::initialize( |
38 |
|
TypeNode tn, TypeEnumeratorProperties* tep, const std::vector<Node>& aes) |
39 |
|
{ |
40 |
10 |
d_te.reset(new TypeEnumerator(tn, tep)); |
41 |
10 |
d_assignExcSet.insert(d_assignExcSet.end(), aes.begin(), aes.end()); |
42 |
10 |
} |
43 |
|
|
44 |
45 |
Node TheoryEngineModelBuilder::Assigner::getNextAssignment() |
45 |
|
{ |
46 |
45 |
Assert(d_te != nullptr); |
47 |
90 |
Node n; |
48 |
45 |
bool success = false; |
49 |
45 |
TypeEnumerator& te = *d_te; |
50 |
|
// Check if we have run out of elements. This should never happen; if it |
51 |
|
// does we assert false and return null. |
52 |
45 |
if (te.isFinished()) |
53 |
|
{ |
54 |
|
Assert(false); |
55 |
|
return Node::null(); |
56 |
|
} |
57 |
|
// must increment until we find one that is not in the assignment |
58 |
|
// exclusion set |
59 |
12 |
do |
60 |
|
{ |
61 |
57 |
n = *te; |
62 |
57 |
success = std::find(d_assignExcSet.begin(), d_assignExcSet.end(), n) |
63 |
114 |
== d_assignExcSet.end(); |
64 |
|
// increment regardless of fail or succeed, to set up the next value |
65 |
57 |
++te; |
66 |
57 |
} while (!success); |
67 |
45 |
return n; |
68 |
|
} |
69 |
|
|
70 |
243121 |
Node TheoryEngineModelBuilder::evaluateEqc(TheoryModel* m, TNode r) |
71 |
|
{ |
72 |
243121 |
eq::EqClassIterator eqc_i = eq::EqClassIterator(r, m->d_equalityEngine); |
73 |
257029 |
for (; !eqc_i.isFinished(); ++eqc_i) |
74 |
|
{ |
75 |
252115 |
Node n = *eqc_i; |
76 |
245161 |
Trace("model-builder-debug") << "Look at term : " << n << std::endl; |
77 |
245161 |
if (!isAssignable(n)) |
78 |
|
{ |
79 |
244667 |
Trace("model-builder-debug") << "...try to normalize" << std::endl; |
80 |
251127 |
Node normalized = normalize(m, n, true); |
81 |
244667 |
if (normalized.isConst()) |
82 |
|
{ |
83 |
238207 |
return normalized; |
84 |
|
} |
85 |
|
} |
86 |
|
} |
87 |
4914 |
return Node::null(); |
88 |
|
} |
89 |
|
|
90 |
45 |
bool TheoryEngineModelBuilder::isAssignerActive(TheoryModel* tm, Assigner& a) |
91 |
|
{ |
92 |
45 |
if (a.d_isActive) |
93 |
|
{ |
94 |
35 |
return true; |
95 |
|
} |
96 |
10 |
std::vector<Node>& eset = a.d_assignExcSet; |
97 |
10 |
std::map<Node, Node>::iterator it; |
98 |
30 |
for (unsigned i = 0, size = eset.size(); i < size; i++) |
99 |
|
{ |
100 |
|
// Members of exclusion set must have values, otherwise we are not yet |
101 |
|
// assignable. |
102 |
26 |
Node er = eset[i]; |
103 |
20 |
if (er.isConst()) |
104 |
|
{ |
105 |
|
// already processed |
106 |
14 |
continue; |
107 |
|
} |
108 |
|
// Assignable members of assignment exclusion set should be representatives |
109 |
|
// of their equivalence classes. This ensures we look up the constant |
110 |
|
// representatives for assignable members of assignment exclusion sets. |
111 |
6 |
Assert(er == tm->getRepresentative(er)); |
112 |
6 |
it = d_constantReps.find(er); |
113 |
6 |
if (it == d_constantReps.end()) |
114 |
|
{ |
115 |
|
Trace("model-build-aes") |
116 |
|
<< "isAssignerActive: not active due to " << er << std::endl; |
117 |
|
return false; |
118 |
|
} |
119 |
|
// update |
120 |
6 |
eset[i] = it->second; |
121 |
|
} |
122 |
10 |
Trace("model-build-aes") << "isAssignerActive: active!" << std::endl; |
123 |
10 |
a.d_isActive = true; |
124 |
10 |
return true; |
125 |
|
} |
126 |
|
|
127 |
2603339 |
bool TheoryEngineModelBuilder::isAssignable(TNode n) |
128 |
|
{ |
129 |
2603339 |
if (n.getKind() == kind::SELECT || n.getKind() == kind::APPLY_SELECTOR_TOTAL) |
130 |
|
{ |
131 |
|
// selectors are always assignable (where we guarantee that they are not |
132 |
|
// evaluatable here) |
133 |
235310 |
if (!logicInfo().isHigherOrder()) |
134 |
|
{ |
135 |
180836 |
Assert(!n.getType().isFunction()); |
136 |
180836 |
return true; |
137 |
|
} |
138 |
|
else |
139 |
|
{ |
140 |
|
// might be a function field |
141 |
54474 |
return !n.getType().isFunction(); |
142 |
|
} |
143 |
|
} |
144 |
2368029 |
else if (n.getKind() == kind::FLOATINGPOINT_COMPONENT_SIGN) |
145 |
|
{ |
146 |
|
// Extracting the sign of a floating-point number acts similar to a |
147 |
|
// selector on a datatype, i.e. if `(sign x)` wasn't assigned a value, we |
148 |
|
// can pick an arbitrary one. Note that the other components of a |
149 |
|
// floating-point number should always be assigned a value. |
150 |
1218 |
return true; |
151 |
|
} |
152 |
|
else |
153 |
|
{ |
154 |
|
// non-function variables, and fully applied functions |
155 |
2366811 |
if (!logicInfo().isHigherOrder()) |
156 |
|
{ |
157 |
|
// no functions exist, all functions are fully applied |
158 |
2289222 |
Assert(n.getKind() != kind::HO_APPLY); |
159 |
2289222 |
Assert(!n.getType().isFunction()); |
160 |
2289222 |
return n.isVar() || n.getKind() == kind::APPLY_UF; |
161 |
|
} |
162 |
|
else |
163 |
|
{ |
164 |
|
// Assert( n.getKind() != kind::APPLY_UF ); |
165 |
169483 |
return (n.isVar() && !n.getType().isFunction()) |
166 |
70523 |
|| n.getKind() == kind::APPLY_UF |
167 |
237641 |
|| (n.getKind() == kind::HO_APPLY |
168 |
83337 |
&& n[0].getType().getNumChildren() == 2); |
169 |
|
} |
170 |
|
} |
171 |
|
} |
172 |
|
|
173 |
2810970 |
void TheoryEngineModelBuilder::addAssignableSubterms(TNode n, |
174 |
|
TheoryModel* tm, |
175 |
|
NodeSet& cache) |
176 |
|
{ |
177 |
2810970 |
if (n.isClosure()) |
178 |
|
{ |
179 |
29590 |
return; |
180 |
|
} |
181 |
2781380 |
if (cache.find(n) != cache.end()) |
182 |
|
{ |
183 |
1426643 |
return; |
184 |
|
} |
185 |
1354737 |
if (isAssignable(n)) |
186 |
|
{ |
187 |
400653 |
tm->d_equalityEngine->addTerm(n); |
188 |
|
} |
189 |
2843154 |
for (TNode::iterator child_it = n.begin(); child_it != n.end(); ++child_it) |
190 |
|
{ |
191 |
1488417 |
addAssignableSubterms(*child_it, tm, cache); |
192 |
|
} |
193 |
1354737 |
cache.insert(n); |
194 |
|
} |
195 |
|
|
196 |
586074 |
void TheoryEngineModelBuilder::assignConstantRep(TheoryModel* tm, |
197 |
|
Node eqc, |
198 |
|
Node constRep) |
199 |
|
{ |
200 |
586074 |
d_constantReps[eqc] = constRep; |
201 |
1172148 |
Trace("model-builder") << " Assign: Setting constant rep of " << eqc |
202 |
586074 |
<< " to " << constRep << endl; |
203 |
586074 |
tm->d_rep_set.setTermForRepresentative(constRep, eqc); |
204 |
586074 |
} |
205 |
|
|
206 |
43 |
bool TheoryEngineModelBuilder::isExcludedCdtValue( |
207 |
|
Node val, |
208 |
|
std::set<Node>* repSet, |
209 |
|
std::map<Node, Node>& assertedReps, |
210 |
|
Node eqc) |
211 |
|
{ |
212 |
86 |
Trace("model-builder-debug") |
213 |
43 |
<< "Is " << val << " and excluded codatatype value for " << eqc << "? " |
214 |
43 |
<< std::endl; |
215 |
95 |
for (set<Node>::iterator i = repSet->begin(); i != repSet->end(); ++i) |
216 |
|
{ |
217 |
58 |
Assert(assertedReps.find(*i) != assertedReps.end()); |
218 |
110 |
Node rep = assertedReps[*i]; |
219 |
58 |
Trace("model-builder-debug") << " Rep : " << rep << std::endl; |
220 |
|
// check matching val to rep with eqc as a free variable |
221 |
110 |
Node eqc_m; |
222 |
58 |
if (isCdtValueMatch(val, rep, eqc, eqc_m)) |
223 |
|
{ |
224 |
28 |
Trace("model-builder-debug") << " ...matches with " << eqc << " -> " |
225 |
14 |
<< eqc_m << std::endl; |
226 |
14 |
if (eqc_m.getKind() == kind::UNINTERPRETED_CONSTANT) |
227 |
|
{ |
228 |
12 |
Trace("model-builder-debug") << "*** " << val |
229 |
6 |
<< " is excluded datatype for " << eqc |
230 |
6 |
<< std::endl; |
231 |
6 |
return true; |
232 |
|
} |
233 |
|
} |
234 |
|
} |
235 |
37 |
return false; |
236 |
|
} |
237 |
|
|
238 |
154 |
bool TheoryEngineModelBuilder::isCdtValueMatch(Node v, |
239 |
|
Node r, |
240 |
|
Node eqc, |
241 |
|
Node& eqc_m) |
242 |
|
{ |
243 |
154 |
if (r == v) |
244 |
|
{ |
245 |
42 |
return true; |
246 |
|
} |
247 |
112 |
else if (r == eqc) |
248 |
|
{ |
249 |
14 |
if (eqc_m.isNull()) |
250 |
|
{ |
251 |
|
// only if an uninterpreted constant? |
252 |
14 |
eqc_m = v; |
253 |
14 |
return true; |
254 |
|
} |
255 |
|
else |
256 |
|
{ |
257 |
|
return v == eqc_m; |
258 |
|
} |
259 |
|
} |
260 |
196 |
else if (v.getKind() == kind::APPLY_CONSTRUCTOR |
261 |
98 |
&& r.getKind() == kind::APPLY_CONSTRUCTOR) |
262 |
|
{ |
263 |
64 |
if (v.getOperator() == r.getOperator()) |
264 |
|
{ |
265 |
110 |
for (unsigned i = 0; i < v.getNumChildren(); i++) |
266 |
|
{ |
267 |
96 |
if (!isCdtValueMatch(v[i], r[i], eqc, eqc_m)) |
268 |
|
{ |
269 |
40 |
return false; |
270 |
|
} |
271 |
|
} |
272 |
14 |
return true; |
273 |
|
} |
274 |
|
} |
275 |
44 |
return false; |
276 |
|
} |
277 |
|
|
278 |
9962 |
bool TheoryEngineModelBuilder::isFiniteType(TypeNode tn) const |
279 |
|
{ |
280 |
9962 |
return isCardinalityClassFinite(tn.getCardinalityClass(), |
281 |
19924 |
options::finiteModelFind()); |
282 |
|
} |
283 |
|
|
284 |
10113 |
bool TheoryEngineModelBuilder::involvesUSort(TypeNode tn) const |
285 |
|
{ |
286 |
10113 |
if (tn.isSort()) |
287 |
|
{ |
288 |
7 |
return true; |
289 |
|
} |
290 |
10106 |
else if (tn.isArray()) |
291 |
|
{ |
292 |
15 |
return involvesUSort(tn.getArrayIndexType()) |
293 |
15 |
|| involvesUSort(tn.getArrayConstituentType()); |
294 |
|
} |
295 |
10101 |
else if (tn.isSet()) |
296 |
|
{ |
297 |
4 |
return involvesUSort(tn.getSetElementType()); |
298 |
|
} |
299 |
10097 |
else if (tn.isDatatype()) |
300 |
|
{ |
301 |
9950 |
const DType& dt = tn.getDType(); |
302 |
9950 |
return dt.involvesUninterpretedType(); |
303 |
|
} |
304 |
|
else |
305 |
|
{ |
306 |
147 |
return false; |
307 |
|
} |
308 |
|
} |
309 |
|
|
310 |
35503 |
bool TheoryEngineModelBuilder::isExcludedUSortValue( |
311 |
|
std::map<TypeNode, unsigned>& eqc_usort_count, |
312 |
|
Node v, |
313 |
|
std::map<Node, bool>& visited) |
314 |
|
{ |
315 |
35503 |
Assert(v.isConst()); |
316 |
35503 |
if (visited.find(v) == visited.end()) |
317 |
|
{ |
318 |
26264 |
visited[v] = true; |
319 |
52342 |
TypeNode tn = v.getType(); |
320 |
26264 |
if (tn.isSort()) |
321 |
|
{ |
322 |
372 |
Trace("model-builder-debug") << "Is excluded usort value : " << v << " " |
323 |
186 |
<< tn << std::endl; |
324 |
186 |
unsigned card = eqc_usort_count[tn]; |
325 |
186 |
Trace("model-builder-debug") << " Cardinality is " << card << std::endl; |
326 |
|
unsigned index = |
327 |
186 |
v.getConst<UninterpretedConstant>().getIndex().toUnsignedInt(); |
328 |
186 |
Trace("model-builder-debug") << " Index is " << index << std::endl; |
329 |
186 |
return index > 0 && index >= card; |
330 |
|
} |
331 |
56084 |
for (unsigned i = 0; i < v.getNumChildren(); i++) |
332 |
|
{ |
333 |
30006 |
if (isExcludedUSortValue(eqc_usort_count, v[i], visited)) |
334 |
|
{ |
335 |
|
return true; |
336 |
|
} |
337 |
|
} |
338 |
|
} |
339 |
35317 |
return false; |
340 |
|
} |
341 |
|
|
342 |
337538 |
void TheoryEngineModelBuilder::addToTypeList( |
343 |
|
TypeNode tn, |
344 |
|
std::vector<TypeNode>& type_list, |
345 |
|
std::unordered_set<TypeNode>& visiting) |
346 |
|
{ |
347 |
337538 |
if (std::find(type_list.begin(), type_list.end(), tn) == type_list.end()) |
348 |
|
{ |
349 |
70320 |
if (visiting.find(tn) == visiting.end()) |
350 |
|
{ |
351 |
28874 |
visiting.insert(tn); |
352 |
|
/* This must make a recursive call on all types that are subterms of |
353 |
|
* values of the current type. |
354 |
|
* Note that recursive traversal here is over enumerated expressions |
355 |
|
* (very low expression depth). */ |
356 |
28874 |
if (tn.isArray()) |
357 |
|
{ |
358 |
511 |
addToTypeList(tn.getArrayIndexType(), type_list, visiting); |
359 |
511 |
addToTypeList(tn.getArrayConstituentType(), type_list, visiting); |
360 |
|
} |
361 |
28363 |
else if (tn.isSet()) |
362 |
|
{ |
363 |
299 |
addToTypeList(tn.getSetElementType(), type_list, visiting); |
364 |
|
} |
365 |
28064 |
else if (tn.isDatatype()) |
366 |
|
{ |
367 |
12651 |
const DType& dt = tn.getDType(); |
368 |
88646 |
for (unsigned i = 0; i < dt.getNumConstructors(); i++) |
369 |
|
{ |
370 |
144158 |
for (unsigned j = 0; j < dt[i].getNumArgs(); j++) |
371 |
|
{ |
372 |
136326 |
TypeNode ctn = dt[i][j].getRangeType(); |
373 |
68163 |
addToTypeList(ctn, type_list, visiting); |
374 |
|
} |
375 |
|
} |
376 |
|
} |
377 |
28874 |
Assert(std::find(type_list.begin(), type_list.end(), tn) |
378 |
|
== type_list.end()); |
379 |
28874 |
type_list.push_back(tn); |
380 |
|
} |
381 |
|
} |
382 |
337538 |
} |
383 |
|
|
384 |
16395 |
bool TheoryEngineModelBuilder::buildModel(TheoryModel* tm) |
385 |
|
{ |
386 |
16395 |
Trace("model-builder") << "TheoryEngineModelBuilder: buildModel" << std::endl; |
387 |
|
|
388 |
32790 |
Trace("model-builder") |
389 |
16395 |
<< "TheoryEngineModelBuilder: Preprocess build model..." << std::endl; |
390 |
|
// model-builder specific initialization |
391 |
16395 |
if (!preProcessBuildModel(tm)) |
392 |
|
{ |
393 |
|
Trace("model-builder") |
394 |
|
<< "TheoryEngineModelBuilder: fail preprocess build model." |
395 |
|
<< std::endl; |
396 |
|
return false; |
397 |
|
} |
398 |
|
|
399 |
32790 |
Trace("model-builder") |
400 |
|
<< "TheoryEngineModelBuilder: Add assignable subterms " |
401 |
16395 |
", collect representatives and compute assignable information..." |
402 |
16395 |
<< std::endl; |
403 |
|
|
404 |
|
// type enumerator properties |
405 |
32790 |
TypeEnumeratorProperties tep; |
406 |
|
|
407 |
|
// In the first step of model building, we do a traversal of the |
408 |
|
// equality engine and record the information in the following: |
409 |
|
|
410 |
|
// The constant representatives, per equivalence class |
411 |
16395 |
d_constantReps.clear(); |
412 |
|
// The representatives that have been asserted by theories. This includes |
413 |
|
// non-constant "skeletons" that have been specified by parametric theories. |
414 |
32790 |
std::map<Node, Node> assertedReps; |
415 |
|
// A parition of the set of equivalence classes that have: |
416 |
|
// (1) constant representatives, |
417 |
|
// (2) an assigned representative specified by a theory in collectModelInfo, |
418 |
|
// (3) no assigned representative. |
419 |
32790 |
TypeSet typeConstSet, typeRepSet, typeNoRepSet; |
420 |
|
// An ordered list of types, such that T1 comes before T2 if T1 is a |
421 |
|
// "component type" of T2, e.g. U comes before (Set U). This is only strictly |
422 |
|
// necessary for finite model finding + parametric types instantiated with |
423 |
|
// uninterpreted sorts, but is probably a good idea to do in general since it |
424 |
|
// leads to models with smaller term sizes. -AJR |
425 |
32790 |
std::vector<TypeNode> type_list; |
426 |
|
// The count of equivalence classes per sort (for finite model finding). |
427 |
32790 |
std::map<TypeNode, unsigned> eqc_usort_count; |
428 |
|
|
429 |
|
// the set of equivalence classes that are "assignable", i.e. those that have |
430 |
|
// an assignable expression in them (see isAssignable), and have not already |
431 |
|
// been assigned a constant. |
432 |
32790 |
std::unordered_set<Node> assignableEqc; |
433 |
|
// The set of equivalence classes that are "evaluable", i.e. those that have |
434 |
|
// an expression in them that is not assignable, and have not already been |
435 |
|
// assigned a constant. |
436 |
32790 |
std::unordered_set<Node> evaluableEqc; |
437 |
|
// Assigner objects for relevant equivalence classes that require special |
438 |
|
// ways of assigning values, e.g. those that take into account assignment |
439 |
|
// exclusion sets. |
440 |
32790 |
std::map<Node, Assigner> eqcToAssigner; |
441 |
|
// A map from equivalence classes to the equivalence class that it shares an |
442 |
|
// assigner object with (all elements in the range of this map are in the |
443 |
|
// domain of eqcToAssigner). |
444 |
32790 |
std::map<Node, Node> eqcToAssignerMaster; |
445 |
|
|
446 |
|
// Loop through equivalence classes of the equality engine of the model. |
447 |
16395 |
eq::EqualityEngine* ee = tm->d_equalityEngine; |
448 |
32790 |
NodeSet assignableCache; |
449 |
16395 |
std::map<Node, Node>::iterator itm; |
450 |
16395 |
eq::EqClassesIterator eqcs_i = eq::EqClassesIterator(ee); |
451 |
|
// should we compute assigner objects? |
452 |
16395 |
bool computeAssigners = tm->hasAssignmentExclusionSets(); |
453 |
|
// the set of exclusion sets we have processed |
454 |
32790 |
std::unordered_set<Node> processedExcSet; |
455 |
1190829 |
for (; !eqcs_i.isFinished(); ++eqcs_i) |
456 |
|
{ |
457 |
855271 |
Node eqc = *eqcs_i; |
458 |
|
|
459 |
|
// Information computed for each equivalence class |
460 |
|
|
461 |
|
// The assigned represenative and constant representative |
462 |
855271 |
Node rep, constRep; |
463 |
|
// A flag set to true if the current equivalence class is assignable (see |
464 |
|
// assignableEqc). |
465 |
587217 |
bool assignable = false; |
466 |
|
// Set to true if the current equivalence class is evaluatable (see |
467 |
|
// evaluableEqc). |
468 |
587217 |
bool evaluable = false; |
469 |
|
// Set to true if a term in the current equivalence class has been given an |
470 |
|
// assignment exclusion set. |
471 |
587217 |
bool hasESet CVC5_UNUSED = false; |
472 |
|
// Set to true if we found that a term in the current equivalence class has |
473 |
|
// been given an assignment exclusion set, and we have not seen this term |
474 |
|
// as part of a previous assignment exclusion group. In other words, when |
475 |
|
// this flag is true we construct a new assigner object with the current |
476 |
|
// equivalence class as its master. |
477 |
587217 |
bool foundESet = false; |
478 |
|
// The assignment exclusion set for the current equivalence class. |
479 |
855271 |
std::vector<Node> eset; |
480 |
|
// The group to which this equivalence class belongs when exclusion sets |
481 |
|
// were assigned (see the argument group of |
482 |
|
// TheoryModel::getAssignmentExclusionSet). |
483 |
855271 |
std::vector<Node> esetGroup; |
484 |
|
|
485 |
|
// Loop through terms in this EC |
486 |
587217 |
eq::EqClassIterator eqc_i = eq::EqClassIterator(eqc, ee); |
487 |
3232323 |
for (; !eqc_i.isFinished(); ++eqc_i) |
488 |
|
{ |
489 |
1322759 |
Node n = *eqc_i; |
490 |
1322553 |
Trace("model-builder") << " Processing Term: " << n << endl; |
491 |
|
|
492 |
|
// For each term n in this equivalence class, below we register its |
493 |
|
// assignable subterms, compute whether it is a constant or assigned |
494 |
|
// representative, then if we don't have a constant representative, |
495 |
|
// compute information regarding how we will assign values. |
496 |
|
|
497 |
|
// (1) Add assignable subterms, which ensures that e.g. models for |
498 |
|
// uninterpreted functions take into account all subterms in the |
499 |
|
// equality engine of the model |
500 |
1322553 |
addAssignableSubterms(n, tm, assignableCache); |
501 |
|
// model-specific processing of the term |
502 |
1322553 |
tm->addTermInternal(n); |
503 |
|
|
504 |
|
// (2) Record constant representative or assign representative, if |
505 |
|
// applicable |
506 |
1641716 |
if (n.isConst()) |
507 |
|
{ |
508 |
319163 |
Assert(constRep.isNull()); |
509 |
319163 |
constRep = n; |
510 |
638326 |
Trace("model-builder") |
511 |
319163 |
<< " ConstRep( " << eqc << " ) = " << constRep << std::endl; |
512 |
|
// if we have a constant representative, nothing else matters |
513 |
319163 |
continue; |
514 |
|
} |
515 |
|
|
516 |
|
// If we don't have a constant rep, check if this is an assigned rep. |
517 |
1003390 |
itm = tm->d_reps.find(n); |
518 |
1003390 |
if (itm != tm->d_reps.end()) |
519 |
|
{ |
520 |
|
// Notice that this equivalence class may contain multiple terms that |
521 |
|
// were specified as being a representative, since e.g. datatypes may |
522 |
|
// assert representative for two constructor terms that are not in the |
523 |
|
// care graph and are merged during collectModeInfo due to equality |
524 |
|
// information from another theory. We overwrite the value of rep in |
525 |
|
// these cases here. |
526 |
14504 |
rep = itm->second; |
527 |
29008 |
Trace("model-builder") |
528 |
14504 |
<< " Rep( " << eqc << " ) = " << rep << std::endl; |
529 |
|
} |
530 |
|
|
531 |
|
// (3) Finally, process assignable information |
532 |
1606127 |
if (!isAssignable(n)) |
533 |
|
{ |
534 |
602737 |
evaluable = true; |
535 |
|
// expressions that are not assignable should not be given assignment |
536 |
|
// exclusion sets |
537 |
602737 |
Assert(!tm->getAssignmentExclusionSet(n, esetGroup, eset)); |
538 |
602737 |
continue; |
539 |
|
} |
540 |
400653 |
assignable = true; |
541 |
400653 |
if (!computeAssigners) |
542 |
|
{ |
543 |
|
// we don't compute assigners, skip |
544 |
400412 |
continue; |
545 |
|
} |
546 |
|
// process the assignment exclusion set for term n |
547 |
|
// was it processed based on a master exclusion group (see |
548 |
|
// eqcToAssignerMaster)? |
549 |
276 |
if (processedExcSet.find(n) != processedExcSet.end()) |
550 |
|
{ |
551 |
|
// Should not have two assignment exclusion sets for the same |
552 |
|
// equivalence class |
553 |
35 |
Assert(!hasESet); |
554 |
35 |
Assert(eqcToAssignerMaster.find(eqc) != eqcToAssignerMaster.end()); |
555 |
|
// already processed as a slave term |
556 |
35 |
hasESet = true; |
557 |
35 |
continue; |
558 |
|
} |
559 |
|
// was it assigned one? |
560 |
206 |
if (tm->getAssignmentExclusionSet(n, esetGroup, eset)) |
561 |
|
{ |
562 |
|
// Should not have two assignment exclusion sets for the same |
563 |
|
// equivalence class |
564 |
10 |
Assert(!hasESet); |
565 |
10 |
foundESet = true; |
566 |
10 |
hasESet = true; |
567 |
|
} |
568 |
|
} |
569 |
|
|
570 |
|
// finished traversing the equality engine |
571 |
855271 |
TypeNode eqct = eqc.getType(); |
572 |
|
// count the number of equivalence classes of sorts in finite model finding |
573 |
587217 |
if (options::finiteModelFind()) |
574 |
|
{ |
575 |
21417 |
if (eqct.isSort()) |
576 |
|
{ |
577 |
3615 |
eqc_usort_count[eqct]++; |
578 |
|
} |
579 |
|
} |
580 |
|
// Assign representative for this equivalence class |
581 |
906380 |
if (!constRep.isNull()) |
582 |
|
{ |
583 |
|
// Theories should not specify a rep if there is already a constant in the |
584 |
|
// equivalence class. However, it may be the case that the representative |
585 |
|
// specified by a theory may be merged with a constant based on equality |
586 |
|
// information from another class. Thus, rep may be non-null here. |
587 |
|
// Regardless, we assign constRep as the representative here. |
588 |
319163 |
assignConstantRep(tm, eqc, constRep); |
589 |
319163 |
typeConstSet.add(eqct.getBaseType(), constRep); |
590 |
319163 |
continue; |
591 |
|
} |
592 |
268054 |
else if (!rep.isNull()) |
593 |
|
{ |
594 |
14452 |
assertedReps[eqc] = rep; |
595 |
14452 |
typeRepSet.add(eqct.getBaseType(), eqc); |
596 |
28904 |
std::unordered_set<TypeNode> visiting; |
597 |
14452 |
addToTypeList(eqct.getBaseType(), type_list, visiting); |
598 |
|
} |
599 |
|
else |
600 |
|
{ |
601 |
253602 |
typeNoRepSet.add(eqct, eqc); |
602 |
507204 |
std::unordered_set<TypeNode> visiting; |
603 |
253602 |
addToTypeList(eqct, type_list, visiting); |
604 |
|
} |
605 |
|
|
606 |
268054 |
if (assignable) |
607 |
|
{ |
608 |
27404 |
assignableEqc.insert(eqc); |
609 |
|
} |
610 |
268054 |
if (evaluable) |
611 |
|
{ |
612 |
253811 |
evaluableEqc.insert(eqc); |
613 |
|
} |
614 |
|
// If we found an assignment exclusion set, we construct a new assigner |
615 |
|
// object. |
616 |
268054 |
if (foundESet) |
617 |
|
{ |
618 |
|
// we don't accept assignment exclusion sets for evaluable eqc |
619 |
10 |
Assert(!evaluable); |
620 |
|
// construct the assigner |
621 |
10 |
Assigner& a = eqcToAssigner[eqc]; |
622 |
|
// Take the representatives of each term in the assignment exclusion |
623 |
|
// set, which ensures we can look up their value in d_constReps later. |
624 |
20 |
std::vector<Node> aes; |
625 |
30 |
for (const Node& e : eset) |
626 |
|
{ |
627 |
|
// Should only supply terms that occur in the model or constants |
628 |
|
// in assignment exclusion sets. |
629 |
20 |
Assert(tm->hasTerm(e) || e.isConst()); |
630 |
40 |
Node er = tm->hasTerm(e) ? tm->getRepresentative(e) : e; |
631 |
20 |
aes.push_back(er); |
632 |
|
} |
633 |
|
// initialize |
634 |
10 |
a.initialize(eqc.getType(), &tep, aes); |
635 |
|
// all others in the group are slaves of this |
636 |
55 |
for (const Node& g : esetGroup) |
637 |
|
{ |
638 |
45 |
Assert(isAssignable(g)); |
639 |
45 |
if (!tm->hasTerm(g)) |
640 |
|
{ |
641 |
|
// Ignore those that aren't in the model, in the case the user |
642 |
|
// has supplied an assignment exclusion set to a variable not in |
643 |
|
// the model. |
644 |
|
continue; |
645 |
|
} |
646 |
90 |
Node gr = tm->getRepresentative(g); |
647 |
45 |
if (gr != eqc) |
648 |
|
{ |
649 |
35 |
eqcToAssignerMaster[gr] = eqc; |
650 |
|
// remember that this term has been processed |
651 |
35 |
processedExcSet.insert(g); |
652 |
|
} |
653 |
|
} |
654 |
|
} |
655 |
|
} |
656 |
|
|
657 |
|
// Now finished initialization |
658 |
|
|
659 |
|
// Compute type enumerator properties. This code ensures we do not |
660 |
|
// enumerate terms that have uninterpreted constants that violate the |
661 |
|
// bounds imposed by finite model finding. For example, if finite |
662 |
|
// model finding insists that there are only 2 values { U1, U2 } of type U, |
663 |
|
// then the type enumerator for list of U should enumerate: |
664 |
|
// nil, (cons U1 nil), (cons U2 nil), (cons U1 (cons U1 nil)), ... |
665 |
|
// instead of enumerating (cons U3 nil). |
666 |
16395 |
if (options::finiteModelFind()) |
667 |
|
{ |
668 |
937 |
tep.d_fixed_usort_card = true; |
669 |
2887 |
for (std::map<TypeNode, unsigned>::iterator it = eqc_usort_count.begin(); |
670 |
2887 |
it != eqc_usort_count.end(); |
671 |
|
++it) |
672 |
|
{ |
673 |
3900 |
Trace("model-builder") << "Fixed bound (#eqc) for " << it->first << " : " |
674 |
1950 |
<< it->second << std::endl; |
675 |
1950 |
tep.d_fixed_card[it->first] = Integer(it->second); |
676 |
|
} |
677 |
937 |
typeConstSet.setTypeEnumeratorProperties(&tep); |
678 |
|
} |
679 |
|
|
680 |
|
// Need to ensure that each EC has a constant representative. |
681 |
|
|
682 |
16395 |
Trace("model-builder") << "Processing EC's..." << std::endl; |
683 |
|
|
684 |
16395 |
TypeSet::iterator it; |
685 |
16395 |
vector<TypeNode>::iterator type_it; |
686 |
16395 |
set<Node>::iterator i, i2; |
687 |
16395 |
bool changed, unassignedAssignable, assignOne = false; |
688 |
32790 |
set<TypeNode> evaluableSet; |
689 |
|
|
690 |
|
// Double-fixed-point loop |
691 |
|
// Outer loop handles a special corner case (see code at end of loop for |
692 |
|
// details) |
693 |
|
for (;;) |
694 |
|
{ |
695 |
|
// Inner fixed-point loop: we are trying to learn constant values for every |
696 |
|
// EC. Each time through this loop, we process all of the |
697 |
|
// types by type and may learn some new EC values. EC's in one type may |
698 |
|
// depend on EC's in another type, so we need a fixed-point loop |
699 |
|
// to ensure that we learn as many EC values as possible |
700 |
38505 |
do |
701 |
|
{ |
702 |
38505 |
changed = false; |
703 |
38505 |
unassignedAssignable = false; |
704 |
38505 |
evaluableSet.clear(); |
705 |
|
|
706 |
|
// Iterate over all types we've seen |
707 |
182666 |
for (type_it = type_list.begin(); type_it != type_list.end(); ++type_it) |
708 |
|
{ |
709 |
288322 |
TypeNode t = *type_it; |
710 |
288322 |
TypeNode tb = t.getBaseType(); |
711 |
144161 |
set<Node>* noRepSet = typeNoRepSet.getSet(t); |
712 |
|
|
713 |
|
// 1. Try to evaluate the EC's in this type |
714 |
144161 |
if (noRepSet != NULL && !noRepSet->empty()) |
715 |
|
{ |
716 |
64448 |
Trace("model-builder") << " Eval phase, working on type: " << t |
717 |
32224 |
<< endl; |
718 |
|
bool evaluable; |
719 |
32224 |
d_normalizedCache.clear(); |
720 |
699716 |
for (i = noRepSet->begin(); i != noRepSet->end();) |
721 |
|
{ |
722 |
667492 |
i2 = i; |
723 |
667492 |
++i; |
724 |
1334984 |
Trace("model-builder-debug") << "Look at eqc : " << (*i2) |
725 |
667492 |
<< std::endl; |
726 |
1334984 |
Node normalized; |
727 |
|
// only possible to normalize if we are evaluable |
728 |
667492 |
evaluable = evaluableEqc.find(*i2) != evaluableEqc.end(); |
729 |
667492 |
if (evaluable) |
730 |
|
{ |
731 |
243121 |
normalized = evaluateEqc(tm, *i2); |
732 |
|
} |
733 |
667492 |
if (!normalized.isNull()) |
734 |
|
{ |
735 |
238207 |
Assert(normalized.isConst()); |
736 |
238207 |
typeConstSet.add(tb, normalized); |
737 |
238207 |
assignConstantRep(tm, *i2, normalized); |
738 |
476414 |
Trace("model-builder") << " Eval: Setting constant rep of " |
739 |
238207 |
<< (*i2) << " to " << normalized << endl; |
740 |
238207 |
changed = true; |
741 |
238207 |
noRepSet->erase(i2); |
742 |
|
} |
743 |
|
else |
744 |
|
{ |
745 |
429285 |
if (evaluable) |
746 |
|
{ |
747 |
4914 |
evaluableSet.insert(tb); |
748 |
|
} |
749 |
|
// If assignable, remember there is an equivalence class that is |
750 |
|
// not assigned and assignable. |
751 |
429285 |
if (assignableEqc.find(*i2) != assignableEqc.end()) |
752 |
|
{ |
753 |
424431 |
unassignedAssignable = true; |
754 |
|
} |
755 |
|
} |
756 |
|
} |
757 |
|
} |
758 |
|
|
759 |
|
// 2. Normalize any non-const representative terms for this type |
760 |
144161 |
set<Node>* repSet = typeRepSet.getSet(t); |
761 |
144161 |
if (repSet != NULL && !repSet->empty()) |
762 |
|
{ |
763 |
59346 |
Trace("model-builder") |
764 |
29673 |
<< " Normalization phase, working on type: " << t << endl; |
765 |
29673 |
d_normalizedCache.clear(); |
766 |
249547 |
for (i = repSet->begin(); i != repSet->end();) |
767 |
|
{ |
768 |
219874 |
Assert(assertedReps.find(*i) != assertedReps.end()); |
769 |
439748 |
Node rep = assertedReps[*i]; |
770 |
439748 |
Node normalized = normalize(tm, rep, false); |
771 |
439748 |
Trace("model-builder") << " Normalizing rep (" << rep |
772 |
219874 |
<< "), normalized to (" << normalized << ")" |
773 |
219874 |
<< endl; |
774 |
219874 |
if (normalized.isConst()) |
775 |
|
{ |
776 |
14452 |
changed = true; |
777 |
14452 |
typeConstSet.add(tb, normalized); |
778 |
14452 |
assignConstantRep(tm, *i, normalized); |
779 |
14452 |
assertedReps.erase(*i); |
780 |
14452 |
i2 = i; |
781 |
14452 |
++i; |
782 |
14452 |
repSet->erase(i2); |
783 |
|
} |
784 |
|
else |
785 |
|
{ |
786 |
205422 |
if (normalized != rep) |
787 |
|
{ |
788 |
3837 |
assertedReps[*i] = normalized; |
789 |
3837 |
changed = true; |
790 |
|
} |
791 |
205422 |
++i; |
792 |
|
} |
793 |
|
} |
794 |
|
} |
795 |
|
} |
796 |
|
} while (changed); |
797 |
|
|
798 |
26041 |
if (!unassignedAssignable) |
799 |
|
{ |
800 |
16395 |
break; |
801 |
|
} |
802 |
|
|
803 |
|
// 3. Assign unassigned assignable EC's using type enumeration - assign a |
804 |
|
// value *different* from all other EC's if the type is infinite |
805 |
|
// Assign first value from type enumerator otherwise - for finite types, we |
806 |
|
// rely on polite framework to ensure that EC's that have to be |
807 |
|
// different are different. |
808 |
|
|
809 |
|
// Only make assignments on a type if: |
810 |
|
// 1. there are no terms that share the same base type with un-normalized |
811 |
|
// representatives |
812 |
|
// 2. there are no terms that share teh same base type that are unevaluated |
813 |
|
// evaluable terms |
814 |
|
// Alternatively, if 2 or 3 don't hold but we are in a special |
815 |
|
// deadlock-breaking mode where assignOne is true, go ahead and make one |
816 |
|
// assignment |
817 |
9646 |
changed = false; |
818 |
|
// must iterate over the ordered type list to ensure that we do not |
819 |
|
// enumerate values with subterms |
820 |
|
// having types that we are currently enumerating (when possible) |
821 |
|
// for example, this ensures we enumerate uninterpreted sort U before (List |
822 |
|
// of U) and (Array U U) |
823 |
|
// however, it does not break cyclic type dependencies for mutually |
824 |
|
// recursive datatypes, but this is handled |
825 |
|
// by recording all subterms of enumerated values in TypeSet::addSubTerms. |
826 |
75474 |
for (type_it = type_list.begin(); type_it != type_list.end(); ++type_it) |
827 |
|
{ |
828 |
73344 |
TypeNode t = *type_it; |
829 |
|
// continue if there are no more equivalence classes of this type to |
830 |
|
// assign |
831 |
65828 |
std::set<Node>* noRepSetPtr = typeNoRepSet.getSet(t); |
832 |
65828 |
if (noRepSetPtr == NULL) |
833 |
|
{ |
834 |
10877 |
continue; |
835 |
|
} |
836 |
54951 |
set<Node>& noRepSet = *noRepSetPtr; |
837 |
54951 |
if (noRepSet.empty()) |
838 |
|
{ |
839 |
40466 |
continue; |
840 |
|
} |
841 |
|
|
842 |
|
// get properties of this type |
843 |
14485 |
bool isCorecursive = false; |
844 |
14485 |
if (t.isDatatype()) |
845 |
|
{ |
846 |
10373 |
const DType& dt = t.getDType(); |
847 |
20746 |
isCorecursive = dt.isCodatatype() |
848 |
31119 |
&& (!isFiniteType(t) || dt.isRecursiveSingleton(t)); |
849 |
|
} |
850 |
|
#ifdef CVC5_ASSERTIONS |
851 |
14485 |
bool isUSortFiniteRestricted = false; |
852 |
14485 |
if (options::finiteModelFind()) |
853 |
|
{ |
854 |
12056 |
isUSortFiniteRestricted = !t.isSort() && involvesUSort(t); |
855 |
|
} |
856 |
|
#endif |
857 |
|
|
858 |
22001 |
TypeNode tb = t.getBaseType(); |
859 |
14485 |
if (!assignOne) |
860 |
|
{ |
861 |
10775 |
set<Node>* repSet = typeRepSet.getSet(tb); |
862 |
10775 |
if (repSet != NULL && !repSet->empty()) |
863 |
|
{ |
864 |
6442 |
continue; |
865 |
|
} |
866 |
4333 |
if (evaluableSet.find(tb) != evaluableSet.end()) |
867 |
|
{ |
868 |
527 |
continue; |
869 |
|
} |
870 |
|
} |
871 |
15032 |
Trace("model-builder") << " Assign phase, working on type: " << t |
872 |
7516 |
<< endl; |
873 |
|
bool assignable, evaluable CVC5_UNUSED; |
874 |
7516 |
std::map<Node, Assigner>::iterator itAssigner; |
875 |
7516 |
std::map<Node, Node>::iterator itAssignerM; |
876 |
7516 |
set<Node>* repSet = typeRepSet.getSet(t); |
877 |
18132 |
for (i = noRepSet.begin(); i != noRepSet.end();) |
878 |
|
{ |
879 |
14326 |
i2 = i; |
880 |
14326 |
++i; |
881 |
|
// check whether it has an assigner object |
882 |
14326 |
itAssignerM = eqcToAssignerMaster.find(*i2); |
883 |
14326 |
if (itAssignerM != eqcToAssignerMaster.end()) |
884 |
|
{ |
885 |
|
// Take the master's assigner. Notice we don't care which order |
886 |
|
// equivalence classes are assigned. For instance, the master can |
887 |
|
// be assigned after one of its slaves. |
888 |
35 |
itAssigner = eqcToAssigner.find(itAssignerM->second); |
889 |
|
} |
890 |
|
else |
891 |
|
{ |
892 |
14291 |
itAssigner = eqcToAssigner.find(*i2); |
893 |
|
} |
894 |
14326 |
if (itAssigner != eqcToAssigner.end()) |
895 |
|
{ |
896 |
45 |
assignable = isAssignerActive(tm, itAssigner->second); |
897 |
|
} |
898 |
|
else |
899 |
|
{ |
900 |
14281 |
assignable = assignableEqc.find(*i2) != assignableEqc.end(); |
901 |
|
} |
902 |
14326 |
evaluable = evaluableEqc.find(*i2) != evaluableEqc.end(); |
903 |
28652 |
Trace("model-builder-debug") |
904 |
14326 |
<< " eqc " << *i2 << " is assignable=" << assignable |
905 |
14326 |
<< ", evaluable=" << evaluable << std::endl; |
906 |
14326 |
if (assignable) |
907 |
|
{ |
908 |
14252 |
Assert(!evaluable || assignOne); |
909 |
|
// this assertion ensures that if we are assigning to a term of |
910 |
|
// Boolean type, then the term must be assignable. |
911 |
|
// Note we only assign to terms of Boolean type if the term occurs in |
912 |
|
// a singleton equivalence class; otherwise the term would have been |
913 |
|
// in the equivalence class of true or false and would not need |
914 |
|
// assigning. |
915 |
14252 |
Assert(!t.isBoolean() || isAssignable(*i2)); |
916 |
24794 |
Node n; |
917 |
14252 |
if (itAssigner != eqcToAssigner.end()) |
918 |
|
{ |
919 |
90 |
Trace("model-builder-debug") |
920 |
45 |
<< "Get value from assigner for finite type..." << std::endl; |
921 |
|
// if it has an assigner, get the value from the assigner. |
922 |
45 |
n = itAssigner->second.getNextAssignment(); |
923 |
45 |
Assert(!n.isNull()); |
924 |
|
} |
925 |
14207 |
else if (t.isSort() || !isFiniteType(t)) |
926 |
|
{ |
927 |
|
// If its interpreted as infinite, we get a fresh value that does |
928 |
|
// not occur in the model. |
929 |
|
// Note we also consider uninterpreted sorts to be infinite here |
930 |
|
// regardless of whether the cardinality class of t is |
931 |
|
// CardinalityClass::INTERPRETED_FINITE. |
932 |
|
// This is required because the UF solver does not explicitly |
933 |
|
// assign uninterpreted constants to equivalence classes in its |
934 |
|
// collectModelValues method. Doing so would have the same effect |
935 |
|
// as running the code in this case. |
936 |
|
bool success; |
937 |
6 |
do |
938 |
|
{ |
939 |
28136 |
Trace("model-builder-debug") << "Enumerate term of type " << t |
940 |
14068 |
<< std::endl; |
941 |
14068 |
n = typeConstSet.nextTypeEnum(t, true); |
942 |
|
//--- AJR: this code checks whether n is a legal value |
943 |
14068 |
Assert(!n.isNull()); |
944 |
14068 |
success = true; |
945 |
28136 |
Trace("model-builder-debug") << "Check if excluded : " << n |
946 |
14068 |
<< std::endl; |
947 |
|
#ifdef CVC5_ASSERTIONS |
948 |
14068 |
if (isUSortFiniteRestricted) |
949 |
|
{ |
950 |
|
// must not involve uninterpreted constants beyond cardinality |
951 |
|
// bound (which assumed to coincide with #eqc) |
952 |
|
// this is just an assertion now, since TypeEnumeratorProperties |
953 |
|
// should ensure that only legal values are enumerated wrt this |
954 |
|
// constraint. |
955 |
10994 |
std::map<Node, bool> visited; |
956 |
5497 |
success = !isExcludedUSortValue(eqc_usort_count, n, visited); |
957 |
5497 |
if (!success) |
958 |
|
{ |
959 |
|
Trace("model-builder") |
960 |
|
<< "Excluded value for " << t << " : " << n |
961 |
|
<< " due to out of range uninterpreted constant." |
962 |
|
<< std::endl; |
963 |
|
} |
964 |
5497 |
Assert(success); |
965 |
|
} |
966 |
|
#endif |
967 |
14068 |
if (success && isCorecursive) |
968 |
|
{ |
969 |
51 |
if (repSet != NULL && !repSet->empty()) |
970 |
|
{ |
971 |
|
// in the case of codatatypes, check if it is in the set of |
972 |
|
// values that we cannot assign |
973 |
43 |
success = !isExcludedCdtValue(n, repSet, assertedReps, *i2); |
974 |
43 |
if (!success) |
975 |
|
{ |
976 |
12 |
Trace("model-builder") |
977 |
6 |
<< "Excluded value : " << n |
978 |
6 |
<< " due to alpha-equivalent codatatype expression." |
979 |
6 |
<< std::endl; |
980 |
|
} |
981 |
|
} |
982 |
|
} |
983 |
|
//--- |
984 |
14068 |
} while (!success); |
985 |
14062 |
Assert(!n.isNull()); |
986 |
|
} |
987 |
|
else |
988 |
|
{ |
989 |
290 |
Trace("model-builder-debug") |
990 |
145 |
<< "Get first value from finite type..." << std::endl; |
991 |
|
// Otherwise, we get the first value from the type enumerator. |
992 |
290 |
TypeEnumerator te(t); |
993 |
145 |
n = *te; |
994 |
|
} |
995 |
14252 |
Trace("model-builder-debug") << "...got " << n << std::endl; |
996 |
14252 |
assignConstantRep(tm, *i2, n); |
997 |
14252 |
changed = true; |
998 |
14252 |
noRepSet.erase(i2); |
999 |
14252 |
if (assignOne) |
1000 |
|
{ |
1001 |
3710 |
assignOne = false; |
1002 |
3710 |
break; |
1003 |
|
} |
1004 |
|
} |
1005 |
|
} |
1006 |
|
} |
1007 |
|
|
1008 |
|
// Corner case - I'm not sure this can even happen - but it's theoretically |
1009 |
|
// possible to have a cyclical dependency |
1010 |
|
// in EC assignment/evaluation, e.g. EC1 = {a, b + 1}; EC2 = {b, a - 1}. In |
1011 |
|
// this case, neither one will get assigned because we are waiting |
1012 |
|
// to be able to evaluate. But we will never be able to evaluate because |
1013 |
|
// the variables that need to be assigned are in |
1014 |
|
// these same EC's. In this case, repeat the whole fixed-point computation |
1015 |
|
// with the difference that the first EC |
1016 |
|
// that has both assignable and evaluable expressions will get assigned. |
1017 |
9646 |
if (!changed) |
1018 |
|
{ |
1019 |
3710 |
Assert(!assignOne); // check for infinite loop! |
1020 |
3710 |
assignOne = true; |
1021 |
|
} |
1022 |
9646 |
} |
1023 |
|
|
1024 |
|
#ifdef CVC5_ASSERTIONS |
1025 |
|
// Assert that all representatives have been converted to constants |
1026 |
24739 |
for (it = typeRepSet.begin(); it != typeRepSet.end(); ++it) |
1027 |
|
{ |
1028 |
8344 |
set<Node>& repSet = TypeSet::getSet(it); |
1029 |
8344 |
if (!repSet.empty()) |
1030 |
|
{ |
1031 |
|
Trace("model-builder") << "***Non-empty repSet, size = " << repSet.size() |
1032 |
|
<< ", first = " << *(repSet.begin()) << endl; |
1033 |
|
Assert(false); |
1034 |
|
} |
1035 |
|
} |
1036 |
|
#endif /* CVC5_ASSERTIONS */ |
1037 |
|
|
1038 |
16395 |
Trace("model-builder") << "Copy representatives to model..." << std::endl; |
1039 |
16395 |
tm->d_reps.clear(); |
1040 |
16395 |
std::map<Node, Node>::iterator itMap; |
1041 |
602469 |
for (itMap = d_constantReps.begin(); itMap != d_constantReps.end(); ++itMap) |
1042 |
|
{ |
1043 |
586074 |
tm->d_reps[itMap->first] = itMap->second; |
1044 |
586074 |
tm->d_rep_set.add(itMap->second.getType(), itMap->second); |
1045 |
|
} |
1046 |
|
|
1047 |
16395 |
Trace("model-builder") << "Make sure ECs have reps..." << std::endl; |
1048 |
|
// Make sure every EC has a rep |
1049 |
16395 |
for (itMap = assertedReps.begin(); itMap != assertedReps.end(); ++itMap) |
1050 |
|
{ |
1051 |
|
tm->d_reps[itMap->first] = itMap->second; |
1052 |
|
tm->d_rep_set.add(itMap->second.getType(), itMap->second); |
1053 |
|
} |
1054 |
31773 |
for (it = typeNoRepSet.begin(); it != typeNoRepSet.end(); ++it) |
1055 |
|
{ |
1056 |
15378 |
set<Node>& noRepSet = TypeSet::getSet(it); |
1057 |
16521 |
for (const Node& node : noRepSet) |
1058 |
|
{ |
1059 |
1143 |
tm->d_reps[node] = node; |
1060 |
1143 |
tm->d_rep_set.add(node.getType(), node); |
1061 |
|
} |
1062 |
|
} |
1063 |
|
|
1064 |
|
// modelBuilder-specific initialization |
1065 |
16395 |
if (!processBuildModel(tm)) |
1066 |
|
{ |
1067 |
|
Trace("model-builder") |
1068 |
|
<< "TheoryEngineModelBuilder: fail process build model." << std::endl; |
1069 |
|
return false; |
1070 |
|
} |
1071 |
16395 |
Trace("model-builder") << "TheoryEngineModelBuilder: success" << std::endl; |
1072 |
16395 |
return true; |
1073 |
|
} |
1074 |
|
|
1075 |
629 |
void TheoryEngineModelBuilder::postProcessModel(bool incomplete, TheoryModel* m) |
1076 |
|
{ |
1077 |
|
// if we are incomplete, there is no guarantee on the model. |
1078 |
|
// thus, we do not check the model here. |
1079 |
629 |
if (incomplete) |
1080 |
|
{ |
1081 |
28 |
return; |
1082 |
|
} |
1083 |
601 |
Assert(m != nullptr); |
1084 |
|
// debug-check the model if the checkModels() is enabled. |
1085 |
601 |
if (options::debugCheckModels()) |
1086 |
|
{ |
1087 |
260 |
debugCheckModel(m); |
1088 |
|
} |
1089 |
|
} |
1090 |
|
|
1091 |
260 |
void TheoryEngineModelBuilder::debugCheckModel(TheoryModel* tm) |
1092 |
|
{ |
1093 |
260 |
if (tm->hasApproximations()) |
1094 |
|
{ |
1095 |
|
// models with approximations may fail the assertions below |
1096 |
|
return; |
1097 |
|
} |
1098 |
260 |
eq::EqClassesIterator eqcs_i = eq::EqClassesIterator(tm->d_equalityEngine); |
1099 |
260 |
std::map<Node, Node>::iterator itMap; |
1100 |
|
// Check that every term evaluates to its representative in the model |
1101 |
3982 |
for (eqcs_i = eq::EqClassesIterator(tm->d_equalityEngine); |
1102 |
3982 |
!eqcs_i.isFinished(); |
1103 |
|
++eqcs_i) |
1104 |
|
{ |
1105 |
|
// eqc is the equivalence class representative |
1106 |
7444 |
Node eqc = (*eqcs_i); |
1107 |
|
// get the representative |
1108 |
7444 |
Node rep = tm->getRepresentative(eqc); |
1109 |
3722 |
if (!rep.isConst() && eqc.getType().isBoolean()) |
1110 |
|
{ |
1111 |
|
// if Boolean, it does not necessarily have a constant representative, use |
1112 |
|
// get value instead |
1113 |
|
rep = tm->getValue(eqc); |
1114 |
|
AlwaysAssert(rep.isConst()); |
1115 |
|
} |
1116 |
3722 |
eq::EqClassIterator eqc_i = eq::EqClassIterator(eqc, tm->d_equalityEngine); |
1117 |
37970 |
for (; !eqc_i.isFinished(); ++eqc_i) |
1118 |
|
{ |
1119 |
34248 |
Node n = *eqc_i; |
1120 |
|
static int repCheckInstance = 0; |
1121 |
17124 |
++repCheckInstance; |
1122 |
34248 |
AlwaysAssert(rep.getType().isSubtypeOf(n.getType())) |
1123 |
17124 |
<< "Representative " << rep << " of " << n |
1124 |
17124 |
<< " violates type constraints (" << rep.getType() << " and " |
1125 |
17124 |
<< n.getType() << ")"; |
1126 |
|
// non-linear mult is not necessarily accurate wrt getValue |
1127 |
17124 |
if (n.getKind() != kind::NONLINEAR_MULT) |
1128 |
|
{ |
1129 |
17106 |
if (tm->getValue(*eqc_i) != rep) |
1130 |
|
{ |
1131 |
|
std::stringstream err; |
1132 |
|
err << "Failed representative check:" << std::endl |
1133 |
|
<< "( " << repCheckInstance << ") " |
1134 |
|
<< "n: " << n << endl |
1135 |
|
<< "getValue(n): " << tm->getValue(n) << std::endl |
1136 |
|
<< "rep: " << rep << std::endl; |
1137 |
|
AlwaysAssert(tm->getValue(*eqc_i) == rep) << err.str(); |
1138 |
|
} |
1139 |
|
} |
1140 |
|
} |
1141 |
|
} |
1142 |
|
|
1143 |
|
// builder-specific debugging |
1144 |
260 |
debugModel(tm); |
1145 |
|
} |
1146 |
|
|
1147 |
533007 |
Node TheoryEngineModelBuilder::normalize(TheoryModel* m, TNode r, bool evalOnly) |
1148 |
|
{ |
1149 |
533007 |
std::map<Node, Node>::iterator itMap = d_constantReps.find(r); |
1150 |
533007 |
if (itMap != d_constantReps.end()) |
1151 |
|
{ |
1152 |
|
return (*itMap).second; |
1153 |
|
} |
1154 |
533007 |
NodeMap::iterator it = d_normalizedCache.find(r); |
1155 |
533007 |
if (it != d_normalizedCache.end()) |
1156 |
|
{ |
1157 |
18230 |
return (*it).second; |
1158 |
|
} |
1159 |
514777 |
Trace("model-builder-debug") << "do normalize on " << r << std::endl; |
1160 |
1029554 |
Node retNode = r; |
1161 |
514777 |
if (r.getNumChildren() > 0) |
1162 |
|
{ |
1163 |
1018076 |
std::vector<Node> children; |
1164 |
509038 |
if (r.getMetaKind() == kind::metakind::PARAMETERIZED) |
1165 |
|
{ |
1166 |
239091 |
children.push_back(r.getOperator()); |
1167 |
|
} |
1168 |
509038 |
bool childrenConst = true; |
1169 |
1557858 |
for (size_t i = 0; i < r.getNumChildren(); ++i) |
1170 |
|
{ |
1171 |
2097640 |
Node ri = r[i]; |
1172 |
1048820 |
bool recurse = true; |
1173 |
1048820 |
if (!ri.isConst()) |
1174 |
|
{ |
1175 |
726302 |
if (m->d_equalityEngine->hasTerm(ri)) |
1176 |
|
{ |
1177 |
704281 |
itMap = |
1178 |
1408562 |
d_constantReps.find(m->d_equalityEngine->getRepresentative(ri)); |
1179 |
704281 |
if (itMap != d_constantReps.end()) |
1180 |
|
{ |
1181 |
390217 |
ri = (*itMap).second; |
1182 |
390217 |
Trace("model-builder-debug") << i << ": const child " << ri << std::endl; |
1183 |
390217 |
recurse = false; |
1184 |
|
} |
1185 |
314064 |
else if (!evalOnly) |
1186 |
|
{ |
1187 |
267619 |
recurse = false; |
1188 |
267619 |
Trace("model-builder-debug") << i << ": keep " << ri << std::endl; |
1189 |
|
} |
1190 |
|
} |
1191 |
|
else |
1192 |
|
{ |
1193 |
22021 |
Trace("model-builder-debug") << i << ": no hasTerm " << ri << std::endl; |
1194 |
|
} |
1195 |
726302 |
if (recurse) |
1196 |
|
{ |
1197 |
68466 |
ri = normalize(m, ri, evalOnly); |
1198 |
|
} |
1199 |
726302 |
if (!ri.isConst()) |
1200 |
|
{ |
1201 |
272472 |
childrenConst = false; |
1202 |
|
} |
1203 |
|
} |
1204 |
1048820 |
children.push_back(ri); |
1205 |
|
} |
1206 |
509038 |
retNode = NodeManager::currentNM()->mkNode(r.getKind(), children); |
1207 |
509038 |
if (childrenConst) |
1208 |
|
{ |
1209 |
299031 |
retNode = Rewriter::rewrite(retNode); |
1210 |
|
} |
1211 |
|
} |
1212 |
514777 |
d_normalizedCache[r] = retNode; |
1213 |
514777 |
return retNode; |
1214 |
|
} |
1215 |
|
|
1216 |
3395 |
bool TheoryEngineModelBuilder::preProcessBuildModel(TheoryModel* m) |
1217 |
|
{ |
1218 |
3395 |
return true; |
1219 |
|
} |
1220 |
|
|
1221 |
16395 |
bool TheoryEngineModelBuilder::processBuildModel(TheoryModel* m) |
1222 |
|
{ |
1223 |
16395 |
if (m->areFunctionValuesEnabled()) |
1224 |
|
{ |
1225 |
16395 |
assignFunctions(m); |
1226 |
|
} |
1227 |
16395 |
return true; |
1228 |
|
} |
1229 |
|
|
1230 |
4485 |
void TheoryEngineModelBuilder::assignFunction(TheoryModel* m, Node f) |
1231 |
|
{ |
1232 |
4485 |
Assert(!logicInfo().isHigherOrder()); |
1233 |
8970 |
uf::UfModelTree ufmt(f); |
1234 |
8970 |
Node default_v; |
1235 |
24804 |
for (size_t i = 0; i < m->d_uf_terms[f].size(); i++) |
1236 |
|
{ |
1237 |
40638 |
Node un = m->d_uf_terms[f][i]; |
1238 |
40638 |
vector<TNode> children; |
1239 |
20319 |
children.push_back(f); |
1240 |
20319 |
Trace("model-builder-debug") << " process term : " << un << std::endl; |
1241 |
104965 |
for (size_t j = 0; j < un.getNumChildren(); ++j) |
1242 |
|
{ |
1243 |
169292 |
Node rc = m->getRepresentative(un[j]); |
1244 |
169292 |
Trace("model-builder-debug2") << " get rep : " << un[j] << " returned " |
1245 |
84646 |
<< rc << std::endl; |
1246 |
84646 |
Assert(rc.isConst()); |
1247 |
84646 |
children.push_back(rc); |
1248 |
|
} |
1249 |
40638 |
Node simp = NodeManager::currentNM()->mkNode(un.getKind(), children); |
1250 |
40638 |
Node v = m->getRepresentative(un); |
1251 |
40638 |
Trace("model-builder") << " Setting (" << simp << ") to (" << v << ")" |
1252 |
20319 |
<< endl; |
1253 |
20319 |
ufmt.setValue(m, simp, v); |
1254 |
20319 |
default_v = v; |
1255 |
|
} |
1256 |
4485 |
if (default_v.isNull()) |
1257 |
|
{ |
1258 |
|
// choose default value from model if none exists |
1259 |
|
TypeEnumerator te(f.getType().getRangeType()); |
1260 |
|
default_v = (*te); |
1261 |
|
} |
1262 |
4485 |
ufmt.setDefaultValue(m, default_v); |
1263 |
4485 |
bool condenseFuncValues = options::condenseFunctionValues(); |
1264 |
4485 |
if (condenseFuncValues) |
1265 |
|
{ |
1266 |
4485 |
ufmt.simplify(); |
1267 |
|
} |
1268 |
8970 |
std::stringstream ss; |
1269 |
4485 |
ss << "_arg_"; |
1270 |
8970 |
Node val = ufmt.getFunctionValue(ss.str().c_str(), condenseFuncValues); |
1271 |
4485 |
m->assignFunctionDefinition(f, val); |
1272 |
|
// ufmt.debugPrint( std::cout, m ); |
1273 |
4485 |
} |
1274 |
|
|
1275 |
1102 |
void TheoryEngineModelBuilder::assignHoFunction(TheoryModel* m, Node f) |
1276 |
|
{ |
1277 |
1102 |
Assert(logicInfo().isHigherOrder()); |
1278 |
2204 |
TypeNode type = f.getType(); |
1279 |
2204 |
std::vector<TypeNode> argTypes = type.getArgTypes(); |
1280 |
2204 |
std::vector<Node> args; |
1281 |
2204 |
std::vector<TNode> apply_args; |
1282 |
2293 |
for (unsigned i = 0; i < argTypes.size(); i++) |
1283 |
|
{ |
1284 |
2382 |
Node v = NodeManager::currentNM()->mkBoundVar(argTypes[i]); |
1285 |
1191 |
args.push_back(v); |
1286 |
1191 |
if (i > 0) |
1287 |
|
{ |
1288 |
89 |
apply_args.push_back(v); |
1289 |
|
} |
1290 |
|
} |
1291 |
|
// start with the base return value (currently we use the same default value |
1292 |
|
// for all functions) |
1293 |
2204 |
TypeEnumerator te(type.getRangeType()); |
1294 |
2204 |
Node curr = (*te); |
1295 |
1102 |
std::map<Node, std::vector<Node> >::iterator itht = m->d_ho_uf_terms.find(f); |
1296 |
1102 |
if (itht != m->d_ho_uf_terms.end()) |
1297 |
|
{ |
1298 |
3759 |
for (size_t i = 0; i < itht->second.size(); i++) |
1299 |
|
{ |
1300 |
5314 |
Node hn = itht->second[i]; |
1301 |
2657 |
Trace("model-builder-debug") << " process : " << hn << std::endl; |
1302 |
2657 |
Assert(hn.getKind() == kind::HO_APPLY); |
1303 |
2657 |
Assert(m->areEqual(hn[0], f)); |
1304 |
5314 |
Node hni = m->getRepresentative(hn[1]); |
1305 |
5314 |
Trace("model-builder-debug2") << " get rep : " << hn[0] |
1306 |
2657 |
<< " returned " << hni << std::endl; |
1307 |
2657 |
Assert(hni.isConst()); |
1308 |
2657 |
Assert(hni.getType().isSubtypeOf(args[0].getType())); |
1309 |
2657 |
hni = Rewriter::rewrite(args[0].eqNode(hni)); |
1310 |
5314 |
Node hnv = m->getRepresentative(hn); |
1311 |
5314 |
Trace("model-builder-debug2") << " get rep val : " << hn |
1312 |
2657 |
<< " returned " << hnv << std::endl; |
1313 |
2657 |
Assert(hnv.isConst()); |
1314 |
2657 |
if (!apply_args.empty()) |
1315 |
|
{ |
1316 |
220 |
Assert(hnv.getKind() == kind::LAMBDA |
1317 |
|
&& hnv[0].getNumChildren() + 1 == args.size()); |
1318 |
440 |
std::vector<TNode> largs; |
1319 |
468 |
for (unsigned j = 0; j < hnv[0].getNumChildren(); j++) |
1320 |
|
{ |
1321 |
248 |
largs.push_back(hnv[0][j]); |
1322 |
|
} |
1323 |
220 |
Assert(largs.size() == apply_args.size()); |
1324 |
220 |
hnv = hnv[1].substitute( |
1325 |
|
largs.begin(), largs.end(), apply_args.begin(), apply_args.end()); |
1326 |
220 |
hnv = Rewriter::rewrite(hnv); |
1327 |
|
} |
1328 |
2657 |
Assert(!TypeNode::leastCommonTypeNode(hnv.getType(), curr.getType()) |
1329 |
|
.isNull()); |
1330 |
2657 |
curr = NodeManager::currentNM()->mkNode(kind::ITE, hni, hnv, curr); |
1331 |
|
} |
1332 |
|
} |
1333 |
|
Node val = NodeManager::currentNM()->mkNode( |
1334 |
|
kind::LAMBDA, |
1335 |
2204 |
NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, args), |
1336 |
4408 |
curr); |
1337 |
1102 |
m->assignFunctionDefinition(f, val); |
1338 |
1102 |
} |
1339 |
|
|
1340 |
|
// This struct is used to sort terms by the "size" of their type |
1341 |
|
// The size of the type is the number of nodes in the type, for example |
1342 |
|
// size of Int is 1 |
1343 |
|
// size of Function( Int, Int ) is 3 |
1344 |
|
// size of Function( Function( Bool, Int ), Int ) is 5 |
1345 |
9272 |
struct sortTypeSize |
1346 |
|
{ |
1347 |
|
// stores the size of the type |
1348 |
|
std::map<TypeNode, unsigned> d_type_size; |
1349 |
|
// get the size of type tn |
1350 |
6066 |
unsigned getTypeSize(TypeNode tn) |
1351 |
|
{ |
1352 |
6066 |
std::map<TypeNode, unsigned>::iterator it = d_type_size.find(tn); |
1353 |
6066 |
if (it != d_type_size.end()) |
1354 |
|
{ |
1355 |
4718 |
return it->second; |
1356 |
|
} |
1357 |
|
else |
1358 |
|
{ |
1359 |
1348 |
unsigned sum = 1; |
1360 |
3042 |
for (unsigned i = 0; i < tn.getNumChildren(); i++) |
1361 |
|
{ |
1362 |
1694 |
sum += getTypeSize(tn[i]); |
1363 |
|
} |
1364 |
1348 |
d_type_size[tn] = sum; |
1365 |
1348 |
return sum; |
1366 |
|
} |
1367 |
|
} |
1368 |
|
|
1369 |
|
public: |
1370 |
|
// compares the type size of i and j |
1371 |
|
// returns true iff the size of i is less than that of j |
1372 |
|
// tiebreaks are determined by node value |
1373 |
2186 |
bool operator()(Node i, Node j) |
1374 |
|
{ |
1375 |
2186 |
int si = getTypeSize(i.getType()); |
1376 |
2186 |
int sj = getTypeSize(j.getType()); |
1377 |
2186 |
if (si < sj) |
1378 |
|
{ |
1379 |
220 |
return true; |
1380 |
|
} |
1381 |
1966 |
else if (si == sj) |
1382 |
|
{ |
1383 |
1852 |
return i < j; |
1384 |
|
} |
1385 |
|
else |
1386 |
|
{ |
1387 |
114 |
return false; |
1388 |
|
} |
1389 |
|
} |
1390 |
|
}; |
1391 |
|
|
1392 |
16395 |
void TheoryEngineModelBuilder::assignFunctions(TheoryModel* m) |
1393 |
|
{ |
1394 |
16395 |
if (!options::assignFunctionValues()) |
1395 |
|
{ |
1396 |
|
return; |
1397 |
|
} |
1398 |
16395 |
Trace("model-builder") << "Assigning function values..." << std::endl; |
1399 |
32790 |
std::vector<Node> funcs_to_assign = m->getFunctionsToAssign(); |
1400 |
|
|
1401 |
16395 |
if (logicInfo().isHigherOrder()) |
1402 |
|
{ |
1403 |
|
// sort based on type size if higher-order |
1404 |
421 |
Trace("model-builder") << "Sort functions by type..." << std::endl; |
1405 |
842 |
sortTypeSize sts; |
1406 |
421 |
std::sort(funcs_to_assign.begin(), funcs_to_assign.end(), sts); |
1407 |
|
} |
1408 |
|
|
1409 |
16395 |
if (Trace.isOn("model-builder")) |
1410 |
|
{ |
1411 |
|
Trace("model-builder") << "...have " << funcs_to_assign.size() |
1412 |
|
<< " functions to assign:" << std::endl; |
1413 |
|
for (unsigned k = 0; k < funcs_to_assign.size(); k++) |
1414 |
|
{ |
1415 |
|
Node f = funcs_to_assign[k]; |
1416 |
|
Trace("model-builder") << " [" << k << "] : " << f << " : " |
1417 |
|
<< f.getType() << std::endl; |
1418 |
|
} |
1419 |
|
} |
1420 |
|
|
1421 |
|
// construct function values |
1422 |
21982 |
for (unsigned k = 0; k < funcs_to_assign.size(); k++) |
1423 |
|
{ |
1424 |
11174 |
Node f = funcs_to_assign[k]; |
1425 |
5587 |
Trace("model-builder") << " Function #" << k << " is " << f << std::endl; |
1426 |
|
// std::map< Node, std::vector< Node > >::iterator itht = |
1427 |
|
// m->d_ho_uf_terms.find( f ); |
1428 |
5587 |
if (!logicInfo().isHigherOrder()) |
1429 |
|
{ |
1430 |
8970 |
Trace("model-builder") << " Assign function value for " << f |
1431 |
4485 |
<< " based on APPLY_UF" << std::endl; |
1432 |
4485 |
assignFunction(m, f); |
1433 |
|
} |
1434 |
|
else |
1435 |
|
{ |
1436 |
2204 |
Trace("model-builder") << " Assign function value for " << f |
1437 |
1102 |
<< " based on curried HO_APPLY" << std::endl; |
1438 |
1102 |
assignHoFunction(m, f); |
1439 |
|
} |
1440 |
|
} |
1441 |
16395 |
Trace("model-builder") << "Finished assigning function values." << std::endl; |
1442 |
|
} |
1443 |
|
|
1444 |
|
} // namespace theory |
1445 |
29574 |
} // namespace cvc5 |