1 /********************* */
2 /*! \file infer_proof_cons.cpp
4 ** Top contributors (to current version):
6 ** This file is part of the CVC4 project.
7 ** Copyright (c) 2009-2020 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.\endverbatim
12 ** \brief Implementation of inference to proof conversion
15 #include "theory/strings/infer_proof_cons.h"
17 #include "expr/skolem_manager.h"
18 #include "options/smt_options.h"
19 #include "options/strings_options.h"
20 #include "theory/builtin/proof_checker.h"
21 #include "theory/rewriter.h"
22 #include "theory/strings/regexp_operation.h"
23 #include "theory/strings/theory_strings_utils.h"
25 using namespace CVC4::kind
;
31 InferProofCons::InferProofCons(context::Context
* c
,
32 ProofNodeManager
* pnm
,
33 SequencesStatistics
& statistics
)
34 : d_pnm(pnm
), d_lazyFactMap(c
), d_statistics(statistics
)
36 Assert(d_pnm
!= nullptr);
39 void InferProofCons::notifyFact(const InferInfo
& ii
)
41 Node fact
= ii
.d_conc
;
42 Trace("strings-ipc-debug")
43 << "InferProofCons::notifyFact: " << ii
<< std::endl
;
44 if (d_lazyFactMap
.find(fact
) != d_lazyFactMap
.end())
46 Trace("strings-ipc-debug") << "...duplicate!" << std::endl
;
49 Node symFact
= CDProof::getSymmFact(fact
);
50 if (!symFact
.isNull() && d_lazyFactMap
.find(symFact
) != d_lazyFactMap
.end())
52 Trace("strings-ipc-debug") << "...duplicate (sym)!" << std::endl
;
55 std::shared_ptr
<InferInfo
> iic
= std::make_shared
<InferInfo
>(ii
);
56 d_lazyFactMap
.insert(ii
.d_conc
, iic
);
59 void InferProofCons::convert(Inference infer
,
62 const std::vector
<Node
>& exp
,
64 TheoryProofStepBuffer
& psb
,
67 // by default, don't use the buffer
69 // Must flatten children with respect to AND to be ready to explain.
70 // We store the index where each flattened vector begins, since some
71 // explanations are grouped together using AND.
72 std::vector
<size_t> startExpIndex
;
73 for (const Node
& ec
: exp
)
75 // store the index in the flattened vector
76 startExpIndex
.push_back(ps
.d_children
.size());
77 utils::flattenOp(AND
, ec
, ps
.d_children
);
80 if (Trace
.isOn("strings-ipc-debug"))
82 Trace("strings-ipc-debug") << "InferProofCons::convert: " << infer
83 << (isRev
? " :rev " : " ") << conc
<< std::endl
;
84 for (const Node
& ec
: exp
)
86 Trace("strings-ipc-debug") << " e: " << ec
<< std::endl
;
89 // try to find a set of proof steps to incorporate into the buffer
91 NodeManager
* nm
= NodeManager::currentNM();
92 Node nodeIsRev
= nm
->mkConst(isRev
);
95 // ========================== equal by substitution+rewriting
96 case Inference::I_NORM_S
:
97 case Inference::I_CONST_MERGE
:
98 case Inference::I_NORM
:
99 case Inference::LEN_NORM
:
100 case Inference::NORMAL_FORM
:
101 case Inference::CODE_PROXY
:
103 ps
.d_args
.push_back(conc
);
104 // will attempt this rule
105 ps
.d_rule
= PfRule::MACRO_SR_PRED_INTRO
;
108 // ========================== substitution + rewriting
109 case Inference::RE_NF_CONFLICT
:
110 case Inference::EXTF
:
111 case Inference::EXTF_N
:
112 case Inference::EXTF_D
:
113 case Inference::EXTF_D_N
:
114 case Inference::I_CONST_CONFLICT
:
115 case Inference::UNIT_CONST_CONFLICT
:
117 if (!ps
.d_children
.empty())
119 std::vector
<Node
> exps(ps
.d_children
.begin(), ps
.d_children
.end() - 1);
120 Node src
= ps
.d_children
[ps
.d_children
.size() - 1];
121 if (psb
.applyPredTransform(src
, conc
, exps
))
128 // use the predicate version?
129 ps
.d_args
.push_back(conc
);
130 ps
.d_rule
= PfRule::MACRO_SR_PRED_INTRO
;
134 // ========================== rewrite pred
135 case Inference::EXTF_EQ_REW
:
136 case Inference::INFER_EMP
:
138 // the last child is the predicate we are operating on, move to front
139 Node src
= ps
.d_children
[ps
.d_children
.size() - 1];
140 std::vector
<Node
> expe(ps
.d_children
.begin(), ps
.d_children
.end() - 1);
141 // start with a default rewrite
142 Node mainEqSRew
= psb
.applyPredElim(src
, expe
);
143 if (mainEqSRew
== conc
)
148 // may need the "extended equality rewrite"
149 Node mainEqSRew2
= psb
.applyPredElim(
150 mainEqSRew
, {}, MethodId::SB_DEFAULT
, MethodId::RW_REWRITE_EQ_EXT
);
151 if (mainEqSRew2
== conc
)
156 // rewrite again with default rewriter
157 Node mainEqSRew3
= psb
.applyPredElim(mainEqSRew2
, {});
158 useBuffer
= (mainEqSRew3
== conc
);
161 // ========================== substitution+rewriting, CONCAT_EQ, ...
162 case Inference::F_CONST
:
163 case Inference::F_UNIFY
:
164 case Inference::F_ENDPOINT_EMP
:
165 case Inference::F_ENDPOINT_EQ
:
166 case Inference::F_NCTN
:
167 case Inference::N_EQ_CONF
:
168 case Inference::N_CONST
:
169 case Inference::N_UNIFY
:
170 case Inference::N_ENDPOINT_EMP
:
171 case Inference::N_ENDPOINT_EQ
:
172 case Inference::N_NCTN
:
173 case Inference::SSPLIT_CST_PROP
:
174 case Inference::SSPLIT_VAR_PROP
:
175 case Inference::SSPLIT_CST
:
176 case Inference::SSPLIT_VAR
:
178 Trace("strings-ipc-core") << "Generate core rule for " << infer
179 << " (rev=" << isRev
<< ")" << std::endl
;
180 // All of the above inferences have the form:
181 // (explanation for why t and s have the same prefix/suffix) ^
183 // (length constraint)?
184 // We call t=s the "main equality" below. The length constraint is
185 // optional, which we split on below.
186 size_t nchild
= ps
.d_children
.size();
187 size_t mainEqIndex
= 0;
188 bool mainEqIndexSet
= false;
189 // the length constraint
190 std::vector
<Node
> lenConstraint
;
191 // these inferences have a length constraint as the last explain
192 if (infer
== Inference::N_UNIFY
|| infer
== Inference::F_UNIFY
193 || infer
== Inference::SSPLIT_CST
|| infer
== Inference::SSPLIT_VAR
194 || infer
== Inference::SSPLIT_VAR_PROP
195 || infer
== Inference::SSPLIT_CST_PROP
)
199 Assert(exp
.size() <= startExpIndex
.size());
200 // The index of the "main" equality is the last equality before
201 // the length explanation.
202 mainEqIndex
= startExpIndex
[exp
.size() - 1] - 1;
203 mainEqIndexSet
= true;
204 // the remainder is the length constraint
205 lenConstraint
.insert(lenConstraint
.end(),
206 ps
.d_children
.begin() + mainEqIndex
+ 1,
207 ps
.d_children
.end());
210 else if (nchild
>= 1)
212 // The index of the main equality is the last child.
213 mainEqIndex
= nchild
- 1;
214 mainEqIndexSet
= true;
219 mainEq
= ps
.d_children
[mainEqIndex
];
220 Trace("strings-ipc-core") << "Main equality " << mainEq
<< " at index "
221 << mainEqIndex
<< std::endl
;
223 if (mainEq
.isNull() || mainEq
.getKind() != EQUAL
)
225 Trace("strings-ipc-core")
226 << "...failed to find main equality" << std::endl
;
229 // apply MACRO_SR_PRED_ELIM using equalities up to the main eq
230 std::vector
<Node
> childrenSRew
;
231 childrenSRew
.push_back(mainEq
);
232 childrenSRew
.insert(childrenSRew
.end(),
233 ps
.d_children
.begin(),
234 ps
.d_children
.begin() + mainEqIndex
);
236 psb
.tryStep(PfRule::MACRO_SR_PRED_ELIM
, childrenSRew
, {});
237 if (CDProof::isSame(mainEqSRew
, mainEq
))
239 Trace("strings-ipc-core") << "...undo step" << std::endl
;
240 // the rule added above was not necessary
243 else if (mainEqSRew
== conc
)
245 Trace("strings-ipc-core") << "...success after rewrite!" << std::endl
;
249 Trace("strings-ipc-core")
250 << "Main equality after subs+rewrite " << mainEqSRew
<< std::endl
;
251 // now, apply CONCAT_EQ to get the remainder
252 std::vector
<Node
> childrenCeq
;
253 childrenCeq
.push_back(mainEqSRew
);
254 std::vector
<Node
> argsCeq
;
255 argsCeq
.push_back(nodeIsRev
);
256 Node mainEqCeq
= psb
.tryStep(PfRule::CONCAT_EQ
, childrenCeq
, argsCeq
);
257 Trace("strings-ipc-core")
258 << "Main equality after CONCAT_EQ " << mainEqCeq
<< std::endl
;
259 if (mainEqCeq
.isNull() || mainEqCeq
.getKind() != EQUAL
)
264 else if (mainEqCeq
== mainEqSRew
)
266 Trace("strings-ipc-core") << "...undo step" << std::endl
;
267 // not necessary, probably first component of equality
270 // Now, mainEqCeq is an equality t ++ ... == s ++ ... where the
271 // inference involved t and s.
272 if (infer
== Inference::N_ENDPOINT_EQ
273 || infer
== Inference::N_ENDPOINT_EMP
274 || infer
== Inference::F_ENDPOINT_EQ
275 || infer
== Inference::F_ENDPOINT_EMP
)
277 // Should be equal to conclusion already, or rewrite to it.
278 // Notice that this step is necessary to handle the "rproc"
279 // optimization in processSimpleNEq. Alternatively, this could
280 // possibly be done by CONCAT_EQ with !isRev.
281 std::vector
<Node
> cexp
;
282 if (psb
.applyPredTransform(mainEqCeq
,
285 MethodId::SB_DEFAULT
,
286 MethodId::RW_REWRITE_EQ_EXT
))
288 Trace("strings-ipc-core") << "Transformed to " << conc
289 << " via pred transform" << std::endl
;
292 Trace("strings-ipc-core") << "...success!" << std::endl
;
294 // Otherwise, note that EMP rules conclude ti = "" where
295 // t1 ++ ... ++ tn == "". However, these are very rarely applied, let
296 // alone for 2+ children. This case is intentionally unhandled here.
298 else if (infer
== Inference::N_CONST
|| infer
== Inference::F_CONST
299 || infer
== Inference::N_EQ_CONF
)
301 // should be a constant conflict
302 std::vector
<Node
> childrenC
;
303 childrenC
.push_back(mainEqCeq
);
304 std::vector
<Node
> argsC
;
305 argsC
.push_back(nodeIsRev
);
306 Node mainEqC
= psb
.tryStep(PfRule::CONCAT_CONFLICT
, childrenC
, argsC
);
310 Trace("strings-ipc-core") << "...success!" << std::endl
;
315 std::vector
<Node
> tvec
;
316 std::vector
<Node
> svec
;
317 utils::getConcat(mainEqCeq
[0], tvec
);
318 utils::getConcat(mainEqCeq
[1], svec
);
319 Node t0
= tvec
[isRev
? tvec
.size() - 1 : 0];
320 Node s0
= svec
[isRev
? svec
.size() - 1 : 0];
321 bool applySym
= false;
322 // may need to apply symmetry
323 if ((infer
== Inference::SSPLIT_CST
324 || infer
== Inference::SSPLIT_CST_PROP
)
327 Assert(!s0
.isConst());
331 if (infer
== Inference::N_UNIFY
|| infer
== Inference::F_UNIFY
)
333 if (conc
.getKind() != EQUAL
)
337 // one side should match, the other side could be a split constant
338 if (conc
[0] != t0
&& conc
[1] != s0
)
343 Assert(conc
[0].isConst() == t0
.isConst());
344 Assert(conc
[1].isConst() == s0
.isConst());
346 PfRule rule
= PfRule::UNKNOWN
;
347 // the form of the required length constraint expected by the proof
349 bool lenSuccess
= false;
350 if (infer
== Inference::N_UNIFY
|| infer
== Inference::F_UNIFY
)
352 // the required premise for unify is always len(x) = len(y),
353 // however the explanation may not be literally this. Thus, we
354 // need to reconstruct a proof from the given explanation.
355 // it should be the case that lenConstraint => lenReq.
356 // We use terms in the conclusion equality, not t0, s0 here.
357 lenReq
= nm
->mkNode(STRING_LENGTH
, conc
[0])
358 .eqNode(nm
->mkNode(STRING_LENGTH
, conc
[1]));
359 lenSuccess
= convertLengthPf(lenReq
, lenConstraint
, psb
);
360 rule
= PfRule::CONCAT_UNIFY
;
362 else if (infer
== Inference::SSPLIT_VAR
)
364 // it should be the case that lenConstraint => lenReq
365 lenReq
= nm
->mkNode(STRING_LENGTH
, t0
)
366 .eqNode(nm
->mkNode(STRING_LENGTH
, s0
))
368 lenSuccess
= convertLengthPf(lenReq
, lenConstraint
, psb
);
369 rule
= PfRule::CONCAT_SPLIT
;
371 else if (infer
== Inference::SSPLIT_CST
)
373 // it should be the case that lenConstraint => lenReq
374 lenReq
= nm
->mkNode(STRING_LENGTH
, t0
)
375 .eqNode(nm
->mkConst(Rational(0)))
377 lenSuccess
= convertLengthPf(lenReq
, lenConstraint
, psb
);
378 rule
= PfRule::CONCAT_CSPLIT
;
380 else if (infer
== Inference::SSPLIT_VAR_PROP
)
382 // it should be the case that lenConstraint => lenReq
383 for (unsigned r
= 0; r
< 2; r
++)
385 lenReq
= nm
->mkNode(GT
,
386 nm
->mkNode(STRING_LENGTH
, t0
),
387 nm
->mkNode(STRING_LENGTH
, s0
));
388 if (convertLengthPf(lenReq
, lenConstraint
, psb
))
395 // may be the other direction
400 rule
= PfRule::CONCAT_LPROP
;
402 else if (infer
== Inference::SSPLIT_CST_PROP
)
404 // it should be the case that lenConstraint => lenReq
405 lenReq
= nm
->mkNode(STRING_LENGTH
, t0
)
406 .eqNode(nm
->mkConst(Rational(0)))
408 lenSuccess
= convertLengthPf(lenReq
, lenConstraint
, psb
);
409 rule
= PfRule::CONCAT_CPROP
;
413 Trace("strings-ipc-core")
414 << "...failed due to length constraint" << std::endl
;
417 // apply symmetry if necessary
420 std::vector
<Node
> childrenSymm
;
421 childrenSymm
.push_back(mainEqCeq
);
422 // note this explicit step may not be necessary
423 mainEqCeq
= psb
.tryStep(PfRule::SYMM
, childrenSymm
, {});
424 Trace("strings-ipc-core")
425 << "Main equality after SYMM " << mainEqCeq
<< std::endl
;
427 if (rule
!= PfRule::UNKNOWN
)
429 Trace("strings-ipc-core")
430 << "Core rule length requirement is " << lenReq
<< std::endl
;
431 // apply the given rule
432 std::vector
<Node
> childrenMain
;
433 childrenMain
.push_back(mainEqCeq
);
434 childrenMain
.push_back(lenReq
);
435 std::vector
<Node
> argsMain
;
436 argsMain
.push_back(nodeIsRev
);
437 Node mainEqMain
= psb
.tryStep(rule
, childrenMain
, argsMain
);
438 Trace("strings-ipc-core") << "Main equality after " << rule
<< " "
439 << mainEqMain
<< std::endl
;
440 if (mainEqMain
== mainEqCeq
)
442 Trace("strings-ipc-core") << "...undo step" << std::endl
;
443 // not necessary, probably first component of equality
446 // either equal or rewrites to it
447 std::vector
<Node
> cexp
;
448 if (psb
.applyPredTransform(mainEqMain
, conc
, cexp
))
450 // requires that length success is also true
452 Trace("strings-ipc-core") << "...success" << std::endl
;
456 Trace("strings-ipc-core") << "...fail" << std::endl
;
461 // should always have given a rule to try above
462 Assert(false) << "No reconstruction rule given for " << infer
;
467 // ========================== Disequalities
468 case Inference::DEQ_DISL_FIRST_CHAR_STRING_SPLIT
:
469 case Inference::DEQ_DISL_STRINGS_SPLIT
:
471 if (conc
.getKind() != AND
|| conc
.getNumChildren() != 2
472 || conc
[0].getKind() != EQUAL
|| !conc
[0][0].getType().isStringLike()
473 || conc
[1].getKind() != EQUAL
474 || conc
[1][0].getKind() != STRING_LENGTH
)
476 Trace("strings-ipc-deq") << "malformed application" << std::endl
;
477 Assert(false) << "unexpected conclusion " << conc
<< " for " << infer
;
482 nm
->mkNode(GEQ
, nm
->mkNode(STRING_LENGTH
, conc
[0][0]), conc
[1][1]);
483 Trace("strings-ipc-deq")
484 << "length requirement is " << lenReq
<< std::endl
;
485 if (convertLengthPf(lenReq
, ps
.d_children
, psb
))
487 Trace("strings-ipc-deq") << "...success length" << std::endl
;
489 std::vector
<Node
> childrenMain
;
490 childrenMain
.push_back(lenReq
);
491 std::vector
<Node
> argsMain
;
492 argsMain
.push_back(nodeIsRev
);
494 psb
.tryStep(PfRule::STRING_DECOMPOSE
, childrenMain
, argsMain
);
495 Trace("strings-ipc-deq")
496 << "...main conclusion is " << mainConc
<< std::endl
;
497 useBuffer
= (mainConc
== conc
);
498 Trace("strings-ipc-deq")
499 << "...success is " << useBuffer
<< std::endl
;
503 Trace("strings-ipc-deq") << "...fail length" << std::endl
;
508 // ========================== Boolean split
509 case Inference::CARD_SP
:
510 case Inference::LEN_SPLIT
:
511 case Inference::LEN_SPLIT_EMP
:
512 case Inference::DEQ_DISL_EMP_SPLIT
:
513 case Inference::DEQ_DISL_FIRST_CHAR_EQ_SPLIT
:
514 case Inference::DEQ_STRINGS_EQ
:
515 case Inference::DEQ_LENS_EQ
:
516 case Inference::DEQ_LENGTH_SP
:
518 if (conc
.getKind() != OR
)
520 // This should never happen. If it does, we resort to using
521 // STRING_TRUST below (in production mode).
522 Assert(false) << "Expected OR conclusion for " << infer
;
526 ps
.d_rule
= PfRule::SPLIT
;
527 Assert(ps
.d_children
.empty());
528 ps
.d_args
.push_back(conc
[0]);
532 // ========================== Regular expression unfolding
533 case Inference::RE_UNFOLD_POS
:
534 case Inference::RE_UNFOLD_NEG
:
536 if (infer
== Inference::RE_UNFOLD_POS
)
538 ps
.d_rule
= PfRule::RE_UNFOLD_POS
;
542 ps
.d_rule
= PfRule::RE_UNFOLD_NEG
;
543 // it may be an optimized form of concat simplification
544 Assert(ps
.d_children
.size() == 1);
545 Node mem
= ps
.d_children
[0];
546 Assert(mem
.getKind() == NOT
&& mem
[0].getKind() == STRING_IN_REGEXP
);
547 if (mem
[0][1].getKind() == REGEXP_CONCAT
)
550 Node reLen
= RegExpOpr::getRegExpConcatFixed(mem
[0][1], index
);
551 // if we can find a fixed length for a component, use the optimized
555 ps
.d_rule
= PfRule::RE_UNFOLD_NEG_CONCAT_FIXED
;
561 // ========================== Reduction
562 case Inference::CTN_POS
:
563 case Inference::CTN_NEG_EQUAL
:
565 if (ps
.d_children
.size() != 1)
569 bool polarity
= ps
.d_children
[0].getKind() != NOT
;
570 Node atom
= polarity
? ps
.d_children
[0] : ps
.d_children
[0][0];
571 std::vector
<Node
> args
;
572 args
.push_back(atom
);
573 Node res
= psb
.tryStep(PfRule::STRING_EAGER_REDUCTION
, {}, args
);
578 // ite( contains(x,t), x = k1 ++ t ++ k2, x != t )
579 std::vector
<Node
> tiChildren
;
580 tiChildren
.push_back(ps
.d_children
[0]);
581 Node ctnt
= psb
.tryStep(
582 polarity
? PfRule::TRUE_INTRO
: PfRule::FALSE_INTRO
, tiChildren
, {});
583 if (ctnt
.isNull() || ctnt
.getKind() != EQUAL
)
587 std::vector
<Node
> tchildren
;
588 tchildren
.push_back(ctnt
);
589 // apply substitution { contains(x,t) -> true|false } and rewrite to get
590 // conclusion x = k1 ++ t ++ k2 or x != t.
591 if (psb
.applyPredTransform(res
, conc
, tchildren
))
598 case Inference::REDUCTION
:
600 size_t nchild
= conc
.getNumChildren();
602 if (conc
.getKind() == EQUAL
)
606 else if (conc
.getKind() == AND
&& conc
[nchild
- 1].getKind() == EQUAL
)
608 mainEq
= conc
[nchild
- 1];
612 Trace("strings-ipc-red") << "Bad Reduction: " << conc
<< std::endl
;
613 Assert(false) << "Unexpected reduction " << conc
;
616 std::vector
<Node
> argsRed
;
617 // the left hand side of the last conjunct is the term we are reducing
618 argsRed
.push_back(mainEq
[0]);
619 Node red
= psb
.tryStep(PfRule::STRING_REDUCTION
, {}, argsRed
);
620 Trace("strings-ipc-red") << "Reduction : " << red
<< std::endl
;
623 // either equal or rewrites to it
624 std::vector
<Node
> cexp
;
625 if (psb
.applyPredTransform(red
, conc
, cexp
))
627 Trace("strings-ipc-red") << "...success!" << std::endl
;
632 Trace("strings-ipc-red") << "...failed to reduce" << std::endl
;
637 // ========================== code injectivity
638 case Inference::CODE_INJ
:
640 ps
.d_rule
= PfRule::STRING_CODE_INJ
;
641 Assert(conc
.getKind() == OR
&& conc
.getNumChildren() == 3
642 && conc
[2].getKind() == EQUAL
);
643 ps
.d_args
.push_back(conc
[2][0]);
644 ps
.d_args
.push_back(conc
[2][1]);
647 // ========================== unit injectivity
648 case Inference::UNIT_INJ
: { ps
.d_rule
= PfRule::STRING_SEQ_UNIT_INJ
;
651 // ========================== prefix conflict
652 case Inference::PREFIX_CONFLICT
:
654 Trace("strings-ipc-prefix") << "Prefix conflict..." << std::endl
;
655 std::vector
<Node
> eqs
;
656 for (const Node
& e
: ps
.d_children
)
658 Kind ek
= e
.getKind();
661 Trace("strings-ipc-prefix") << "- equality : " << e
<< std::endl
;
664 else if (ek
== STRING_IN_REGEXP
)
666 // unfold it and extract the equality
667 std::vector
<Node
> children
;
668 children
.push_back(e
);
669 std::vector
<Node
> args
;
670 Node eunf
= psb
.tryStep(PfRule::RE_UNFOLD_POS
, children
, args
);
671 Trace("strings-ipc-prefix")
672 << "--- " << e
<< " unfolds to " << eunf
<< std::endl
;
677 else if (eunf
.getKind() == AND
)
679 // equality is the last conjunct
680 std::vector
<Node
> childrenAE
;
681 childrenAE
.push_back(eunf
);
682 std::vector
<Node
> argsAE
;
683 argsAE
.push_back(nm
->mkConst(Rational(eunf
.getNumChildren() - 1)));
684 Node eunfAE
= psb
.tryStep(PfRule::AND_ELIM
, childrenAE
, argsAE
);
685 Trace("strings-ipc-prefix")
686 << "--- and elim to " << eunfAE
<< std::endl
;
687 if (eunfAE
.isNull() || eunfAE
.getKind() != EQUAL
)
690 << "Unexpected unfolded premise " << eunf
<< " for " << infer
;
693 Trace("strings-ipc-prefix")
694 << "- equality : " << eunfAE
<< std::endl
;
695 eqs
.push_back(eunfAE
);
697 else if (eunf
.getKind() == EQUAL
)
699 Trace("strings-ipc-prefix") << "- equality : " << eunf
<< std::endl
;
705 // not sure how to use this assumption
706 Assert(false) << "Unexpected premise " << e
<< " for " << infer
;
713 // connect via transitivity
715 for (size_t i
= 1, esize
= eqs
.size(); i
< esize
; i
++)
718 curr
= convertTrans(curr
, eqs
[1], psb
);
723 Trace("strings-ipc-prefix") << "- Via trans: " << curr
<< std::endl
;
729 Trace("strings-ipc-prefix")
730 << "- Possible conflicting equality : " << curr
<< std::endl
;
731 std::vector
<Node
> emp
;
732 Node concE
= psb
.applyPredElim(curr
, emp
);
733 Trace("strings-ipc-prefix")
734 << "- After pred elim: " << concE
<< std::endl
;
737 Trace("strings-ipc-prefix") << "...success!" << std::endl
;
742 // ========================== regular expressions
743 case Inference::RE_INTER_INCLUDE
:
744 case Inference::RE_INTER_CONF
:
745 case Inference::RE_INTER_INFER
:
747 std::vector
<Node
> reiExp
;
748 std::vector
<Node
> reis
;
749 std::vector
<Node
> reiChildren
;
750 std::vector
<Node
> reiChildrenOrig
;
752 // make the regular expression intersection that summarizes all
753 // memberships in the explanation
754 for (const Node
& c
: ps
.d_children
)
756 bool polarity
= c
.getKind() != NOT
;
757 Node catom
= polarity
? c
: c
[0];
758 if (catom
.getKind() != STRING_IN_REGEXP
)
760 Assert(c
.getKind() == EQUAL
);
761 if (c
.getKind() == EQUAL
)
769 // just take the first LHS; others should be equated to it by exp
773 polarity
? catom
[1] : nm
->mkNode(REGEXP_COMPLEMENT
, catom
[1]);
774 reis
.push_back(rcurr
);
775 Node mem
= nm
->mkNode(STRING_IN_REGEXP
, catom
[0], rcurr
);
776 reiChildren
.push_back(mem
);
777 reiChildrenOrig
.push_back(c
);
779 // go back and justify each premise
780 bool successChildren
= true;
781 for (size_t i
= 0, nchild
= reiChildren
.size(); i
< nchild
; i
++)
783 if (!psb
.applyPredTransform(reiChildrenOrig
[i
], reiChildren
[i
], reiExp
))
785 Trace("strings-ipc-re")
786 << "... failed to justify child " << reiChildren
[i
] << " from "
787 << reiChildrenOrig
[i
] << std::endl
;
788 successChildren
= false;
792 if (!successChildren
)
796 Node mem
= psb
.tryStep(PfRule::RE_INTER
, reiChildren
, {});
797 Trace("strings-ipc-re")
798 << "Regular expression summary: " << mem
<< std::endl
;
799 // the conclusion is rewritable to the premises via rewriting?
800 if (psb
.applyPredTransform(mem
, conc
, {}))
802 Trace("strings-ipc-re") << "... success!" << std::endl
;
807 Trace("strings-ipc-re")
808 << "...failed to rewrite to conclusion" << std::endl
;
812 // ========================== unknown and currently unsupported
813 case Inference::CARDINALITY
:
814 case Inference::I_CYCLE_E
:
815 case Inference::I_CYCLE
:
816 case Inference::RE_DELTA
:
817 case Inference::RE_DELTA_CONF
:
818 case Inference::RE_DERIVE
:
819 case Inference::FLOOP
:
820 case Inference::FLOOP_CONFLICT
:
821 case Inference::DEQ_NORM_EMP
:
822 case Inference::CTN_TRANS
:
823 case Inference::CTN_DECOMPOSE
:
825 // do nothing, these will be converted to STRING_TRUST below since the
830 // now see if we would succeed with the checker-to-try
831 bool success
= false;
832 if (ps
.d_rule
!= PfRule::UNKNOWN
)
834 Trace("strings-ipc") << "For " << infer
<< ", try proof rule " << ps
.d_rule
836 Assert(ps
.d_rule
!= PfRule::UNKNOWN
);
837 Node pconc
= psb
.tryStep(ps
.d_rule
, ps
.d_children
, ps
.d_args
);
838 if (pconc
.isNull() || pconc
!= conc
)
840 Trace("strings-ipc") << "failed, pconc is " << pconc
<< " (expected "
841 << conc
<< ")" << std::endl
;
842 ps
.d_rule
= PfRule::UNKNOWN
;
846 // successfully set up a single step proof in ps
848 Trace("strings-ipc") << "success!" << std::endl
;
853 // successfully set up a multi step proof in psb
858 Trace("strings-ipc") << "For " << infer
<< " " << conc
859 << ", no proof rule, failed" << std::endl
;
864 if (Trace
.isOn("strings-ipc-fail"))
866 Trace("strings-ipc-fail")
867 << "InferProofCons::convert: Failed " << infer
868 << (isRev
? " :rev " : " ") << conc
<< std::endl
;
869 for (const Node
& ec
: exp
)
871 Trace("strings-ipc-fail") << " e: " << ec
<< std::endl
;
874 // untrustworthy conversion, the argument of STRING_TRUST is its conclusion
876 ps
.d_args
.push_back(conc
);
877 // use the trust rule
878 ps
.d_rule
= PfRule::STRING_TRUST
;
880 d_statistics
.d_inferencesNoPf
<< infer
;
882 if (Trace
.isOn("strings-ipc-debug"))
886 Trace("strings-ipc-debug")
887 << "InferProofCons::convert returned buffer with "
888 << psb
.getNumSteps() << " steps:" << std::endl
;
889 const std::vector
<std::pair
<Node
, ProofStep
>>& steps
= psb
.getSteps();
890 for (const std::pair
<Node
, ProofStep
>& step
: steps
)
892 Trace("strings-ipc-debug")
893 << "- " << step
.first
<< " via " << step
.second
<< std::endl
;
898 Trace("strings-ipc-debug")
899 << "InferProofCons::convert returned " << ps
<< std::endl
;
904 bool InferProofCons::convertLengthPf(Node lenReq
,
905 const std::vector
<Node
>& lenExp
,
906 TheoryProofStepBuffer
& psb
)
908 for (const Node
& le
: lenExp
)
915 Trace("strings-ipc-len") << "Must explain " << lenReq
<< " by " << lenExp
917 for (const Node
& le
: lenExp
)
919 // probably rewrites to it?
920 std::vector
<Node
> exp
;
921 if (psb
.applyPredTransform(le
, lenReq
, exp
))
923 Trace("strings-ipc-len") << "...success by rewrite" << std::endl
;
926 // maybe x != "" => len(x) != 0
927 std::vector
<Node
> children
;
928 children
.push_back(le
);
929 std::vector
<Node
> args
;
930 Node res
= psb
.tryStep(PfRule::STRING_LENGTH_NON_EMPTY
, children
, args
);
933 Trace("strings-ipc-len") << "...success by LENGTH_NON_EMPTY" << std::endl
;
937 Trace("strings-ipc-len") << "...failed" << std::endl
;
941 Node
InferProofCons::convertTrans(Node eqa
,
943 TheoryProofStepBuffer
& psb
)
945 if (eqa
.getKind() != EQUAL
|| eqb
.getKind() != EQUAL
)
949 for (uint32_t i
= 0; i
< 2; i
++)
951 Node eqaSym
= i
== 0 ? eqa
[1].eqNode(eqa
[0]) : eqa
;
952 for (uint32_t j
= 0; j
< 2; j
++)
954 Node eqbSym
= j
== 0 ? eqb
: eqb
[1].eqNode(eqb
[1]);
955 if (eqa
[i
] == eqb
[j
])
957 std::vector
<Node
> children
;
958 children
.push_back(eqaSym
);
959 children
.push_back(eqbSym
);
960 return psb
.tryStep(PfRule::TRANS
, children
, {});
967 std::shared_ptr
<ProofNode
> InferProofCons::getProofFor(Node fact
)
972 NodeInferInfoMap::iterator it
= d_lazyFactMap
.find(fact
);
973 if (it
== d_lazyFactMap
.end())
975 Node factSym
= CDProof::getSymmFact(fact
);
976 if (!factSym
.isNull())
978 // Use the symmetric fact. There is no need to explictly make a
979 // SYMM proof, as this is handled by CDProof::getProofFor below.
980 it
= d_lazyFactMap
.find(factSym
);
983 AlwaysAssert(it
!= d_lazyFactMap
.end());
984 // now go back and convert it to proof steps and add to proof
985 bool useBuffer
= false;
987 TheoryProofStepBuffer
psb(d_pnm
->getChecker());
988 std::shared_ptr
<InferInfo
> ii
= (*it
).second
;
989 // run the conversion
990 convert(ii
->d_id
, ii
->d_idRev
, ii
->d_conc
, ii
->d_premises
, ps
, psb
, useBuffer
);
991 // make the proof based on the step or the buffer
994 if (!pf
.addSteps(psb
))
1001 if (!pf
.addStep(fact
, ps
))
1006 return pf
.getProofFor(fact
);
1009 std::string
InferProofCons::identify() const
1011 return "strings::InferProofCons";
1014 } // namespace strings
1015 } // namespace theory