1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Gereon Kremer, Aina Niemetz |
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 inference to proof conversion. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "theory/strings/infer_proof_cons.h" |
17 |
|
|
18 |
|
#include "expr/skolem_manager.h" |
19 |
|
#include "options/smt_options.h" |
20 |
|
#include "options/strings_options.h" |
21 |
|
#include "proof/proof_node_manager.h" |
22 |
|
#include "theory/builtin/proof_checker.h" |
23 |
|
#include "theory/rewriter.h" |
24 |
|
#include "theory/strings/regexp_operation.h" |
25 |
|
#include "theory/strings/theory_strings_utils.h" |
26 |
|
#include "util/statistics_registry.h" |
27 |
|
|
28 |
|
using namespace cvc5::kind; |
29 |
|
|
30 |
|
namespace cvc5 { |
31 |
|
namespace theory { |
32 |
|
namespace strings { |
33 |
|
|
34 |
1245 |
InferProofCons::InferProofCons(context::Context* c, |
35 |
|
ProofNodeManager* pnm, |
36 |
1245 |
SequencesStatistics& statistics) |
37 |
1245 |
: d_pnm(pnm), d_lazyFactMap(c), d_statistics(statistics) |
38 |
|
{ |
39 |
1245 |
Assert(d_pnm != nullptr); |
40 |
1245 |
} |
41 |
|
|
42 |
5024 |
void InferProofCons::notifyFact(const InferInfo& ii) |
43 |
|
{ |
44 |
9988 |
Node fact = ii.d_conc; |
45 |
10048 |
Trace("strings-ipc-debug") |
46 |
5024 |
<< "InferProofCons::notifyFact: " << ii << std::endl; |
47 |
5024 |
if (d_lazyFactMap.find(fact) != d_lazyFactMap.end()) |
48 |
|
{ |
49 |
58 |
Trace("strings-ipc-debug") << "...duplicate!" << std::endl; |
50 |
58 |
return; |
51 |
|
} |
52 |
9930 |
Node symFact = CDProof::getSymmFact(fact); |
53 |
4966 |
if (!symFact.isNull() && d_lazyFactMap.find(symFact) != d_lazyFactMap.end()) |
54 |
|
{ |
55 |
2 |
Trace("strings-ipc-debug") << "...duplicate (sym)!" << std::endl; |
56 |
2 |
return; |
57 |
|
} |
58 |
9928 |
std::shared_ptr<InferInfo> iic = std::make_shared<InferInfo>(ii); |
59 |
4964 |
d_lazyFactMap.insert(ii.d_conc, iic); |
60 |
|
} |
61 |
|
|
62 |
2879 |
void InferProofCons::convert(InferenceId infer, |
63 |
|
bool isRev, |
64 |
|
Node conc, |
65 |
|
const std::vector<Node>& exp, |
66 |
|
ProofStep& ps, |
67 |
|
TheoryProofStepBuffer& psb, |
68 |
|
bool& useBuffer) |
69 |
|
{ |
70 |
|
// by default, don't use the buffer |
71 |
2879 |
useBuffer = false; |
72 |
|
// Must flatten children with respect to AND to be ready to explain. |
73 |
|
// We store the index where each flattened vector begins, since some |
74 |
|
// explanations are grouped together using AND. |
75 |
5758 |
std::vector<size_t> startExpIndex; |
76 |
8578 |
for (const Node& ec : exp) |
77 |
|
{ |
78 |
|
// store the index in the flattened vector |
79 |
5699 |
startExpIndex.push_back(ps.d_children.size()); |
80 |
5699 |
utils::flattenOp(AND, ec, ps.d_children); |
81 |
|
} |
82 |
|
// debug print |
83 |
2879 |
if (Trace.isOn("strings-ipc-debug")) |
84 |
|
{ |
85 |
|
Trace("strings-ipc-debug") << "InferProofCons::convert: " << infer |
86 |
|
<< (isRev ? " :rev " : " ") << conc << std::endl; |
87 |
|
for (const Node& ec : exp) |
88 |
|
{ |
89 |
|
Trace("strings-ipc-debug") << " e: " << ec << std::endl; |
90 |
|
} |
91 |
|
} |
92 |
|
// try to find a set of proof steps to incorporate into the buffer |
93 |
2879 |
psb.clear(); |
94 |
2879 |
NodeManager* nm = NodeManager::currentNM(); |
95 |
5758 |
Node nodeIsRev = nm->mkConst(isRev); |
96 |
2879 |
switch (infer) |
97 |
|
{ |
98 |
|
// ========================== equal by substitution+rewriting |
99 |
730 |
case InferenceId::STRINGS_I_NORM_S: |
100 |
|
case InferenceId::STRINGS_I_CONST_MERGE: |
101 |
|
case InferenceId::STRINGS_I_NORM: |
102 |
|
case InferenceId::STRINGS_LEN_NORM: |
103 |
|
case InferenceId::STRINGS_NORMAL_FORM: |
104 |
|
case InferenceId::STRINGS_CODE_PROXY: |
105 |
|
{ |
106 |
730 |
ps.d_args.push_back(conc); |
107 |
|
// will attempt this rule |
108 |
730 |
ps.d_rule = PfRule::MACRO_SR_PRED_INTRO; |
109 |
|
} |
110 |
730 |
break; |
111 |
|
// ========================== substitution + rewriting |
112 |
290 |
case InferenceId::STRINGS_RE_NF_CONFLICT: |
113 |
|
case InferenceId::STRINGS_EXTF: |
114 |
|
case InferenceId::STRINGS_EXTF_N: |
115 |
|
case InferenceId::STRINGS_EXTF_D: |
116 |
|
case InferenceId::STRINGS_EXTF_D_N: |
117 |
|
case InferenceId::STRINGS_I_CONST_CONFLICT: |
118 |
|
case InferenceId::STRINGS_UNIT_CONST_CONFLICT: |
119 |
|
{ |
120 |
290 |
if (!ps.d_children.empty()) |
121 |
|
{ |
122 |
342 |
std::vector<Node> exps(ps.d_children.begin(), ps.d_children.end() - 1); |
123 |
342 |
Node src = ps.d_children[ps.d_children.size() - 1]; |
124 |
171 |
if (psb.applyPredTransform(src, conc, exps)) |
125 |
|
{ |
126 |
100 |
useBuffer = true; |
127 |
|
} |
128 |
|
} |
129 |
290 |
if (!useBuffer) |
130 |
|
{ |
131 |
|
// use the predicate version? |
132 |
190 |
ps.d_args.push_back(conc); |
133 |
190 |
ps.d_rule = PfRule::MACRO_SR_PRED_INTRO; |
134 |
|
} |
135 |
|
} |
136 |
290 |
break; |
137 |
|
// ========================== rewrite pred |
138 |
49 |
case InferenceId::STRINGS_EXTF_EQ_REW: |
139 |
|
{ |
140 |
|
// the last child is the predicate we are operating on, move to front |
141 |
67 |
Node src = ps.d_children[ps.d_children.size() - 1]; |
142 |
67 |
std::vector<Node> expe(ps.d_children.begin(), ps.d_children.end() - 1); |
143 |
|
// start with a default rewrite |
144 |
67 |
Node mainEqSRew = psb.applyPredElim(src, expe); |
145 |
49 |
if (mainEqSRew == conc) |
146 |
|
{ |
147 |
1 |
useBuffer = true; |
148 |
1 |
break; |
149 |
|
} |
150 |
|
// may need the "extended equality rewrite" |
151 |
|
Node mainEqSRew2 = psb.applyPredElim(mainEqSRew, |
152 |
|
{}, |
153 |
|
MethodId::SB_DEFAULT, |
154 |
|
MethodId::SBA_SEQUENTIAL, |
155 |
66 |
MethodId::RW_REWRITE_EQ_EXT); |
156 |
48 |
if (mainEqSRew2 == conc) |
157 |
|
{ |
158 |
30 |
useBuffer = true; |
159 |
30 |
break; |
160 |
|
} |
161 |
|
// rewrite again with default rewriter |
162 |
36 |
Node mainEqSRew3 = psb.applyPredElim(mainEqSRew2, {}); |
163 |
36 |
useBuffer = (mainEqSRew3 == conc); |
164 |
|
} |
165 |
18 |
break; |
166 |
|
// ========================== substitution+rewriting, CONCAT_EQ, ... |
167 |
824 |
case InferenceId::STRINGS_F_CONST: |
168 |
|
case InferenceId::STRINGS_F_UNIFY: |
169 |
|
case InferenceId::STRINGS_F_ENDPOINT_EMP: |
170 |
|
case InferenceId::STRINGS_F_ENDPOINT_EQ: |
171 |
|
case InferenceId::STRINGS_F_NCTN: |
172 |
|
case InferenceId::STRINGS_N_EQ_CONF: |
173 |
|
case InferenceId::STRINGS_N_CONST: |
174 |
|
case InferenceId::STRINGS_N_UNIFY: |
175 |
|
case InferenceId::STRINGS_N_ENDPOINT_EMP: |
176 |
|
case InferenceId::STRINGS_N_ENDPOINT_EQ: |
177 |
|
case InferenceId::STRINGS_N_NCTN: |
178 |
|
case InferenceId::STRINGS_SSPLIT_CST_PROP: |
179 |
|
case InferenceId::STRINGS_SSPLIT_VAR_PROP: |
180 |
|
case InferenceId::STRINGS_SSPLIT_CST: |
181 |
|
case InferenceId::STRINGS_SSPLIT_VAR: |
182 |
|
{ |
183 |
1648 |
Trace("strings-ipc-core") << "Generate core rule for " << infer |
184 |
824 |
<< " (rev=" << isRev << ")" << std::endl; |
185 |
|
// All of the above inferences have the form: |
186 |
|
// (explanation for why t and s have the same prefix/suffix) ^ |
187 |
|
// t = s ^ |
188 |
|
// (length constraint)? |
189 |
|
// We call t=s the "main equality" below. The length constraint is |
190 |
|
// optional, which we split on below. |
191 |
824 |
size_t nchild = ps.d_children.size(); |
192 |
824 |
size_t mainEqIndex = 0; |
193 |
824 |
bool mainEqIndexSet = false; |
194 |
|
// the length constraint |
195 |
1591 |
std::vector<Node> lenConstraint; |
196 |
|
// these inferences have a length constraint as the last explain |
197 |
824 |
if (infer == InferenceId::STRINGS_N_UNIFY || infer == InferenceId::STRINGS_F_UNIFY |
198 |
533 |
|| infer == InferenceId::STRINGS_SSPLIT_CST || infer == InferenceId::STRINGS_SSPLIT_VAR |
199 |
397 |
|| infer == InferenceId::STRINGS_SSPLIT_VAR_PROP |
200 |
358 |
|| infer == InferenceId::STRINGS_SSPLIT_CST_PROP) |
201 |
|
{ |
202 |
956 |
if (exp.size() >= 2) |
203 |
|
{ |
204 |
478 |
Assert(exp.size() <= startExpIndex.size()); |
205 |
|
// The index of the "main" equality is the last equality before |
206 |
|
// the length explanation. |
207 |
478 |
mainEqIndex = startExpIndex[exp.size() - 1] - 1; |
208 |
478 |
mainEqIndexSet = true; |
209 |
|
// the remainder is the length constraint |
210 |
1434 |
lenConstraint.insert(lenConstraint.end(), |
211 |
956 |
ps.d_children.begin() + mainEqIndex + 1, |
212 |
1912 |
ps.d_children.end()); |
213 |
|
} |
214 |
|
} |
215 |
346 |
else if (nchild >= 1) |
216 |
|
{ |
217 |
|
// The index of the main equality is the last child. |
218 |
346 |
mainEqIndex = nchild - 1; |
219 |
346 |
mainEqIndexSet = true; |
220 |
|
} |
221 |
1591 |
Node mainEq; |
222 |
824 |
if (mainEqIndexSet) |
223 |
|
{ |
224 |
824 |
mainEq = ps.d_children[mainEqIndex]; |
225 |
1648 |
Trace("strings-ipc-core") << "Main equality " << mainEq << " at index " |
226 |
824 |
<< mainEqIndex << std::endl; |
227 |
|
} |
228 |
824 |
if (mainEq.isNull() || mainEq.getKind() != EQUAL) |
229 |
|
{ |
230 |
|
Trace("strings-ipc-core") |
231 |
|
<< "...failed to find main equality" << std::endl; |
232 |
|
break; |
233 |
|
} |
234 |
|
// apply MACRO_SR_PRED_ELIM using equalities up to the main eq |
235 |
1591 |
std::vector<Node> childrenSRew; |
236 |
824 |
childrenSRew.push_back(mainEq); |
237 |
2472 |
childrenSRew.insert(childrenSRew.end(), |
238 |
|
ps.d_children.begin(), |
239 |
3296 |
ps.d_children.begin() + mainEqIndex); |
240 |
|
Node mainEqSRew = |
241 |
1591 |
psb.tryStep(PfRule::MACRO_SR_PRED_ELIM, childrenSRew, {}); |
242 |
824 |
if (CDProof::isSame(mainEqSRew, mainEq)) |
243 |
|
{ |
244 |
191 |
Trace("strings-ipc-core") << "...undo step" << std::endl; |
245 |
|
// the rule added above was not necessary |
246 |
191 |
psb.popStep(); |
247 |
|
} |
248 |
633 |
else if (mainEqSRew == conc) |
249 |
|
{ |
250 |
|
Trace("strings-ipc-core") << "...success after rewrite!" << std::endl; |
251 |
|
useBuffer = true; |
252 |
|
break; |
253 |
|
} |
254 |
1648 |
Trace("strings-ipc-core") |
255 |
824 |
<< "Main equality after subs+rewrite " << mainEqSRew << std::endl; |
256 |
|
// now, apply CONCAT_EQ to get the remainder |
257 |
1591 |
std::vector<Node> childrenCeq; |
258 |
824 |
childrenCeq.push_back(mainEqSRew); |
259 |
1591 |
std::vector<Node> argsCeq; |
260 |
824 |
argsCeq.push_back(nodeIsRev); |
261 |
1591 |
Node mainEqCeq = psb.tryStep(PfRule::CONCAT_EQ, childrenCeq, argsCeq); |
262 |
1648 |
Trace("strings-ipc-core") |
263 |
824 |
<< "Main equality after CONCAT_EQ " << mainEqCeq << std::endl; |
264 |
824 |
if (mainEqCeq.isNull() || mainEqCeq.getKind() != EQUAL) |
265 |
|
{ |
266 |
|
// fail |
267 |
|
break; |
268 |
|
} |
269 |
824 |
else if (mainEqCeq == mainEqSRew) |
270 |
|
{ |
271 |
340 |
Trace("strings-ipc-core") << "...undo step" << std::endl; |
272 |
|
// not necessary, probably first component of equality |
273 |
340 |
psb.popStep(); |
274 |
|
} |
275 |
|
// Now, mainEqCeq is an equality t ++ ... == s ++ ... where the |
276 |
|
// inference involved t and s. |
277 |
824 |
if (infer == InferenceId::STRINGS_N_ENDPOINT_EQ |
278 |
611 |
|| infer == InferenceId::STRINGS_N_ENDPOINT_EMP |
279 |
611 |
|| infer == InferenceId::STRINGS_F_ENDPOINT_EQ |
280 |
552 |
|| infer == InferenceId::STRINGS_F_ENDPOINT_EMP) |
281 |
|
{ |
282 |
|
// Should be equal to conclusion already, or rewrite to it. |
283 |
|
// Notice that this step is necessary to handle the "rproc" |
284 |
|
// optimization in processSimpleNEq. Alternatively, this could |
285 |
|
// possibly be done by CONCAT_EQ with !isRev. |
286 |
544 |
std::vector<Node> cexp; |
287 |
272 |
if (psb.applyPredTransform(mainEqCeq, |
288 |
|
conc, |
289 |
|
cexp, |
290 |
|
MethodId::SB_DEFAULT, |
291 |
|
MethodId::SBA_SEQUENTIAL, |
292 |
|
MethodId::RW_REWRITE_EQ_EXT)) |
293 |
|
{ |
294 |
540 |
Trace("strings-ipc-core") << "Transformed to " << conc |
295 |
270 |
<< " via pred transform" << std::endl; |
296 |
|
// success |
297 |
270 |
useBuffer = true; |
298 |
270 |
Trace("strings-ipc-core") << "...success!" << std::endl; |
299 |
272 |
} |
300 |
|
// Otherwise, note that EMP rules conclude ti = "" where |
301 |
|
// t1 ++ ... ++ tn == "". However, these are very rarely applied, let |
302 |
|
// alone for 2+ children. This case is intentionally unhandled here. |
303 |
|
} |
304 |
552 |
else if (infer == InferenceId::STRINGS_N_CONST || infer == InferenceId::STRINGS_F_CONST |
305 |
535 |
|| infer == InferenceId::STRINGS_N_EQ_CONF) |
306 |
|
{ |
307 |
|
// should be a constant conflict |
308 |
34 |
std::vector<Node> childrenC; |
309 |
17 |
childrenC.push_back(mainEqCeq); |
310 |
34 |
std::vector<Node> argsC; |
311 |
17 |
argsC.push_back(nodeIsRev); |
312 |
34 |
Node mainEqC = psb.tryStep(PfRule::CONCAT_CONFLICT, childrenC, argsC); |
313 |
17 |
if (mainEqC == conc) |
314 |
|
{ |
315 |
17 |
useBuffer = true; |
316 |
17 |
Trace("strings-ipc-core") << "...success!" << std::endl; |
317 |
17 |
} |
318 |
|
} |
319 |
|
else |
320 |
|
{ |
321 |
1013 |
std::vector<Node> tvec; |
322 |
1013 |
std::vector<Node> svec; |
323 |
535 |
utils::getConcat(mainEqCeq[0], tvec); |
324 |
535 |
utils::getConcat(mainEqCeq[1], svec); |
325 |
1013 |
Node t0 = tvec[isRev ? tvec.size() - 1 : 0]; |
326 |
1013 |
Node s0 = svec[isRev ? svec.size() - 1 : 0]; |
327 |
535 |
bool applySym = false; |
328 |
|
// may need to apply symmetry |
329 |
1070 |
if ((infer == InferenceId::STRINGS_SSPLIT_CST |
330 |
406 |
|| infer == InferenceId::STRINGS_SSPLIT_CST_PROP) |
331 |
676 |
&& t0.isConst()) |
332 |
|
{ |
333 |
79 |
Assert(!s0.isConst()); |
334 |
79 |
applySym = true; |
335 |
79 |
std::swap(t0, s0); |
336 |
|
} |
337 |
535 |
if (infer == InferenceId::STRINGS_N_UNIFY || infer == InferenceId::STRINGS_F_UNIFY) |
338 |
|
{ |
339 |
291 |
if (conc.getKind() != EQUAL) |
340 |
|
{ |
341 |
|
break; |
342 |
|
} |
343 |
|
// one side should match, the other side could be a split constant |
344 |
291 |
if (conc[0] != t0 && conc[1] != s0) |
345 |
|
{ |
346 |
144 |
applySym = true; |
347 |
144 |
std::swap(t0, s0); |
348 |
|
} |
349 |
291 |
Assert(conc[0].isConst() == t0.isConst()); |
350 |
291 |
Assert(conc[1].isConst() == s0.isConst()); |
351 |
|
} |
352 |
535 |
PfRule rule = PfRule::UNKNOWN; |
353 |
|
// the form of the required length constraint expected by the proof |
354 |
1013 |
Node lenReq; |
355 |
535 |
bool lenSuccess = false; |
356 |
535 |
if (infer == InferenceId::STRINGS_N_UNIFY || infer == InferenceId::STRINGS_F_UNIFY) |
357 |
|
{ |
358 |
|
// the required premise for unify is always len(x) = len(y), |
359 |
|
// however the explanation may not be literally this. Thus, we |
360 |
|
// need to reconstruct a proof from the given explanation. |
361 |
|
// it should be the case that lenConstraint => lenReq. |
362 |
|
// We use terms in the conclusion equality, not t0, s0 here. |
363 |
873 |
lenReq = nm->mkNode(STRING_LENGTH, conc[0]) |
364 |
582 |
.eqNode(nm->mkNode(STRING_LENGTH, conc[1])); |
365 |
291 |
lenSuccess = convertLengthPf(lenReq, lenConstraint, psb); |
366 |
291 |
rule = PfRule::CONCAT_UNIFY; |
367 |
|
} |
368 |
244 |
else if (infer == InferenceId::STRINGS_SSPLIT_VAR) |
369 |
|
{ |
370 |
|
// it should be the case that lenConstraint => lenReq |
371 |
21 |
lenReq = nm->mkNode(STRING_LENGTH, t0) |
372 |
14 |
.eqNode(nm->mkNode(STRING_LENGTH, s0)) |
373 |
14 |
.notNode(); |
374 |
7 |
lenSuccess = convertLengthPf(lenReq, lenConstraint, psb); |
375 |
7 |
rule = PfRule::CONCAT_SPLIT; |
376 |
|
} |
377 |
237 |
else if (infer == InferenceId::STRINGS_SSPLIT_CST) |
378 |
|
{ |
379 |
|
// it should be the case that lenConstraint => lenReq |
380 |
387 |
lenReq = nm->mkNode(STRING_LENGTH, t0) |
381 |
258 |
.eqNode(nm->mkConst(Rational(0))) |
382 |
258 |
.notNode(); |
383 |
129 |
lenSuccess = convertLengthPf(lenReq, lenConstraint, psb); |
384 |
129 |
rule = PfRule::CONCAT_CSPLIT; |
385 |
|
} |
386 |
108 |
else if (infer == InferenceId::STRINGS_SSPLIT_VAR_PROP) |
387 |
|
{ |
388 |
|
// it should be the case that lenConstraint => lenReq |
389 |
60 |
for (unsigned r = 0; r < 2; r++) |
390 |
|
{ |
391 |
180 |
lenReq = nm->mkNode(GT, |
392 |
120 |
nm->mkNode(STRING_LENGTH, t0), |
393 |
120 |
nm->mkNode(STRING_LENGTH, s0)); |
394 |
60 |
if (convertLengthPf(lenReq, lenConstraint, psb)) |
395 |
|
{ |
396 |
39 |
lenSuccess = true; |
397 |
39 |
break; |
398 |
|
} |
399 |
21 |
if (r == 0) |
400 |
|
{ |
401 |
|
// may be the other direction |
402 |
21 |
applySym = true; |
403 |
21 |
std::swap(t0, s0); |
404 |
|
} |
405 |
|
} |
406 |
39 |
rule = PfRule::CONCAT_LPROP; |
407 |
|
} |
408 |
69 |
else if (infer == InferenceId::STRINGS_SSPLIT_CST_PROP) |
409 |
|
{ |
410 |
|
// it should be the case that lenConstraint => lenReq |
411 |
36 |
lenReq = nm->mkNode(STRING_LENGTH, t0) |
412 |
24 |
.eqNode(nm->mkConst(Rational(0))) |
413 |
24 |
.notNode(); |
414 |
12 |
lenSuccess = convertLengthPf(lenReq, lenConstraint, psb); |
415 |
12 |
rule = PfRule::CONCAT_CPROP; |
416 |
|
} |
417 |
535 |
if (!lenSuccess) |
418 |
|
{ |
419 |
114 |
Trace("strings-ipc-core") |
420 |
57 |
<< "...failed due to length constraint" << std::endl; |
421 |
57 |
break; |
422 |
|
} |
423 |
|
// apply symmetry if necessary |
424 |
478 |
if (applySym) |
425 |
|
{ |
426 |
488 |
std::vector<Node> childrenSymm; |
427 |
244 |
childrenSymm.push_back(mainEqCeq); |
428 |
|
// note this explicit step may not be necessary |
429 |
244 |
mainEqCeq = psb.tryStep(PfRule::SYMM, childrenSymm, {}); |
430 |
488 |
Trace("strings-ipc-core") |
431 |
244 |
<< "Main equality after SYMM " << mainEqCeq << std::endl; |
432 |
|
} |
433 |
478 |
if (rule != PfRule::UNKNOWN) |
434 |
|
{ |
435 |
956 |
Trace("strings-ipc-core") |
436 |
478 |
<< "Core rule length requirement is " << lenReq << std::endl; |
437 |
|
// apply the given rule |
438 |
956 |
std::vector<Node> childrenMain; |
439 |
478 |
childrenMain.push_back(mainEqCeq); |
440 |
478 |
childrenMain.push_back(lenReq); |
441 |
956 |
std::vector<Node> argsMain; |
442 |
478 |
argsMain.push_back(nodeIsRev); |
443 |
956 |
Node mainEqMain = psb.tryStep(rule, childrenMain, argsMain); |
444 |
956 |
Trace("strings-ipc-core") << "Main equality after " << rule << " " |
445 |
478 |
<< mainEqMain << std::endl; |
446 |
478 |
if (mainEqMain == mainEqCeq) |
447 |
|
{ |
448 |
3 |
Trace("strings-ipc-core") << "...undo step" << std::endl; |
449 |
|
// not necessary, probably first component of equality |
450 |
3 |
psb.popStep(); |
451 |
|
} |
452 |
|
// either equal or rewrites to it |
453 |
956 |
std::vector<Node> cexp; |
454 |
478 |
if (psb.applyPredTransform(mainEqMain, conc, cexp)) |
455 |
|
{ |
456 |
|
// requires that length success is also true |
457 |
477 |
useBuffer = true; |
458 |
477 |
Trace("strings-ipc-core") << "...success" << std::endl; |
459 |
|
} |
460 |
|
else |
461 |
|
{ |
462 |
1 |
Trace("strings-ipc-core") << "...fail" << std::endl; |
463 |
|
} |
464 |
|
} |
465 |
|
else |
466 |
|
{ |
467 |
|
// should always have given a rule to try above |
468 |
|
Assert(false) << "No reconstruction rule given for " << infer; |
469 |
|
} |
470 |
767 |
} |
471 |
|
} |
472 |
767 |
break; |
473 |
|
// ========================== Disequalities |
474 |
6 |
case InferenceId::STRINGS_DEQ_DISL_FIRST_CHAR_STRING_SPLIT: |
475 |
|
case InferenceId::STRINGS_DEQ_DISL_STRINGS_SPLIT: |
476 |
|
{ |
477 |
18 |
if (conc.getKind() != AND || conc.getNumChildren() != 2 |
478 |
12 |
|| conc[0].getKind() != EQUAL || !conc[0][0].getType().isStringLike() |
479 |
12 |
|| conc[1].getKind() != EQUAL |
480 |
18 |
|| conc[1][0].getKind() != STRING_LENGTH) |
481 |
|
{ |
482 |
|
Trace("strings-ipc-deq") << "malformed application" << std::endl; |
483 |
|
Assert(false) << "unexpected conclusion " << conc << " for " << infer; |
484 |
|
} |
485 |
|
else |
486 |
|
{ |
487 |
|
Node lenReq = |
488 |
12 |
nm->mkNode(GEQ, nm->mkNode(STRING_LENGTH, conc[0][0]), conc[1][1]); |
489 |
12 |
Trace("strings-ipc-deq") |
490 |
6 |
<< "length requirement is " << lenReq << std::endl; |
491 |
6 |
if (convertLengthPf(lenReq, ps.d_children, psb)) |
492 |
|
{ |
493 |
2 |
Trace("strings-ipc-deq") << "...success length" << std::endl; |
494 |
|
// make the proof |
495 |
4 |
std::vector<Node> childrenMain; |
496 |
2 |
childrenMain.push_back(lenReq); |
497 |
4 |
std::vector<Node> argsMain; |
498 |
2 |
argsMain.push_back(nodeIsRev); |
499 |
|
Node mainConc = |
500 |
4 |
psb.tryStep(PfRule::STRING_DECOMPOSE, childrenMain, argsMain); |
501 |
4 |
Trace("strings-ipc-deq") |
502 |
2 |
<< "...main conclusion is " << mainConc << std::endl; |
503 |
2 |
useBuffer = (mainConc == conc); |
504 |
4 |
Trace("strings-ipc-deq") |
505 |
2 |
<< "...success is " << useBuffer << std::endl; |
506 |
|
} |
507 |
|
else |
508 |
|
{ |
509 |
4 |
Trace("strings-ipc-deq") << "...fail length" << std::endl; |
510 |
|
} |
511 |
|
} |
512 |
|
} |
513 |
6 |
break; |
514 |
|
// ========================== Boolean split |
515 |
145 |
case InferenceId::STRINGS_CARD_SP: |
516 |
|
case InferenceId::STRINGS_LEN_SPLIT: |
517 |
|
case InferenceId::STRINGS_LEN_SPLIT_EMP: |
518 |
|
case InferenceId::STRINGS_DEQ_DISL_EMP_SPLIT: |
519 |
|
case InferenceId::STRINGS_DEQ_DISL_FIRST_CHAR_EQ_SPLIT: |
520 |
|
case InferenceId::STRINGS_DEQ_STRINGS_EQ: |
521 |
|
case InferenceId::STRINGS_DEQ_LENS_EQ: |
522 |
|
case InferenceId::STRINGS_DEQ_LENGTH_SP: |
523 |
|
{ |
524 |
145 |
if (conc.getKind() != OR) |
525 |
|
{ |
526 |
|
// This should never happen. If it does, we resort to using |
527 |
|
// STRING_TRUST below (in production mode). |
528 |
|
Assert(false) << "Expected OR conclusion for " << infer; |
529 |
|
} |
530 |
|
else |
531 |
|
{ |
532 |
145 |
ps.d_rule = PfRule::SPLIT; |
533 |
145 |
Assert(ps.d_children.empty()); |
534 |
145 |
ps.d_args.push_back(conc[0]); |
535 |
|
} |
536 |
|
} |
537 |
145 |
break; |
538 |
|
// ========================== Regular expression unfolding |
539 |
52 |
case InferenceId::STRINGS_RE_UNFOLD_POS: |
540 |
|
case InferenceId::STRINGS_RE_UNFOLD_NEG: |
541 |
|
{ |
542 |
52 |
Assert (!ps.d_children.empty()); |
543 |
52 |
size_t nchild = ps.d_children.size(); |
544 |
104 |
Node mem = ps.d_children[nchild-1]; |
545 |
52 |
if (nchild>1) |
546 |
|
{ |
547 |
|
// if more than one, apply MACRO_SR_PRED_ELIM |
548 |
|
std::vector<Node> tcs; |
549 |
|
tcs.insert(tcs.end(), |
550 |
|
ps.d_children.begin(), |
551 |
|
ps.d_children.begin() + (nchild-1)); |
552 |
|
mem = psb.applyPredElim(mem, tcs); |
553 |
|
useBuffer = true; |
554 |
|
} |
555 |
52 |
PfRule r = PfRule::UNKNOWN; |
556 |
52 |
if (mem.isNull()) |
557 |
|
{ |
558 |
|
// failed to eliminate above |
559 |
|
Assert(false) << "Failed to apply MACRO_SR_PRED_ELIM for RE unfold"; |
560 |
|
useBuffer = false; |
561 |
|
} |
562 |
52 |
else if (infer == InferenceId::STRINGS_RE_UNFOLD_POS) |
563 |
|
{ |
564 |
39 |
r = PfRule::RE_UNFOLD_POS; |
565 |
|
} |
566 |
|
else |
567 |
|
{ |
568 |
13 |
r = PfRule::RE_UNFOLD_NEG; |
569 |
|
// it may be an optimized form of concat simplification |
570 |
13 |
Assert(mem.getKind() == NOT && mem[0].getKind() == STRING_IN_REGEXP); |
571 |
13 |
if (mem[0][1].getKind() == REGEXP_CONCAT) |
572 |
|
{ |
573 |
|
size_t index; |
574 |
22 |
Node reLen = RegExpOpr::getRegExpConcatFixed(mem[0][1], index); |
575 |
|
// if we can find a fixed length for a component, use the optimized |
576 |
|
// version |
577 |
11 |
if (!reLen.isNull()) |
578 |
|
{ |
579 |
9 |
r = PfRule::RE_UNFOLD_NEG_CONCAT_FIXED; |
580 |
|
} |
581 |
|
} |
582 |
|
} |
583 |
52 |
if (useBuffer) |
584 |
|
{ |
585 |
|
mem = psb.tryStep(r, {mem}, {}); |
586 |
|
// should match the conclusion |
587 |
|
useBuffer = (mem==conc); |
588 |
|
} |
589 |
|
else |
590 |
|
{ |
591 |
52 |
ps.d_rule = r; |
592 |
52 |
} |
593 |
|
} |
594 |
52 |
break; |
595 |
|
// ========================== Reduction |
596 |
457 |
case InferenceId::STRINGS_CTN_POS: |
597 |
|
case InferenceId::STRINGS_CTN_NEG_EQUAL: |
598 |
|
{ |
599 |
457 |
if (ps.d_children.size() != 1) |
600 |
|
{ |
601 |
2 |
break; |
602 |
|
} |
603 |
456 |
bool polarity = ps.d_children[0].getKind() != NOT; |
604 |
912 |
Node atom = polarity ? ps.d_children[0] : ps.d_children[0][0]; |
605 |
912 |
std::vector<Node> args; |
606 |
456 |
args.push_back(atom); |
607 |
912 |
Node res = psb.tryStep(PfRule::STRING_EAGER_REDUCTION, {}, args); |
608 |
456 |
if (res.isNull()) |
609 |
|
{ |
610 |
|
break; |
611 |
|
} |
612 |
|
// ite( contains(x,t), x = k1 ++ t ++ k2, x != t ) |
613 |
912 |
std::vector<Node> tiChildren; |
614 |
456 |
tiChildren.push_back(ps.d_children[0]); |
615 |
|
Node ctnt = psb.tryStep( |
616 |
912 |
polarity ? PfRule::TRUE_INTRO : PfRule::FALSE_INTRO, tiChildren, {}); |
617 |
456 |
if (ctnt.isNull() || ctnt.getKind() != EQUAL) |
618 |
|
{ |
619 |
|
break; |
620 |
|
} |
621 |
912 |
std::vector<Node> tchildren; |
622 |
456 |
tchildren.push_back(ctnt); |
623 |
|
// apply substitution { contains(x,t) -> true|false } and rewrite to get |
624 |
|
// conclusion x = k1 ++ t ++ k2 or x != t. |
625 |
456 |
if (psb.applyPredTransform(res, conc, tchildren)) |
626 |
|
{ |
627 |
456 |
useBuffer = true; |
628 |
457 |
} |
629 |
|
} |
630 |
456 |
break; |
631 |
|
|
632 |
215 |
case InferenceId::STRINGS_REDUCTION: |
633 |
|
{ |
634 |
215 |
size_t nchild = conc.getNumChildren(); |
635 |
430 |
Node mainEq; |
636 |
215 |
if (conc.getKind() == EQUAL) |
637 |
|
{ |
638 |
4 |
mainEq = conc; |
639 |
|
} |
640 |
211 |
else if (conc.getKind() == AND && conc[nchild - 1].getKind() == EQUAL) |
641 |
|
{ |
642 |
211 |
mainEq = conc[nchild - 1]; |
643 |
|
} |
644 |
215 |
if (mainEq.isNull()) |
645 |
|
{ |
646 |
|
Trace("strings-ipc-red") << "Bad Reduction: " << conc << std::endl; |
647 |
|
Assert(false) << "Unexpected reduction " << conc; |
648 |
|
break; |
649 |
|
} |
650 |
430 |
std::vector<Node> argsRed; |
651 |
|
// the left hand side of the last conjunct is the term we are reducing |
652 |
215 |
argsRed.push_back(mainEq[0]); |
653 |
430 |
Node red = psb.tryStep(PfRule::STRING_REDUCTION, {}, argsRed); |
654 |
215 |
Trace("strings-ipc-red") << "Reduction : " << red << std::endl; |
655 |
215 |
if (!red.isNull()) |
656 |
|
{ |
657 |
|
// either equal or rewrites to it |
658 |
430 |
std::vector<Node> cexp; |
659 |
215 |
if (psb.applyPredTransform(red, conc, cexp)) |
660 |
|
{ |
661 |
203 |
Trace("strings-ipc-red") << "...success!" << std::endl; |
662 |
203 |
useBuffer = true; |
663 |
|
} |
664 |
|
else |
665 |
|
{ |
666 |
12 |
Trace("strings-ipc-red") << "...failed to reduce" << std::endl; |
667 |
|
} |
668 |
215 |
} |
669 |
|
} |
670 |
215 |
break; |
671 |
|
// ========================== code injectivity |
672 |
53 |
case InferenceId::STRINGS_CODE_INJ: |
673 |
|
{ |
674 |
53 |
ps.d_rule = PfRule::STRING_CODE_INJ; |
675 |
53 |
Assert(conc.getKind() == OR && conc.getNumChildren() == 3 |
676 |
|
&& conc[2].getKind() == EQUAL); |
677 |
53 |
ps.d_args.push_back(conc[2][0]); |
678 |
53 |
ps.d_args.push_back(conc[2][1]); |
679 |
|
} |
680 |
53 |
break; |
681 |
|
// ========================== unit injectivity |
682 |
8 |
case InferenceId::STRINGS_UNIT_INJ: |
683 |
|
{ |
684 |
8 |
ps.d_rule = PfRule::STRING_SEQ_UNIT_INJ; |
685 |
|
} |
686 |
8 |
break; |
687 |
|
// ========================== prefix conflict |
688 |
39 |
case InferenceId::STRINGS_PREFIX_CONFLICT: |
689 |
|
{ |
690 |
39 |
Trace("strings-ipc-prefix") << "Prefix conflict..." << std::endl; |
691 |
78 |
std::vector<Node> eqs; |
692 |
94 |
for (const Node& e : ps.d_children) |
693 |
|
{ |
694 |
55 |
Kind ek = e.getKind(); |
695 |
55 |
if (ek == EQUAL) |
696 |
|
{ |
697 |
39 |
Trace("strings-ipc-prefix") << "- equality : " << e << std::endl; |
698 |
39 |
eqs.push_back(e); |
699 |
|
} |
700 |
16 |
else if (ek == STRING_IN_REGEXP) |
701 |
|
{ |
702 |
|
// unfold it and extract the equality |
703 |
32 |
std::vector<Node> children; |
704 |
16 |
children.push_back(e); |
705 |
32 |
std::vector<Node> args; |
706 |
32 |
Node eunf = psb.tryStep(PfRule::RE_UNFOLD_POS, children, args); |
707 |
32 |
Trace("strings-ipc-prefix") |
708 |
16 |
<< "--- " << e << " unfolds to " << eunf << std::endl; |
709 |
16 |
if (eunf.isNull()) |
710 |
|
{ |
711 |
|
continue; |
712 |
|
} |
713 |
16 |
else if (eunf.getKind() == AND) |
714 |
|
{ |
715 |
|
// equality is the first conjunct |
716 |
32 |
std::vector<Node> childrenAE; |
717 |
16 |
childrenAE.push_back(eunf); |
718 |
32 |
std::vector<Node> argsAE; |
719 |
16 |
argsAE.push_back(nm->mkConst(Rational(0))); |
720 |
32 |
Node eunfAE = psb.tryStep(PfRule::AND_ELIM, childrenAE, argsAE); |
721 |
32 |
Trace("strings-ipc-prefix") |
722 |
16 |
<< "--- and elim to " << eunfAE << std::endl; |
723 |
16 |
if (eunfAE.isNull() || eunfAE.getKind() != EQUAL) |
724 |
|
{ |
725 |
|
Assert(false) |
726 |
|
<< "Unexpected unfolded premise " << eunf << " for " << infer; |
727 |
|
continue; |
728 |
|
} |
729 |
32 |
Trace("strings-ipc-prefix") |
730 |
16 |
<< "- equality : " << eunfAE << std::endl; |
731 |
16 |
eqs.push_back(eunfAE); |
732 |
|
} |
733 |
|
else if (eunf.getKind() == EQUAL) |
734 |
|
{ |
735 |
|
Trace("strings-ipc-prefix") << "- equality : " << eunf << std::endl; |
736 |
|
eqs.push_back(eunf); |
737 |
|
} |
738 |
|
} |
739 |
|
else |
740 |
|
{ |
741 |
|
// not sure how to use this assumption |
742 |
|
Assert(false) << "Unexpected premise " << e << " for " << infer; |
743 |
|
} |
744 |
|
} |
745 |
39 |
if (eqs.empty()) |
746 |
|
{ |
747 |
|
break; |
748 |
|
} |
749 |
|
// connect via transitivity |
750 |
78 |
Node curr = eqs[0]; |
751 |
55 |
for (size_t i = 1, esize = eqs.size(); i < esize; i++) |
752 |
|
{ |
753 |
32 |
Node prev = curr; |
754 |
16 |
curr = convertTrans(curr, eqs[1], psb); |
755 |
16 |
if (curr.isNull()) |
756 |
|
{ |
757 |
|
break; |
758 |
|
} |
759 |
16 |
Trace("strings-ipc-prefix") << "- Via trans: " << curr << std::endl; |
760 |
|
} |
761 |
39 |
if (curr.isNull()) |
762 |
|
{ |
763 |
|
break; |
764 |
|
} |
765 |
78 |
Trace("strings-ipc-prefix") |
766 |
39 |
<< "- Possible conflicting equality : " << curr << std::endl; |
767 |
78 |
std::vector<Node> emp; |
768 |
78 |
Node concE = psb.applyPredElim(curr, emp); |
769 |
78 |
Trace("strings-ipc-prefix") |
770 |
39 |
<< "- After pred elim: " << concE << std::endl; |
771 |
39 |
if (concE == conc) |
772 |
|
{ |
773 |
|
Trace("strings-ipc-prefix") << "...success!" << std::endl; |
774 |
|
useBuffer = true; |
775 |
39 |
} |
776 |
|
} |
777 |
39 |
break; |
778 |
|
// ========================== regular expressions |
779 |
7 |
case InferenceId::STRINGS_RE_INTER_INCLUDE: |
780 |
|
case InferenceId::STRINGS_RE_INTER_CONF: |
781 |
|
case InferenceId::STRINGS_RE_INTER_INFER: |
782 |
|
{ |
783 |
14 |
std::vector<Node> reiExp; |
784 |
14 |
std::vector<Node> reis; |
785 |
14 |
std::vector<Node> reiChildren; |
786 |
14 |
std::vector<Node> reiChildrenOrig; |
787 |
14 |
Node x; |
788 |
|
// make the regular expression intersection that summarizes all |
789 |
|
// memberships in the explanation |
790 |
23 |
for (const Node& c : ps.d_children) |
791 |
|
{ |
792 |
16 |
bool polarity = c.getKind() != NOT; |
793 |
30 |
Node catom = polarity ? c : c[0]; |
794 |
18 |
if (catom.getKind() != STRING_IN_REGEXP) |
795 |
|
{ |
796 |
2 |
Assert(c.getKind() == EQUAL); |
797 |
2 |
if (c.getKind() == EQUAL) |
798 |
|
{ |
799 |
2 |
reiExp.push_back(c); |
800 |
|
} |
801 |
2 |
continue; |
802 |
|
} |
803 |
14 |
if (x.isNull()) |
804 |
|
{ |
805 |
|
// just take the first LHS; others should be equated to it by exp |
806 |
7 |
x = catom[0]; |
807 |
|
} |
808 |
|
Node rcurr = |
809 |
28 |
polarity ? catom[1] : nm->mkNode(REGEXP_COMPLEMENT, catom[1]); |
810 |
14 |
reis.push_back(rcurr); |
811 |
28 |
Node mem = nm->mkNode(STRING_IN_REGEXP, catom[0], rcurr); |
812 |
14 |
reiChildren.push_back(mem); |
813 |
14 |
reiChildrenOrig.push_back(c); |
814 |
|
} |
815 |
|
// go back and justify each premise |
816 |
7 |
bool successChildren = true; |
817 |
21 |
for (size_t i = 0, nchild = reiChildren.size(); i < nchild; i++) |
818 |
|
{ |
819 |
14 |
if (!psb.applyPredTransform(reiChildrenOrig[i], reiChildren[i], reiExp)) |
820 |
|
{ |
821 |
|
Trace("strings-ipc-re") |
822 |
|
<< "... failed to justify child " << reiChildren[i] << " from " |
823 |
|
<< reiChildrenOrig[i] << std::endl; |
824 |
|
successChildren = false; |
825 |
|
break; |
826 |
|
} |
827 |
|
} |
828 |
7 |
if (!successChildren) |
829 |
|
{ |
830 |
|
break; |
831 |
|
} |
832 |
14 |
Node mem = psb.tryStep(PfRule::RE_INTER, reiChildren, {}); |
833 |
14 |
Trace("strings-ipc-re") |
834 |
7 |
<< "Regular expression summary: " << mem << std::endl; |
835 |
|
// the conclusion is rewritable to the premises via rewriting? |
836 |
7 |
if (psb.applyPredTransform(mem, conc, {})) |
837 |
|
{ |
838 |
2 |
Trace("strings-ipc-re") << "... success!" << std::endl; |
839 |
2 |
useBuffer = true; |
840 |
|
} |
841 |
|
else |
842 |
|
{ |
843 |
10 |
Trace("strings-ipc-re") |
844 |
5 |
<< "...failed to rewrite to conclusion" << std::endl; |
845 |
7 |
} |
846 |
|
} |
847 |
7 |
break; |
848 |
|
// ========================== unknown and currently unsupported |
849 |
4 |
case InferenceId::STRINGS_CARDINALITY: |
850 |
|
case InferenceId::STRINGS_I_CYCLE_E: |
851 |
|
case InferenceId::STRINGS_I_CYCLE: |
852 |
|
case InferenceId::STRINGS_INFER_EMP: |
853 |
|
case InferenceId::STRINGS_RE_DELTA: |
854 |
|
case InferenceId::STRINGS_RE_DELTA_CONF: |
855 |
|
case InferenceId::STRINGS_RE_DERIVE: |
856 |
|
case InferenceId::STRINGS_FLOOP: |
857 |
|
case InferenceId::STRINGS_FLOOP_CONFLICT: |
858 |
|
case InferenceId::STRINGS_DEQ_NORM_EMP: |
859 |
|
case InferenceId::STRINGS_CTN_TRANS: |
860 |
|
case InferenceId::STRINGS_CTN_DECOMPOSE: |
861 |
|
default: |
862 |
|
// do nothing, these will be converted to STRING_TRUST below since the |
863 |
|
// rule is unknown. |
864 |
4 |
break; |
865 |
|
} |
866 |
|
|
867 |
|
// now see if we would succeed with the checker-to-try |
868 |
2879 |
bool success = false; |
869 |
2879 |
if (ps.d_rule != PfRule::UNKNOWN) |
870 |
|
{ |
871 |
2356 |
Trace("strings-ipc") << "For " << infer << ", try proof rule " << ps.d_rule |
872 |
1178 |
<< "..."; |
873 |
1178 |
Assert(ps.d_rule != PfRule::UNKNOWN); |
874 |
2356 |
Node pconc = psb.tryStep(ps.d_rule, ps.d_children, ps.d_args); |
875 |
1178 |
if (pconc.isNull() || pconc != conc) |
876 |
|
{ |
877 |
50 |
Trace("strings-ipc") << "failed, pconc is " << pconc << " (expected " |
878 |
25 |
<< conc << ")" << std::endl; |
879 |
25 |
ps.d_rule = PfRule::UNKNOWN; |
880 |
|
} |
881 |
|
else |
882 |
|
{ |
883 |
|
// successfully set up a single step proof in ps |
884 |
1153 |
success = true; |
885 |
1153 |
Trace("strings-ipc") << "success!" << std::endl; |
886 |
|
} |
887 |
|
} |
888 |
1701 |
else if (useBuffer) |
889 |
|
{ |
890 |
|
// successfully set up a multi step proof in psb |
891 |
1566 |
success = true; |
892 |
|
} |
893 |
|
else |
894 |
|
{ |
895 |
270 |
Trace("strings-ipc") << "For " << infer << " " << conc |
896 |
135 |
<< ", no proof rule, failed" << std::endl; |
897 |
|
} |
898 |
2879 |
if (!success) |
899 |
|
{ |
900 |
|
// debug print |
901 |
160 |
if (Trace.isOn("strings-ipc-fail")) |
902 |
|
{ |
903 |
|
Trace("strings-ipc-fail") |
904 |
|
<< "InferProofCons::convert: Failed " << infer |
905 |
|
<< (isRev ? " :rev " : " ") << conc << std::endl; |
906 |
|
for (const Node& ec : exp) |
907 |
|
{ |
908 |
|
Trace("strings-ipc-fail") << " e: " << ec << std::endl; |
909 |
|
} |
910 |
|
} |
911 |
|
// untrustworthy conversion, the argument of STRING_TRUST is its conclusion |
912 |
160 |
ps.d_args.clear(); |
913 |
160 |
ps.d_args.push_back(conc); |
914 |
|
// use the trust rule |
915 |
160 |
ps.d_rule = PfRule::STRING_TRUST; |
916 |
|
// add to stats |
917 |
160 |
d_statistics.d_inferencesNoPf << infer; |
918 |
|
} |
919 |
2879 |
if (Trace.isOn("strings-ipc-debug")) |
920 |
|
{ |
921 |
|
if (useBuffer) |
922 |
|
{ |
923 |
|
Trace("strings-ipc-debug") |
924 |
|
<< "InferProofCons::convert returned buffer with " |
925 |
|
<< psb.getNumSteps() << " steps:" << std::endl; |
926 |
|
const std::vector<std::pair<Node, ProofStep>>& steps = psb.getSteps(); |
927 |
|
for (const std::pair<Node, ProofStep>& step : steps) |
928 |
|
{ |
929 |
|
Trace("strings-ipc-debug") |
930 |
|
<< "- " << step.first << " via " << step.second << std::endl; |
931 |
|
} |
932 |
|
} |
933 |
|
else |
934 |
|
{ |
935 |
|
Trace("strings-ipc-debug") |
936 |
|
<< "InferProofCons::convert returned " << ps << std::endl; |
937 |
|
} |
938 |
|
} |
939 |
2879 |
} |
940 |
|
|
941 |
505 |
bool InferProofCons::convertLengthPf(Node lenReq, |
942 |
|
const std::vector<Node>& lenExp, |
943 |
|
TheoryProofStepBuffer& psb) |
944 |
|
{ |
945 |
865 |
for (const Node& le : lenExp) |
946 |
|
{ |
947 |
637 |
if (lenReq == le) |
948 |
|
{ |
949 |
277 |
return true; |
950 |
|
} |
951 |
|
} |
952 |
456 |
Trace("strings-ipc-len") << "Must explain " << lenReq << " by " << lenExp |
953 |
228 |
<< std::endl; |
954 |
295 |
for (const Node& le : lenExp) |
955 |
|
{ |
956 |
|
// probably rewrites to it? |
957 |
337 |
std::vector<Node> exp; |
958 |
270 |
if (psb.applyPredTransform(le, lenReq, exp)) |
959 |
|
{ |
960 |
118 |
Trace("strings-ipc-len") << "...success by rewrite" << std::endl; |
961 |
118 |
return true; |
962 |
|
} |
963 |
|
// maybe x != "" => len(x) != 0 |
964 |
219 |
std::vector<Node> children; |
965 |
152 |
children.push_back(le); |
966 |
219 |
std::vector<Node> args; |
967 |
219 |
Node res = psb.tryStep(PfRule::STRING_LENGTH_NON_EMPTY, children, args); |
968 |
152 |
if (res == lenReq) |
969 |
|
{ |
970 |
85 |
Trace("strings-ipc-len") << "...success by LENGTH_NON_EMPTY" << std::endl; |
971 |
85 |
return true; |
972 |
|
} |
973 |
|
} |
974 |
25 |
Trace("strings-ipc-len") << "...failed" << std::endl; |
975 |
25 |
return false; |
976 |
|
} |
977 |
|
|
978 |
16 |
Node InferProofCons::convertTrans(Node eqa, |
979 |
|
Node eqb, |
980 |
|
TheoryProofStepBuffer& psb) |
981 |
|
{ |
982 |
16 |
if (eqa.getKind() != EQUAL || eqb.getKind() != EQUAL) |
983 |
|
{ |
984 |
|
return Node::null(); |
985 |
|
} |
986 |
16 |
for (uint32_t i = 0; i < 2; i++) |
987 |
|
{ |
988 |
16 |
Node eqaSym = i == 0 ? eqa[1].eqNode(eqa[0]) : eqa; |
989 |
18 |
for (uint32_t j = 0; j < 2; j++) |
990 |
|
{ |
991 |
20 |
Node eqbSym = j == 0 ? eqb : eqb[1].eqNode(eqb[1]); |
992 |
18 |
if (eqa[i] == eqb[j]) |
993 |
|
{ |
994 |
32 |
std::vector<Node> children; |
995 |
16 |
children.push_back(eqaSym); |
996 |
16 |
children.push_back(eqbSym); |
997 |
16 |
return psb.tryStep(PfRule::TRANS, children, {}); |
998 |
|
} |
999 |
|
} |
1000 |
|
} |
1001 |
|
return Node::null(); |
1002 |
|
} |
1003 |
|
|
1004 |
2879 |
std::shared_ptr<ProofNode> InferProofCons::getProofFor(Node fact) |
1005 |
|
{ |
1006 |
|
// temporary proof |
1007 |
5758 |
CDProof pf(d_pnm); |
1008 |
|
// get the inference |
1009 |
2879 |
NodeInferInfoMap::iterator it = d_lazyFactMap.find(fact); |
1010 |
2879 |
if (it == d_lazyFactMap.end()) |
1011 |
|
{ |
1012 |
|
Node factSym = CDProof::getSymmFact(fact); |
1013 |
|
if (!factSym.isNull()) |
1014 |
|
{ |
1015 |
|
// Use the symmetric fact. There is no need to explictly make a |
1016 |
|
// SYMM proof, as this is handled by CDProof::getProofFor below. |
1017 |
|
it = d_lazyFactMap.find(factSym); |
1018 |
|
} |
1019 |
|
} |
1020 |
2879 |
AlwaysAssert(it != d_lazyFactMap.end()); |
1021 |
|
// now go back and convert it to proof steps and add to proof |
1022 |
2879 |
bool useBuffer = false; |
1023 |
5758 |
ProofStep ps; |
1024 |
5758 |
TheoryProofStepBuffer psb(d_pnm->getChecker()); |
1025 |
5758 |
std::shared_ptr<InferInfo> ii = (*it).second; |
1026 |
|
// run the conversion |
1027 |
2879 |
convert(ii->getId(), ii->d_idRev, ii->d_conc, ii->d_premises, ps, psb, useBuffer); |
1028 |
|
// make the proof based on the step or the buffer |
1029 |
2879 |
if (useBuffer) |
1030 |
|
{ |
1031 |
1566 |
if (!pf.addSteps(psb)) |
1032 |
|
{ |
1033 |
|
return nullptr; |
1034 |
|
} |
1035 |
|
} |
1036 |
|
else |
1037 |
|
{ |
1038 |
1313 |
if (!pf.addStep(fact, ps)) |
1039 |
|
{ |
1040 |
|
return nullptr; |
1041 |
|
} |
1042 |
|
} |
1043 |
2879 |
return pf.getProofFor(fact); |
1044 |
|
} |
1045 |
|
|
1046 |
7810 |
std::string InferProofCons::identify() const |
1047 |
|
{ |
1048 |
7810 |
return "strings::InferProofCons"; |
1049 |
|
} |
1050 |
|
|
1051 |
|
} // namespace strings |
1052 |
|
} // namespace theory |
1053 |
29337 |
} // namespace cvc5 |