1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Andres Noetzli, Morgan Deters |
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 term registry for the theory of strings. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "theory/strings/term_registry.h" |
17 |
|
|
18 |
|
#include "expr/attribute.h" |
19 |
|
#include "options/smt_options.h" |
20 |
|
#include "options/strings_options.h" |
21 |
|
#include "smt/logic_exception.h" |
22 |
|
#include "theory/rewriter.h" |
23 |
|
#include "theory/strings/inference_manager.h" |
24 |
|
#include "theory/strings/theory_strings_utils.h" |
25 |
|
#include "theory/strings/word.h" |
26 |
|
#include "util/rational.h" |
27 |
|
#include "util/string.h" |
28 |
|
|
29 |
|
using namespace std; |
30 |
|
using namespace cvc5::context; |
31 |
|
using namespace cvc5::kind; |
32 |
|
|
33 |
|
namespace cvc5 { |
34 |
|
namespace theory { |
35 |
|
namespace strings { |
36 |
|
|
37 |
|
struct StringsProxyVarAttributeId |
38 |
|
{ |
39 |
|
}; |
40 |
|
typedef expr::Attribute<StringsProxyVarAttributeId, bool> |
41 |
|
StringsProxyVarAttribute; |
42 |
|
|
43 |
6278 |
TermRegistry::TermRegistry(Env& env, |
44 |
|
SolverState& s, |
45 |
|
SequencesStatistics& statistics, |
46 |
6278 |
ProofNodeManager* pnm) |
47 |
|
: EnvObj(env), |
48 |
|
d_state(s), |
49 |
|
d_im(nullptr), |
50 |
|
d_statistics(statistics), |
51 |
|
d_hasStrCode(false), |
52 |
|
d_aent(env.getRewriter()), |
53 |
|
d_functionsTerms(context()), |
54 |
6278 |
d_inputVars(userContext()), |
55 |
|
d_preregisteredTerms(context()), |
56 |
6278 |
d_registeredTerms(userContext()), |
57 |
6278 |
d_registeredTypes(userContext()), |
58 |
6278 |
d_proxyVar(userContext()), |
59 |
6278 |
d_lengthLemmaTermsCache(userContext()), |
60 |
|
d_epg( |
61 |
|
pnm ? new EagerProofGenerator( |
62 |
120 |
pnm, userContext(), "strings::TermRegistry::EagerProofGenerator") |
63 |
37788 |
: nullptr) |
64 |
|
{ |
65 |
6278 |
NodeManager* nm = NodeManager::currentNM(); |
66 |
6278 |
d_zero = nm->mkConst(Rational(0)); |
67 |
6278 |
d_one = nm->mkConst(Rational(1)); |
68 |
6278 |
d_negOne = NodeManager::currentNM()->mkConst(Rational(-1)); |
69 |
6278 |
d_cardSize = utils::getAlphabetCardinality(); |
70 |
6278 |
} |
71 |
|
|
72 |
6275 |
TermRegistry::~TermRegistry() {} |
73 |
|
|
74 |
6278 |
void TermRegistry::finishInit(InferenceManager* im) { d_im = im; } |
75 |
|
|
76 |
20197 |
Node TermRegistry::eagerReduce(Node t, SkolemCache* sc) |
77 |
|
{ |
78 |
20197 |
NodeManager* nm = NodeManager::currentNM(); |
79 |
20197 |
Node lemma; |
80 |
20197 |
Kind tk = t.getKind(); |
81 |
20197 |
if (tk == STRING_TO_CODE) |
82 |
|
{ |
83 |
|
// ite( str.len(s)==1, 0 <= str.code(s) < |A|, str.code(s)=-1 ) |
84 |
2450 |
Node code_len = utils::mkNLength(t[0]).eqNode(nm->mkConst(Rational(1))); |
85 |
2450 |
Node code_eq_neg1 = t.eqNode(nm->mkConst(Rational(-1))); |
86 |
|
Node code_range = nm->mkNode( |
87 |
|
AND, |
88 |
2450 |
nm->mkNode(GEQ, t, nm->mkConst(Rational(0))), |
89 |
2450 |
nm->mkNode( |
90 |
7350 |
LT, t, nm->mkConst(Rational(utils::getAlphabetCardinality())))); |
91 |
1225 |
lemma = nm->mkNode(ITE, code_len, code_range, code_eq_neg1); |
92 |
|
} |
93 |
18972 |
else if (tk == STRING_INDEXOF || tk == STRING_INDEXOF_RE) |
94 |
|
{ |
95 |
|
// (and |
96 |
|
// (or (= (f x y n) (- 1)) (>= (f x y n) n)) |
97 |
|
// (<= (f x y n) (str.len x))) |
98 |
|
// |
99 |
|
// where f in { str.indexof, str.indexof_re } |
100 |
378 |
Node l = nm->mkNode(STRING_LENGTH, t[0]); |
101 |
567 |
lemma = nm->mkNode( |
102 |
|
AND, |
103 |
756 |
nm->mkNode( |
104 |
756 |
OR, nm->mkConst(Rational(-1)).eqNode(t), nm->mkNode(GEQ, t, t[2])), |
105 |
567 |
nm->mkNode(LEQ, t, l)); |
106 |
|
} |
107 |
18783 |
else if (tk == STRING_STOI) |
108 |
|
{ |
109 |
|
// (>= (str.to_int x) (- 1)) |
110 |
61 |
lemma = nm->mkNode(GEQ, t, nm->mkConst(Rational(-1))); |
111 |
|
} |
112 |
18722 |
else if (tk == STRING_CONTAINS) |
113 |
|
{ |
114 |
|
// ite( (str.contains s r), (= s (str.++ sk1 r sk2)), (not (= s r))) |
115 |
|
Node sk1 = |
116 |
4148 |
sc->mkSkolemCached(t[0], t[1], SkolemCache::SK_FIRST_CTN_PRE, "sc1"); |
117 |
|
Node sk2 = |
118 |
4148 |
sc->mkSkolemCached(t[0], t[1], SkolemCache::SK_FIRST_CTN_POST, "sc2"); |
119 |
2074 |
lemma = t[0].eqNode(utils::mkNConcat(sk1, t[1], sk2)); |
120 |
2074 |
lemma = nm->mkNode(ITE, t, lemma, t[0].eqNode(t[1]).notNode()); |
121 |
|
} |
122 |
20197 |
return lemma; |
123 |
|
} |
124 |
|
|
125 |
7739 |
Node TermRegistry::lengthPositive(Node t) |
126 |
|
{ |
127 |
7739 |
NodeManager* nm = NodeManager::currentNM(); |
128 |
15478 |
Node zero = nm->mkConst(Rational(0)); |
129 |
15478 |
Node emp = Word::mkEmptyWord(t.getType()); |
130 |
15478 |
Node tlen = nm->mkNode(STRING_LENGTH, t); |
131 |
15478 |
Node tlenEqZero = tlen.eqNode(zero); |
132 |
15478 |
Node tEqEmp = t.eqNode(emp); |
133 |
15478 |
Node caseEmpty = nm->mkNode(AND, tlenEqZero, tEqEmp); |
134 |
15478 |
Node caseNEmpty = nm->mkNode(GT, tlen, zero); |
135 |
|
// (or (and (= (str.len t) 0) (= t "")) (> (str.len t) 0)) |
136 |
15478 |
return nm->mkNode(OR, caseEmpty, caseNEmpty); |
137 |
|
} |
138 |
|
|
139 |
110699 |
void TermRegistry::preRegisterTerm(TNode n) |
140 |
|
{ |
141 |
110699 |
if (d_preregisteredTerms.find(n) != d_preregisteredTerms.end()) |
142 |
|
{ |
143 |
45630 |
return; |
144 |
|
} |
145 |
110123 |
eq::EqualityEngine* ee = d_state.getEqualityEngine(); |
146 |
110123 |
d_preregisteredTerms.insert(n); |
147 |
220246 |
Trace("strings-preregister") |
148 |
110123 |
<< "TheoryString::preregister : " << n << std::endl; |
149 |
|
// check for logic exceptions |
150 |
110123 |
Kind k = n.getKind(); |
151 |
110123 |
if (!options::stringExp()) |
152 |
|
{ |
153 |
3057 |
if (k == STRING_INDEXOF || k == STRING_INDEXOF_RE || k == STRING_ITOS |
154 |
3057 |
|| k == STRING_STOI || k == STRING_REPLACE || k == STRING_SUBSTR |
155 |
3057 |
|| k == STRING_REPLACE_ALL || k == SEQ_NTH || k == STRING_REPLACE_RE |
156 |
3057 |
|| k == STRING_REPLACE_RE_ALL || k == STRING_CONTAINS || k == STRING_LEQ |
157 |
3057 |
|| k == STRING_TOLOWER || k == STRING_TOUPPER || k == STRING_REV |
158 |
3057 |
|| k == STRING_UPDATE) |
159 |
|
{ |
160 |
|
std::stringstream ss; |
161 |
|
ss << "Term of kind " << k |
162 |
|
<< " not supported in default mode, try --strings-exp"; |
163 |
|
throw LogicException(ss.str()); |
164 |
|
} |
165 |
|
} |
166 |
110123 |
if (k == EQUAL) |
167 |
|
{ |
168 |
42152 |
if (n[0].getType().isRegExp()) |
169 |
|
{ |
170 |
|
std::stringstream ss; |
171 |
|
ss << "Equality between regular expressions is not supported"; |
172 |
|
throw LogicException(ss.str()); |
173 |
|
} |
174 |
42152 |
ee->addTriggerPredicate(n); |
175 |
42152 |
return; |
176 |
|
} |
177 |
67971 |
else if (k == STRING_IN_REGEXP) |
178 |
|
{ |
179 |
2326 |
d_im->requirePhase(n, true); |
180 |
2326 |
ee->addTriggerPredicate(n); |
181 |
2326 |
ee->addTerm(n[0]); |
182 |
2326 |
ee->addTerm(n[1]); |
183 |
2326 |
return; |
184 |
|
} |
185 |
65645 |
else if (k == STRING_TO_CODE) |
186 |
|
{ |
187 |
2227 |
d_hasStrCode = true; |
188 |
|
} |
189 |
63418 |
else if (k == SEQ_NTH || k == STRING_UPDATE) |
190 |
|
{ |
191 |
33 |
d_hasSeqUpdate = true; |
192 |
|
} |
193 |
63385 |
else if (k == REGEXP_RANGE) |
194 |
|
{ |
195 |
159 |
for (const Node& nc : n) |
196 |
|
{ |
197 |
106 |
if (!nc.isConst()) |
198 |
|
{ |
199 |
|
throw LogicException( |
200 |
|
"expecting a constant string term in regexp range"); |
201 |
|
} |
202 |
106 |
if (nc.getConst<String>().size() != 1) |
203 |
|
{ |
204 |
|
throw LogicException( |
205 |
|
"expecting a single constant string term in regexp range"); |
206 |
|
} |
207 |
|
} |
208 |
|
} |
209 |
65645 |
registerTerm(n, 0); |
210 |
131290 |
TypeNode tn = n.getType(); |
211 |
65645 |
if (tn.isRegExp() && n.isVar()) |
212 |
|
{ |
213 |
|
std::stringstream ss; |
214 |
|
ss << "Regular expression variables are not supported."; |
215 |
|
throw LogicException(ss.str()); |
216 |
|
} |
217 |
65645 |
if (tn.isString()) // string-only |
218 |
|
{ |
219 |
|
// all characters of constants should fall in the alphabet |
220 |
33525 |
if (n.isConst()) |
221 |
|
{ |
222 |
5864 |
std::vector<unsigned> vec = n.getConst<String>().getVec(); |
223 |
14617 |
for (unsigned u : vec) |
224 |
|
{ |
225 |
11685 |
if (u >= d_cardSize) |
226 |
|
{ |
227 |
|
std::stringstream ss; |
228 |
|
ss << "Characters in string \"" << n |
229 |
|
<< "\" are outside of the given alphabet."; |
230 |
|
throw LogicException(ss.str()); |
231 |
|
} |
232 |
|
} |
233 |
|
} |
234 |
33525 |
ee->addTerm(n); |
235 |
|
} |
236 |
32120 |
else if (tn.isBoolean()) |
237 |
|
{ |
238 |
|
// All kinds that we do congruence over that may return a Boolean go here |
239 |
1323 |
if (k == STRING_CONTAINS || k == STRING_LEQ || k == SEQ_NTH) |
240 |
|
{ |
241 |
|
// Get triggered for both equal and dis-equal |
242 |
1317 |
ee->addTriggerPredicate(n); |
243 |
|
} |
244 |
|
} |
245 |
|
else |
246 |
|
{ |
247 |
|
// Function applications/predicates |
248 |
30797 |
ee->addTerm(n); |
249 |
|
} |
250 |
|
// Set d_functionsTerms stores all function applications that are |
251 |
|
// relevant to theory combination. Notice that this is a subset of |
252 |
|
// the applications whose kinds are function kinds in the equality |
253 |
|
// engine. This means it does not include applications of operators |
254 |
|
// like re.++, which is not a function kind in the equality engine. |
255 |
|
// Concatenation terms do not need to be considered here because |
256 |
|
// their arguments have string type and do not introduce any shared |
257 |
|
// terms. |
258 |
171692 |
if (n.hasOperator() && ee->isFunctionKind(k) |
259 |
103734 |
&& kindToTheoryId(k) == THEORY_STRINGS && k != STRING_CONCAT) |
260 |
|
{ |
261 |
30588 |
d_functionsTerms.push_back(n); |
262 |
|
} |
263 |
65645 |
if (options::stringFMF()) |
264 |
|
{ |
265 |
13762 |
if (tn.isStringLike()) |
266 |
|
{ |
267 |
|
// Our decision strategy will minimize the length of this term if it is a |
268 |
|
// variable but not an internally generated Skolem, or a term that does |
269 |
|
// not belong to this theory. |
270 |
10014 |
if (n.isVar() ? !d_skCache.isSkolem(n) |
271 |
2890 |
: kindToTheoryId(k) != THEORY_STRINGS) |
272 |
|
{ |
273 |
480 |
d_inputVars.insert(n); |
274 |
480 |
Trace("strings-preregister") << "input variable: " << n << std::endl; |
275 |
|
} |
276 |
|
} |
277 |
|
} |
278 |
|
} |
279 |
|
|
280 |
144762 |
void TermRegistry::registerTerm(Node n, int effort) |
281 |
|
{ |
282 |
289524 |
Trace("strings-register") << "TheoryStrings::registerTerm() " << n |
283 |
144762 |
<< ", effort = " << effort << std::endl; |
284 |
144762 |
if (d_registeredTerms.find(n) != d_registeredTerms.end()) |
285 |
|
{ |
286 |
105275 |
Trace("strings-register") << "...already registered" << std::endl; |
287 |
210550 |
return; |
288 |
|
} |
289 |
39487 |
bool do_register = true; |
290 |
78974 |
TypeNode tn = n.getType(); |
291 |
39487 |
if (!tn.isStringLike()) |
292 |
|
{ |
293 |
18898 |
if (options::stringEagerLen()) |
294 |
|
{ |
295 |
18898 |
do_register = effort == 0; |
296 |
|
} |
297 |
|
else |
298 |
|
{ |
299 |
|
do_register = effort > 0 || n.getKind() != STRING_CONCAT; |
300 |
|
} |
301 |
|
} |
302 |
39487 |
if (!do_register) |
303 |
|
{ |
304 |
|
Trace("strings-register") << "...do not register" << std::endl; |
305 |
|
return; |
306 |
|
} |
307 |
39487 |
Trace("strings-register") << "...register" << std::endl; |
308 |
39487 |
d_registeredTerms.insert(n); |
309 |
|
// ensure the type is registered |
310 |
39487 |
registerType(tn); |
311 |
78974 |
TrustNode regTermLem; |
312 |
39487 |
if (tn.isStringLike()) |
313 |
|
{ |
314 |
|
// register length information: |
315 |
|
// for variables, split on empty vs positive length |
316 |
|
// for concat/const/replace, introduce proxy var and state length relation |
317 |
20589 |
regTermLem = getRegisterTermLemma(n); |
318 |
|
} |
319 |
18898 |
else if (n.getKind() != STRING_CONTAINS) |
320 |
|
{ |
321 |
|
// we don't send out eager reduction lemma for str.contains currently |
322 |
36246 |
Node eagerRedLemma = eagerReduce(n, &d_skCache); |
323 |
18123 |
if (!eagerRedLemma.isNull()) |
324 |
|
{ |
325 |
|
// if there was an eager reduction, we make the trust node for it |
326 |
1475 |
if (d_epg != nullptr) |
327 |
|
{ |
328 |
232 |
regTermLem = d_epg->mkTrustNode( |
329 |
116 |
eagerRedLemma, PfRule::STRING_EAGER_REDUCTION, {}, {n}); |
330 |
|
} |
331 |
|
else |
332 |
|
{ |
333 |
1359 |
regTermLem = TrustNode::mkTrustLemma(eagerRedLemma, nullptr); |
334 |
|
} |
335 |
|
} |
336 |
|
} |
337 |
39487 |
if (!regTermLem.isNull()) |
338 |
|
{ |
339 |
15906 |
Trace("strings-lemma") << "Strings::Lemma REG-TERM : " << regTermLem |
340 |
7953 |
<< std::endl; |
341 |
15906 |
Trace("strings-assert") |
342 |
7953 |
<< "(assert " << regTermLem.getNode() << ")" << std::endl; |
343 |
7953 |
d_im->trustedLemma(regTermLem, InferenceId::STRINGS_REGISTER_TERM); |
344 |
|
} |
345 |
|
} |
346 |
|
|
347 |
39487 |
void TermRegistry::registerType(TypeNode tn) |
348 |
|
{ |
349 |
39487 |
if (d_registeredTypes.find(tn) != d_registeredTypes.end()) |
350 |
|
{ |
351 |
38058 |
return; |
352 |
|
} |
353 |
1429 |
d_registeredTypes.insert(tn); |
354 |
1429 |
if (tn.isStringLike()) |
355 |
|
{ |
356 |
|
// preregister the empty word for the type |
357 |
1152 |
Node emp = Word::mkEmptyWord(tn); |
358 |
576 |
if (!d_state.hasTerm(emp)) |
359 |
|
{ |
360 |
576 |
preRegisterTerm(emp); |
361 |
|
} |
362 |
|
} |
363 |
|
} |
364 |
|
|
365 |
20589 |
TrustNode TermRegistry::getRegisterTermLemma(Node n) |
366 |
|
{ |
367 |
20589 |
Assert(n.getType().isStringLike()); |
368 |
20589 |
NodeManager* nm = NodeManager::currentNM(); |
369 |
|
// register length information: |
370 |
|
// for variables, split on empty vs positive length |
371 |
|
// for concat/const/replace, introduce proxy var and state length relation |
372 |
41178 |
Node lsum; |
373 |
20589 |
if (n.getKind() != STRING_CONCAT && !n.isConst()) |
374 |
|
{ |
375 |
14303 |
Node lsumb = nm->mkNode(STRING_LENGTH, n); |
376 |
14207 |
lsum = Rewriter::rewrite(lsumb); |
377 |
|
// can register length term if it does not rewrite |
378 |
14207 |
if (lsum == lsumb) |
379 |
|
{ |
380 |
14111 |
registerTermAtomic(n, LENGTH_SPLIT); |
381 |
14111 |
return TrustNode::null(); |
382 |
|
} |
383 |
|
} |
384 |
12956 |
Node sk = d_skCache.mkSkolemCached(n, SkolemCache::SK_PURIFY, "lsym"); |
385 |
|
StringsProxyVarAttribute spva; |
386 |
6478 |
sk.setAttribute(spva, true); |
387 |
12956 |
Node eq = Rewriter::rewrite(sk.eqNode(n)); |
388 |
6478 |
d_proxyVar[n] = sk; |
389 |
|
// If we are introducing a proxy for a constant or concat term, we do not |
390 |
|
// need to send lemmas about its length, since its length is already |
391 |
|
// implied. |
392 |
6478 |
if (n.isConst() || n.getKind() == STRING_CONCAT) |
393 |
|
{ |
394 |
|
// do not send length lemma for sk. |
395 |
6382 |
registerTermAtomic(sk, LENGTH_IGNORE); |
396 |
|
} |
397 |
12956 |
Node skl = nm->mkNode(STRING_LENGTH, sk); |
398 |
6478 |
if (n.getKind() == STRING_CONCAT) |
399 |
|
{ |
400 |
8090 |
std::vector<Node> nodeVec; |
401 |
13774 |
for (const Node& nc : n) |
402 |
|
{ |
403 |
9729 |
if (nc.getAttribute(StringsProxyVarAttribute())) |
404 |
|
{ |
405 |
15 |
Assert(d_proxyVarToLength.find(nc) != d_proxyVarToLength.end()); |
406 |
15 |
nodeVec.push_back(d_proxyVarToLength[nc]); |
407 |
|
} |
408 |
|
else |
409 |
|
{ |
410 |
19428 |
Node lni = nm->mkNode(STRING_LENGTH, nc); |
411 |
9714 |
nodeVec.push_back(lni); |
412 |
|
} |
413 |
|
} |
414 |
4045 |
lsum = nm->mkNode(PLUS, nodeVec); |
415 |
4045 |
lsum = Rewriter::rewrite(lsum); |
416 |
|
} |
417 |
2433 |
else if (n.isConst()) |
418 |
|
{ |
419 |
2337 |
lsum = nm->mkConst(Rational(Word::getLength(n))); |
420 |
|
} |
421 |
6478 |
Assert(!lsum.isNull()); |
422 |
6478 |
d_proxyVarToLength[sk] = lsum; |
423 |
12956 |
Node ceq = Rewriter::rewrite(skl.eqNode(lsum)); |
424 |
|
|
425 |
12956 |
Node ret = nm->mkNode(AND, eq, ceq); |
426 |
|
|
427 |
|
// it is a simple rewrite to justify this |
428 |
6478 |
if (d_epg != nullptr) |
429 |
|
{ |
430 |
197 |
return d_epg->mkTrustNode(ret, PfRule::MACRO_SR_PRED_INTRO, {}, {ret}); |
431 |
|
} |
432 |
6281 |
return TrustNode::mkTrustLemma(ret, nullptr); |
433 |
|
} |
434 |
|
|
435 |
21562 |
void TermRegistry::registerTermAtomic(Node n, LengthStatus s) |
436 |
|
{ |
437 |
21562 |
if (d_lengthLemmaTermsCache.find(n) != d_lengthLemmaTermsCache.end()) |
438 |
|
{ |
439 |
21272 |
return; |
440 |
|
} |
441 |
14112 |
d_lengthLemmaTermsCache.insert(n); |
442 |
|
|
443 |
14112 |
if (s == LENGTH_IGNORE) |
444 |
|
{ |
445 |
|
// ignore it |
446 |
6372 |
return; |
447 |
|
} |
448 |
15480 |
std::map<Node, bool> reqPhase; |
449 |
15480 |
TrustNode lenLem = getRegisterTermAtomicLemma(n, s, reqPhase); |
450 |
7740 |
if (!lenLem.isNull()) |
451 |
|
{ |
452 |
15478 |
Trace("strings-lemma") << "Strings::Lemma REGISTER-TERM-ATOMIC : " << lenLem |
453 |
7739 |
<< std::endl; |
454 |
15478 |
Trace("strings-assert") |
455 |
7739 |
<< "(assert " << lenLem.getNode() << ")" << std::endl; |
456 |
7739 |
d_im->trustedLemma(lenLem, InferenceId::STRINGS_REGISTER_TERM_ATOMIC); |
457 |
|
} |
458 |
23210 |
for (const std::pair<const Node, bool>& rp : reqPhase) |
459 |
|
{ |
460 |
15470 |
d_im->requirePhase(rp.first, rp.second); |
461 |
|
} |
462 |
|
} |
463 |
|
|
464 |
26880 |
SkolemCache* TermRegistry::getSkolemCache() { return &d_skCache; } |
465 |
|
|
466 |
5721 |
const context::CDList<TNode>& TermRegistry::getFunctionTerms() const |
467 |
|
{ |
468 |
5721 |
return d_functionsTerms; |
469 |
|
} |
470 |
|
|
471 |
55 |
const context::CDHashSet<Node>& TermRegistry::getInputVars() const |
472 |
|
{ |
473 |
55 |
return d_inputVars; |
474 |
|
} |
475 |
|
|
476 |
9218 |
bool TermRegistry::hasStringCode() const { return d_hasStrCode; } |
477 |
|
|
478 |
|
bool TermRegistry::hasSeqUpdate() const { return d_hasSeqUpdate; } |
479 |
|
|
480 |
|
bool TermRegistry::isHandledUpdate(Node n) |
481 |
|
{ |
482 |
|
Assert(n.getKind() == STRING_UPDATE || n.getKind() == STRING_SUBSTR); |
483 |
|
NodeManager* nm = NodeManager::currentNM(); |
484 |
|
Node lenN = n[2]; |
485 |
|
if (n.getKind() == STRING_UPDATE) |
486 |
|
{ |
487 |
|
lenN = nm->mkNode(STRING_LENGTH, n[2]); |
488 |
|
} |
489 |
|
Node one = nm->mkConst(Rational(1)); |
490 |
|
return d_aent.checkEq(lenN, one); |
491 |
|
} |
492 |
|
|
493 |
|
Node TermRegistry::getUpdateBase(Node n) |
494 |
|
{ |
495 |
|
while (n.getKind() == STRING_UPDATE) |
496 |
|
{ |
497 |
|
n = n[0]; |
498 |
|
} |
499 |
|
return n; |
500 |
|
} |
501 |
|
|
502 |
7740 |
TrustNode TermRegistry::getRegisterTermAtomicLemma( |
503 |
|
Node n, LengthStatus s, std::map<Node, bool>& reqPhase) |
504 |
|
{ |
505 |
7740 |
if (n.isConst()) |
506 |
|
{ |
507 |
|
// No need to send length for constant terms. This case may be triggered |
508 |
|
// for cases where the skolem cache automatically replaces a skolem by |
509 |
|
// a constant. |
510 |
1 |
return TrustNode::null(); |
511 |
|
} |
512 |
7739 |
Assert(n.getType().isStringLike()); |
513 |
7739 |
NodeManager* nm = NodeManager::currentNM(); |
514 |
15478 |
Node n_len = nm->mkNode(kind::STRING_LENGTH, n); |
515 |
15478 |
Node emp = Word::mkEmptyWord(n.getType()); |
516 |
7739 |
if (s == LENGTH_GEQ_ONE) |
517 |
|
{ |
518 |
8 |
Node neq_empty = n.eqNode(emp).negate(); |
519 |
8 |
Node len_n_gt_z = nm->mkNode(GT, n_len, d_zero); |
520 |
8 |
Node len_geq_one = nm->mkNode(AND, neq_empty, len_n_gt_z); |
521 |
8 |
Trace("strings-lemma") << "Strings::Lemma SK-GEQ-ONE : " << len_geq_one |
522 |
4 |
<< std::endl; |
523 |
4 |
Trace("strings-assert") << "(assert " << len_geq_one << ")" << std::endl; |
524 |
4 |
return TrustNode::mkTrustLemma(len_geq_one, nullptr); |
525 |
|
} |
526 |
|
|
527 |
7735 |
if (s == LENGTH_ONE) |
528 |
|
{ |
529 |
|
Node len_one = n_len.eqNode(d_one); |
530 |
|
Trace("strings-lemma") << "Strings::Lemma SK-ONE : " << len_one |
531 |
|
<< std::endl; |
532 |
|
Trace("strings-assert") << "(assert " << len_one << ")" << std::endl; |
533 |
|
return TrustNode::mkTrustLemma(len_one, nullptr); |
534 |
|
} |
535 |
7735 |
Assert(s == LENGTH_SPLIT); |
536 |
|
|
537 |
|
// get the positive length lemma |
538 |
15470 |
Node lenLemma = lengthPositive(n); |
539 |
|
// split whether the string is empty |
540 |
15470 |
Node n_len_eq_z = n_len.eqNode(d_zero); |
541 |
15470 |
Node n_len_eq_z_2 = n.eqNode(emp); |
542 |
15470 |
Node case_empty = nm->mkNode(AND, n_len_eq_z, n_len_eq_z_2); |
543 |
15470 |
Node case_emptyr = Rewriter::rewrite(case_empty); |
544 |
7735 |
if (!case_emptyr.isConst()) |
545 |
|
{ |
546 |
|
// prefer trying the empty case first |
547 |
|
// notice that requirePhase must only be called on rewritten literals that |
548 |
|
// occur in the CNF stream. |
549 |
7735 |
n_len_eq_z = Rewriter::rewrite(n_len_eq_z); |
550 |
7735 |
Assert(!n_len_eq_z.isConst()); |
551 |
7735 |
reqPhase[n_len_eq_z] = true; |
552 |
7735 |
n_len_eq_z_2 = Rewriter::rewrite(n_len_eq_z_2); |
553 |
7735 |
Assert(!n_len_eq_z_2.isConst()); |
554 |
7735 |
reqPhase[n_len_eq_z_2] = true; |
555 |
|
} |
556 |
|
else |
557 |
|
{ |
558 |
|
// If n = "" ---> true or len( n ) = 0 ----> true, then we expect that |
559 |
|
// n ---> "". Since this method is only called on non-constants n, it must |
560 |
|
// be that n = "" ^ len( n ) = 0 does not rewrite to true. |
561 |
|
Assert(!case_emptyr.getConst<bool>()); |
562 |
|
} |
563 |
|
|
564 |
7735 |
if (d_epg != nullptr) |
565 |
|
{ |
566 |
271 |
return d_epg->mkTrustNode(lenLemma, PfRule::STRING_LENGTH_POS, {}, {n}); |
567 |
|
} |
568 |
7464 |
return TrustNode::mkTrustLemma(lenLemma, nullptr); |
569 |
|
} |
570 |
|
|
571 |
325034 |
Node TermRegistry::getSymbolicDefinition(Node n, std::vector<Node>& exp) const |
572 |
|
{ |
573 |
325034 |
if (n.getNumChildren() == 0) |
574 |
|
{ |
575 |
280374 |
Node pn = getProxyVariableFor(n); |
576 |
140187 |
if (pn.isNull()) |
577 |
|
{ |
578 |
16597 |
return Node::null(); |
579 |
|
} |
580 |
247180 |
Node eq = n.eqNode(pn); |
581 |
123590 |
eq = Rewriter::rewrite(eq); |
582 |
123590 |
if (std::find(exp.begin(), exp.end(), eq) == exp.end()) |
583 |
|
{ |
584 |
121295 |
exp.push_back(eq); |
585 |
|
} |
586 |
123590 |
return pn; |
587 |
|
} |
588 |
369694 |
std::vector<Node> children; |
589 |
184847 |
if (n.getMetaKind() == metakind::PARAMETERIZED) |
590 |
|
{ |
591 |
39 |
children.push_back(n.getOperator()); |
592 |
|
} |
593 |
495277 |
for (const Node& nc : n) |
594 |
|
{ |
595 |
332824 |
if (n.getType().isRegExp()) |
596 |
|
{ |
597 |
125411 |
children.push_back(nc); |
598 |
|
} |
599 |
|
else |
600 |
|
{ |
601 |
392432 |
Node ns = getSymbolicDefinition(nc, exp); |
602 |
207413 |
if (ns.isNull()) |
603 |
|
{ |
604 |
22394 |
return Node::null(); |
605 |
|
} |
606 |
|
else |
607 |
|
{ |
608 |
185019 |
children.push_back(ns); |
609 |
|
} |
610 |
|
} |
611 |
|
} |
612 |
162453 |
return NodeManager::currentNM()->mkNode(n.getKind(), children); |
613 |
|
} |
614 |
|
|
615 |
146127 |
Node TermRegistry::getProxyVariableFor(Node n) const |
616 |
|
{ |
617 |
146127 |
NodeNodeMap::const_iterator it = d_proxyVar.find(n); |
618 |
146127 |
if (it != d_proxyVar.end()) |
619 |
|
{ |
620 |
129277 |
return (*it).second; |
621 |
|
} |
622 |
16850 |
return Node::null(); |
623 |
|
} |
624 |
|
|
625 |
5683 |
Node TermRegistry::ensureProxyVariableFor(Node n) |
626 |
|
{ |
627 |
5683 |
Node proxy = getProxyVariableFor(n); |
628 |
5683 |
if (proxy.isNull()) |
629 |
|
{ |
630 |
249 |
registerTerm(n, 0); |
631 |
249 |
proxy = getProxyVariableFor(n); |
632 |
|
} |
633 |
5683 |
Assert(!proxy.isNull()); |
634 |
5683 |
return proxy; |
635 |
|
} |
636 |
|
|
637 |
72903 |
void TermRegistry::removeProxyEqs(Node n, std::vector<Node>& unproc) const |
638 |
|
{ |
639 |
72903 |
if (n.getKind() == AND) |
640 |
|
{ |
641 |
2196 |
for (const Node& nc : n) |
642 |
|
{ |
643 |
1754 |
removeProxyEqs(nc, unproc); |
644 |
|
} |
645 |
888 |
return; |
646 |
|
} |
647 |
72461 |
Trace("strings-subs-proxy") << "Input : " << n << std::endl; |
648 |
144918 |
Node ns = Rewriter::rewrite(n); |
649 |
72461 |
if (ns.getKind() == EQUAL) |
650 |
|
{ |
651 |
216929 |
for (size_t i = 0; i < 2; i++) |
652 |
|
{ |
653 |
|
// determine whether this side has a proxy variable |
654 |
144622 |
if (ns[i].getAttribute(StringsProxyVarAttribute())) |
655 |
|
{ |
656 |
8 |
if (getProxyVariableFor(ns[1 - i]) == ns[i]) |
657 |
|
{ |
658 |
8 |
Trace("strings-subs-proxy") |
659 |
4 |
<< "...trivial definition via " << ns[i] << std::endl; |
660 |
|
// it is a trivial equality, e.g. between a proxy variable |
661 |
|
// and its definition |
662 |
4 |
return; |
663 |
|
} |
664 |
|
} |
665 |
|
} |
666 |
|
} |
667 |
72457 |
if (!n.isConst() || !n.getConst<bool>()) |
668 |
|
{ |
669 |
72455 |
Trace("strings-subs-proxy") << "...unprocessed" << std::endl; |
670 |
72455 |
unproc.push_back(n); |
671 |
|
} |
672 |
|
} |
673 |
|
|
674 |
|
} // namespace strings |
675 |
|
} // namespace theory |
676 |
22755 |
} // namespace cvc5 |