4b12d2673cd3875bef6d7ebdf45b398f114431f6
1 /******************************************************************************
2 * Top contributors (to current version):
3 * Andrew Reynolds, Gereon Kremer, Andres Noetzli
5 * This file is part of the cvc5 project.
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 * ****************************************************************************
13 * Implementation of solver for extended functions of theory of strings.
16 #include "theory/strings/extf_solver.h"
18 #include "options/strings_options.h"
19 #include "theory/strings/sequences_rewriter.h"
20 #include "theory/strings/theory_strings_preprocess.h"
21 #include "theory/strings/theory_strings_utils.h"
22 #include "util/statistics_registry.h"
25 using namespace cvc5::context
;
26 using namespace cvc5::kind
;
32 ExtfSolver::ExtfSolver(Env
& env
,
36 StringsRewriter
& rewriter
,
40 SequencesStatistics
& statistics
)
49 d_statistics(statistics
),
50 d_preproc(d_termReg
.getSkolemCache(), &statistics
.d_reductions
),
51 d_hasExtf(context(), false),
52 d_extfInferCache(context()),
53 d_reduced(userContext())
55 d_extt
.addFunctionKind(kind::STRING_SUBSTR
);
56 d_extt
.addFunctionKind(kind::STRING_UPDATE
);
57 d_extt
.addFunctionKind(kind::STRING_INDEXOF
);
58 d_extt
.addFunctionKind(kind::STRING_INDEXOF_RE
);
59 d_extt
.addFunctionKind(kind::STRING_ITOS
);
60 d_extt
.addFunctionKind(kind::STRING_STOI
);
61 d_extt
.addFunctionKind(kind::STRING_REPLACE
);
62 d_extt
.addFunctionKind(kind::STRING_REPLACE_ALL
);
63 d_extt
.addFunctionKind(kind::STRING_REPLACE_RE
);
64 d_extt
.addFunctionKind(kind::STRING_REPLACE_RE_ALL
);
65 d_extt
.addFunctionKind(kind::STRING_CONTAINS
);
66 d_extt
.addFunctionKind(kind::STRING_IN_REGEXP
);
67 d_extt
.addFunctionKind(kind::STRING_LEQ
);
68 d_extt
.addFunctionKind(kind::STRING_TO_CODE
);
69 d_extt
.addFunctionKind(kind::STRING_TOLOWER
);
70 d_extt
.addFunctionKind(kind::STRING_TOUPPER
);
71 d_extt
.addFunctionKind(kind::STRING_REV
);
72 d_extt
.addFunctionKind(kind::SEQ_UNIT
);
73 d_extt
.addFunctionKind(kind::SEQ_NTH
);
75 d_true
= NodeManager::currentNM()->mkConst(true);
76 d_false
= NodeManager::currentNM()->mkConst(false);
79 ExtfSolver::~ExtfSolver() {}
81 bool ExtfSolver::doReduction(int effort
, Node n
)
83 Assert(d_extfInfoTmp
.find(n
) != d_extfInfoTmp
.end());
84 if (!d_extfInfoTmp
[n
].d_modelActive
)
86 // n is not active in the model, no need to reduce
87 Trace("strings-extf-debug") << "...skip due to model active" << std::endl
;
90 if (d_reduced
.find(n
)!=d_reduced
.end())
92 // already sent a reduction lemma
93 Trace("strings-extf-debug") << "...skip due to reduced" << std::endl
;
96 // determine the effort level to process the extf at
97 // 0 - at assertion time, 1+ - after no other reduction is applicable
99 // polarity : 1 true, -1 false, 0 neither
101 Kind k
= n
.getKind();
102 if (n
.getType().isBoolean() && !d_extfInfoTmp
[n
].d_const
.isNull())
104 pol
= d_extfInfoTmp
[n
].d_const
.getConst
<bool>() ? 1 : -1;
106 if (k
== STRING_CONTAINS
)
118 std::vector
<Node
> lexp
;
119 Node lenx
= d_state
.getLength(x
, lexp
);
120 Node lens
= d_state
.getLength(s
, lexp
);
121 if (d_state
.areEqual(lenx
, lens
))
123 Trace("strings-extf-debug")
124 << " resolve extf : " << n
125 << " based on equal lengths disequality." << std::endl
;
126 // We can reduce negative contains to a disequality when lengths are
127 // equal. In other words, len( x ) = len( s ) implies
128 // ~contains( x, s ) reduces to x != s.
129 if (!d_state
.areDisequal(x
, s
))
131 // len( x ) = len( s ) ^ ~contains( x, s ) => x != s
132 lexp
.push_back(lenx
.eqNode(lens
));
133 lexp
.push_back(n
.negate());
134 Node xneqs
= x
.eqNode(s
).negate();
136 lexp
, xneqs
, InferenceId::STRINGS_CTN_NEG_EQUAL
, false, true);
138 // this depends on the current assertions, so this
139 // inference is context-dependent
140 d_extt
.markReduced(n
, ExtReducedId::STRINGS_NEG_CTN_DEQ
, true);
150 else if (k
== STRING_SUBSTR
)
154 else if (k
== SEQ_UNIT
)
156 // never necessary to reduce seq.unit
159 else if (k
!= STRING_IN_REGEXP
)
163 if (effort
!= r_effort
)
165 Trace("strings-extf-debug") << "...skip due to effort" << std::endl
;
166 // not the right effort level to reduce
169 Node c_n
= pol
== -1 ? n
.negate() : n
;
170 Trace("strings-process-debug")
171 << "Process reduction for " << n
<< ", pol = " << pol
<< std::endl
;
172 if (k
== STRING_CONTAINS
&& pol
== 1)
176 // positive contains reduces to a equality
177 SkolemCache
* skc
= d_termReg
.getSkolemCache();
178 Node eq
= d_termReg
.eagerReduce(n
, skc
, d_termReg
.getAlphabetCardinality());
179 Assert(!eq
.isNull());
180 Assert(eq
.getKind() == ITE
&& eq
[0] == n
);
182 std::vector
<Node
> expn
;
184 d_im
.sendInference(expn
, expn
, eq
, InferenceId::STRINGS_CTN_POS
, false, true);
185 Trace("strings-extf-debug")
186 << " resolve extf : " << n
<< " based on positive contain reduction."
188 Trace("strings-red-lemma") << "Reduction (positive contains) lemma : " << n
189 << " => " << eq
<< std::endl
;
190 // context-dependent because it depends on the polarity of n itself
191 d_extt
.markReduced(n
, ExtReducedId::STRINGS_POS_CTN
, true);
193 else if (k
!= kind::STRING_TO_CODE
)
195 NodeManager
* nm
= NodeManager::currentNM();
196 Assert(k
== STRING_SUBSTR
|| k
== STRING_UPDATE
|| k
== STRING_CONTAINS
197 || k
== STRING_INDEXOF
|| k
== STRING_INDEXOF_RE
|| k
== STRING_ITOS
198 || k
== STRING_STOI
|| k
== STRING_REPLACE
|| k
== STRING_REPLACE_ALL
199 || k
== SEQ_NTH
|| k
== STRING_REPLACE_RE
200 || k
== STRING_REPLACE_RE_ALL
|| k
== STRING_LEQ
201 || k
== STRING_TOLOWER
|| k
== STRING_TOUPPER
|| k
== STRING_REV
);
202 std::vector
<Node
> new_nodes
;
203 Node res
= d_preproc
.simplify(n
, new_nodes
);
205 new_nodes
.push_back(n
.eqNode(res
));
207 new_nodes
.size() == 1 ? new_nodes
[0] : nm
->mkNode(AND
, new_nodes
);
208 Trace("strings-red-lemma")
209 << "Reduction_" << effort
<< " lemma : " << nnlem
<< std::endl
;
210 Trace("strings-red-lemma") << "...from " << n
<< std::endl
;
211 Trace("strings-red-lemma")
212 << "Reduction_" << effort
<< " rewritten : " << Rewriter::rewrite(nnlem
) << std::endl
;
213 d_im
.sendInference(d_emptyVec
, nnlem
, InferenceId::STRINGS_REDUCTION
, false, true);
214 Trace("strings-extf-debug")
215 << " resolve extf : " << n
<< " based on reduction." << std::endl
;
216 // add as reduction lemma
222 void ExtfSolver::checkExtfReductions(int effort
)
224 // Notice we don't make a standard call to ExtTheory::doReductions here,
225 // since certain optimizations like context-dependent reductions and
226 // stratifying effort levels are done in doReduction below.
227 std::vector
<Node
> extf
= d_extt
.getActive();
228 Trace("strings-process") << " checking " << extf
.size() << " active extf"
230 for (const Node
& n
: extf
)
232 Assert(!d_state
.isInConflict());
233 Trace("strings-extf-debug")
235 << ", active in model=" << d_extfInfoTmp
[n
].d_modelActive
<< std::endl
;
236 bool ret
= doReduction(effort
, n
);
239 // we do not mark as reduced, since we may want to evaluate
240 if (d_im
.hasProcessed())
248 void ExtfSolver::checkExtfEval(int effort
)
250 Trace("strings-extf-list")
251 << "Active extended functions, effort=" << effort
<< " : " << std::endl
;
252 d_extfInfoTmp
.clear();
253 NodeManager
* nm
= NodeManager::currentNM();
254 bool has_nreduce
= false;
255 std::vector
<Node
> terms
= d_extt
.getActive();
256 // the set of terms we have done extf inferences for
257 std::unordered_set
<Node
> inferProcessed
;
258 for (const Node
& n
: terms
)
260 // Setup information about n, including if it is equal to a constant.
261 ExtfInfoTmp
& einfo
= d_extfInfoTmp
[n
];
262 Node r
= d_state
.getRepresentative(n
);
263 einfo
.d_const
= d_bsolver
.getConstantEqc(r
);
264 // Get the current values of the children of n.
265 // Notice that we look up the value of the direct children of n, and not
266 // their free variables. In other words, given a term:
267 // t = (str.replace "B" (str.replace x "A" "B") "C")
268 // we may build the explanation that:
269 // ((str.replace x "A" "B") = "B") => t = (str.replace "B" "B" "C")
270 // instead of basing this on the free variable x:
271 // (x = "A") => t = (str.replace "B" (str.replace "A" "A" "B") "C")
272 // Although both allow us to infer t = "C", it is important to use the
273 // first kind of inference since it ensures that its subterms have the
274 // expected values. Otherwise, we may in rare cases fail to realize that
275 // the subterm (str.replace x "A" "B") does not currently have the correct
276 // value, say in this example that (str.replace x "A" "B") != "B".
277 std::vector
<Node
> exp
;
278 std::vector
<Node
> schildren
;
279 bool schanged
= false;
280 for (const Node
& nc
: n
)
282 Node sc
= getCurrentSubstitutionFor(effort
, nc
, exp
);
283 schildren
.push_back(sc
);
284 schanged
= schanged
|| sc
!= nc
;
286 // If there is information involving the children, attempt to do an
287 // inference and/or mark n as reduced.
288 bool reduced
= false;
292 Node sn
= nm
->mkNode(n
.getKind(), schildren
);
293 Trace("strings-extf-debug")
294 << "Check extf " << n
<< " == " << sn
295 << ", constant = " << einfo
.d_const
<< ", effort=" << effort
<< "..."
297 einfo
.d_exp
.insert(einfo
.d_exp
.end(), exp
.begin(), exp
.end());
298 // inference is rewriting the substituted node
299 Node nrc
= Rewriter::rewrite(sn
);
300 // if rewrites to a constant, then do the inference and mark as reduced
305 d_extt
.markReduced(n
, ExtReducedId::STRINGS_SR_CONST
);
306 Trace("strings-extf-debug")
307 << " resolvable by evaluation..." << std::endl
;
308 std::vector
<Node
> exps
;
309 // The following optimization gets the "symbolic definition" of
310 // an extended term. The symbolic definition of a term t is a term
311 // t' where constants are replaced by their corresponding proxy
313 // For example, if lsym is a proxy variable for "", then
314 // str.replace( lsym, lsym, lsym ) is the symbolic definition for
315 // str.replace( "", "", "" ). It is generally better to use symbolic
316 // definitions when doing cd-rewriting for the purpose of minimizing
317 // clauses, e.g. we infer the unit equality:
318 // str.replace( lsym, lsym, lsym ) == ""
319 // instead of making this inference multiple times:
320 // x = "" => str.replace( x, x, x ) == ""
321 // y = "" => str.replace( y, y, y ) == ""
322 Trace("strings-extf-debug")
323 << " get symbolic definition..." << std::endl
;
325 // only use symbolic definitions if option is set
326 if (options::stringInferSym())
328 nrs
= d_termReg
.getSymbolicDefinition(sn
, exps
);
332 Trace("strings-extf-debug")
333 << " rewrite " << nrs
<< "..." << std::endl
;
334 Node nrsr
= Rewriter::rewrite(nrs
);
335 // ensure the symbolic form is not rewritable
338 // we cannot use the symbolic definition if it rewrites
339 Trace("strings-extf-debug")
340 << " symbolic definition is trivial..." << std::endl
;
346 Trace("strings-extf-debug")
347 << " could not infer symbolic definition." << std::endl
;
352 Trace("strings-extf-debug")
353 << " symbolic def : " << nrs
<< std::endl
;
354 if (!d_state
.areEqual(nrs
, nrc
))
356 // infer symbolic unit
357 if (n
.getType().isBoolean())
359 conc
= nrc
== d_true
? nrs
: nrs
.negate();
363 conc
= nrs
.eqNode(nrc
);
370 if (!d_state
.areEqual(n
, nrc
))
372 if (n
.getType().isBoolean())
374 if (d_state
.areEqual(n
, nrc
== d_true
? d_false
: d_true
))
376 einfo
.d_exp
.push_back(nrc
== d_true
? n
.negate() : n
);
381 conc
= nrc
== d_true
? n
: n
.negate();
386 conc
= n
.eqNode(nrc
);
392 Trace("strings-extf")
393 << " resolve extf : " << sn
<< " -> " << nrc
<< std::endl
;
394 InferenceId inf
= effort
== 0 ? InferenceId::STRINGS_EXTF
: InferenceId::STRINGS_EXTF_N
;
395 d_im
.sendInference(einfo
.d_exp
, conc
, inf
, false, true);
396 d_statistics
.d_cdSimplifications
<< n
.getKind();
401 // check if it is already equal, if so, mark as reduced. Otherwise, do
403 if (d_state
.areEqual(n
, nrc
))
405 Trace("strings-extf")
406 << " resolved extf, since satisfied by model: " << n
408 einfo
.d_modelActive
= false;
415 // if this was a predicate which changed after substitution + rewriting
416 if (!einfo
.d_const
.isNull() && nrc
.getType().isBoolean() && nrc
!= n
)
418 bool pol
= einfo
.d_const
== d_true
;
419 Node nrcAssert
= pol
? nrc
: nrc
.negate();
420 Node nAssert
= pol
? n
: n
.negate();
422 einfo
.d_exp
.push_back(nAssert
);
423 Trace("strings-extf-debug") << " decomposable..." << std::endl
;
424 Trace("strings-extf") << " resolve extf : " << sn
<< " -> " << nrc
425 << ", const = " << einfo
.d_const
<< std::endl
;
426 // We send inferences internal here, which may help show unsat.
427 // However, we do not make a determination whether n can be marked
428 // reduced since this argument may be circular: we may infer than n
429 // can be reduced to something else, but that thing may argue that it
430 // can be reduced to n, in theory.
432 effort
== 0 ? InferenceId::STRINGS_EXTF_D
: InferenceId::STRINGS_EXTF_D_N
;
433 d_im
.sendInternalInference(einfo
.d_exp
, nrcAssert
, infer
);
438 // We must use the original n here to avoid circular justifications for
439 // why extended functions are reduced. In particular, n should never be a
440 // duplicate of another term considered in the block of code for
441 // checkExtfInference below.
442 // if not reduced and not processed
443 if (!reduced
&& !n
.isNull()
444 && inferProcessed
.find(n
) == inferProcessed
.end())
446 inferProcessed
.insert(n
);
450 Trace("strings-extf")
451 << " cannot rewrite extf : " << to_reduce
<< std::endl
;
453 // we take to_reduce to be the (partially) reduced version of n, which
454 // is justified by the explanation in einfo.
455 checkExtfInference(n
, to_reduce
, einfo
, effort
);
456 if (Trace
.isOn("strings-extf-list"))
458 Trace("strings-extf-list") << " * " << to_reduce
;
459 if (!einfo
.d_const
.isNull())
461 Trace("strings-extf-list") << ", const = " << einfo
.d_const
;
465 Trace("strings-extf-list") << ", from " << n
;
467 Trace("strings-extf-list") << std::endl
;
469 if (d_extt
.isActive(n
) && einfo
.d_modelActive
)
474 if (d_state
.isInConflict())
476 Trace("strings-extf-debug") << " conflict, return." << std::endl
;
480 d_hasExtf
= has_nreduce
;
483 void ExtfSolver::checkExtfInference(Node n
,
488 if (in
.d_const
.isNull())
492 NodeManager
* nm
= NodeManager::currentNM();
493 Trace("strings-extf-infer") << "checkExtfInference: " << n
<< " : " << nr
494 << " == " << in
.d_const
<< std::endl
;
496 // add original to explanation
497 if (n
.getType().isBoolean())
499 // if Boolean, it's easy
500 in
.d_exp
.push_back(in
.d_const
.getConst
<bool>() ? n
: n
.negate());
504 // otherwise, must explain via base node
505 Node r
= d_state
.getRepresentative(n
);
506 // explain using the base solver
507 d_bsolver
.explainConstantEqc(n
, r
, in
.d_exp
);
510 // d_extfInferCache stores whether we have made the inferences associated
512 // this may need to be generalized if multiple inferences apply
514 if (nr
.getKind() == STRING_CONTAINS
)
516 Assert(in
.d_const
.isConst());
517 bool pol
= in
.d_const
.getConst
<bool>();
518 if ((pol
&& nr
[1].getKind() == STRING_CONCAT
)
519 || (!pol
&& nr
[0].getKind() == STRING_CONCAT
))
521 // If str.contains( x, str.++( y1, ..., yn ) ),
522 // we may infer str.contains( x, y1 ), ..., str.contains( x, yn )
523 // The following recognizes two situations related to the above reasoning:
524 // (1) If ~str.contains( x, yi ) holds for some i, we are in conflict,
525 // (2) If str.contains( x, yj ) already holds for some j, then the term
526 // str.contains( x, yj ) is irrelevant since it is satisfied by all models
527 // for str.contains( x, str.++( y1, ..., yn ) ).
529 // Notice that the dual of the above reasoning also holds, i.e.
530 // If ~str.contains( str.++( x1, ..., xn ), y ),
531 // we may infer ~str.contains( x1, y ), ..., ~str.contains( xn, y )
532 // This is also handled here.
533 if (d_extfInferCache
.find(nr
) == d_extfInferCache
.end())
535 d_extfInferCache
.insert(nr
);
537 int index
= pol
? 1 : 0;
538 std::vector
<Node
> children
;
539 children
.push_back(nr
[0]);
540 children
.push_back(nr
[1]);
541 for (const Node
& nrc
: nr
[index
])
543 children
[index
] = nrc
;
544 Node conc
= nm
->mkNode(STRING_CONTAINS
, children
);
545 conc
= Rewriter::rewrite(pol
? conc
: conc
.negate());
546 // check if it already (does not) hold
547 if (d_state
.hasTerm(conc
))
549 if (d_state
.areEqual(conc
, d_false
))
551 // we are in conflict
552 d_im
.addToExplanation(conc
, d_false
, in
.d_exp
);
554 in
.d_exp
, d_false
, InferenceId::STRINGS_CTN_DECOMPOSE
);
555 Assert(d_state
.isInConflict());
558 else if (d_extt
.hasFunctionKind(conc
.getKind()))
560 // can mark as reduced, since model for n implies model for conc
561 d_extt
.markReduced(conc
, ExtReducedId::STRINGS_CTN_DECOMPOSE
);
569 if (std::find(d_extfInfoTmp
[nr
[0]].d_ctn
[pol
].begin(),
570 d_extfInfoTmp
[nr
[0]].d_ctn
[pol
].end(),
572 == d_extfInfoTmp
[nr
[0]].d_ctn
[pol
].end())
574 Trace("strings-extf-debug") << " store contains info : " << nr
[0]
575 << " " << pol
<< " " << nr
[1] << std::endl
;
576 // Store s (does not) contains t, since nr = (~)contains( s, t ) holds.
577 d_extfInfoTmp
[nr
[0]].d_ctn
[pol
].push_back(nr
[1]);
578 d_extfInfoTmp
[nr
[0]].d_ctnFrom
[pol
].push_back(n
);
579 // Do transistive closure on contains, e.g.
580 // if contains( s, t ) and ~contains( s, r ), then ~contains( t, r ).
582 // The following infers new (negative) contains based on the above
583 // reasoning, provided that ~contains( t, r ) does not
584 // already hold in the current context. We test this by checking that
585 // contains( t, r ) is not already asserted false in the current
586 // context. We also handle the case where contains( t, r ) is equivalent
587 // to t = r, in which case we check that t != r does not already hold
588 // in the current context.
590 // Notice that form of the above inference is enough to find
591 // conflicts purely due to contains predicates. For example, if we
592 // have only positive occurrences of contains, then no conflicts due to
593 // contains predicates are possible and this schema does nothing. For
594 // example, note that contains( s, t ) and contains( t, r ) implies
595 // contains( s, r ), which we could but choose not to infer. Instead,
596 // we prefer being lazy: only if ~contains( s, r ) appears later do we
597 // infer ~contains( t, r ), which suffices to show a conflict.
599 for (unsigned i
= 0, size
= d_extfInfoTmp
[nr
[0]].d_ctn
[opol
].size();
603 Node onr
= d_extfInfoTmp
[nr
[0]].d_ctn
[opol
][i
];
605 nm
->mkNode(STRING_CONTAINS
, pol
? nr
[1] : onr
, pol
? onr
: nr
[1]);
606 Node conc
= Rewriter::rewrite(concOrig
);
607 // For termination concerns, we only do the inference if the contains
608 // does not rewrite (and thus does not introduce new terms).
609 if (conc
== concOrig
)
611 bool do_infer
= false;
612 conc
= conc
.negate();
613 bool pol2
= conc
.getKind() != NOT
;
614 Node lit
= pol2
? conc
: conc
[0];
615 if (lit
.getKind() == EQUAL
)
617 do_infer
= pol2
? !d_state
.areEqual(lit
[0], lit
[1])
618 : !d_state
.areDisequal(lit
[0], lit
[1]);
622 do_infer
= !d_state
.areEqual(lit
, pol2
? d_true
: d_false
);
626 std::vector
<Node
> exp_c
;
627 exp_c
.insert(exp_c
.end(), in
.d_exp
.begin(), in
.d_exp
.end());
628 Node ofrom
= d_extfInfoTmp
[nr
[0]].d_ctnFrom
[opol
][i
];
629 Assert(d_extfInfoTmp
.find(ofrom
) != d_extfInfoTmp
.end());
630 exp_c
.insert(exp_c
.end(),
631 d_extfInfoTmp
[ofrom
].d_exp
.begin(),
632 d_extfInfoTmp
[ofrom
].d_exp
.end());
633 d_im
.sendInference(exp_c
, conc
, InferenceId::STRINGS_CTN_TRANS
);
640 // If we already know that s (does not) contain t, then n may be
641 // redundant. However, we do not mark n as reduced here, since strings
642 // reductions may require dependencies between extended functions.
643 // Marking reduced here could lead to incorrect models if an
644 // extended function is marked reduced based on an assignment to
645 // something that depends on n.
646 Trace("strings-extf-debug") << " redundant." << std::endl
;
652 // If it's not a predicate, see if we can solve the equality n = c, where c
653 // is the constant that extended term n is equal to.
654 Node inferEq
= nr
.eqNode(in
.d_const
);
655 Node inferEqr
= Rewriter::rewrite(inferEq
);
656 Node inferEqrr
= inferEqr
;
657 if (inferEqr
.getKind() == EQUAL
)
659 // try to use the extended rewriter for equalities
660 inferEqrr
= d_rewriter
.rewriteEqualityExt(inferEqr
);
662 if (inferEqrr
!= inferEqr
)
664 inferEqrr
= Rewriter::rewrite(inferEqrr
);
665 Trace("strings-extf-infer") << "checkExtfInference: " << inferEq
666 << " ...reduces to " << inferEqrr
<< std::endl
;
667 d_im
.sendInternalInference(in
.d_exp
, inferEqrr
, InferenceId::STRINGS_EXTF_EQ_REW
);
671 Node
ExtfSolver::getCurrentSubstitutionFor(int effort
,
673 std::vector
<Node
>& exp
)
678 Node mv
= d_state
.getModel()->getRepresentative(n
);
679 Trace("strings-subs") << " model val : " << mv
<< std::endl
;
682 Node nr
= d_state
.getRepresentative(n
);
683 // if the normal form is available, use it
684 if (effort
>= 1 && n
.getType().isStringLike())
688 NormalForm
& nfnr
= d_csolver
.getNormalForm(nr
);
689 Node ns
= d_csolver
.getNormalString(nfnr
.d_base
, exp
);
690 Trace("strings-subs") << " normal eqc : " << ns
<< " " << nfnr
.d_base
691 << " " << nr
<< std::endl
;
692 if (!nfnr
.d_base
.isNull())
694 d_im
.addToExplanation(n
, nfnr
.d_base
, exp
);
698 // otherwise, we use the best content heuristic
699 Node c
= d_bsolver
.explainBestContentEqc(n
, nr
, exp
);
707 const std::map
<Node
, ExtfInfoTmp
>& ExtfSolver::getInfo() const
709 return d_extfInfoTmp
;
711 bool ExtfSolver::hasExtendedFunctions() const { return d_hasExtf
.get(); }
713 std::vector
<Node
> ExtfSolver::getActive(Kind k
) const
715 return d_extt
.getActive(k
);
718 bool StringsExtfCallback::getCurrentSubstitution(
720 const std::vector
<Node
>& vars
,
721 std::vector
<Node
>& subs
,
722 std::map
<Node
, std::vector
<Node
> >& exp
)
724 Trace("strings-subs") << "getCurrentSubstitution, effort = " << effort
726 for (const Node
& v
: vars
)
728 Trace("strings-subs") << " get subs for " << v
<< "..." << std::endl
;
729 Node s
= d_esolver
->getCurrentSubstitutionFor(effort
, v
, exp
[v
]);
735 std::string
ExtfSolver::debugPrintModel()
737 std::stringstream ss
;
738 std::vector
<Node
> extf
;
739 d_extt
.getTerms(extf
);
740 // each extended function should have at least one annotation below
741 for (const Node
& n
: extf
)
745 if (!d_extt
.isActive(n
, id
))
747 ss
<< " :extt-inactive " << id
;
749 if (!d_extfInfoTmp
[n
].d_modelActive
)
751 ss
<< " :model-inactive";
753 if (d_reduced
.find(n
) != d_reduced
.end())
762 } // namespace strings
763 } // namespace theory