1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Tianyi Liang, 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 the theory of strings. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "theory/strings/theory_strings.h" |
17 |
|
|
18 |
|
#include "expr/attribute.h" |
19 |
|
#include "expr/bound_var_manager.h" |
20 |
|
#include "expr/kind.h" |
21 |
|
#include "expr/sequence.h" |
22 |
|
#include "options/smt_options.h" |
23 |
|
#include "options/strings_options.h" |
24 |
|
#include "options/theory_options.h" |
25 |
|
#include "smt/logic_exception.h" |
26 |
|
#include "theory/decision_manager.h" |
27 |
|
#include "theory/ext_theory.h" |
28 |
|
#include "theory/rewriter.h" |
29 |
|
#include "theory/strings/theory_strings_utils.h" |
30 |
|
#include "theory/strings/type_enumerator.h" |
31 |
|
#include "theory/strings/word.h" |
32 |
|
#include "theory/theory_model.h" |
33 |
|
#include "theory/valuation.h" |
34 |
|
|
35 |
|
using namespace std; |
36 |
|
using namespace cvc5::context; |
37 |
|
using namespace cvc5::kind; |
38 |
|
|
39 |
|
namespace cvc5 { |
40 |
|
namespace theory { |
41 |
|
namespace strings { |
42 |
|
|
43 |
|
/** |
44 |
|
* Attribute used for making unique (bound variables) which correspond to |
45 |
|
* unique element values used in sequence models. See use in collectModelValues |
46 |
|
* below. |
47 |
|
*/ |
48 |
|
struct SeqModelVarAttributeId |
49 |
|
{ |
50 |
|
}; |
51 |
|
using SeqModelVarAttribute = expr::Attribute<SeqModelVarAttributeId, Node>; |
52 |
|
|
53 |
9915 |
TheoryStrings::TheoryStrings(Env& env, OutputChannel& out, Valuation valuation) |
54 |
|
: Theory(THEORY_STRINGS, env, out, valuation), |
55 |
|
d_notify(*this), |
56 |
|
d_statistics(), |
57 |
|
d_state(env, d_valuation), |
58 |
|
d_eagerSolver(d_state), |
59 |
|
d_termReg(d_state, d_statistics, d_pnm), |
60 |
|
d_extTheoryCb(), |
61 |
|
d_im(env, *this, d_state, d_termReg, d_extTheory, d_statistics, d_pnm), |
62 |
|
d_extTheory(d_extTheoryCb, context(), userContext(), d_im), |
63 |
|
d_rewriter(&d_statistics.d_rewrites), |
64 |
|
d_bsolver(d_state, d_im), |
65 |
|
d_csolver(d_state, d_im, d_termReg, d_bsolver), |
66 |
|
d_esolver(d_state, |
67 |
|
d_im, |
68 |
|
d_termReg, |
69 |
|
d_rewriter, |
70 |
|
d_bsolver, |
71 |
|
d_csolver, |
72 |
|
d_extTheory, |
73 |
|
d_statistics), |
74 |
|
d_rsolver(d_state, |
75 |
|
d_im, |
76 |
|
d_termReg.getSkolemCache(), |
77 |
|
d_csolver, |
78 |
|
d_esolver, |
79 |
|
d_statistics), |
80 |
19830 |
d_regexp_elim(options::regExpElimAgg(), d_pnm, userContext()), |
81 |
29745 |
d_stringsFmf(context(), userContext(), valuation, d_termReg) |
82 |
|
{ |
83 |
9915 |
d_termReg.finishInit(&d_im); |
84 |
|
|
85 |
9915 |
d_zero = NodeManager::currentNM()->mkConst( Rational( 0 ) ); |
86 |
9915 |
d_one = NodeManager::currentNM()->mkConst( Rational( 1 ) ); |
87 |
9915 |
d_neg_one = NodeManager::currentNM()->mkConst(Rational(-1)); |
88 |
9915 |
d_true = NodeManager::currentNM()->mkConst( true ); |
89 |
9915 |
d_false = NodeManager::currentNM()->mkConst( false ); |
90 |
|
|
91 |
9915 |
d_cardSize = utils::getAlphabetCardinality(); |
92 |
|
|
93 |
|
// set up the extended function callback |
94 |
9915 |
d_extTheoryCb.d_esolver = &d_esolver; |
95 |
|
|
96 |
|
// use the state object as the official theory state |
97 |
9915 |
d_theoryState = &d_state; |
98 |
|
// use the inference manager as the official inference manager |
99 |
9915 |
d_inferManager = &d_im; |
100 |
9915 |
} |
101 |
|
|
102 |
19824 |
TheoryStrings::~TheoryStrings() { |
103 |
|
|
104 |
19824 |
} |
105 |
|
|
106 |
9915 |
TheoryRewriter* TheoryStrings::getTheoryRewriter() { return &d_rewriter; } |
107 |
|
|
108 |
3781 |
ProofRuleChecker* TheoryStrings::getProofChecker() { return &d_checker; } |
109 |
|
|
110 |
9915 |
bool TheoryStrings::needsEqualityEngine(EeSetupInfo& esi) |
111 |
|
{ |
112 |
9915 |
esi.d_notify = &d_notify; |
113 |
9915 |
esi.d_name = "theory::strings::ee"; |
114 |
9915 |
esi.d_notifyNewClass = true; |
115 |
9915 |
esi.d_notifyMerge = true; |
116 |
9915 |
esi.d_notifyDisequal = true; |
117 |
9915 |
return true; |
118 |
|
} |
119 |
|
|
120 |
9915 |
void TheoryStrings::finishInit() |
121 |
|
{ |
122 |
9915 |
Assert(d_equalityEngine != nullptr); |
123 |
|
|
124 |
|
// witness is used to eliminate str.from_code |
125 |
9915 |
d_valuation.setUnevaluatedKind(WITNESS); |
126 |
|
|
127 |
9915 |
bool eagerEval = options::stringEagerEval(); |
128 |
|
// The kinds we are treating as function application in congruence |
129 |
9915 |
d_equalityEngine->addFunctionKind(kind::STRING_LENGTH, eagerEval); |
130 |
9915 |
d_equalityEngine->addFunctionKind(kind::STRING_CONCAT, eagerEval); |
131 |
9915 |
d_equalityEngine->addFunctionKind(kind::STRING_IN_REGEXP, eagerEval); |
132 |
9915 |
d_equalityEngine->addFunctionKind(kind::STRING_TO_CODE, eagerEval); |
133 |
9915 |
d_equalityEngine->addFunctionKind(kind::SEQ_UNIT, eagerEval); |
134 |
|
// `seq.nth` is not always defined, and so we do not evaluate it eagerly. |
135 |
9915 |
d_equalityEngine->addFunctionKind(kind::SEQ_NTH, false); |
136 |
|
// extended functions |
137 |
9915 |
d_equalityEngine->addFunctionKind(kind::STRING_CONTAINS, eagerEval); |
138 |
9915 |
d_equalityEngine->addFunctionKind(kind::STRING_LEQ, eagerEval); |
139 |
9915 |
d_equalityEngine->addFunctionKind(kind::STRING_SUBSTR, eagerEval); |
140 |
9915 |
d_equalityEngine->addFunctionKind(kind::STRING_UPDATE, eagerEval); |
141 |
9915 |
d_equalityEngine->addFunctionKind(kind::STRING_ITOS, eagerEval); |
142 |
9915 |
d_equalityEngine->addFunctionKind(kind::STRING_STOI, eagerEval); |
143 |
9915 |
d_equalityEngine->addFunctionKind(kind::STRING_INDEXOF, eagerEval); |
144 |
9915 |
d_equalityEngine->addFunctionKind(kind::STRING_INDEXOF_RE, eagerEval); |
145 |
9915 |
d_equalityEngine->addFunctionKind(kind::STRING_REPLACE, eagerEval); |
146 |
9915 |
d_equalityEngine->addFunctionKind(kind::STRING_REPLACE_ALL, eagerEval); |
147 |
9915 |
d_equalityEngine->addFunctionKind(kind::STRING_REPLACE_RE, eagerEval); |
148 |
9915 |
d_equalityEngine->addFunctionKind(kind::STRING_REPLACE_RE_ALL, eagerEval); |
149 |
9915 |
d_equalityEngine->addFunctionKind(kind::STRING_REPLACE_ALL, eagerEval); |
150 |
9915 |
d_equalityEngine->addFunctionKind(kind::STRING_TOLOWER, eagerEval); |
151 |
9915 |
d_equalityEngine->addFunctionKind(kind::STRING_TOUPPER, eagerEval); |
152 |
9915 |
d_equalityEngine->addFunctionKind(kind::STRING_REV, eagerEval); |
153 |
9915 |
} |
154 |
|
|
155 |
|
std::string TheoryStrings::identify() const |
156 |
|
{ |
157 |
|
return std::string("TheoryStrings"); |
158 |
|
} |
159 |
|
|
160 |
7129 |
bool TheoryStrings::areCareDisequal( TNode x, TNode y ) { |
161 |
7129 |
Assert(d_equalityEngine->hasTerm(x)); |
162 |
7129 |
Assert(d_equalityEngine->hasTerm(y)); |
163 |
21387 |
if (d_equalityEngine->isTriggerTerm(x, THEORY_STRINGS) |
164 |
21387 |
&& d_equalityEngine->isTriggerTerm(y, THEORY_STRINGS)) |
165 |
|
{ |
166 |
|
TNode x_shared = |
167 |
8579 |
d_equalityEngine->getTriggerTermRepresentative(x, THEORY_STRINGS); |
168 |
|
TNode y_shared = |
169 |
8579 |
d_equalityEngine->getTriggerTermRepresentative(y, THEORY_STRINGS); |
170 |
5723 |
EqualityStatus eqStatus = d_valuation.getEqualityStatus(x_shared, y_shared); |
171 |
5723 |
if( eqStatus==EQUALITY_FALSE_AND_PROPAGATED || eqStatus==EQUALITY_FALSE || eqStatus==EQUALITY_FALSE_IN_MODEL ){ |
172 |
2867 |
return true; |
173 |
|
} |
174 |
|
} |
175 |
4262 |
return false; |
176 |
|
} |
177 |
|
|
178 |
1073722 |
bool TheoryStrings::propagateLit(TNode literal) |
179 |
|
{ |
180 |
1073722 |
return d_im.propagateLit(literal); |
181 |
|
} |
182 |
|
|
183 |
47099 |
TrustNode TheoryStrings::explain(TNode literal) |
184 |
|
{ |
185 |
47099 |
Debug("strings-explain") << "explain called on " << literal << std::endl; |
186 |
47099 |
return d_im.explainLit(literal); |
187 |
|
} |
188 |
|
|
189 |
15239 |
void TheoryStrings::presolve() { |
190 |
15239 |
Debug("strings-presolve") << "TheoryStrings::Presolving : get fmf options " << (options::stringFMF() ? "true" : "false") << std::endl; |
191 |
15239 |
d_strat.initializeStrategy(); |
192 |
|
|
193 |
|
// if strings fmf is enabled, register the strategy |
194 |
15239 |
if (options::stringFMF()) |
195 |
|
{ |
196 |
78 |
d_stringsFmf.presolve(); |
197 |
|
// This strategy is local to a check-sat call, since we refresh the strategy |
198 |
|
// on every call to presolve. |
199 |
78 |
d_im.getDecisionManager()->registerStrategy( |
200 |
|
DecisionManager::STRAT_STRINGS_SUM_LENGTHS, |
201 |
|
d_stringsFmf.getDecisionStrategy(), |
202 |
|
DecisionManager::STRAT_SCOPE_LOCAL_SOLVE); |
203 |
|
} |
204 |
15239 |
Debug("strings-presolve") << "Finished presolve" << std::endl; |
205 |
15239 |
} |
206 |
|
|
207 |
|
|
208 |
|
///////////////////////////////////////////////////////////////////////////// |
209 |
|
// MODEL GENERATION |
210 |
|
///////////////////////////////////////////////////////////////////////////// |
211 |
|
|
212 |
5238 |
bool TheoryStrings::collectModelValues(TheoryModel* m, |
213 |
|
const std::set<Node>& termSet) |
214 |
|
{ |
215 |
5238 |
if (Trace.isOn("strings-debug-model")) |
216 |
|
{ |
217 |
|
Trace("strings-debug-model") |
218 |
|
<< "TheoryStrings::collectModelValues" << std::endl; |
219 |
|
Trace("strings-debug-model") << "Equivalence classes are:" << std::endl; |
220 |
|
Trace("strings-debug-model") << debugPrintStringsEqc() << std::endl; |
221 |
|
Trace("strings-debug-model") << "Extended functions are:" << std::endl; |
222 |
|
Trace("strings-debug-model") << d_esolver.debugPrintModel() << std::endl; |
223 |
|
} |
224 |
5238 |
Trace("strings-model") << "TheoryStrings::collectModelValues" << std::endl; |
225 |
|
// Collects representatives by types and orders sequence types by how nested |
226 |
|
// they are |
227 |
10476 |
std::map<TypeNode, std::unordered_set<Node> > repSet; |
228 |
10476 |
std::unordered_set<TypeNode> toProcess; |
229 |
|
// Generate model |
230 |
|
// get the relevant string equivalence classes |
231 |
98540 |
for (const Node& s : termSet) |
232 |
|
{ |
233 |
186604 |
TypeNode tn = s.getType(); |
234 |
93302 |
if (tn.isStringLike()) |
235 |
|
{ |
236 |
91290 |
Node r = d_state.getRepresentative(s); |
237 |
45645 |
repSet[tn].insert(r); |
238 |
45645 |
toProcess.insert(tn); |
239 |
|
} |
240 |
|
} |
241 |
|
|
242 |
7038 |
while (!toProcess.empty()) |
243 |
|
{ |
244 |
|
// Pick one of the remaining types to collect model values for |
245 |
1802 |
TypeNode tn = *toProcess.begin(); |
246 |
902 |
if (!collectModelInfoType(tn, toProcess, repSet, m)) |
247 |
|
{ |
248 |
|
return false; |
249 |
|
} |
250 |
|
} |
251 |
|
|
252 |
5237 |
return true; |
253 |
|
} |
254 |
|
|
255 |
901 |
bool TheoryStrings::collectModelInfoType( |
256 |
|
TypeNode tn, |
257 |
|
std::unordered_set<TypeNode>& toProcess, |
258 |
|
const std::map<TypeNode, std::unordered_set<Node> >& repSet, |
259 |
|
TheoryModel* m) |
260 |
|
{ |
261 |
|
// Make sure that the model values for the element type of sequences are |
262 |
|
// computed first |
263 |
901 |
if (tn.isSequence() && tn.getSequenceElementType().isSequence()) |
264 |
|
{ |
265 |
6 |
TypeNode tnElem = tn.getSequenceElementType(); |
266 |
6 |
if (toProcess.find(tnElem) != toProcess.end() |
267 |
6 |
&& !collectModelInfoType(tnElem, toProcess, repSet, m)) |
268 |
|
{ |
269 |
|
return false; |
270 |
|
} |
271 |
|
} |
272 |
901 |
toProcess.erase(tn); |
273 |
|
|
274 |
|
// get partition of strings of equal lengths for the representatives of the |
275 |
|
// current type |
276 |
1802 |
std::map<TypeNode, std::vector<std::vector<Node> > > colT; |
277 |
1802 |
std::map<TypeNode, std::vector<Node> > ltsT; |
278 |
1802 |
const std::vector<Node> repVec(repSet.at(tn).begin(), repSet.at(tn).end()); |
279 |
901 |
d_state.separateByLength(repVec, colT, ltsT); |
280 |
901 |
const std::vector<std::vector<Node> >& col = colT[tn]; |
281 |
901 |
const std::vector<Node>& lts = ltsT[tn]; |
282 |
|
|
283 |
901 |
NodeManager* nm = NodeManager::currentNM(); |
284 |
1802 |
std::map< Node, Node > processed; |
285 |
|
//step 1 : get all values for known lengths |
286 |
1802 |
std::vector< Node > lts_values; |
287 |
1802 |
std::map<std::size_t, Node> values_used; |
288 |
1802 |
std::vector<Node> len_splits; |
289 |
6233 |
for( unsigned i=0; i<col.size(); i++ ) { |
290 |
5333 |
Trace("strings-model") << "Checking length for {"; |
291 |
14451 |
for( unsigned j=0; j<col[i].size(); j++ ) { |
292 |
9118 |
if( j>0 ) { |
293 |
3785 |
Trace("strings-model") << ", "; |
294 |
|
} |
295 |
9118 |
Trace("strings-model") << col[i][j]; |
296 |
|
} |
297 |
5333 |
Trace("strings-model") << " } (length is " << lts[i] << ")" << std::endl; |
298 |
10666 |
Node len_value; |
299 |
5333 |
if( lts[i].isConst() ) { |
300 |
3751 |
len_value = lts[i]; |
301 |
|
} |
302 |
1582 |
else if (!lts[i].isNull()) |
303 |
|
{ |
304 |
|
// get the model value for lts[i] |
305 |
1582 |
len_value = d_valuation.getModelValue(lts[i]); |
306 |
|
} |
307 |
5333 |
if (len_value.isNull()) |
308 |
|
{ |
309 |
|
lts_values.push_back(Node::null()); |
310 |
|
} |
311 |
|
else |
312 |
|
{ |
313 |
|
// must throw logic exception if we cannot construct the string |
314 |
5333 |
if (len_value.getConst<Rational>() > String::maxSize()) |
315 |
|
{ |
316 |
2 |
std::stringstream ss; |
317 |
2 |
ss << "The model was computed to have strings of length " << len_value |
318 |
1 |
<< ". We only allow strings up to length " << String::maxSize(); |
319 |
1 |
throw LogicException(ss.str()); |
320 |
|
} |
321 |
|
std::size_t lvalue = |
322 |
5332 |
len_value.getConst<Rational>().getNumerator().toUnsignedInt(); |
323 |
5332 |
auto itvu = values_used.find(lvalue); |
324 |
5332 |
if (itvu == values_used.end()) |
325 |
|
{ |
326 |
4201 |
values_used[lvalue] = lts[i]; |
327 |
|
} |
328 |
|
else |
329 |
|
{ |
330 |
1131 |
len_splits.push_back(lts[i].eqNode(itvu->second)); |
331 |
|
} |
332 |
5332 |
lts_values.push_back(len_value); |
333 |
|
} |
334 |
|
} |
335 |
|
////step 2 : assign arbitrary values for unknown lengths? |
336 |
|
// confirmed by calculus invariant, see paper |
337 |
900 |
Trace("strings-model") << "Assign to equivalence classes..." << std::endl; |
338 |
1800 |
std::map<Node, Node> pure_eq_assign; |
339 |
|
//step 3 : assign values to equivalence classes that are pure variables |
340 |
6231 |
for( unsigned i=0; i<col.size(); i++ ){ |
341 |
10662 |
std::vector< Node > pure_eq; |
342 |
10662 |
Trace("strings-model") << "The (" << col[i].size() |
343 |
5331 |
<< ") equivalence classes "; |
344 |
14447 |
for (const Node& eqc : col[i]) |
345 |
|
{ |
346 |
9116 |
Trace("strings-model") << eqc << " "; |
347 |
|
//check if col[i][j] has only variables |
348 |
9116 |
if (!eqc.isConst()) |
349 |
|
{ |
350 |
3122 |
NormalForm& nfe = d_csolver.getNormalForm(eqc); |
351 |
3122 |
if (nfe.d_nf.size() == 1) |
352 |
|
{ |
353 |
|
// is it an equivalence class with a seq.unit term? |
354 |
999 |
if (nfe.d_nf[0].getKind() == SEQ_UNIT) |
355 |
|
{ |
356 |
28 |
Node argVal; |
357 |
14 |
if (nfe.d_nf[0][0].getType().isStringLike()) |
358 |
|
{ |
359 |
|
// By this point, we should have assigned model values for the |
360 |
|
// elements of this sequence type because of the check in the |
361 |
|
// beginning of this method |
362 |
3 |
argVal = m->getRepresentative(nfe.d_nf[0][0]); |
363 |
|
} |
364 |
|
else |
365 |
|
{ |
366 |
|
// Otherwise, we use the term itself. We cannot get the model |
367 |
|
// value of this term, since it might not be available yet, as |
368 |
|
// it may belong to a theory that has not built its model yet. |
369 |
|
// Hence, we assign a (non-constant) skeleton (seq.unit argVal). |
370 |
11 |
argVal = nfe.d_nf[0][0]; |
371 |
|
} |
372 |
14 |
Assert(!argVal.isNull()) << "No value for " << nfe.d_nf[0][0]; |
373 |
28 |
Node c = rewrite(nm->mkNode(SEQ_UNIT, argVal)); |
374 |
14 |
pure_eq_assign[eqc] = c; |
375 |
14 |
Trace("strings-model") << "(unit: " << nfe.d_nf[0] << ") "; |
376 |
14 |
m->getEqualityEngine()->addTerm(c); |
377 |
|
} |
378 |
|
// does it have a code and the length of these equivalence classes are |
379 |
|
// one? |
380 |
985 |
else if (d_termReg.hasStringCode() && lts_values[i] == d_one) |
381 |
|
{ |
382 |
325 |
EqcInfo* eip = d_state.getOrMakeEqcInfo(eqc, false); |
383 |
325 |
if (eip && !eip->d_codeTerm.get().isNull()) |
384 |
|
{ |
385 |
|
// its value must be equal to its code |
386 |
372 |
Node ct = nm->mkNode(kind::STRING_TO_CODE, eip->d_codeTerm.get()); |
387 |
372 |
Node ctv = d_valuation.getModelValue(ct); |
388 |
|
unsigned cvalue = |
389 |
186 |
ctv.getConst<Rational>().getNumerator().toUnsignedInt(); |
390 |
186 |
Trace("strings-model") << "(code: " << cvalue << ") "; |
391 |
372 |
std::vector<unsigned> vec; |
392 |
186 |
vec.push_back(cvalue); |
393 |
372 |
Node mv = nm->mkConst(String(vec)); |
394 |
186 |
pure_eq_assign[eqc] = mv; |
395 |
186 |
m->getEqualityEngine()->addTerm(mv); |
396 |
|
} |
397 |
|
} |
398 |
999 |
pure_eq.push_back(eqc); |
399 |
|
} |
400 |
|
} |
401 |
|
else |
402 |
|
{ |
403 |
5994 |
processed[eqc] = eqc; |
404 |
|
// Make sure that constants are asserted to the theory model that we |
405 |
|
// are building. It is possible that new constants were introduced by |
406 |
|
// the eager evaluation in the equality engine. These terms are missing |
407 |
|
// in the term set and, as a result, are skipped when the equality |
408 |
|
// engine is asserted to the theory model. |
409 |
5994 |
m->getEqualityEngine()->addTerm(eqc); |
410 |
|
} |
411 |
|
} |
412 |
5331 |
Trace("strings-model") << "have length " << lts_values[i] << std::endl; |
413 |
|
|
414 |
|
//assign a new length if necessary |
415 |
5331 |
if( !pure_eq.empty() ){ |
416 |
703 |
if( lts_values[i].isNull() ){ |
417 |
|
// start with length two (other lengths have special precendence) |
418 |
|
std::size_t lvalue = 2; |
419 |
|
while( values_used.find( lvalue )!=values_used.end() ){ |
420 |
|
lvalue++; |
421 |
|
} |
422 |
|
Trace("strings-model") << "*** Decide to make length of " << lvalue << std::endl; |
423 |
|
lts_values[i] = nm->mkConst(Rational(lvalue)); |
424 |
|
values_used[lvalue] = Node::null(); |
425 |
|
} |
426 |
703 |
Trace("strings-model") << "Need to assign values of length " << lts_values[i] << " to equivalence classes "; |
427 |
1702 |
for( unsigned j=0; j<pure_eq.size(); j++ ){ |
428 |
999 |
Trace("strings-model") << pure_eq[j] << " "; |
429 |
|
} |
430 |
703 |
Trace("strings-model") << std::endl; |
431 |
|
|
432 |
|
//use type enumerator |
433 |
703 |
Assert(lts_values[i].getConst<Rational>() <= Rational(String::maxSize())) |
434 |
|
<< "Exceeded UINT32_MAX in string model"; |
435 |
|
uint32_t currLen = |
436 |
703 |
lts_values[i].getConst<Rational>().getNumerator().toUnsignedInt(); |
437 |
1406 |
std::unique_ptr<SEnumLen> sel; |
438 |
1406 |
Trace("strings-model") << "Cardinality of alphabet is " |
439 |
703 |
<< utils::getAlphabetCardinality() << std::endl; |
440 |
703 |
if (tn.isString()) // string-only |
441 |
|
{ |
442 |
683 |
sel.reset(new StringEnumLen( |
443 |
683 |
currLen, currLen, utils::getAlphabetCardinality())); |
444 |
|
} |
445 |
|
else |
446 |
|
{ |
447 |
20 |
sel.reset(new SeqEnumLen(tn, nullptr, currLen, currLen)); |
448 |
|
} |
449 |
1702 |
for (const Node& eqc : pure_eq) |
450 |
|
{ |
451 |
1998 |
Node c; |
452 |
999 |
std::map<Node, Node>::iterator itp = pure_eq_assign.find(eqc); |
453 |
999 |
if (itp == pure_eq_assign.end()) |
454 |
|
{ |
455 |
649 |
do |
456 |
|
{ |
457 |
1448 |
if (sel->isFinished()) |
458 |
|
{ |
459 |
|
// We are in a case where model construction is impossible due to |
460 |
|
// an insufficient number of constants of a given length. |
461 |
|
|
462 |
|
// Consider an integer equivalence class E whose value is assigned |
463 |
|
// n in the model. Let { S_1, ..., S_m } be the set of string |
464 |
|
// equivalence classes such that len( x ) is a member of E for |
465 |
|
// some member x of each class S1, ...,Sm. Since our calculus is |
466 |
|
// saturated with respect to cardinality inference (see Liang |
467 |
|
// et al, Figure 6, CAV 2014), we have that m <= A^n, where A is |
468 |
|
// the cardinality of our alphabet. |
469 |
|
|
470 |
|
// Now, consider the case where there exists two integer |
471 |
|
// equivalence classes E1 and E2 that are assigned n, and moreover |
472 |
|
// we did not received notification from arithmetic that E1 = E2. |
473 |
|
// This typically should never happen, but assume in the following |
474 |
|
// that it does. |
475 |
|
|
476 |
|
// Now, it may be the case that there are string equivalence |
477 |
|
// classes { S_1, ..., S_m1 } whose lengths are in E1, |
478 |
|
// and classes { S'_1, ..., S'_m2 } whose lengths are in E2, where |
479 |
|
// m1 + m2 > A^n. In this case, we have insufficient strings to |
480 |
|
// assign to { S_1, ..., S_m1, S'_1, ..., S'_m2 }. If this |
481 |
|
// happens, we add a split on len( u1 ) = len( u2 ) for some |
482 |
|
// len( u1 ) in E1, len( u2 ) in E2. We do this for each pair of |
483 |
|
// integer equivalence classes that are assigned to the same value |
484 |
|
// in the model. |
485 |
|
AlwaysAssert(!len_splits.empty()); |
486 |
|
for (const Node& sl : len_splits) |
487 |
|
{ |
488 |
|
Node spl = nm->mkNode(OR, sl, sl.negate()); |
489 |
|
d_im.lemma(spl, InferenceId::STRINGS_CMI_SPLIT); |
490 |
|
Trace("strings-lemma") |
491 |
|
<< "Strings::CollectModelInfoSplit: " << spl << std::endl; |
492 |
|
} |
493 |
|
// we added a lemma, so can return here |
494 |
|
return false; |
495 |
|
} |
496 |
1448 |
c = sel->getCurrent(); |
497 |
|
// if we are a sequence with infinite element type |
498 |
2896 |
if (tn.isSequence() |
499 |
2896 |
&& !d_state.isFiniteType(tn.getSequenceElementType())) |
500 |
|
{ |
501 |
|
// Make a skeleton instead. In particular, this means that |
502 |
|
// a value: |
503 |
|
// (seq.++ (seq.unit 0) (seq.unit 1) (seq.unit 2)) |
504 |
|
// becomes: |
505 |
|
// (seq.++ (seq.unit k_0) (seq.unit k_1) (seq.unit k_2)) |
506 |
|
// where k_0, k_1, k_2 are fresh integer variables. These |
507 |
|
// variables will be assigned values in the standard way by the |
508 |
|
// model. This construction is necessary since the strings solver |
509 |
|
// must constrain the length of the model of an equivalence class |
510 |
|
// (e.g. in this case to length 3); moreover we cannot assign a |
511 |
|
// concrete value since it may conflict with other skeletons we |
512 |
|
// have assigned, e.g. for the case of (seq.unit argVal) above. |
513 |
11 |
SkolemManager* sm = nm->getSkolemManager(); |
514 |
11 |
BoundVarManager* bvm = nm->getBoundVarManager(); |
515 |
11 |
Assert(c.getKind() == CONST_SEQUENCE); |
516 |
11 |
const Sequence& sn = c.getConst<Sequence>(); |
517 |
11 |
const std::vector<Node>& snvec = sn.getVec(); |
518 |
22 |
std::vector<Node> skChildren; |
519 |
37 |
for (const Node& snv : snvec) |
520 |
|
{ |
521 |
52 |
TypeNode etn = snv.getType(); |
522 |
52 |
Node v = bvm->mkBoundVar<SeqModelVarAttribute>(snv, etn); |
523 |
|
// use a skolem, not a bound variable |
524 |
52 |
Node kv = sm->mkPurifySkolem(v, "smv"); |
525 |
26 |
skChildren.push_back(nm->mkNode(SEQ_UNIT, kv)); |
526 |
|
} |
527 |
11 |
c = utils::mkConcat(skChildren, tn); |
528 |
|
} |
529 |
|
// increment |
530 |
1448 |
sel->increment(); |
531 |
1448 |
} while (m->hasTerm(c)); |
532 |
|
} |
533 |
|
else |
534 |
|
{ |
535 |
200 |
c = itp->second; |
536 |
|
} |
537 |
1998 |
Trace("strings-model") << "*** Assigned constant " << c << " for " |
538 |
999 |
<< eqc << std::endl; |
539 |
999 |
processed[eqc] = c; |
540 |
999 |
if (!m->assertEquality(eqc, c, true)) |
541 |
|
{ |
542 |
|
// this should never happen due to the model soundness argument |
543 |
|
// for strings |
544 |
|
Unreachable() |
545 |
|
<< "TheoryStrings::collectModelInfoType: Inconsistent equality" |
546 |
|
<< std::endl; |
547 |
|
return false; |
548 |
|
} |
549 |
|
} |
550 |
|
} |
551 |
|
} |
552 |
900 |
Trace("strings-model") << "String Model : Pure Assigned." << std::endl; |
553 |
|
//step 4 : assign constants to all other equivalence classes |
554 |
10016 |
for (const Node& rn : repVec) |
555 |
|
{ |
556 |
9116 |
if (processed.find(rn) == processed.end()) |
557 |
|
{ |
558 |
2123 |
NormalForm& nf = d_csolver.getNormalForm(rn); |
559 |
2123 |
if (Trace.isOn("strings-model")) |
560 |
|
{ |
561 |
|
Trace("strings-model") |
562 |
|
<< "Construct model for " << rn << " based on normal form "; |
563 |
|
for (unsigned j = 0, size = nf.d_nf.size(); j < size; j++) |
564 |
|
{ |
565 |
|
Node n = nf.d_nf[j]; |
566 |
|
if (j > 0) |
567 |
|
{ |
568 |
|
Trace("strings-model") << " ++ "; |
569 |
|
} |
570 |
|
Trace("strings-model") << n; |
571 |
|
Node r = d_state.getRepresentative(n); |
572 |
|
if (!r.isConst() && processed.find(r) == processed.end()) |
573 |
|
{ |
574 |
|
Trace("strings-model") << "(UNPROCESSED)"; |
575 |
|
} |
576 |
|
} |
577 |
|
} |
578 |
2123 |
Trace("strings-model") << std::endl; |
579 |
4246 |
std::vector< Node > nc; |
580 |
10000 |
for (const Node& n : nf.d_nf) |
581 |
|
{ |
582 |
15754 |
Node r = d_state.getRepresentative(n); |
583 |
7877 |
Assert(r.isConst() || processed.find(r) != processed.end()); |
584 |
7877 |
nc.push_back(r.isConst() ? r : processed[r]); |
585 |
|
} |
586 |
4246 |
Node cc = utils::mkNConcat(nc, tn); |
587 |
4246 |
Trace("strings-model") |
588 |
2123 |
<< "*** Determined constant " << cc << " for " << rn << std::endl; |
589 |
2123 |
processed[rn] = cc; |
590 |
2123 |
if (!m->assertEquality(rn, cc, true)) |
591 |
|
{ |
592 |
|
// this should never happen due to the model soundness argument |
593 |
|
// for strings |
594 |
|
Unreachable() << "TheoryStrings::collectModelInfoType: " |
595 |
|
"Inconsistent equality (unprocessed eqc)" |
596 |
|
<< std::endl; |
597 |
|
return false; |
598 |
|
} |
599 |
2123 |
else if (!cc.isConst()) |
600 |
|
{ |
601 |
|
// the value may be specified by seq.unit components, ensure this |
602 |
|
// is marked as the skeleton for constructing values in this class. |
603 |
3 |
m->assertSkeleton(cc); |
604 |
|
} |
605 |
|
} |
606 |
|
} |
607 |
|
//Trace("strings-model") << "String Model : Assigned." << std::endl; |
608 |
900 |
Trace("strings-model") << "String Model : Finished." << std::endl; |
609 |
900 |
return true; |
610 |
|
} |
611 |
|
|
612 |
|
///////////////////////////////////////////////////////////////////////////// |
613 |
|
// MAIN SOLVER |
614 |
|
///////////////////////////////////////////////////////////////////////////// |
615 |
|
|
616 |
157519 |
void TheoryStrings::preRegisterTerm(TNode n) |
617 |
|
{ |
618 |
315038 |
Trace("strings-preregister") |
619 |
157519 |
<< "TheoryStrings::preRegisterTerm: " << n << std::endl; |
620 |
157519 |
d_termReg.preRegisterTerm(n); |
621 |
|
// Register the term with the extended theory. Notice we do not recurse on |
622 |
|
// this term here since preRegisterTerm is already called recursively on all |
623 |
|
// subterms in preregistered literals. |
624 |
157519 |
d_extTheory.registerTerm(n); |
625 |
157519 |
} |
626 |
|
|
627 |
1073118 |
bool TheoryStrings::preNotifyFact( |
628 |
|
TNode atom, bool pol, TNode fact, bool isPrereg, bool isInternal) |
629 |
|
{ |
630 |
|
// this is only required for internal facts, others are already registered |
631 |
1073118 |
if (isInternal && atom.getKind() == EQUAL) |
632 |
|
{ |
633 |
|
// We must ensure these terms are registered. We register eagerly here for |
634 |
|
// performance reasons. Alternatively, terms could be registered at full |
635 |
|
// effort in e.g. BaseSolver::init. |
636 |
112773 |
for (const Node& t : atom) |
637 |
|
{ |
638 |
75182 |
d_termReg.registerTerm(t, 0); |
639 |
|
} |
640 |
|
} |
641 |
1073118 |
return false; |
642 |
|
} |
643 |
|
|
644 |
1073118 |
void TheoryStrings::notifyFact(TNode atom, |
645 |
|
bool polarity, |
646 |
|
TNode fact, |
647 |
|
bool isInternal) |
648 |
|
{ |
649 |
1073118 |
d_eagerSolver.notifyFact(atom, polarity, fact, isInternal); |
650 |
|
// process pending conflicts due to reasoning about endpoints |
651 |
1073118 |
if (!d_state.isInConflict() && d_state.hasPendingConflict()) |
652 |
|
{ |
653 |
886 |
InferInfo iiPendingConf(InferenceId::UNKNOWN); |
654 |
443 |
d_state.getPendingConflict(iiPendingConf); |
655 |
886 |
Trace("strings-pending") |
656 |
443 |
<< "Process pending conflict " << iiPendingConf.d_premises << std::endl; |
657 |
886 |
Trace("strings-conflict") |
658 |
443 |
<< "CONFLICT: Eager : " << iiPendingConf.d_premises << std::endl; |
659 |
443 |
++(d_statistics.d_conflictsEager); |
660 |
|
// call the inference manager to send the conflict |
661 |
443 |
d_im.processConflict(iiPendingConf); |
662 |
443 |
return; |
663 |
|
} |
664 |
1072675 |
Trace("strings-pending-debug") << " Now collect terms" << std::endl; |
665 |
1072675 |
Trace("strings-pending-debug") << " Finished collect terms" << std::endl; |
666 |
|
} |
667 |
|
|
668 |
450715 |
void TheoryStrings::postCheck(Effort e) |
669 |
|
{ |
670 |
450715 |
d_im.doPendingFacts(); |
671 |
|
|
672 |
450715 |
Assert(d_strat.isStrategyInit()); |
673 |
1349953 |
if (!d_state.isInConflict() && !d_valuation.needCheck() |
674 |
560063 |
&& d_strat.hasStrategyEffort(e)) |
675 |
|
{ |
676 |
52174 |
Trace("strings-check-debug") |
677 |
26087 |
<< "Theory of strings " << e << " effort check " << std::endl; |
678 |
26087 |
if (Trace.isOn("strings-eqc")) |
679 |
|
{ |
680 |
|
Trace("strings-eqc") << debugPrintStringsEqc() << std::endl; |
681 |
|
} |
682 |
26087 |
++(d_statistics.d_checkRuns); |
683 |
26087 |
bool sentLemma = false; |
684 |
26087 |
bool hadPending = false; |
685 |
26087 |
Trace("strings-check") << "Full effort check..." << std::endl; |
686 |
15574 |
do{ |
687 |
41661 |
d_im.reset(); |
688 |
41661 |
++(d_statistics.d_strategyRuns); |
689 |
41661 |
Trace("strings-check") << " * Run strategy..." << std::endl; |
690 |
41661 |
runStrategy(e); |
691 |
|
// remember if we had pending facts or lemmas |
692 |
41661 |
hadPending = d_im.hasPending(); |
693 |
|
// Send the facts *and* the lemmas. We send lemmas regardless of whether |
694 |
|
// we send facts since some lemmas cannot be dropped. Other lemmas are |
695 |
|
// otherwise avoided by aborting the strategy when a fact is ready. |
696 |
41661 |
d_im.doPending(); |
697 |
|
// Did we successfully send a lemma? Notice that if hasPending = true |
698 |
|
// and sentLemma = false, then the above call may have: |
699 |
|
// (1) had no pending lemmas, but successfully processed pending facts, |
700 |
|
// (2) unsuccessfully processed pending lemmas. |
701 |
|
// In either case, we repeat the strategy if we are not in conflict. |
702 |
41661 |
sentLemma = d_im.hasSentLemma(); |
703 |
41661 |
if (Trace.isOn("strings-check")) |
704 |
|
{ |
705 |
|
Trace("strings-check") << " ...finish run strategy: "; |
706 |
|
Trace("strings-check") << (hadPending ? "hadPending " : ""); |
707 |
|
Trace("strings-check") << (sentLemma ? "sentLemma " : ""); |
708 |
|
Trace("strings-check") << (d_state.isInConflict() ? "conflict " : ""); |
709 |
|
if (!hadPending && !sentLemma && !d_state.isInConflict()) |
710 |
|
{ |
711 |
|
Trace("strings-check") << "(none)"; |
712 |
|
} |
713 |
|
Trace("strings-check") << std::endl; |
714 |
|
} |
715 |
|
// repeat if we did not add a lemma or conflict, and we had pending |
716 |
|
// facts or lemmas. |
717 |
41661 |
} while (!d_state.isInConflict() && !sentLemma && hadPending); |
718 |
|
} |
719 |
450715 |
Trace("strings-check") << "Theory of strings, done check : " << e << std::endl; |
720 |
450715 |
Assert(!d_im.hasPendingFact()); |
721 |
450715 |
Assert(!d_im.hasPendingLemma()); |
722 |
450715 |
} |
723 |
|
|
724 |
6379 |
bool TheoryStrings::needsCheckLastEffort() { |
725 |
6379 |
if( options::stringGuessModel() ){ |
726 |
|
return d_esolver.hasExtendedFunctions(); |
727 |
|
} |
728 |
6379 |
return false; |
729 |
|
} |
730 |
|
|
731 |
|
/** Conflict when merging two constants */ |
732 |
1135 |
void TheoryStrings::conflict(TNode a, TNode b){ |
733 |
1135 |
if (d_state.isInConflict()) |
734 |
|
{ |
735 |
|
// already in conflict |
736 |
|
return; |
737 |
|
} |
738 |
1135 |
d_im.conflictEqConstantMerge(a, b); |
739 |
1135 |
Trace("strings-conflict") << "CONFLICT: Eq engine conflict" << std::endl; |
740 |
1135 |
++(d_statistics.d_conflictsEqEngine); |
741 |
|
} |
742 |
|
|
743 |
124678 |
void TheoryStrings::eqNotifyNewClass(TNode t){ |
744 |
124678 |
Kind k = t.getKind(); |
745 |
124678 |
if (k == STRING_LENGTH || k == STRING_TO_CODE) |
746 |
|
{ |
747 |
36954 |
Trace("strings-debug") << "New length eqc : " << t << std::endl; |
748 |
|
//we care about the length of this string |
749 |
36954 |
d_termReg.registerTerm(t[0], 1); |
750 |
|
} |
751 |
124678 |
d_eagerSolver.eqNotifyNewClass(t); |
752 |
124678 |
} |
753 |
|
|
754 |
13570 |
void TheoryStrings::addCarePairs(TNodeTrie* t1, |
755 |
|
TNodeTrie* t2, |
756 |
|
unsigned arity, |
757 |
|
unsigned depth) |
758 |
|
{ |
759 |
13570 |
if( depth==arity ){ |
760 |
1243 |
if( t2!=NULL ){ |
761 |
2486 |
Node f1 = t1->getData(); |
762 |
2486 |
Node f2 = t2->getData(); |
763 |
1243 |
if (!d_equalityEngine->areEqual(f1, f2)) |
764 |
|
{ |
765 |
493 |
Trace("strings-cg-debug") << "TheoryStrings::computeCareGraph(): checking function " << f1 << " and " << f2 << std::endl; |
766 |
986 |
vector< pair<TNode, TNode> > currentPairs; |
767 |
1525 |
for (unsigned k = 0; k < f1.getNumChildren(); ++ k) { |
768 |
2064 |
TNode x = f1[k]; |
769 |
2064 |
TNode y = f2[k]; |
770 |
1032 |
Assert(d_equalityEngine->hasTerm(x)); |
771 |
1032 |
Assert(d_equalityEngine->hasTerm(y)); |
772 |
1032 |
Assert(!d_equalityEngine->areDisequal(x, y, false)); |
773 |
1032 |
Assert(!areCareDisequal(x, y)); |
774 |
1032 |
if (!d_equalityEngine->areEqual(x, y)) |
775 |
|
{ |
776 |
1533 |
if (d_equalityEngine->isTriggerTerm(x, THEORY_STRINGS) |
777 |
1533 |
&& d_equalityEngine->isTriggerTerm(y, THEORY_STRINGS)) |
778 |
|
{ |
779 |
192 |
TNode x_shared = d_equalityEngine->getTriggerTermRepresentative( |
780 |
384 |
x, THEORY_STRINGS); |
781 |
192 |
TNode y_shared = d_equalityEngine->getTriggerTermRepresentative( |
782 |
384 |
y, THEORY_STRINGS); |
783 |
192 |
currentPairs.push_back(make_pair(x_shared, y_shared)); |
784 |
|
} |
785 |
|
} |
786 |
|
} |
787 |
685 |
for (unsigned c = 0; c < currentPairs.size(); ++ c) { |
788 |
192 |
Trace("strings-cg-pair") << "TheoryStrings::computeCareGraph(): pair : " << currentPairs[c].first << " " << currentPairs[c].second << std::endl; |
789 |
192 |
addCarePair(currentPairs[c].first, currentPairs[c].second); |
790 |
|
} |
791 |
|
} |
792 |
|
} |
793 |
|
}else{ |
794 |
12327 |
if( t2==NULL ){ |
795 |
10340 |
if( depth<(arity-1) ){ |
796 |
|
//add care pairs internal to each child |
797 |
10845 |
for (std::pair<const TNode, TNodeTrie>& tt : t1->d_data) |
798 |
|
{ |
799 |
7117 |
addCarePairs(&tt.second, nullptr, arity, depth + 1); |
800 |
|
} |
801 |
|
} |
802 |
|
//add care pairs based on each pair of non-disequal arguments |
803 |
33551 |
for (std::map<TNode, TNodeTrie>::iterator it = t1->d_data.begin(); |
804 |
33551 |
it != t1->d_data.end(); |
805 |
|
++it) |
806 |
|
{ |
807 |
23211 |
std::map<TNode, TNodeTrie>::iterator it2 = it; |
808 |
23211 |
++it2; |
809 |
200541 |
for( ; it2 != t1->d_data.end(); ++it2 ){ |
810 |
88665 |
if (!d_equalityEngine->areDisequal(it->first, it2->first, false)) |
811 |
|
{ |
812 |
2890 |
if( !areCareDisequal(it->first, it2->first) ){ |
813 |
1591 |
addCarePairs( &it->second, &it2->second, arity, depth+1 ); |
814 |
|
} |
815 |
|
} |
816 |
|
} |
817 |
|
} |
818 |
|
}else{ |
819 |
|
//add care pairs based on product of indices, non-disequal arguments |
820 |
5051 |
for (std::pair<const TNode, TNodeTrie>& tt1 : t1->d_data) |
821 |
|
{ |
822 |
8794 |
for (std::pair<const TNode, TNodeTrie>& tt2 : t2->d_data) |
823 |
|
{ |
824 |
5730 |
if (!d_equalityEngine->areDisequal(tt1.first, tt2.first, false)) |
825 |
|
{ |
826 |
3207 |
if (!areCareDisequal(tt1.first, tt2.first)) |
827 |
|
{ |
828 |
1639 |
addCarePairs(&tt1.second, &tt2.second, arity, depth + 1); |
829 |
|
} |
830 |
|
} |
831 |
|
} |
832 |
|
} |
833 |
|
} |
834 |
|
} |
835 |
13570 |
} |
836 |
|
|
837 |
6957 |
void TheoryStrings::computeCareGraph(){ |
838 |
|
//computing the care graph here is probably still necessary, due to operators that take non-string arguments TODO: verify |
839 |
6957 |
Trace("strings-cg") << "TheoryStrings::computeCareGraph(): Build term indices..." << std::endl; |
840 |
|
// Term index for each (type, operator) pair. We require the operator here |
841 |
|
// since operators are polymorphic, taking strings/sequences. |
842 |
13914 |
std::map<std::pair<TypeNode, Node>, TNodeTrie> index; |
843 |
13914 |
std::map< Node, unsigned > arity; |
844 |
6957 |
const context::CDList<TNode>& fterms = d_termReg.getFunctionTerms(); |
845 |
6957 |
size_t functionTerms = fterms.size(); |
846 |
56292 |
for (unsigned i = 0; i < functionTerms; ++ i) { |
847 |
98670 |
TNode f1 = fterms[i]; |
848 |
49335 |
Trace("strings-cg") << "...build for " << f1 << std::endl; |
849 |
98670 |
Node op = f1.getOperator(); |
850 |
98670 |
std::vector< TNode > reps; |
851 |
49335 |
bool has_trigger_arg = false; |
852 |
113331 |
for( unsigned j=0; j<f1.getNumChildren(); j++ ){ |
853 |
63996 |
reps.push_back(d_equalityEngine->getRepresentative(f1[j])); |
854 |
63996 |
if (d_equalityEngine->isTriggerTerm(f1[j], THEORY_STRINGS)) |
855 |
|
{ |
856 |
47252 |
has_trigger_arg = true; |
857 |
|
} |
858 |
|
} |
859 |
49335 |
if( has_trigger_arg ){ |
860 |
73428 |
TypeNode ft = utils::getOwnerStringType(f1); |
861 |
73428 |
std::pair<TypeNode, Node> ikey = std::pair<TypeNode, Node>(ft, op); |
862 |
36714 |
index[ikey].addTerm(f1, reps); |
863 |
36714 |
arity[op] = reps.size(); |
864 |
|
} |
865 |
|
} |
866 |
|
//for each index |
867 |
10180 |
for (std::pair<const std::pair<TypeNode, Node>, TNodeTrie>& ti : index) |
868 |
|
{ |
869 |
6446 |
Trace("strings-cg") << "TheoryStrings::computeCareGraph(): Process index " |
870 |
3223 |
<< ti.first << "..." << std::endl; |
871 |
6446 |
Node op = ti.first.second; |
872 |
3223 |
addCarePairs(&ti.second, nullptr, arity[op], 0); |
873 |
|
} |
874 |
6957 |
} |
875 |
|
|
876 |
|
void TheoryStrings::checkRegisterTermsPreNormalForm() |
877 |
|
{ |
878 |
|
const std::vector<Node>& seqc = d_bsolver.getStringEqc(); |
879 |
|
for (const Node& eqc : seqc) |
880 |
|
{ |
881 |
|
eq::EqClassIterator eqc_i = eq::EqClassIterator(eqc, d_equalityEngine); |
882 |
|
while (!eqc_i.isFinished()) |
883 |
|
{ |
884 |
|
Node n = (*eqc_i); |
885 |
|
if (!d_bsolver.isCongruent(n)) |
886 |
|
{ |
887 |
|
d_termReg.registerTerm(n, 2); |
888 |
|
} |
889 |
|
++eqc_i; |
890 |
|
} |
891 |
|
} |
892 |
|
} |
893 |
|
|
894 |
10910 |
void TheoryStrings::checkCodes() |
895 |
|
{ |
896 |
|
// ensure that lemmas regarding str.code been added for each constant string |
897 |
|
// of length one |
898 |
10910 |
if (d_termReg.hasStringCode()) |
899 |
|
{ |
900 |
1634 |
NodeManager* nm = NodeManager::currentNM(); |
901 |
|
// str.code applied to the code term for each equivalence class that has a |
902 |
|
// code term but is not a constant |
903 |
3052 |
std::vector<Node> nconst_codes; |
904 |
|
// str.code applied to the proxy variables for each equivalence classes that |
905 |
|
// are constants of size one |
906 |
3052 |
std::vector<Node> const_codes; |
907 |
1634 |
const std::vector<Node>& seqc = d_bsolver.getStringEqc(); |
908 |
22614 |
for (const Node& eqc : seqc) |
909 |
|
{ |
910 |
20980 |
NormalForm& nfe = d_csolver.getNormalForm(eqc); |
911 |
20980 |
if (nfe.d_nf.size() == 1 && nfe.d_nf[0].isConst()) |
912 |
|
{ |
913 |
15104 |
Node c = nfe.d_nf[0]; |
914 |
15104 |
Trace("strings-code-debug") << "Get proxy variable for " << c |
915 |
7552 |
<< std::endl; |
916 |
15104 |
Node cc = nm->mkNode(kind::STRING_TO_CODE, c); |
917 |
7552 |
cc = rewrite(cc); |
918 |
7552 |
Assert(cc.isConst()); |
919 |
15104 |
Node cp = d_termReg.ensureProxyVariableFor(c); |
920 |
15104 |
Node vc = nm->mkNode(STRING_TO_CODE, cp); |
921 |
7552 |
if (!d_state.areEqual(cc, vc)) |
922 |
|
{ |
923 |
1328 |
std::vector<Node> emptyVec; |
924 |
664 |
d_im.sendInference(emptyVec, cc.eqNode(vc), InferenceId::STRINGS_CODE_PROXY); |
925 |
|
} |
926 |
7552 |
const_codes.push_back(vc); |
927 |
|
} |
928 |
|
else |
929 |
|
{ |
930 |
13428 |
EqcInfo* ei = d_state.getOrMakeEqcInfo(eqc, false); |
931 |
13428 |
if (ei && !ei->d_codeTerm.get().isNull()) |
932 |
|
{ |
933 |
7132 |
Node vc = nm->mkNode(kind::STRING_TO_CODE, ei->d_codeTerm.get()); |
934 |
3566 |
nconst_codes.push_back(vc); |
935 |
|
} |
936 |
|
} |
937 |
|
} |
938 |
1634 |
if (d_im.hasProcessed()) |
939 |
|
{ |
940 |
216 |
return; |
941 |
|
} |
942 |
|
// now, ensure that str.code is injective |
943 |
2836 |
std::vector<Node> cmps; |
944 |
1418 |
cmps.insert(cmps.end(), const_codes.rbegin(), const_codes.rend()); |
945 |
1418 |
cmps.insert(cmps.end(), nconst_codes.rbegin(), nconst_codes.rend()); |
946 |
4590 |
for (unsigned i = 0, num_ncc = nconst_codes.size(); i < num_ncc; i++) |
947 |
|
{ |
948 |
6344 |
Node c1 = nconst_codes[i]; |
949 |
3172 |
cmps.pop_back(); |
950 |
19975 |
for (const Node& c2 : cmps) |
951 |
|
{ |
952 |
33606 |
Trace("strings-code-debug") |
953 |
16803 |
<< "Compare codes : " << c1 << " " << c2 << std::endl; |
954 |
16803 |
if (!d_state.areDisequal(c1, c2) && !d_state.areEqual(c1, d_neg_one)) |
955 |
|
{ |
956 |
2532 |
Node eq_no = c1.eqNode(d_neg_one); |
957 |
2532 |
Node deq = c1.eqNode(c2).negate(); |
958 |
2532 |
Node eqn = c1[0].eqNode(c2[0]); |
959 |
|
// str.code(x)==-1 V str.code(x)!=str.code(y) V x==y |
960 |
2532 |
Node inj_lem = nm->mkNode(kind::OR, eq_no, deq, eqn); |
961 |
1266 |
deq = rewrite(deq); |
962 |
1266 |
d_im.addPendingPhaseRequirement(deq, false); |
963 |
2532 |
std::vector<Node> emptyVec; |
964 |
1266 |
d_im.sendInference(emptyVec, inj_lem, InferenceId::STRINGS_CODE_INJ); |
965 |
|
} |
966 |
|
} |
967 |
|
} |
968 |
|
} |
969 |
|
} |
970 |
|
|
971 |
|
void TheoryStrings::checkRegisterTermsNormalForms() |
972 |
|
{ |
973 |
|
const std::vector<Node>& seqc = d_bsolver.getStringEqc(); |
974 |
|
for (const Node& eqc : seqc) |
975 |
|
{ |
976 |
|
NormalForm& nfi = d_csolver.getNormalForm(eqc); |
977 |
|
// check if there is a length term for this equivalence class |
978 |
|
EqcInfo* ei = d_state.getOrMakeEqcInfo(eqc, false); |
979 |
|
Node lt = ei ? ei->d_lengthTerm : Node::null(); |
980 |
|
if (lt.isNull()) |
981 |
|
{ |
982 |
|
Node c = utils::mkNConcat(nfi.d_nf, eqc.getType()); |
983 |
|
d_termReg.registerTerm(c, 3); |
984 |
|
} |
985 |
|
} |
986 |
|
} |
987 |
|
|
988 |
125355 |
TrustNode TheoryStrings::ppRewrite(TNode atom, std::vector<SkolemLemma>& lems) |
989 |
|
{ |
990 |
125355 |
Trace("strings-ppr") << "TheoryStrings::ppRewrite " << atom << std::endl; |
991 |
125355 |
if (atom.getKind() == EQUAL) |
992 |
|
{ |
993 |
|
// always apply aggressive equality rewrites here |
994 |
3889 |
Node ret = d_rewriter.rewriteEqualityExt(atom); |
995 |
2008 |
if (ret != atom) |
996 |
|
{ |
997 |
127 |
return TrustNode::mkTrustRewrite(atom, ret, nullptr); |
998 |
|
} |
999 |
|
} |
1000 |
125228 |
if (atom.getKind() == STRING_FROM_CODE) |
1001 |
|
{ |
1002 |
|
// str.from_code(t) ---> |
1003 |
|
// witness k. ite(0 <= t < |A|, t = str.to_code(k), k = "") |
1004 |
63 |
NodeManager* nm = NodeManager::currentNM(); |
1005 |
126 |
Node t = atom[0]; |
1006 |
126 |
Node card = nm->mkConst(Rational(utils::getAlphabetCardinality())); |
1007 |
|
Node cond = |
1008 |
126 |
nm->mkNode(AND, nm->mkNode(LEQ, d_zero, t), nm->mkNode(LT, t, card)); |
1009 |
126 |
Node v = nm->mkBoundVar(nm->stringType()); |
1010 |
126 |
Node emp = Word::mkEmptyWord(atom.getType()); |
1011 |
|
Node pred = nm->mkNode( |
1012 |
126 |
ITE, cond, t.eqNode(nm->mkNode(STRING_TO_CODE, v)), v.eqNode(emp)); |
1013 |
63 |
SkolemManager* sm = nm->getSkolemManager(); |
1014 |
126 |
Node ret = sm->mkSkolem(v, pred, "kFromCode"); |
1015 |
63 |
lems.push_back(SkolemLemma(ret, nullptr)); |
1016 |
63 |
return TrustNode::mkTrustRewrite(atom, ret, nullptr); |
1017 |
|
} |
1018 |
250330 |
TrustNode ret; |
1019 |
250330 |
Node atomRet = atom; |
1020 |
125165 |
if (options::regExpElim() && atom.getKind() == STRING_IN_REGEXP) |
1021 |
|
{ |
1022 |
|
// aggressive elimination of regular expression membership |
1023 |
134 |
ret = d_regexp_elim.eliminateTrusted(atomRet); |
1024 |
134 |
if (!ret.isNull()) |
1025 |
|
{ |
1026 |
184 |
Trace("strings-ppr") << " rewrote " << atom << " -> " << ret.getNode() |
1027 |
92 |
<< " via regular expression elimination." |
1028 |
92 |
<< std::endl; |
1029 |
92 |
atomRet = ret.getNode(); |
1030 |
|
} |
1031 |
|
} |
1032 |
125165 |
return ret; |
1033 |
|
} |
1034 |
|
|
1035 |
|
/** run the given inference step */ |
1036 |
287245 |
void TheoryStrings::runInferStep(InferStep s, int effort) |
1037 |
|
{ |
1038 |
287245 |
Trace("strings-process") << "Run " << s; |
1039 |
287245 |
if (effort > 0) |
1040 |
|
{ |
1041 |
41201 |
Trace("strings-process") << ", effort = " << effort; |
1042 |
|
} |
1043 |
287245 |
Trace("strings-process") << "..." << std::endl; |
1044 |
287245 |
switch (s) |
1045 |
|
{ |
1046 |
41661 |
case CHECK_INIT: d_bsolver.checkInit(); break; |
1047 |
34790 |
case CHECK_CONST_EQC: d_bsolver.checkConstantEquivalenceClasses(); break; |
1048 |
46689 |
case CHECK_EXTF_EVAL: d_esolver.checkExtfEval(effort); break; |
1049 |
32619 |
case CHECK_CYCLES: d_csolver.checkCycles(); break; |
1050 |
32615 |
case CHECK_FLAT_FORMS: d_csolver.checkFlatForms(); break; |
1051 |
|
case CHECK_REGISTER_TERMS_PRE_NF: checkRegisterTermsPreNormalForm(); break; |
1052 |
20896 |
case CHECK_NORMAL_FORMS_EQ: d_csolver.checkNormalFormsEq(); break; |
1053 |
11391 |
case CHECK_NORMAL_FORMS_DEQ: d_csolver.checkNormalFormsDeq(); break; |
1054 |
10910 |
case CHECK_CODES: checkCodes(); break; |
1055 |
10486 |
case CHECK_LENGTH_EQC: d_csolver.checkLengthsEqc(); break; |
1056 |
|
case CHECK_REGISTER_TERMS_NF: checkRegisterTermsNormalForms(); break; |
1057 |
29302 |
case CHECK_EXTF_REDUCTION: d_esolver.checkExtfReductions(effort); break; |
1058 |
8150 |
case CHECK_MEMBERSHIP: d_rsolver.checkMemberships(); break; |
1059 |
7736 |
case CHECK_CARDINALITY: d_bsolver.checkCardinality(); break; |
1060 |
|
default: Unreachable(); break; |
1061 |
|
} |
1062 |
574490 |
Trace("strings-process") << "Done " << s |
1063 |
574490 |
<< ", addedFact = " << d_im.hasPendingFact() |
1064 |
574490 |
<< ", addedLemma = " << d_im.hasPendingLemma() |
1065 |
574490 |
<< ", conflict = " << d_state.isInConflict() |
1066 |
287245 |
<< std::endl; |
1067 |
287245 |
} |
1068 |
|
|
1069 |
41661 |
void TheoryStrings::runStrategy(Theory::Effort e) |
1070 |
|
{ |
1071 |
41661 |
std::vector<std::pair<InferStep, int> >::iterator it = d_strat.stepBegin(e); |
1072 |
|
std::vector<std::pair<InferStep, int> >::iterator stepEnd = |
1073 |
41661 |
d_strat.stepEnd(e); |
1074 |
|
|
1075 |
41661 |
Trace("strings-process") << "----check, next round---" << std::endl; |
1076 |
1104531 |
while (it != stepEnd) |
1077 |
|
{ |
1078 |
565348 |
InferStep curr = it->first; |
1079 |
565348 |
if (curr == BREAK) |
1080 |
|
{ |
1081 |
278103 |
if (d_im.hasProcessed()) |
1082 |
|
{ |
1083 |
32519 |
break; |
1084 |
|
} |
1085 |
|
} |
1086 |
|
else |
1087 |
|
{ |
1088 |
287245 |
runInferStep(curr, it->second); |
1089 |
287245 |
if (d_state.isInConflict()) |
1090 |
|
{ |
1091 |
1394 |
break; |
1092 |
|
} |
1093 |
|
} |
1094 |
531435 |
++it; |
1095 |
|
} |
1096 |
41661 |
Trace("strings-process") << "----finished round---" << std::endl; |
1097 |
41661 |
} |
1098 |
|
|
1099 |
|
std::string TheoryStrings::debugPrintStringsEqc() |
1100 |
|
{ |
1101 |
|
std::stringstream ss; |
1102 |
|
for (unsigned t = 0; t < 2; t++) |
1103 |
|
{ |
1104 |
|
eq::EqClassesIterator eqcs2_i = eq::EqClassesIterator(d_equalityEngine); |
1105 |
|
ss << (t == 0 ? "STRINGS:" : "OTHER:") << std::endl; |
1106 |
|
while (!eqcs2_i.isFinished()) |
1107 |
|
{ |
1108 |
|
Node eqc = (*eqcs2_i); |
1109 |
|
bool print = (t == 0 && eqc.getType().isStringLike()) |
1110 |
|
|| (t == 1 && !eqc.getType().isStringLike()); |
1111 |
|
if (print) |
1112 |
|
{ |
1113 |
|
eq::EqClassIterator eqc2_i = eq::EqClassIterator(eqc, d_equalityEngine); |
1114 |
|
ss << "Eqc( " << eqc << " ) : { "; |
1115 |
|
while (!eqc2_i.isFinished()) |
1116 |
|
{ |
1117 |
|
if ((*eqc2_i) != eqc && (*eqc2_i).getKind() != kind::EQUAL) |
1118 |
|
{ |
1119 |
|
ss << (*eqc2_i) << " "; |
1120 |
|
} |
1121 |
|
++eqc2_i; |
1122 |
|
} |
1123 |
|
ss << " } " << std::endl; |
1124 |
|
EqcInfo* ei = d_state.getOrMakeEqcInfo(eqc, false); |
1125 |
|
if (ei) |
1126 |
|
{ |
1127 |
|
Trace("strings-eqc-debug") |
1128 |
|
<< "* Length term : " << ei->d_lengthTerm.get() << std::endl; |
1129 |
|
Trace("strings-eqc-debug") |
1130 |
|
<< "* Cardinality lemma k : " << ei->d_cardinalityLemK.get() |
1131 |
|
<< std::endl; |
1132 |
|
Trace("strings-eqc-debug") |
1133 |
|
<< "* Normalization length lemma : " |
1134 |
|
<< ei->d_normalizedLength.get() << std::endl; |
1135 |
|
} |
1136 |
|
} |
1137 |
|
++eqcs2_i; |
1138 |
|
} |
1139 |
|
ss << std::endl; |
1140 |
|
} |
1141 |
|
ss << std::endl; |
1142 |
|
return ss.str(); |
1143 |
|
} |
1144 |
|
|
1145 |
|
} // namespace strings |
1146 |
|
} // namespace theory |
1147 |
29511 |
} // namespace cvc5 |