1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Morgan Deters, Kshitij Bansal |
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 engine class. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "theory/quantifiers/fmf/model_engine.h" |
17 |
|
|
18 |
|
#include "options/quantifiers_options.h" |
19 |
|
#include "theory/quantifiers/first_order_model.h" |
20 |
|
#include "theory/quantifiers/fmf/full_model_check.h" |
21 |
|
#include "theory/quantifiers/instantiate.h" |
22 |
|
#include "theory/quantifiers/quant_rep_bound_ext.h" |
23 |
|
#include "theory/quantifiers/quantifiers_attributes.h" |
24 |
|
#include "theory/quantifiers/term_database.h" |
25 |
|
|
26 |
|
using namespace cvc5::kind; |
27 |
|
using namespace cvc5::context; |
28 |
|
|
29 |
|
namespace cvc5 { |
30 |
|
namespace theory { |
31 |
|
namespace quantifiers { |
32 |
|
|
33 |
|
//Model Engine constructor |
34 |
1457 |
ModelEngine::ModelEngine(Env& env, |
35 |
|
QuantifiersState& qs, |
36 |
|
QuantifiersInferenceManager& qim, |
37 |
|
QuantifiersRegistry& qr, |
38 |
|
TermRegistry& tr, |
39 |
1457 |
QModelBuilder* builder) |
40 |
|
: QuantifiersModule(env, qs, qim, qr, tr), |
41 |
|
d_incomplete_check(true), |
42 |
|
d_addedLemmas(0), |
43 |
|
d_triedLemmas(0), |
44 |
|
d_totalLemmas(0), |
45 |
1457 |
d_builder(builder) |
46 |
|
{ |
47 |
|
|
48 |
1457 |
} |
49 |
|
|
50 |
2914 |
ModelEngine::~ModelEngine() { |
51 |
|
|
52 |
2914 |
} |
53 |
|
|
54 |
24668 |
bool ModelEngine::needsCheck( Theory::Effort e ) { |
55 |
24668 |
return e==Theory::EFFORT_LAST_CALL; |
56 |
|
} |
57 |
|
|
58 |
1795 |
QuantifiersModule::QEffort ModelEngine::needsModel(Theory::Effort e) |
59 |
|
{ |
60 |
1795 |
if( options::mbqiInterleave() ){ |
61 |
|
return QEFFORT_STANDARD; |
62 |
|
}else{ |
63 |
1795 |
return QEFFORT_MODEL; |
64 |
|
} |
65 |
|
} |
66 |
|
|
67 |
13378 |
void ModelEngine::reset_round( Theory::Effort e ) { |
68 |
13378 |
d_incomplete_check = true; |
69 |
13378 |
} |
70 |
5362 |
void ModelEngine::check(Theory::Effort e, QEffort quant_e) |
71 |
|
{ |
72 |
5362 |
bool doCheck = false; |
73 |
5362 |
if( options::mbqiInterleave() ){ |
74 |
|
doCheck = quant_e == QEFFORT_STANDARD && d_qim.hasPendingLemma(); |
75 |
|
} |
76 |
5362 |
if( !doCheck ){ |
77 |
5362 |
doCheck = quant_e == QEFFORT_MODEL; |
78 |
|
} |
79 |
5362 |
if( doCheck ){ |
80 |
1697 |
Assert(!d_qstate.isInConflict()); |
81 |
1697 |
int addedLemmas = 0; |
82 |
|
|
83 |
|
//the following will test that the model satisfies all asserted universal quantifiers by |
84 |
|
// (model-based) exhaustive instantiation. |
85 |
1697 |
double clSet = 0; |
86 |
1697 |
if( Trace.isOn("model-engine") ){ |
87 |
|
Trace("model-engine") << "---Model Engine Round---" << std::endl; |
88 |
|
clSet = double(clock())/double(CLOCKS_PER_SEC); |
89 |
|
} |
90 |
1697 |
Trace("model-engine-debug") << "Check model..." << std::endl; |
91 |
1697 |
d_incomplete_check = false; |
92 |
|
// print debug |
93 |
1697 |
if (Trace.isOn("fmf-model-complete")) |
94 |
|
{ |
95 |
|
Trace("fmf-model-complete") << std::endl; |
96 |
|
debugPrint("fmf-model-complete"); |
97 |
|
} |
98 |
|
// successfully built an acceptable model, now check it |
99 |
1697 |
addedLemmas += checkModel(); |
100 |
|
|
101 |
1697 |
if( Trace.isOn("model-engine") ){ |
102 |
|
double clSet2 = double(clock())/double(CLOCKS_PER_SEC); |
103 |
|
Trace("model-engine") << "Finished model engine, time = " << (clSet2-clSet) << std::endl; |
104 |
|
} |
105 |
|
|
106 |
1697 |
if( addedLemmas==0 ){ |
107 |
1018 |
Trace("model-engine-debug") |
108 |
509 |
<< "No lemmas added, incomplete = " |
109 |
509 |
<< (d_incomplete_check || !d_incompleteQuants.empty()) << std::endl; |
110 |
|
// cvc5 will answer SAT or unknown |
111 |
509 |
if( Trace.isOn("fmf-consistent") ){ |
112 |
|
Trace("fmf-consistent") << std::endl; |
113 |
|
debugPrint("fmf-consistent"); |
114 |
|
} |
115 |
|
} |
116 |
|
} |
117 |
5362 |
} |
118 |
|
|
119 |
446 |
bool ModelEngine::checkComplete(IncompleteId& incId) |
120 |
|
{ |
121 |
446 |
if (d_incomplete_check) |
122 |
|
{ |
123 |
|
incId = IncompleteId::QUANTIFIERS_FMF; |
124 |
|
return false; |
125 |
|
} |
126 |
446 |
return true; |
127 |
|
} |
128 |
|
|
129 |
1205 |
bool ModelEngine::checkCompleteFor( Node q ) { |
130 |
1205 |
return d_incompleteQuants.find(q) == d_incompleteQuants.end(); |
131 |
|
} |
132 |
|
|
133 |
3089 |
void ModelEngine::registerQuantifier( Node f ){ |
134 |
3089 |
if( Trace.isOn("fmf-warn") ){ |
135 |
|
bool canHandle = true; |
136 |
|
for( unsigned i=0; i<f[0].getNumChildren(); i++ ){ |
137 |
|
TypeNode tn = f[0][i].getType(); |
138 |
|
if( !tn.isSort() ){ |
139 |
|
if (!d_qstate.isFiniteType(tn)) |
140 |
|
{ |
141 |
|
if( tn.isInteger() ){ |
142 |
|
if( !options::fmfBound() ){ |
143 |
|
canHandle = false; |
144 |
|
} |
145 |
|
}else{ |
146 |
|
canHandle = false; |
147 |
|
} |
148 |
|
} |
149 |
|
} |
150 |
|
} |
151 |
|
if( !canHandle ){ |
152 |
|
Trace("fmf-warn") << "Warning : Model Engine : may not be able to answer SAT because of formula : " << f << std::endl; |
153 |
|
} |
154 |
|
} |
155 |
3089 |
} |
156 |
|
|
157 |
1697 |
int ModelEngine::checkModel(){ |
158 |
1697 |
FirstOrderModel* fm = d_treg.getModel(); |
159 |
|
|
160 |
|
//for debugging, setup |
161 |
7799 |
for (std::map<TypeNode, std::vector<Node> >::iterator it = |
162 |
1697 |
fm->getRepSetPtr()->d_type_reps.begin(); |
163 |
9496 |
it != fm->getRepSetPtr()->d_type_reps.end(); |
164 |
|
++it) |
165 |
|
{ |
166 |
7799 |
if( it->first.isSort() ){ |
167 |
1908 |
Trace("model-engine") << "Cardinality( " << it->first << " )" << " = " << it->second.size() << std::endl; |
168 |
1908 |
Trace("model-engine-debug") << " Reps : "; |
169 |
5430 |
for( size_t i=0; i<it->second.size(); i++ ){ |
170 |
3522 |
Trace("model-engine-debug") << it->second[i] << " "; |
171 |
|
} |
172 |
1908 |
Trace("model-engine-debug") << std::endl; |
173 |
1908 |
Trace("model-engine-debug") << " Term reps : "; |
174 |
5430 |
for( size_t i=0; i<it->second.size(); i++ ){ |
175 |
7044 |
Node r = fm->getInternalRepresentative(it->second[i], Node::null(), 0); |
176 |
3522 |
if (r.isNull()) |
177 |
|
{ |
178 |
|
// there was an invalid equivalence class |
179 |
|
d_incomplete_check = true; |
180 |
|
} |
181 |
3522 |
Trace("model-engine-debug") << r << " "; |
182 |
|
} |
183 |
1908 |
Trace("model-engine-debug") << std::endl; |
184 |
3816 |
Node mbt = fm->getModelBasisTerm(it->first); |
185 |
1908 |
Trace("model-engine-debug") << " Basis term : " << mbt << std::endl; |
186 |
|
} |
187 |
|
} |
188 |
|
|
189 |
1697 |
d_triedLemmas = 0; |
190 |
1697 |
d_addedLemmas = 0; |
191 |
1697 |
d_totalLemmas = 0; |
192 |
|
//for statistics |
193 |
1697 |
if( Trace.isOn("model-engine") ){ |
194 |
|
for( unsigned i=0; i<fm->getNumAssertedQuantifiers(); i++ ){ |
195 |
|
Node f = fm->getAssertedQuantifier( i ); |
196 |
|
if (fm->isQuantifierActive(f) && shouldProcess(f)) |
197 |
|
{ |
198 |
|
int totalInst = 1; |
199 |
|
for( unsigned j=0; j<f[0].getNumChildren(); j++ ){ |
200 |
|
TypeNode tn = f[0][j].getType(); |
201 |
|
if (fm->getRepSet()->hasType(tn)) |
202 |
|
{ |
203 |
|
totalInst = |
204 |
|
totalInst * (int)fm->getRepSet()->getNumRepresentatives(tn); |
205 |
|
} |
206 |
|
} |
207 |
|
d_totalLemmas += totalInst; |
208 |
|
} |
209 |
|
} |
210 |
|
} |
211 |
|
|
212 |
1697 |
Trace("model-engine-debug") << "Do exhaustive instantiation..." << std::endl; |
213 |
|
// FMC uses two sub-effort levels |
214 |
1697 |
int e_max = options::mbqiMode() == options::MbqiMode::FMC |
215 |
2046 |
? 2 |
216 |
2046 |
: (options::mbqiMode() == options::MbqiMode::TRUST ? 0 : 1); |
217 |
3103 |
for( int e=0; e<e_max; e++) { |
218 |
2594 |
d_incompleteQuants.clear(); |
219 |
14756 |
for( unsigned i=0; i<fm->getNumAssertedQuantifiers(); i++ ){ |
220 |
23900 |
Node q = fm->getAssertedQuantifier( i, true ); |
221 |
12162 |
Trace("fmf-exh-inst") << "-> Exhaustive instantiate " << q << ", effort = " << e << "..." << std::endl; |
222 |
|
//determine if we should check this quantifier |
223 |
12172 |
if (!fm->isQuantifierActive(q)) |
224 |
|
{ |
225 |
10 |
Trace("fmf-exh-inst") << "-> Inactive : " << q << std::endl; |
226 |
10 |
continue; |
227 |
|
} |
228 |
12566 |
if (!shouldProcess(q)) |
229 |
|
{ |
230 |
414 |
Trace("fmf-exh-inst") << "-> Not processed : " << q << std::endl; |
231 |
414 |
d_incompleteQuants.insert(q); |
232 |
414 |
continue; |
233 |
|
} |
234 |
11738 |
exhaustiveInstantiate(q, e); |
235 |
11738 |
if (d_qstate.isInConflict()) |
236 |
|
{ |
237 |
|
break; |
238 |
|
} |
239 |
|
} |
240 |
2594 |
if( d_addedLemmas>0 ){ |
241 |
1188 |
break; |
242 |
|
}else{ |
243 |
1406 |
Assert(!d_qstate.isInConflict()); |
244 |
|
} |
245 |
|
} |
246 |
|
|
247 |
|
//print debug information |
248 |
1697 |
if (d_qstate.isInConflict()) |
249 |
|
{ |
250 |
|
Trace("model-engine") << "Conflict, added lemmas = "; |
251 |
|
}else{ |
252 |
1697 |
Trace("model-engine") << "Added Lemmas = "; |
253 |
|
} |
254 |
1697 |
Trace("model-engine") << d_addedLemmas << " / " << d_triedLemmas << " / "; |
255 |
1697 |
Trace("model-engine") << d_totalLemmas << std::endl; |
256 |
1697 |
return d_addedLemmas; |
257 |
|
} |
258 |
|
|
259 |
|
|
260 |
|
|
261 |
11738 |
void ModelEngine::exhaustiveInstantiate( Node f, int effort ){ |
262 |
|
//first check if the builder can do the exhaustive instantiation |
263 |
11738 |
unsigned prev_alem = d_builder->getNumAddedLemmas(); |
264 |
11738 |
unsigned prev_tlem = d_builder->getNumTriedLemmas(); |
265 |
11738 |
FirstOrderModel* fm = d_treg.getModel(); |
266 |
11738 |
int retEi = d_builder->doExhaustiveInstantiation(fm, f, effort); |
267 |
11738 |
if( retEi!=0 ){ |
268 |
11514 |
if( retEi<0 ){ |
269 |
|
Trace("fmf-exh-inst") << "-> Builder determined complete instantiation was impossible." << std::endl; |
270 |
|
d_incompleteQuants.insert(f); |
271 |
|
}else{ |
272 |
11514 |
Trace("fmf-exh-inst") << "-> Builder determined instantiation(s)." << std::endl; |
273 |
|
} |
274 |
11514 |
d_triedLemmas += d_builder->getNumTriedLemmas() - prev_tlem; |
275 |
11514 |
d_addedLemmas += d_builder->getNumAddedLemmas() - prev_alem; |
276 |
|
}else{ |
277 |
224 |
if( Trace.isOn("fmf-exh-inst-debug") ){ |
278 |
|
Trace("fmf-exh-inst-debug") << " Instantiation Constants: "; |
279 |
|
for( size_t i=0; i<f[0].getNumChildren(); i++ ){ |
280 |
|
Trace("fmf-exh-inst-debug") |
281 |
|
<< d_qreg.getInstantiationConstant(f, i) << " "; |
282 |
|
} |
283 |
|
Trace("fmf-exh-inst-debug") << std::endl; |
284 |
|
} |
285 |
224 |
QuantifiersBoundInference& qbi = d_qreg.getQuantifiersBoundInference(); |
286 |
|
//create a rep set iterator and iterate over the (relevant) domain of the quantifier |
287 |
448 |
QRepBoundExt qrbe(qbi, fm); |
288 |
448 |
RepSetIterator riter(fm->getRepSet(), &qrbe); |
289 |
224 |
if( riter.setQuantifier( f ) ){ |
290 |
224 |
Trace("fmf-exh-inst") << "...exhaustive instantiation set, incomplete=" << riter.isIncomplete() << "..." << std::endl; |
291 |
224 |
if( !riter.isIncomplete() ){ |
292 |
210 |
int triedLemmas = 0; |
293 |
210 |
int addedLemmas = 0; |
294 |
210 |
Instantiate* inst = d_qim.getInstantiate(); |
295 |
1536 |
while( !riter.isFinished() && ( addedLemmas==0 || !options::fmfOneInstPerRound() ) ){ |
296 |
|
//instantiation was not shown to be true, construct the match |
297 |
1326 |
InstMatch m( f ); |
298 |
1983 |
for (unsigned i = 0; i < riter.getNumTerms(); i++) |
299 |
|
{ |
300 |
1320 |
m.set(d_qstate, i, riter.getCurrentTerm(i)); |
301 |
|
} |
302 |
663 |
Debug("fmf-model-eval") << "* Add instantiation " << m << std::endl; |
303 |
663 |
triedLemmas++; |
304 |
|
//add as instantiation |
305 |
1326 |
if (inst->addInstantiation(f, |
306 |
|
m.d_vals, |
307 |
|
InferenceId::QUANTIFIERS_INST_FMF_EXH, |
308 |
1326 |
Node::null(), |
309 |
|
true)) |
310 |
|
{ |
311 |
318 |
addedLemmas++; |
312 |
318 |
if (d_qstate.isInConflict()) |
313 |
|
{ |
314 |
|
break; |
315 |
|
} |
316 |
|
}else{ |
317 |
345 |
Debug("fmf-model-eval") << "* Failed Add instantiation " << m << std::endl; |
318 |
|
} |
319 |
663 |
riter.increment(); |
320 |
|
} |
321 |
210 |
d_addedLemmas += addedLemmas; |
322 |
210 |
d_triedLemmas += triedLemmas; |
323 |
|
} |
324 |
|
}else{ |
325 |
|
Trace("fmf-exh-inst") << "...exhaustive instantiation did set, incomplete=" << riter.isIncomplete() << "..." << std::endl; |
326 |
|
} |
327 |
|
//if the iterator is incomplete, we will return unknown instead of sat if no instantiations are added this round |
328 |
224 |
if( riter.isIncomplete() ){ |
329 |
22 |
d_incompleteQuants.insert(f); |
330 |
|
} |
331 |
|
} |
332 |
11738 |
} |
333 |
|
|
334 |
|
void ModelEngine::debugPrint( const char* c ){ |
335 |
|
if (Trace.isOn(c)) |
336 |
|
{ |
337 |
|
Trace(c) << "Quantifiers: " << std::endl; |
338 |
|
FirstOrderModel* m = d_treg.getModel(); |
339 |
|
for (size_t i = 0, nquant = m->getNumAssertedQuantifiers(); i < nquant; i++) |
340 |
|
{ |
341 |
|
Node q = m->getAssertedQuantifier(i); |
342 |
|
if (d_qreg.hasOwnership(q, this)) |
343 |
|
{ |
344 |
|
Trace(c) << " "; |
345 |
|
if (!m->isQuantifierActive(q)) |
346 |
|
{ |
347 |
|
Trace(c) << "*Inactive* "; |
348 |
|
} |
349 |
|
else |
350 |
|
{ |
351 |
|
Trace(c) << " "; |
352 |
|
} |
353 |
|
Trace(c) << q << std::endl; |
354 |
|
} |
355 |
|
} |
356 |
|
} |
357 |
|
} |
358 |
|
|
359 |
12152 |
bool ModelEngine::shouldProcess(Node q) |
360 |
|
{ |
361 |
12152 |
if (!d_qreg.hasOwnership(q, this)) |
362 |
|
{ |
363 |
404 |
return false; |
364 |
|
} |
365 |
|
// if finite model finding or fmf bound is on, we process everything |
366 |
11748 |
if (options::finiteModelFind() || options::fmfBound()) |
367 |
|
{ |
368 |
9584 |
return true; |
369 |
|
} |
370 |
|
// otherwise, we are only using model-based instantiation for internal |
371 |
|
// quantified formulas |
372 |
2164 |
QuantAttributes& qattr = d_qreg.getQuantAttributes(); |
373 |
2164 |
return qattr.isInternal(q); |
374 |
|
} |
375 |
|
|
376 |
|
} // namespace quantifiers |
377 |
|
} // namespace theory |
378 |
29589 |
} // namespace cvc5 |