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