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 |
|
* The eager solver. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "theory/strings/eager_solver.h" |
17 |
|
|
18 |
|
#include "theory/strings/theory_strings_utils.h" |
19 |
|
#include "util/rational.h" |
20 |
|
|
21 |
|
using namespace cvc5::kind; |
22 |
|
|
23 |
|
namespace cvc5 { |
24 |
|
namespace theory { |
25 |
|
namespace strings { |
26 |
|
|
27 |
15271 |
EagerSolver::EagerSolver(Env& env, |
28 |
|
SolverState& state, |
29 |
|
TermRegistry& treg, |
30 |
15271 |
ArithEntail& aent) |
31 |
15271 |
: EnvObj(env), d_state(state), d_treg(treg), d_aent(aent) |
32 |
|
{ |
33 |
15271 |
} |
34 |
|
|
35 |
15266 |
EagerSolver::~EagerSolver() {} |
36 |
|
|
37 |
128430 |
void EagerSolver::eqNotifyNewClass(TNode t) |
38 |
|
{ |
39 |
128430 |
Kind k = t.getKind(); |
40 |
128430 |
if (k == STRING_LENGTH || k == STRING_TO_CODE) |
41 |
|
{ |
42 |
38440 |
eq::EqualityEngine* ee = d_state.getEqualityEngine(); |
43 |
76880 |
Node r = ee->getRepresentative(t[0]); |
44 |
38440 |
EqcInfo* ei = d_state.getOrMakeEqcInfo(r); |
45 |
38440 |
if (k == STRING_LENGTH) |
46 |
|
{ |
47 |
35462 |
ei->d_lengthTerm = t; |
48 |
|
// also assume it as upper/lower bound as applicable for the equivalence |
49 |
|
// class info of t. |
50 |
35462 |
EqcInfo* eil = nullptr; |
51 |
106386 |
for (size_t i = 0; i < 2; i++) |
52 |
|
{ |
53 |
110883 |
Node b = getBoundForLength(t, i == 0); |
54 |
70924 |
if (b.isNull()) |
55 |
|
{ |
56 |
30965 |
continue; |
57 |
|
} |
58 |
39959 |
if (eil == nullptr) |
59 |
|
{ |
60 |
35462 |
eil = d_state.getOrMakeEqcInfo(t); |
61 |
|
} |
62 |
39959 |
if (i == 0) |
63 |
|
{ |
64 |
35462 |
eil->d_firstBound = t; |
65 |
|
} |
66 |
4497 |
else if (i == 1) |
67 |
|
{ |
68 |
4497 |
eil->d_secondBound = t; |
69 |
|
} |
70 |
|
} |
71 |
|
} |
72 |
|
else |
73 |
|
{ |
74 |
2978 |
ei->d_codeTerm = t[0]; |
75 |
38440 |
} |
76 |
|
} |
77 |
89990 |
else if (t.isConst()) |
78 |
|
{ |
79 |
55980 |
TypeNode tn = t.getType(); |
80 |
27990 |
if (tn.isStringLike() || tn.isInteger()) |
81 |
|
{ |
82 |
23665 |
EqcInfo* ei = d_state.getOrMakeEqcInfo(t); |
83 |
23665 |
ei->d_firstBound = t; |
84 |
23665 |
ei->d_secondBound = t; |
85 |
|
} |
86 |
|
} |
87 |
62000 |
else if (k == STRING_CONCAT) |
88 |
|
{ |
89 |
11142 |
addEndpointsToEqcInfo(t, t, t); |
90 |
|
} |
91 |
128430 |
} |
92 |
|
|
93 |
1415310 |
void EagerSolver::eqNotifyMerge(TNode t1, TNode t2) |
94 |
|
{ |
95 |
1415310 |
EqcInfo* e2 = d_state.getOrMakeEqcInfo(t2, false); |
96 |
1415310 |
if (e2 == nullptr) |
97 |
|
{ |
98 |
1359416 |
return; |
99 |
|
} |
100 |
|
// always create it if e2 was non-null |
101 |
735990 |
EqcInfo* e1 = d_state.getOrMakeEqcInfo(t1); |
102 |
|
// check for conflict |
103 |
1471204 |
Node conf = checkForMergeConflict(t1, t2, e1, e2); |
104 |
735990 |
if (!conf.isNull()) |
105 |
|
{ |
106 |
1552 |
InferenceId id = t1.getType().isStringLike() |
107 |
776 |
? InferenceId::STRINGS_PREFIX_CONFLICT |
108 |
776 |
: InferenceId::STRINGS_ARITH_BOUND_CONFLICT; |
109 |
776 |
d_state.setPendingMergeConflict(conf, id); |
110 |
776 |
return; |
111 |
|
} |
112 |
|
// add information from e2 to e1 |
113 |
735214 |
if (!e2->d_lengthTerm.get().isNull()) |
114 |
|
{ |
115 |
365425 |
e1->d_lengthTerm.set(e2->d_lengthTerm); |
116 |
|
} |
117 |
735214 |
if (!e2->d_codeTerm.get().isNull()) |
118 |
|
{ |
119 |
18513 |
e1->d_codeTerm.set(e2->d_codeTerm); |
120 |
|
} |
121 |
735214 |
if (e2->d_cardinalityLemK.get() > e1->d_cardinalityLemK.get()) |
122 |
|
{ |
123 |
|
e1->d_cardinalityLemK.set(e2->d_cardinalityLemK); |
124 |
|
} |
125 |
735214 |
if (!e2->d_normalizedLength.get().isNull()) |
126 |
|
{ |
127 |
124 |
e1->d_normalizedLength.set(e2->d_normalizedLength); |
128 |
|
} |
129 |
|
} |
130 |
|
|
131 |
224852 |
void EagerSolver::eqNotifyDisequal(TNode t1, TNode t2, TNode reason) |
132 |
|
{ |
133 |
224852 |
if (t1.getType().isStringLike()) |
134 |
|
{ |
135 |
|
// store disequalities between strings, may need to check if their lengths |
136 |
|
// are equal/disequal |
137 |
104170 |
d_state.addDisequality(t1, t2); |
138 |
|
} |
139 |
224852 |
} |
140 |
|
|
141 |
16611 |
void EagerSolver::addEndpointsToEqcInfo(Node t, Node concat, Node eqc) |
142 |
|
{ |
143 |
16611 |
Assert(concat.getKind() == STRING_CONCAT |
144 |
|
|| concat.getKind() == REGEXP_CONCAT); |
145 |
16611 |
EqcInfo* ei = nullptr; |
146 |
|
// check each side |
147 |
49809 |
for (unsigned r = 0; r < 2; r++) |
148 |
|
{ |
149 |
33218 |
unsigned index = r == 0 ? 0 : concat.getNumChildren() - 1; |
150 |
66416 |
Node c = utils::getConstantComponent(concat[index]); |
151 |
33218 |
if (!c.isNull()) |
152 |
|
{ |
153 |
9483 |
if (ei == nullptr) |
154 |
|
{ |
155 |
7690 |
ei = d_state.getOrMakeEqcInfo(eqc); |
156 |
|
} |
157 |
18966 |
Trace("strings-eager-pconf-debug") |
158 |
9483 |
<< "New term: " << concat << " for " << t << " with prefix " << c |
159 |
9483 |
<< " (" << (r == 1) << ")" << std::endl; |
160 |
18946 |
Node conf = ei->addEndpointConst(t, c, r == 1); |
161 |
9483 |
if (!conf.isNull()) |
162 |
|
{ |
163 |
20 |
d_state.setPendingMergeConflict(conf, |
164 |
|
InferenceId::STRINGS_PREFIX_CONFLICT); |
165 |
20 |
return; |
166 |
|
} |
167 |
|
} |
168 |
|
} |
169 |
|
} |
170 |
|
|
171 |
735990 |
Node EagerSolver::checkForMergeConflict(Node a, |
172 |
|
Node b, |
173 |
|
EqcInfo* ea, |
174 |
|
EqcInfo* eb) |
175 |
|
{ |
176 |
735990 |
Assert(eb != nullptr && ea != nullptr); |
177 |
735990 |
Assert(a.getType() == b.getType()); |
178 |
735990 |
Assert(a.getType().isStringLike() || a.getType().isInteger()); |
179 |
|
// check prefix, suffix |
180 |
2206625 |
for (size_t i = 0; i < 2; i++) |
181 |
|
{ |
182 |
2942046 |
Node n = i == 0 ? eb->d_firstBound.get() : eb->d_secondBound.get(); |
183 |
1471411 |
if (!n.isNull()) |
184 |
|
{ |
185 |
912612 |
Node conf; |
186 |
456694 |
if (a.getType().isStringLike()) |
187 |
|
{ |
188 |
83570 |
conf = ea->addEndpointConst(n, Node::null(), i == 1); |
189 |
|
} |
190 |
|
else |
191 |
|
{ |
192 |
746248 |
Trace("strings-eager-aconf-debug") |
193 |
373124 |
<< "addArithmeticBound " << n << " into " << a << " from " << b |
194 |
373124 |
<< std::endl; |
195 |
373124 |
conf = addArithmeticBound(ea, n, i == 0); |
196 |
|
} |
197 |
456694 |
if (!conf.isNull()) |
198 |
|
{ |
199 |
776 |
return conf; |
200 |
|
} |
201 |
|
} |
202 |
|
} |
203 |
735214 |
return Node::null(); |
204 |
|
} |
205 |
|
|
206 |
1075217 |
void EagerSolver::notifyFact(TNode atom, |
207 |
|
bool polarity, |
208 |
|
TNode fact, |
209 |
|
bool isInternal) |
210 |
|
{ |
211 |
1075217 |
if (atom.getKind() == STRING_IN_REGEXP) |
212 |
|
{ |
213 |
25621 |
if (polarity && atom[1].getKind() == REGEXP_CONCAT) |
214 |
|
{ |
215 |
5469 |
eq::EqualityEngine* ee = d_state.getEqualityEngine(); |
216 |
10938 |
Node eqc = ee->getRepresentative(atom[0]); |
217 |
5469 |
addEndpointsToEqcInfo(atom, atom[1], eqc); |
218 |
|
} |
219 |
|
} |
220 |
1075217 |
} |
221 |
|
|
222 |
373124 |
Node EagerSolver::addArithmeticBound(EqcInfo* e, Node t, bool isLower) |
223 |
|
{ |
224 |
373124 |
Assert(e != nullptr); |
225 |
373124 |
Assert(!t.isNull()); |
226 |
746248 |
Node tb = t.isConst() ? t : getBoundForLength(t, isLower); |
227 |
746248 |
Assert(!tb.isNull() && tb.getKind() == CONST_RATIONAL) |
228 |
373124 |
<< "Unexpected bound " << tb << " from " << t; |
229 |
746248 |
Rational br = tb.getConst<Rational>(); |
230 |
746248 |
Node prev = isLower ? e->d_firstBound : e->d_secondBound; |
231 |
|
// check if subsumed |
232 |
373124 |
if (!prev.isNull()) |
233 |
|
{ |
234 |
|
// convert to bound |
235 |
394865 |
Node prevb = prev.isConst() ? prev : getBoundForLength(prev, isLower); |
236 |
372452 |
Assert(!prevb.isNull() && prevb.getKind() == CONST_RATIONAL); |
237 |
394865 |
Rational prevbr = prevb.getConst<Rational>(); |
238 |
372452 |
if (prevbr == br || (br < prevbr) == isLower) |
239 |
|
{ |
240 |
|
// subsumed |
241 |
350039 |
return Node::null(); |
242 |
|
} |
243 |
|
} |
244 |
46170 |
Node prevo = isLower ? e->d_secondBound : e->d_firstBound; |
245 |
46170 |
Trace("strings-eager-aconf-debug") |
246 |
23085 |
<< "Check conflict for bounds " << t << " " << prevo << std::endl; |
247 |
23085 |
if (!prevo.isNull()) |
248 |
|
{ |
249 |
|
// are we in conflict? |
250 |
275 |
Node prevob = prevo.isConst() ? prevo : getBoundForLength(prevo, !isLower); |
251 |
275 |
Assert(!prevob.isNull() && prevob.getKind() == CONST_RATIONAL); |
252 |
275 |
Rational prevobr = prevob.getConst<Rational>(); |
253 |
275 |
if (prevobr != br && (prevobr < br) == isLower) |
254 |
|
{ |
255 |
|
// conflict |
256 |
550 |
Node ret = EqcInfo::mkMergeConflict(t, prevo); |
257 |
550 |
Trace("strings-eager-aconf") |
258 |
275 |
<< "String: eager arithmetic bound conflict: " << ret << std::endl; |
259 |
275 |
return ret; |
260 |
|
} |
261 |
|
} |
262 |
22810 |
if (isLower) |
263 |
|
{ |
264 |
22810 |
e->d_firstBound = t; |
265 |
|
} |
266 |
|
else |
267 |
|
{ |
268 |
|
e->d_secondBound = t; |
269 |
|
} |
270 |
22810 |
return Node::null(); |
271 |
|
} |
272 |
|
|
273 |
526540 |
Node EagerSolver::getBoundForLength(Node len, bool isLower) |
274 |
|
{ |
275 |
526540 |
Assert(len.getKind() == STRING_LENGTH); |
276 |
|
// it is prohibitively expensive to convert to original form and rewrite, |
277 |
|
// since this may invoke the rewriter on lengths of complex terms. Instead, |
278 |
|
// we convert to original term the argument, then call the utility method |
279 |
|
// for computing the length of the argument, implicitly under an application |
280 |
|
// of length (ArithEntail::getConstantBoundLength). |
281 |
|
// convert to original form |
282 |
1053080 |
Node olent = SkolemManager::getOriginalForm(len[0]); |
283 |
|
// get the bound |
284 |
526540 |
Node c = d_aent.getConstantBoundLength(olent, isLower); |
285 |
1053080 |
Trace("strings-eager-aconf-debug") |
286 |
526540 |
<< "Constant " << (isLower ? "lower" : "upper") << " bound for " << len |
287 |
526540 |
<< " is " << c << ", from original form " << olent << std::endl; |
288 |
1053080 |
return c; |
289 |
|
} |
290 |
|
|
291 |
|
} // namespace strings |
292 |
|
} // namespace theory |
293 |
31125 |
} // namespace cvc5 |