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