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/kind.h" |
19 |
|
#include "options/smt_options.h" |
20 |
|
#include "options/strings_options.h" |
21 |
|
#include "options/theory_options.h" |
22 |
|
#include "smt/logic_exception.h" |
23 |
|
#include "theory/decision_manager.h" |
24 |
|
#include "theory/ext_theory.h" |
25 |
|
#include "theory/rewriter.h" |
26 |
|
#include "theory/strings/theory_strings_utils.h" |
27 |
|
#include "theory/strings/type_enumerator.h" |
28 |
|
#include "theory/strings/word.h" |
29 |
|
#include "theory/theory_model.h" |
30 |
|
#include "theory/valuation.h" |
31 |
|
|
32 |
|
using namespace std; |
33 |
|
using namespace cvc5::context; |
34 |
|
using namespace cvc5::kind; |
35 |
|
|
36 |
|
namespace cvc5 { |
37 |
|
namespace theory { |
38 |
|
namespace strings { |
39 |
|
|
40 |
8954 |
TheoryStrings::TheoryStrings(context::Context* c, |
41 |
|
context::UserContext* u, |
42 |
|
OutputChannel& out, |
43 |
|
Valuation valuation, |
44 |
|
const LogicInfo& logicInfo, |
45 |
8954 |
ProofNodeManager* pnm) |
46 |
|
: Theory(THEORY_STRINGS, c, u, out, valuation, logicInfo, pnm), |
47 |
|
d_notify(*this), |
48 |
|
d_statistics(), |
49 |
|
d_state(c, u, d_valuation), |
50 |
|
d_eagerSolver(d_state), |
51 |
|
d_termReg(d_state, d_statistics, pnm), |
52 |
|
d_extTheoryCb(), |
53 |
|
d_extTheory(d_extTheoryCb, c, u, out), |
54 |
|
d_im(*this, d_state, d_termReg, d_extTheory, d_statistics, pnm), |
55 |
|
d_rewriter(&d_statistics.d_rewrites), |
56 |
|
d_bsolver(d_state, d_im), |
57 |
|
d_csolver(d_state, d_im, d_termReg, d_bsolver), |
58 |
|
d_esolver(d_state, |
59 |
|
d_im, |
60 |
|
d_termReg, |
61 |
|
d_rewriter, |
62 |
|
d_bsolver, |
63 |
|
d_csolver, |
64 |
|
d_extTheory, |
65 |
|
d_statistics), |
66 |
|
d_rsolver(d_state, |
67 |
|
d_im, |
68 |
|
d_termReg.getSkolemCache(), |
69 |
|
d_csolver, |
70 |
|
d_esolver, |
71 |
|
d_statistics), |
72 |
17917 |
d_regexp_elim(options::regExpElimAgg(), pnm, u), |
73 |
17908 |
d_stringsFmf(c, u, valuation, d_termReg) |
74 |
|
{ |
75 |
8954 |
d_termReg.finishInit(&d_im); |
76 |
|
|
77 |
8954 |
d_zero = NodeManager::currentNM()->mkConst( Rational( 0 ) ); |
78 |
8954 |
d_one = NodeManager::currentNM()->mkConst( Rational( 1 ) ); |
79 |
8954 |
d_neg_one = NodeManager::currentNM()->mkConst(Rational(-1)); |
80 |
8954 |
d_true = NodeManager::currentNM()->mkConst( true ); |
81 |
8954 |
d_false = NodeManager::currentNM()->mkConst( false ); |
82 |
|
|
83 |
8954 |
d_cardSize = utils::getAlphabetCardinality(); |
84 |
|
|
85 |
|
// set up the extended function callback |
86 |
8954 |
d_extTheoryCb.d_esolver = &d_esolver; |
87 |
|
|
88 |
|
// use the state object as the official theory state |
89 |
8954 |
d_theoryState = &d_state; |
90 |
|
// use the inference manager as the official inference manager |
91 |
8954 |
d_inferManager = &d_im; |
92 |
8954 |
} |
93 |
|
|
94 |
17908 |
TheoryStrings::~TheoryStrings() { |
95 |
|
|
96 |
17908 |
} |
97 |
|
|
98 |
8954 |
TheoryRewriter* TheoryStrings::getTheoryRewriter() { return &d_rewriter; } |
99 |
|
|
100 |
3117 |
ProofRuleChecker* TheoryStrings::getProofChecker() { return &d_checker; } |
101 |
|
|
102 |
8954 |
bool TheoryStrings::needsEqualityEngine(EeSetupInfo& esi) |
103 |
|
{ |
104 |
8954 |
esi.d_notify = &d_notify; |
105 |
8954 |
esi.d_name = "theory::strings::ee"; |
106 |
8954 |
return true; |
107 |
|
} |
108 |
|
|
109 |
8954 |
void TheoryStrings::finishInit() |
110 |
|
{ |
111 |
8954 |
Assert(d_equalityEngine != nullptr); |
112 |
|
|
113 |
|
// witness is used to eliminate str.from_code |
114 |
8954 |
d_valuation.setUnevaluatedKind(WITNESS); |
115 |
|
|
116 |
17908 |
bool eagerEval = options::stringEagerEval(); |
117 |
|
// The kinds we are treating as function application in congruence |
118 |
8954 |
d_equalityEngine->addFunctionKind(kind::STRING_LENGTH, eagerEval); |
119 |
8954 |
d_equalityEngine->addFunctionKind(kind::STRING_CONCAT, eagerEval); |
120 |
8954 |
d_equalityEngine->addFunctionKind(kind::STRING_IN_REGEXP, eagerEval); |
121 |
8954 |
d_equalityEngine->addFunctionKind(kind::STRING_TO_CODE, eagerEval); |
122 |
8954 |
d_equalityEngine->addFunctionKind(kind::SEQ_UNIT, eagerEval); |
123 |
|
// `seq.nth` is not always defined, and so we do not evaluate it eagerly. |
124 |
8954 |
d_equalityEngine->addFunctionKind(kind::SEQ_NTH, false); |
125 |
|
// extended functions |
126 |
8954 |
d_equalityEngine->addFunctionKind(kind::STRING_STRCTN, eagerEval); |
127 |
8954 |
d_equalityEngine->addFunctionKind(kind::STRING_LEQ, eagerEval); |
128 |
8954 |
d_equalityEngine->addFunctionKind(kind::STRING_SUBSTR, eagerEval); |
129 |
8954 |
d_equalityEngine->addFunctionKind(kind::STRING_UPDATE, eagerEval); |
130 |
8954 |
d_equalityEngine->addFunctionKind(kind::STRING_ITOS, eagerEval); |
131 |
8954 |
d_equalityEngine->addFunctionKind(kind::STRING_STOI, eagerEval); |
132 |
8954 |
d_equalityEngine->addFunctionKind(kind::STRING_STRIDOF, eagerEval); |
133 |
8954 |
d_equalityEngine->addFunctionKind(kind::STRING_STRREPL, eagerEval); |
134 |
8954 |
d_equalityEngine->addFunctionKind(kind::STRING_STRREPLALL, eagerEval); |
135 |
8954 |
d_equalityEngine->addFunctionKind(kind::STRING_REPLACE_RE, eagerEval); |
136 |
8954 |
d_equalityEngine->addFunctionKind(kind::STRING_REPLACE_RE_ALL, eagerEval); |
137 |
8954 |
d_equalityEngine->addFunctionKind(kind::STRING_STRREPLALL, eagerEval); |
138 |
8954 |
d_equalityEngine->addFunctionKind(kind::STRING_TOLOWER, eagerEval); |
139 |
8954 |
d_equalityEngine->addFunctionKind(kind::STRING_TOUPPER, eagerEval); |
140 |
8954 |
d_equalityEngine->addFunctionKind(kind::STRING_REV, eagerEval); |
141 |
8954 |
} |
142 |
|
|
143 |
|
std::string TheoryStrings::identify() const |
144 |
|
{ |
145 |
|
return std::string("TheoryStrings"); |
146 |
|
} |
147 |
|
|
148 |
2564 |
bool TheoryStrings::areCareDisequal( TNode x, TNode y ) { |
149 |
2564 |
Assert(d_equalityEngine->hasTerm(x)); |
150 |
2564 |
Assert(d_equalityEngine->hasTerm(y)); |
151 |
7692 |
if (d_equalityEngine->isTriggerTerm(x, THEORY_STRINGS) |
152 |
7692 |
&& d_equalityEngine->isTriggerTerm(y, THEORY_STRINGS)) |
153 |
|
{ |
154 |
|
TNode x_shared = |
155 |
2929 |
d_equalityEngine->getTriggerTermRepresentative(x, THEORY_STRINGS); |
156 |
|
TNode y_shared = |
157 |
2929 |
d_equalityEngine->getTriggerTermRepresentative(y, THEORY_STRINGS); |
158 |
1918 |
EqualityStatus eqStatus = d_valuation.getEqualityStatus(x_shared, y_shared); |
159 |
1918 |
if( eqStatus==EQUALITY_FALSE_AND_PROPAGATED || eqStatus==EQUALITY_FALSE || eqStatus==EQUALITY_FALSE_IN_MODEL ){ |
160 |
907 |
return true; |
161 |
|
} |
162 |
|
} |
163 |
1657 |
return false; |
164 |
|
} |
165 |
|
|
166 |
420017 |
bool TheoryStrings::propagateLit(TNode literal) |
167 |
|
{ |
168 |
420017 |
return d_im.propagateLit(literal); |
169 |
|
} |
170 |
|
|
171 |
24323 |
TrustNode TheoryStrings::explain(TNode literal) |
172 |
|
{ |
173 |
24323 |
Debug("strings-explain") << "explain called on " << literal << std::endl; |
174 |
24323 |
return d_im.explainLit(literal); |
175 |
|
} |
176 |
|
|
177 |
12744 |
void TheoryStrings::presolve() { |
178 |
12744 |
Debug("strings-presolve") << "TheoryStrings::Presolving : get fmf options " << (options::stringFMF() ? "true" : "false") << std::endl; |
179 |
12744 |
d_strat.initializeStrategy(); |
180 |
|
|
181 |
|
// if strings fmf is enabled, register the strategy |
182 |
12744 |
if (options::stringFMF()) |
183 |
|
{ |
184 |
63 |
d_stringsFmf.presolve(); |
185 |
|
// This strategy is local to a check-sat call, since we refresh the strategy |
186 |
|
// on every call to presolve. |
187 |
63 |
d_im.getDecisionManager()->registerStrategy( |
188 |
|
DecisionManager::STRAT_STRINGS_SUM_LENGTHS, |
189 |
|
d_stringsFmf.getDecisionStrategy(), |
190 |
|
DecisionManager::STRAT_SCOPE_LOCAL_SOLVE); |
191 |
|
} |
192 |
12744 |
Debug("strings-presolve") << "Finished presolve" << std::endl; |
193 |
12744 |
} |
194 |
|
|
195 |
|
|
196 |
|
///////////////////////////////////////////////////////////////////////////// |
197 |
|
// MODEL GENERATION |
198 |
|
///////////////////////////////////////////////////////////////////////////// |
199 |
|
|
200 |
4854 |
bool TheoryStrings::collectModelValues(TheoryModel* m, |
201 |
|
const std::set<Node>& termSet) |
202 |
|
{ |
203 |
4854 |
if (Trace.isOn("strings-debug-model")) |
204 |
|
{ |
205 |
|
Trace("strings-debug-model") |
206 |
|
<< "TheoryStrings::collectModelValues" << std::endl; |
207 |
|
Trace("strings-debug-model") << "Equivalence classes are:" << std::endl; |
208 |
|
Trace("strings-debug-model") << debugPrintStringsEqc() << std::endl; |
209 |
|
Trace("strings-debug-model") << "Extended functions are:" << std::endl; |
210 |
|
Trace("strings-debug-model") << d_esolver.debugPrintModel() << std::endl; |
211 |
|
} |
212 |
4854 |
Trace("strings-model") << "TheoryStrings::collectModelValues" << std::endl; |
213 |
9708 |
std::map<TypeNode, std::unordered_set<Node> > repSet; |
214 |
|
// Generate model |
215 |
|
// get the relevant string equivalence classes |
216 |
65517 |
for (const Node& s : termSet) |
217 |
|
{ |
218 |
121326 |
TypeNode tn = s.getType(); |
219 |
60663 |
if (tn.isStringLike()) |
220 |
|
{ |
221 |
59824 |
Node r = d_state.getRepresentative(s); |
222 |
29912 |
repSet[tn].insert(r); |
223 |
|
} |
224 |
|
} |
225 |
5557 |
for (const std::pair<const TypeNode, std::unordered_set<Node> >& rst : repSet) |
226 |
|
{ |
227 |
|
// get partition of strings of equal lengths, per type |
228 |
1408 |
std::map<TypeNode, std::vector<std::vector<Node> > > colT; |
229 |
1408 |
std::map<TypeNode, std::vector<Node> > ltsT; |
230 |
1408 |
std::vector<Node> repVec(rst.second.begin(), rst.second.end()); |
231 |
704 |
d_state.separateByLength(repVec, colT, ltsT); |
232 |
|
// now collect model info for the type |
233 |
1408 |
TypeNode st = rst.first; |
234 |
705 |
if (!collectModelInfoType(st, rst.second, colT[st], ltsT[st], m)) |
235 |
|
{ |
236 |
|
return false; |
237 |
|
} |
238 |
|
} |
239 |
4853 |
return true; |
240 |
|
} |
241 |
|
|
242 |
704 |
bool TheoryStrings::collectModelInfoType(TypeNode tn, |
243 |
|
const std::unordered_set<Node>& repSet, |
244 |
|
std::vector<std::vector<Node> >& col, |
245 |
|
std::vector<Node>& lts, |
246 |
|
TheoryModel* m) |
247 |
|
{ |
248 |
704 |
NodeManager* nm = NodeManager::currentNM(); |
249 |
1408 |
std::map< Node, Node > processed; |
250 |
|
//step 1 : get all values for known lengths |
251 |
1408 |
std::vector< Node > lts_values; |
252 |
1408 |
std::map<std::size_t, Node> values_used; |
253 |
1408 |
std::vector<Node> len_splits; |
254 |
4896 |
for( unsigned i=0; i<col.size(); i++ ) { |
255 |
4193 |
Trace("strings-model") << "Checking length for {"; |
256 |
11632 |
for( unsigned j=0; j<col[i].size(); j++ ) { |
257 |
7439 |
if( j>0 ) { |
258 |
3246 |
Trace("strings-model") << ", "; |
259 |
|
} |
260 |
7439 |
Trace("strings-model") << col[i][j]; |
261 |
|
} |
262 |
4193 |
Trace("strings-model") << " } (length is " << lts[i] << ")" << std::endl; |
263 |
8386 |
Node len_value; |
264 |
4193 |
if( lts[i].isConst() ) { |
265 |
2969 |
len_value = lts[i]; |
266 |
|
} |
267 |
1224 |
else if (!lts[i].isNull()) |
268 |
|
{ |
269 |
|
// get the model value for lts[i] |
270 |
1224 |
len_value = d_valuation.getModelValue(lts[i]); |
271 |
|
} |
272 |
4193 |
if (len_value.isNull()) |
273 |
|
{ |
274 |
|
lts_values.push_back(Node::null()); |
275 |
|
} |
276 |
|
else |
277 |
|
{ |
278 |
|
// must throw logic exception if we cannot construct the string |
279 |
4193 |
if (len_value.getConst<Rational>() > String::maxSize()) |
280 |
|
{ |
281 |
2 |
std::stringstream ss; |
282 |
2 |
ss << "The model was computed to have strings of length " << len_value |
283 |
1 |
<< ". We only allow strings up to length " << String::maxSize(); |
284 |
1 |
throw LogicException(ss.str()); |
285 |
|
} |
286 |
|
std::size_t lvalue = |
287 |
4192 |
len_value.getConst<Rational>().getNumerator().toUnsignedInt(); |
288 |
4192 |
auto itvu = values_used.find(lvalue); |
289 |
4192 |
if (itvu == values_used.end()) |
290 |
|
{ |
291 |
3262 |
values_used[lvalue] = lts[i]; |
292 |
|
} |
293 |
|
else |
294 |
|
{ |
295 |
930 |
len_splits.push_back(lts[i].eqNode(itvu->second)); |
296 |
|
} |
297 |
4192 |
lts_values.push_back(len_value); |
298 |
|
} |
299 |
|
} |
300 |
|
////step 2 : assign arbitrary values for unknown lengths? |
301 |
|
// confirmed by calculus invariant, see paper |
302 |
703 |
Trace("strings-model") << "Assign to equivalence classes..." << std::endl; |
303 |
1406 |
std::map<Node, Node> pure_eq_assign; |
304 |
|
//step 3 : assign values to equivalence classes that are pure variables |
305 |
4894 |
for( unsigned i=0; i<col.size(); i++ ){ |
306 |
8382 |
std::vector< Node > pure_eq; |
307 |
8382 |
Trace("strings-model") << "The (" << col[i].size() |
308 |
4191 |
<< ") equivalence classes "; |
309 |
11628 |
for (const Node& eqc : col[i]) |
310 |
|
{ |
311 |
7437 |
Trace("strings-model") << eqc << " "; |
312 |
|
//check if col[i][j] has only variables |
313 |
7437 |
if (!eqc.isConst()) |
314 |
|
{ |
315 |
2373 |
NormalForm& nfe = d_csolver.getNormalForm(eqc); |
316 |
2373 |
if (nfe.d_nf.size() == 1) |
317 |
|
{ |
318 |
|
// is it an equivalence class with a seq.unit term? |
319 |
784 |
if (nfe.d_nf[0].getKind() == SEQ_UNIT) |
320 |
|
{ |
321 |
16 |
Node argVal; |
322 |
8 |
if (nfe.d_nf[0][0].getType().isStringLike()) |
323 |
|
{ |
324 |
1 |
argVal = d_state.getRepresentative(nfe.d_nf[0][0]); |
325 |
|
} |
326 |
|
else |
327 |
|
{ |
328 |
|
// otherwise, it is a shared term |
329 |
7 |
argVal = d_valuation.getModelValue(nfe.d_nf[0][0]); |
330 |
|
} |
331 |
8 |
Assert(!argVal.isNull()); |
332 |
16 |
Node c = Rewriter::rewrite(nm->mkNode(SEQ_UNIT, argVal)); |
333 |
8 |
pure_eq_assign[eqc] = c; |
334 |
8 |
Trace("strings-model") << "(unit: " << nfe.d_nf[0] << ") "; |
335 |
8 |
m->getEqualityEngine()->addTerm(c); |
336 |
|
} |
337 |
|
// does it have a code and the length of these equivalence classes are |
338 |
|
// one? |
339 |
776 |
else if (d_termReg.hasStringCode() && lts_values[i] == d_one) |
340 |
|
{ |
341 |
299 |
EqcInfo* eip = d_state.getOrMakeEqcInfo(eqc, false); |
342 |
299 |
if (eip && !eip->d_codeTerm.get().isNull()) |
343 |
|
{ |
344 |
|
// its value must be equal to its code |
345 |
318 |
Node ct = nm->mkNode(kind::STRING_TO_CODE, eip->d_codeTerm.get()); |
346 |
318 |
Node ctv = d_valuation.getModelValue(ct); |
347 |
|
unsigned cvalue = |
348 |
159 |
ctv.getConst<Rational>().getNumerator().toUnsignedInt(); |
349 |
159 |
Trace("strings-model") << "(code: " << cvalue << ") "; |
350 |
318 |
std::vector<unsigned> vec; |
351 |
159 |
vec.push_back(cvalue); |
352 |
318 |
Node mv = nm->mkConst(String(vec)); |
353 |
159 |
pure_eq_assign[eqc] = mv; |
354 |
159 |
m->getEqualityEngine()->addTerm(mv); |
355 |
|
} |
356 |
|
} |
357 |
784 |
pure_eq.push_back(eqc); |
358 |
|
} |
359 |
|
} |
360 |
|
else |
361 |
|
{ |
362 |
5064 |
processed[eqc] = eqc; |
363 |
|
// Make sure that constants are asserted to the theory model that we |
364 |
|
// are building. It is possible that new constants were introduced by |
365 |
|
// the eager evaluation in the equality engine. These terms are missing |
366 |
|
// in the term set and, as a result, are skipped when the equality |
367 |
|
// engine is asserted to the theory model. |
368 |
5064 |
m->getEqualityEngine()->addTerm(eqc); |
369 |
|
} |
370 |
|
} |
371 |
4191 |
Trace("strings-model") << "have length " << lts_values[i] << std::endl; |
372 |
|
|
373 |
|
//assign a new length if necessary |
374 |
4191 |
if( !pure_eq.empty() ){ |
375 |
560 |
if( lts_values[i].isNull() ){ |
376 |
|
// start with length two (other lengths have special precendence) |
377 |
|
std::size_t lvalue = 2; |
378 |
|
while( values_used.find( lvalue )!=values_used.end() ){ |
379 |
|
lvalue++; |
380 |
|
} |
381 |
|
Trace("strings-model") << "*** Decide to make length of " << lvalue << std::endl; |
382 |
|
lts_values[i] = nm->mkConst(Rational(lvalue)); |
383 |
|
values_used[lvalue] = Node::null(); |
384 |
|
} |
385 |
560 |
Trace("strings-model") << "Need to assign values of length " << lts_values[i] << " to equivalence classes "; |
386 |
1344 |
for( unsigned j=0; j<pure_eq.size(); j++ ){ |
387 |
784 |
Trace("strings-model") << pure_eq[j] << " "; |
388 |
|
} |
389 |
560 |
Trace("strings-model") << std::endl; |
390 |
|
|
391 |
|
//use type enumerator |
392 |
560 |
Assert(lts_values[i].getConst<Rational>() <= Rational(String::maxSize())) |
393 |
|
<< "Exceeded UINT32_MAX in string model"; |
394 |
|
uint32_t currLen = |
395 |
560 |
lts_values[i].getConst<Rational>().getNumerator().toUnsignedInt(); |
396 |
1120 |
std::unique_ptr<SEnumLen> sel; |
397 |
1120 |
Trace("strings-model") << "Cardinality of alphabet is " |
398 |
560 |
<< utils::getAlphabetCardinality() << std::endl; |
399 |
560 |
if (tn.isString()) // string-only |
400 |
|
{ |
401 |
546 |
sel.reset(new StringEnumLen( |
402 |
546 |
currLen, currLen, utils::getAlphabetCardinality())); |
403 |
|
} |
404 |
|
else |
405 |
|
{ |
406 |
14 |
sel.reset(new SeqEnumLen(tn, nullptr, currLen, currLen)); |
407 |
|
} |
408 |
1344 |
for (const Node& eqc : pure_eq) |
409 |
|
{ |
410 |
1568 |
Node c; |
411 |
784 |
std::map<Node, Node>::iterator itp = pure_eq_assign.find(eqc); |
412 |
784 |
if (itp == pure_eq_assign.end()) |
413 |
|
{ |
414 |
619 |
do |
415 |
|
{ |
416 |
1236 |
if (sel->isFinished()) |
417 |
|
{ |
418 |
|
// We are in a case where model construction is impossible due to |
419 |
|
// an insufficient number of constants of a given length. |
420 |
|
|
421 |
|
// Consider an integer equivalence class E whose value is assigned |
422 |
|
// n in the model. Let { S_1, ..., S_m } be the set of string |
423 |
|
// equivalence classes such that len( x ) is a member of E for |
424 |
|
// some member x of each class S1, ...,Sm. Since our calculus is |
425 |
|
// saturated with respect to cardinality inference (see Liang |
426 |
|
// et al, Figure 6, CAV 2014), we have that m <= A^n, where A is |
427 |
|
// the cardinality of our alphabet. |
428 |
|
|
429 |
|
// Now, consider the case where there exists two integer |
430 |
|
// equivalence classes E1 and E2 that are assigned n, and moreover |
431 |
|
// we did not received notification from arithmetic that E1 = E2. |
432 |
|
// This typically should never happen, but assume in the following |
433 |
|
// that it does. |
434 |
|
|
435 |
|
// Now, it may be the case that there are string equivalence |
436 |
|
// classes { S_1, ..., S_m1 } whose lengths are in E1, |
437 |
|
// and classes { S'_1, ..., S'_m2 } whose lengths are in E2, where |
438 |
|
// m1 + m2 > A^n. In this case, we have insufficient strings to |
439 |
|
// assign to { S_1, ..., S_m1, S'_1, ..., S'_m2 }. If this |
440 |
|
// happens, we add a split on len( u1 ) = len( u2 ) for some |
441 |
|
// len( u1 ) in E1, len( u2 ) in E2. We do this for each pair of |
442 |
|
// integer equivalence classes that are assigned to the same value |
443 |
|
// in the model. |
444 |
|
AlwaysAssert(!len_splits.empty()); |
445 |
|
for (const Node& sl : len_splits) |
446 |
|
{ |
447 |
|
Node spl = nm->mkNode(OR, sl, sl.negate()); |
448 |
|
d_im.lemma(spl, InferenceId::STRINGS_CMI_SPLIT); |
449 |
|
Trace("strings-lemma") |
450 |
|
<< "Strings::CollectModelInfoSplit: " << spl << std::endl; |
451 |
|
} |
452 |
|
// we added a lemma, so can return here |
453 |
|
return false; |
454 |
|
} |
455 |
1236 |
c = sel->getCurrent(); |
456 |
|
// increment |
457 |
1236 |
sel->increment(); |
458 |
1236 |
} while (m->hasTerm(c)); |
459 |
|
} |
460 |
|
else |
461 |
|
{ |
462 |
167 |
c = itp->second; |
463 |
|
} |
464 |
1568 |
Trace("strings-model") << "*** Assigned constant " << c << " for " |
465 |
784 |
<< eqc << std::endl; |
466 |
784 |
processed[eqc] = c; |
467 |
784 |
if (!m->assertEquality(eqc, c, true)) |
468 |
|
{ |
469 |
|
// this should never happen due to the model soundness argument |
470 |
|
// for strings |
471 |
|
Unreachable() |
472 |
|
<< "TheoryStrings::collectModelInfoType: Inconsistent equality" |
473 |
|
<< std::endl; |
474 |
|
return false; |
475 |
|
} |
476 |
|
} |
477 |
|
} |
478 |
|
} |
479 |
703 |
Trace("strings-model") << "String Model : Pure Assigned." << std::endl; |
480 |
|
//step 4 : assign constants to all other equivalence classes |
481 |
8140 |
for (const Node& rn : repSet) |
482 |
|
{ |
483 |
7437 |
if (processed.find(rn) == processed.end()) |
484 |
|
{ |
485 |
1589 |
NormalForm& nf = d_csolver.getNormalForm(rn); |
486 |
1589 |
if (Trace.isOn("strings-model")) |
487 |
|
{ |
488 |
|
Trace("strings-model") |
489 |
|
<< "Construct model for " << rn << " based on normal form "; |
490 |
|
for (unsigned j = 0, size = nf.d_nf.size(); j < size; j++) |
491 |
|
{ |
492 |
|
Node n = nf.d_nf[j]; |
493 |
|
if (j > 0) |
494 |
|
{ |
495 |
|
Trace("strings-model") << " ++ "; |
496 |
|
} |
497 |
|
Trace("strings-model") << n; |
498 |
|
Node r = d_state.getRepresentative(n); |
499 |
|
if (!r.isConst() && processed.find(r) == processed.end()) |
500 |
|
{ |
501 |
|
Trace("strings-model") << "(UNPROCESSED)"; |
502 |
|
} |
503 |
|
} |
504 |
|
} |
505 |
1589 |
Trace("strings-model") << std::endl; |
506 |
3178 |
std::vector< Node > nc; |
507 |
7759 |
for (const Node& n : nf.d_nf) |
508 |
|
{ |
509 |
12340 |
Node r = d_state.getRepresentative(n); |
510 |
6170 |
Assert(r.isConst() || processed.find(r) != processed.end()); |
511 |
6170 |
nc.push_back(r.isConst() ? r : processed[r]); |
512 |
|
} |
513 |
3178 |
Node cc = utils::mkNConcat(nc, tn); |
514 |
3178 |
Trace("strings-model") |
515 |
1589 |
<< "*** Determined constant " << cc << " for " << rn << std::endl; |
516 |
1589 |
processed[rn] = cc; |
517 |
1589 |
if (!m->assertEquality(rn, cc, true)) |
518 |
|
{ |
519 |
|
// this should never happen due to the model soundness argument |
520 |
|
// for strings |
521 |
|
Unreachable() << "TheoryStrings::collectModelInfoType: " |
522 |
|
"Inconsistent equality (unprocessed eqc)" |
523 |
|
<< std::endl; |
524 |
|
return false; |
525 |
|
} |
526 |
1589 |
else if (!cc.isConst()) |
527 |
|
{ |
528 |
|
// the value may be specified by seq.unit components, ensure this |
529 |
|
// is marked as the skeleton for constructing values in this class. |
530 |
|
m->assertSkeleton(cc); |
531 |
|
} |
532 |
|
} |
533 |
|
} |
534 |
|
//Trace("strings-model") << "String Model : Assigned." << std::endl; |
535 |
703 |
Trace("strings-model") << "String Model : Finished." << std::endl; |
536 |
703 |
return true; |
537 |
|
} |
538 |
|
|
539 |
|
///////////////////////////////////////////////////////////////////////////// |
540 |
|
// MAIN SOLVER |
541 |
|
///////////////////////////////////////////////////////////////////////////// |
542 |
|
|
543 |
103934 |
void TheoryStrings::preRegisterTerm(TNode n) |
544 |
|
{ |
545 |
207868 |
Trace("strings-preregister") |
546 |
103934 |
<< "TheoryStrings::preRegisterTerm: " << n << std::endl; |
547 |
103934 |
d_termReg.preRegisterTerm(n); |
548 |
|
// Register the term with the extended theory. Notice we do not recurse on |
549 |
|
// this term here since preRegisterTerm is already called recursively on all |
550 |
|
// subterms in preregistered literals. |
551 |
103934 |
d_extTheory.registerTerm(n); |
552 |
103934 |
} |
553 |
|
|
554 |
413726 |
bool TheoryStrings::preNotifyFact( |
555 |
|
TNode atom, bool pol, TNode fact, bool isPrereg, bool isInternal) |
556 |
|
{ |
557 |
|
// this is only required for internal facts, others are already registered |
558 |
413726 |
if (isInternal && atom.getKind() == EQUAL) |
559 |
|
{ |
560 |
|
// We must ensure these terms are registered. We register eagerly here for |
561 |
|
// performance reasons. Alternatively, terms could be registered at full |
562 |
|
// effort in e.g. BaseSolver::init. |
563 |
35058 |
for (const Node& t : atom) |
564 |
|
{ |
565 |
23372 |
d_termReg.registerTerm(t, 0); |
566 |
|
} |
567 |
|
} |
568 |
413726 |
return false; |
569 |
|
} |
570 |
|
|
571 |
413726 |
void TheoryStrings::notifyFact(TNode atom, |
572 |
|
bool polarity, |
573 |
|
TNode fact, |
574 |
|
bool isInternal) |
575 |
|
{ |
576 |
413726 |
d_eagerSolver.notifyFact(atom, polarity, fact, isInternal); |
577 |
|
// process pending conflicts due to reasoning about endpoints |
578 |
413726 |
if (!d_state.isInConflict() && d_state.hasPendingConflict()) |
579 |
|
{ |
580 |
384 |
InferInfo iiPendingConf(InferenceId::UNKNOWN); |
581 |
192 |
d_state.getPendingConflict(iiPendingConf); |
582 |
384 |
Trace("strings-pending") |
583 |
192 |
<< "Process pending conflict " << iiPendingConf.d_premises << std::endl; |
584 |
384 |
Trace("strings-conflict") |
585 |
192 |
<< "CONFLICT: Eager : " << iiPendingConf.d_premises << std::endl; |
586 |
192 |
++(d_statistics.d_conflictsEager); |
587 |
|
// call the inference manager to send the conflict |
588 |
192 |
d_im.processConflict(iiPendingConf); |
589 |
192 |
return; |
590 |
|
} |
591 |
413534 |
Trace("strings-pending-debug") << " Now collect terms" << std::endl; |
592 |
413534 |
Trace("strings-pending-debug") << " Finished collect terms" << std::endl; |
593 |
|
} |
594 |
|
|
595 |
179078 |
void TheoryStrings::postCheck(Effort e) |
596 |
|
{ |
597 |
179078 |
d_im.doPendingFacts(); |
598 |
|
|
599 |
179078 |
Assert(d_strat.isStrategyInit()); |
600 |
535975 |
if (!d_state.isInConflict() && !d_valuation.needCheck() |
601 |
222502 |
&& d_strat.hasStrategyEffort(e)) |
602 |
|
{ |
603 |
34250 |
Trace("strings-check-debug") |
604 |
17125 |
<< "Theory of strings " << e << " effort check " << std::endl; |
605 |
17125 |
if (Trace.isOn("strings-eqc")) |
606 |
|
{ |
607 |
|
Trace("strings-eqc") << debugPrintStringsEqc() << std::endl; |
608 |
|
} |
609 |
17125 |
++(d_statistics.d_checkRuns); |
610 |
17125 |
bool sentLemma = false; |
611 |
17125 |
bool hadPending = false; |
612 |
17125 |
Trace("strings-check") << "Full effort check..." << std::endl; |
613 |
6898 |
do{ |
614 |
24023 |
d_im.reset(); |
615 |
24023 |
++(d_statistics.d_strategyRuns); |
616 |
24023 |
Trace("strings-check") << " * Run strategy..." << std::endl; |
617 |
24023 |
runStrategy(e); |
618 |
|
// remember if we had pending facts or lemmas |
619 |
24023 |
hadPending = d_im.hasPending(); |
620 |
|
// Send the facts *and* the lemmas. We send lemmas regardless of whether |
621 |
|
// we send facts since some lemmas cannot be dropped. Other lemmas are |
622 |
|
// otherwise avoided by aborting the strategy when a fact is ready. |
623 |
24023 |
d_im.doPending(); |
624 |
|
// Did we successfully send a lemma? Notice that if hasPending = true |
625 |
|
// and sentLemma = false, then the above call may have: |
626 |
|
// (1) had no pending lemmas, but successfully processed pending facts, |
627 |
|
// (2) unsuccessfully processed pending lemmas. |
628 |
|
// In either case, we repeat the strategy if we are not in conflict. |
629 |
24023 |
sentLemma = d_im.hasSentLemma(); |
630 |
24023 |
if (Trace.isOn("strings-check")) |
631 |
|
{ |
632 |
|
Trace("strings-check") << " ...finish run strategy: "; |
633 |
|
Trace("strings-check") << (hadPending ? "hadPending " : ""); |
634 |
|
Trace("strings-check") << (sentLemma ? "sentLemma " : ""); |
635 |
|
Trace("strings-check") << (d_state.isInConflict() ? "conflict " : ""); |
636 |
|
if (!hadPending && !sentLemma && !d_state.isInConflict()) |
637 |
|
{ |
638 |
|
Trace("strings-check") << "(none)"; |
639 |
|
} |
640 |
|
Trace("strings-check") << std::endl; |
641 |
|
} |
642 |
|
// repeat if we did not add a lemma or conflict, and we had pending |
643 |
|
// facts or lemmas. |
644 |
24023 |
} while (!d_state.isInConflict() && !sentLemma && hadPending); |
645 |
|
} |
646 |
179078 |
Trace("strings-check") << "Theory of strings, done check : " << e << std::endl; |
647 |
179078 |
Assert(!d_im.hasPendingFact()); |
648 |
179078 |
Assert(!d_im.hasPendingLemma()); |
649 |
179078 |
} |
650 |
|
|
651 |
5892 |
bool TheoryStrings::needsCheckLastEffort() { |
652 |
5892 |
if( options::stringGuessModel() ){ |
653 |
|
return d_esolver.hasExtendedFunctions(); |
654 |
|
} |
655 |
5892 |
return false; |
656 |
|
} |
657 |
|
|
658 |
|
/** Conflict when merging two constants */ |
659 |
693 |
void TheoryStrings::conflict(TNode a, TNode b){ |
660 |
693 |
if (d_state.isInConflict()) |
661 |
|
{ |
662 |
|
// already in conflict |
663 |
|
return; |
664 |
|
} |
665 |
693 |
d_im.conflictEqConstantMerge(a, b); |
666 |
693 |
Trace("strings-conflict") << "CONFLICT: Eq engine conflict" << std::endl; |
667 |
693 |
++(d_statistics.d_conflictsEqEngine); |
668 |
|
} |
669 |
|
|
670 |
78973 |
void TheoryStrings::eqNotifyNewClass(TNode t){ |
671 |
78973 |
Kind k = t.getKind(); |
672 |
78973 |
if (k == STRING_LENGTH || k == STRING_TO_CODE) |
673 |
|
{ |
674 |
24539 |
Trace("strings-debug") << "New length eqc : " << t << std::endl; |
675 |
|
//we care about the length of this string |
676 |
24539 |
d_termReg.registerTerm(t[0], 1); |
677 |
|
} |
678 |
78973 |
d_eagerSolver.eqNotifyNewClass(t); |
679 |
78973 |
} |
680 |
|
|
681 |
8229 |
void TheoryStrings::addCarePairs(TNodeTrie* t1, |
682 |
|
TNodeTrie* t2, |
683 |
|
unsigned arity, |
684 |
|
unsigned depth) |
685 |
|
{ |
686 |
8229 |
if( depth==arity ){ |
687 |
373 |
if( t2!=NULL ){ |
688 |
746 |
Node f1 = t1->getData(); |
689 |
746 |
Node f2 = t2->getData(); |
690 |
373 |
if (!d_equalityEngine->areEqual(f1, f2)) |
691 |
|
{ |
692 |
232 |
Trace("strings-cg-debug") << "TheoryStrings::computeCareGraph(): checking function " << f1 << " and " << f2 << std::endl; |
693 |
464 |
vector< pair<TNode, TNode> > currentPairs; |
694 |
652 |
for (unsigned k = 0; k < f1.getNumChildren(); ++ k) { |
695 |
840 |
TNode x = f1[k]; |
696 |
840 |
TNode y = f2[k]; |
697 |
420 |
Assert(d_equalityEngine->hasTerm(x)); |
698 |
420 |
Assert(d_equalityEngine->hasTerm(y)); |
699 |
420 |
Assert(!d_equalityEngine->areDisequal(x, y, false)); |
700 |
420 |
Assert(!areCareDisequal(x, y)); |
701 |
420 |
if (!d_equalityEngine->areEqual(x, y)) |
702 |
|
{ |
703 |
726 |
if (d_equalityEngine->isTriggerTerm(x, THEORY_STRINGS) |
704 |
726 |
&& d_equalityEngine->isTriggerTerm(y, THEORY_STRINGS)) |
705 |
|
{ |
706 |
114 |
TNode x_shared = d_equalityEngine->getTriggerTermRepresentative( |
707 |
228 |
x, THEORY_STRINGS); |
708 |
114 |
TNode y_shared = d_equalityEngine->getTriggerTermRepresentative( |
709 |
228 |
y, THEORY_STRINGS); |
710 |
114 |
currentPairs.push_back(make_pair(x_shared, y_shared)); |
711 |
|
} |
712 |
|
} |
713 |
|
} |
714 |
346 |
for (unsigned c = 0; c < currentPairs.size(); ++ c) { |
715 |
114 |
Trace("strings-cg-pair") << "TheoryStrings::computeCareGraph(): pair : " << currentPairs[c].first << " " << currentPairs[c].second << std::endl; |
716 |
114 |
addCarePair(currentPairs[c].first, currentPairs[c].second); |
717 |
|
} |
718 |
|
} |
719 |
|
} |
720 |
|
}else{ |
721 |
7856 |
if( t2==NULL ){ |
722 |
6992 |
if( depth<(arity-1) ){ |
723 |
|
//add care pairs internal to each child |
724 |
7016 |
for (std::pair<const TNode, TNodeTrie>& tt : t1->d_data) |
725 |
|
{ |
726 |
4707 |
addCarePairs(&tt.second, nullptr, arity, depth + 1); |
727 |
|
} |
728 |
|
} |
729 |
|
//add care pairs based on each pair of non-disequal arguments |
730 |
23817 |
for (std::map<TNode, TNodeTrie>::iterator it = t1->d_data.begin(); |
731 |
23817 |
it != t1->d_data.end(); |
732 |
|
++it) |
733 |
|
{ |
734 |
16825 |
std::map<TNode, TNodeTrie>::iterator it2 = it; |
735 |
16825 |
++it2; |
736 |
156387 |
for( ; it2 != t1->d_data.end(); ++it2 ){ |
737 |
69781 |
if (!d_equalityEngine->areDisequal(it->first, it2->first, false)) |
738 |
|
{ |
739 |
1238 |
if( !areCareDisequal(it->first, it2->first) ){ |
740 |
703 |
addCarePairs( &it->second, &it2->second, arity, depth+1 ); |
741 |
|
} |
742 |
|
} |
743 |
|
} |
744 |
|
} |
745 |
|
}else{ |
746 |
|
//add care pairs based on product of indices, non-disequal arguments |
747 |
2270 |
for (std::pair<const TNode, TNodeTrie>& tt1 : t1->d_data) |
748 |
|
{ |
749 |
3944 |
for (std::pair<const TNode, TNodeTrie>& tt2 : t2->d_data) |
750 |
|
{ |
751 |
2538 |
if (!d_equalityEngine->areDisequal(tt1.first, tt2.first, false)) |
752 |
|
{ |
753 |
906 |
if (!areCareDisequal(tt1.first, tt2.first)) |
754 |
|
{ |
755 |
534 |
addCarePairs(&tt1.second, &tt2.second, arity, depth + 1); |
756 |
|
} |
757 |
|
} |
758 |
|
} |
759 |
|
} |
760 |
|
} |
761 |
|
} |
762 |
8229 |
} |
763 |
|
|
764 |
6365 |
void TheoryStrings::computeCareGraph(){ |
765 |
|
//computing the care graph here is probably still necessary, due to operators that take non-string arguments TODO: verify |
766 |
6365 |
Trace("strings-cg") << "TheoryStrings::computeCareGraph(): Build term indices..." << std::endl; |
767 |
|
// Term index for each (type, operator) pair. We require the operator here |
768 |
|
// since operators are polymorphic, taking strings/sequences. |
769 |
12730 |
std::map<std::pair<TypeNode, Node>, TNodeTrie> index; |
770 |
12730 |
std::map< Node, unsigned > arity; |
771 |
6365 |
const context::CDList<TNode>& fterms = d_termReg.getFunctionTerms(); |
772 |
6365 |
size_t functionTerms = fterms.size(); |
773 |
38709 |
for (unsigned i = 0; i < functionTerms; ++ i) { |
774 |
64688 |
TNode f1 = fterms[i]; |
775 |
32344 |
Trace("strings-cg") << "...build for " << f1 << std::endl; |
776 |
64688 |
Node op = f1.getOperator(); |
777 |
64688 |
std::vector< TNode > reps; |
778 |
32344 |
bool has_trigger_arg = false; |
779 |
74831 |
for( unsigned j=0; j<f1.getNumChildren(); j++ ){ |
780 |
42487 |
reps.push_back(d_equalityEngine->getRepresentative(f1[j])); |
781 |
42487 |
if (d_equalityEngine->isTriggerTerm(f1[j], THEORY_STRINGS)) |
782 |
|
{ |
783 |
31722 |
has_trigger_arg = true; |
784 |
|
} |
785 |
|
} |
786 |
32344 |
if( has_trigger_arg ){ |
787 |
47634 |
TypeNode ft = utils::getOwnerStringType(f1); |
788 |
47634 |
std::pair<TypeNode, Node> ikey = std::pair<TypeNode, Node>(ft, op); |
789 |
23817 |
index[ikey].addTerm(f1, reps); |
790 |
23817 |
arity[op] = reps.size(); |
791 |
|
} |
792 |
|
} |
793 |
|
//for each index |
794 |
8650 |
for (std::pair<const std::pair<TypeNode, Node>, TNodeTrie>& ti : index) |
795 |
|
{ |
796 |
4570 |
Trace("strings-cg") << "TheoryStrings::computeCareGraph(): Process index " |
797 |
2285 |
<< ti.first << "..." << std::endl; |
798 |
4570 |
Node op = ti.first.second; |
799 |
2285 |
addCarePairs(&ti.second, nullptr, arity[op], 0); |
800 |
|
} |
801 |
6365 |
} |
802 |
|
|
803 |
|
void TheoryStrings::checkRegisterTermsPreNormalForm() |
804 |
|
{ |
805 |
|
const std::vector<Node>& seqc = d_bsolver.getStringEqc(); |
806 |
|
for (const Node& eqc : seqc) |
807 |
|
{ |
808 |
|
eq::EqClassIterator eqc_i = eq::EqClassIterator(eqc, d_equalityEngine); |
809 |
|
while (!eqc_i.isFinished()) |
810 |
|
{ |
811 |
|
Node n = (*eqc_i); |
812 |
|
if (!d_bsolver.isCongruent(n)) |
813 |
|
{ |
814 |
|
d_termReg.registerTerm(n, 2); |
815 |
|
} |
816 |
|
++eqc_i; |
817 |
|
} |
818 |
|
} |
819 |
|
} |
820 |
|
|
821 |
9151 |
void TheoryStrings::checkCodes() |
822 |
|
{ |
823 |
|
// ensure that lemmas regarding str.code been added for each constant string |
824 |
|
// of length one |
825 |
9151 |
if (d_termReg.hasStringCode()) |
826 |
|
{ |
827 |
968 |
NodeManager* nm = NodeManager::currentNM(); |
828 |
|
// str.code applied to the code term for each equivalence class that has a |
829 |
|
// code term but is not a constant |
830 |
1767 |
std::vector<Node> nconst_codes; |
831 |
|
// str.code applied to the proxy variables for each equivalence classes that |
832 |
|
// are constants of size one |
833 |
1767 |
std::vector<Node> const_codes; |
834 |
968 |
const std::vector<Node>& seqc = d_bsolver.getStringEqc(); |
835 |
11688 |
for (const Node& eqc : seqc) |
836 |
|
{ |
837 |
10720 |
NormalForm& nfe = d_csolver.getNormalForm(eqc); |
838 |
10720 |
if (nfe.d_nf.size() == 1 && nfe.d_nf[0].isConst()) |
839 |
|
{ |
840 |
8684 |
Node c = nfe.d_nf[0]; |
841 |
8684 |
Trace("strings-code-debug") << "Get proxy variable for " << c |
842 |
4342 |
<< std::endl; |
843 |
8684 |
Node cc = nm->mkNode(kind::STRING_TO_CODE, c); |
844 |
4342 |
cc = Rewriter::rewrite(cc); |
845 |
4342 |
Assert(cc.isConst()); |
846 |
8684 |
Node cp = d_termReg.ensureProxyVariableFor(c); |
847 |
8684 |
Node vc = nm->mkNode(STRING_TO_CODE, cp); |
848 |
4342 |
if (!d_state.areEqual(cc, vc)) |
849 |
|
{ |
850 |
856 |
std::vector<Node> emptyVec; |
851 |
428 |
d_im.sendInference(emptyVec, cc.eqNode(vc), InferenceId::STRINGS_CODE_PROXY); |
852 |
|
} |
853 |
4342 |
const_codes.push_back(vc); |
854 |
|
} |
855 |
|
else |
856 |
|
{ |
857 |
6378 |
EqcInfo* ei = d_state.getOrMakeEqcInfo(eqc, false); |
858 |
6378 |
if (ei && !ei->d_codeTerm.get().isNull()) |
859 |
|
{ |
860 |
3592 |
Node vc = nm->mkNode(kind::STRING_TO_CODE, ei->d_codeTerm.get()); |
861 |
1796 |
nconst_codes.push_back(vc); |
862 |
|
} |
863 |
|
} |
864 |
|
} |
865 |
968 |
if (d_im.hasProcessed()) |
866 |
|
{ |
867 |
169 |
return; |
868 |
|
} |
869 |
|
// now, ensure that str.code is injective |
870 |
1598 |
std::vector<Node> cmps; |
871 |
799 |
cmps.insert(cmps.end(), const_codes.rbegin(), const_codes.rend()); |
872 |
799 |
cmps.insert(cmps.end(), nconst_codes.rbegin(), nconst_codes.rend()); |
873 |
2306 |
for (unsigned i = 0, num_ncc = nconst_codes.size(); i < num_ncc; i++) |
874 |
|
{ |
875 |
3014 |
Node c1 = nconst_codes[i]; |
876 |
1507 |
cmps.pop_back(); |
877 |
8746 |
for (const Node& c2 : cmps) |
878 |
|
{ |
879 |
14478 |
Trace("strings-code-debug") |
880 |
7239 |
<< "Compare codes : " << c1 << " " << c2 << std::endl; |
881 |
7239 |
if (!d_state.areDisequal(c1, c2) && !d_state.areEqual(c1, d_neg_one)) |
882 |
|
{ |
883 |
2008 |
Node eq_no = c1.eqNode(d_neg_one); |
884 |
2008 |
Node deq = c1.eqNode(c2).negate(); |
885 |
2008 |
Node eqn = c1[0].eqNode(c2[0]); |
886 |
|
// str.code(x)==-1 V str.code(x)!=str.code(y) V x==y |
887 |
2008 |
Node inj_lem = nm->mkNode(kind::OR, eq_no, deq, eqn); |
888 |
1004 |
deq = Rewriter::rewrite(deq); |
889 |
1004 |
d_im.addPendingPhaseRequirement(deq, false); |
890 |
2008 |
std::vector<Node> emptyVec; |
891 |
1004 |
d_im.sendInference(emptyVec, inj_lem, InferenceId::STRINGS_CODE_INJ); |
892 |
|
} |
893 |
|
} |
894 |
|
} |
895 |
|
} |
896 |
|
} |
897 |
|
|
898 |
|
void TheoryStrings::checkRegisterTermsNormalForms() |
899 |
|
{ |
900 |
|
const std::vector<Node>& seqc = d_bsolver.getStringEqc(); |
901 |
|
for (const Node& eqc : seqc) |
902 |
|
{ |
903 |
|
NormalForm& nfi = d_csolver.getNormalForm(eqc); |
904 |
|
// check if there is a length term for this equivalence class |
905 |
|
EqcInfo* ei = d_state.getOrMakeEqcInfo(eqc, false); |
906 |
|
Node lt = ei ? ei->d_lengthTerm : Node::null(); |
907 |
|
if (lt.isNull()) |
908 |
|
{ |
909 |
|
Node c = utils::mkNConcat(nfi.d_nf, eqc.getType()); |
910 |
|
d_termReg.registerTerm(c, 3); |
911 |
|
} |
912 |
|
} |
913 |
|
} |
914 |
|
|
915 |
87061 |
TrustNode TheoryStrings::ppRewrite(TNode atom, std::vector<SkolemLemma>& lems) |
916 |
|
{ |
917 |
87061 |
Trace("strings-ppr") << "TheoryStrings::ppRewrite " << atom << std::endl; |
918 |
87061 |
if (atom.getKind() == STRING_FROM_CODE) |
919 |
|
{ |
920 |
|
// str.from_code(t) ---> |
921 |
|
// witness k. ite(0 <= t < |A|, t = str.to_code(k), k = "") |
922 |
51 |
NodeManager* nm = NodeManager::currentNM(); |
923 |
102 |
Node t = atom[0]; |
924 |
102 |
Node card = nm->mkConst(Rational(utils::getAlphabetCardinality())); |
925 |
|
Node cond = |
926 |
102 |
nm->mkNode(AND, nm->mkNode(LEQ, d_zero, t), nm->mkNode(LT, t, card)); |
927 |
102 |
Node v = nm->mkBoundVar(nm->stringType()); |
928 |
102 |
Node emp = Word::mkEmptyWord(atom.getType()); |
929 |
|
Node pred = nm->mkNode( |
930 |
102 |
ITE, cond, t.eqNode(nm->mkNode(STRING_TO_CODE, v)), v.eqNode(emp)); |
931 |
51 |
SkolemManager* sm = nm->getSkolemManager(); |
932 |
102 |
Node ret = sm->mkSkolem(v, pred, "kFromCode"); |
933 |
51 |
lems.push_back(SkolemLemma(ret, nullptr)); |
934 |
51 |
return TrustNode::mkTrustRewrite(atom, ret, nullptr); |
935 |
|
} |
936 |
174020 |
TrustNode ret; |
937 |
174020 |
Node atomRet = atom; |
938 |
174051 |
if (options::regExpElim() && atom.getKind() == STRING_IN_REGEXP) |
939 |
|
{ |
940 |
|
// aggressive elimination of regular expression membership |
941 |
56 |
ret = d_regexp_elim.eliminateTrusted(atomRet); |
942 |
56 |
if (!ret.isNull()) |
943 |
|
{ |
944 |
92 |
Trace("strings-ppr") << " rewrote " << atom << " -> " << ret.getNode() |
945 |
46 |
<< " via regular expression elimination." |
946 |
46 |
<< std::endl; |
947 |
46 |
atomRet = ret.getNode(); |
948 |
|
} |
949 |
|
} |
950 |
87010 |
return ret; |
951 |
|
} |
952 |
|
|
953 |
|
/** run the given inference step */ |
954 |
192200 |
void TheoryStrings::runInferStep(InferStep s, int effort) |
955 |
|
{ |
956 |
192200 |
Trace("strings-process") << "Run " << s; |
957 |
192200 |
if (effort > 0) |
958 |
|
{ |
959 |
29327 |
Trace("strings-process") << ", effort = " << effort; |
960 |
|
} |
961 |
192200 |
Trace("strings-process") << "..." << std::endl; |
962 |
192200 |
switch (s) |
963 |
|
{ |
964 |
24023 |
case CHECK_INIT: d_bsolver.checkInit(); break; |
965 |
21138 |
case CHECK_CONST_EQC: d_bsolver.checkConstantEquivalenceClasses(); break; |
966 |
30751 |
case CHECK_EXTF_EVAL: d_esolver.checkExtfEval(effort); break; |
967 |
19937 |
case CHECK_CYCLES: d_csolver.checkCycles(); break; |
968 |
19937 |
case CHECK_FLAT_FORMS: d_csolver.checkFlatForms(); break; |
969 |
|
case CHECK_REGISTER_TERMS_PRE_NF: checkRegisterTermsPreNormalForm(); break; |
970 |
14838 |
case CHECK_NORMAL_FORMS_EQ: d_csolver.checkNormalFormsEq(); break; |
971 |
9501 |
case CHECK_NORMAL_FORMS_DEQ: d_csolver.checkNormalFormsDeq(); break; |
972 |
9151 |
case CHECK_CODES: checkCodes(); break; |
973 |
8815 |
case CHECK_LENGTH_EQC: d_csolver.checkLengthsEqc(); break; |
974 |
|
case CHECK_REGISTER_TERMS_NF: checkRegisterTermsNormalForms(); break; |
975 |
19714 |
case CHECK_EXTF_REDUCTION: d_esolver.checkExtfReductions(effort); break; |
976 |
7356 |
case CHECK_MEMBERSHIP: d_rsolver.checkMemberships(); break; |
977 |
7039 |
case CHECK_CARDINALITY: d_bsolver.checkCardinality(); break; |
978 |
|
default: Unreachable(); break; |
979 |
|
} |
980 |
384400 |
Trace("strings-process") << "Done " << s |
981 |
384400 |
<< ", addedFact = " << d_im.hasPendingFact() |
982 |
384400 |
<< ", addedLemma = " << d_im.hasPendingLemma() |
983 |
384400 |
<< ", conflict = " << d_state.isInConflict() |
984 |
192200 |
<< std::endl; |
985 |
192200 |
} |
986 |
|
|
987 |
24023 |
void TheoryStrings::runStrategy(Theory::Effort e) |
988 |
|
{ |
989 |
24023 |
std::vector<std::pair<InferStep, int> >::iterator it = d_strat.stepBegin(e); |
990 |
|
std::vector<std::pair<InferStep, int> >::iterator stepEnd = |
991 |
24023 |
d_strat.stepEnd(e); |
992 |
|
|
993 |
24023 |
Trace("strings-process") << "----check, next round---" << std::endl; |
994 |
743413 |
while (it != stepEnd) |
995 |
|
{ |
996 |
376679 |
InferStep curr = it->first; |
997 |
376679 |
if (curr == BREAK) |
998 |
|
{ |
999 |
184479 |
if (d_im.hasProcessed()) |
1000 |
|
{ |
1001 |
16302 |
break; |
1002 |
|
} |
1003 |
|
} |
1004 |
|
else |
1005 |
|
{ |
1006 |
192200 |
runInferStep(curr, it->second); |
1007 |
192200 |
if (d_state.isInConflict()) |
1008 |
|
{ |
1009 |
682 |
break; |
1010 |
|
} |
1011 |
|
} |
1012 |
359695 |
++it; |
1013 |
|
} |
1014 |
24023 |
Trace("strings-process") << "----finished round---" << std::endl; |
1015 |
24023 |
} |
1016 |
|
|
1017 |
|
std::string TheoryStrings::debugPrintStringsEqc() |
1018 |
|
{ |
1019 |
|
std::stringstream ss; |
1020 |
|
for (unsigned t = 0; t < 2; t++) |
1021 |
|
{ |
1022 |
|
eq::EqClassesIterator eqcs2_i = eq::EqClassesIterator(d_equalityEngine); |
1023 |
|
ss << (t == 0 ? "STRINGS:" : "OTHER:") << std::endl; |
1024 |
|
while (!eqcs2_i.isFinished()) |
1025 |
|
{ |
1026 |
|
Node eqc = (*eqcs2_i); |
1027 |
|
bool print = (t == 0 && eqc.getType().isStringLike()) |
1028 |
|
|| (t == 1 && !eqc.getType().isStringLike()); |
1029 |
|
if (print) |
1030 |
|
{ |
1031 |
|
eq::EqClassIterator eqc2_i = eq::EqClassIterator(eqc, d_equalityEngine); |
1032 |
|
ss << "Eqc( " << eqc << " ) : { "; |
1033 |
|
while (!eqc2_i.isFinished()) |
1034 |
|
{ |
1035 |
|
if ((*eqc2_i) != eqc && (*eqc2_i).getKind() != kind::EQUAL) |
1036 |
|
{ |
1037 |
|
ss << (*eqc2_i) << " "; |
1038 |
|
} |
1039 |
|
++eqc2_i; |
1040 |
|
} |
1041 |
|
ss << " } " << std::endl; |
1042 |
|
EqcInfo* ei = d_state.getOrMakeEqcInfo(eqc, false); |
1043 |
|
if (ei) |
1044 |
|
{ |
1045 |
|
Trace("strings-eqc-debug") |
1046 |
|
<< "* Length term : " << ei->d_lengthTerm.get() << std::endl; |
1047 |
|
Trace("strings-eqc-debug") |
1048 |
|
<< "* Cardinality lemma k : " << ei->d_cardinalityLemK.get() |
1049 |
|
<< std::endl; |
1050 |
|
Trace("strings-eqc-debug") |
1051 |
|
<< "* Normalization length lemma : " |
1052 |
|
<< ei->d_normalizedLength.get() << std::endl; |
1053 |
|
} |
1054 |
|
} |
1055 |
|
++eqcs2_i; |
1056 |
|
} |
1057 |
|
ss << std::endl; |
1058 |
|
} |
1059 |
|
ss << std::endl; |
1060 |
|
return ss.str(); |
1061 |
|
} |
1062 |
|
|
1063 |
|
} // namespace strings |
1064 |
|
} // namespace theory |
1065 |
27735 |
} // namespace cvc5 |