LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/theory/strings - inference_manager.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 275 293 93.9 %
Date: 2026-01-22 12:22:00 Functions: 15 16 93.8 %
Branches: 218 340 64.1 %

           Branch data     Line data    Source code
       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-2025 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 the inference manager for the theory of strings.
      14                 :            :  */
      15                 :            : 
      16                 :            : #include "theory/strings/inference_manager.h"
      17                 :            : 
      18                 :            : #include "options/strings_options.h"
      19                 :            : #include "theory/ext_theory.h"
      20                 :            : #include "theory/rewriter.h"
      21                 :            : #include "theory/strings/theory_strings_utils.h"
      22                 :            : #include "theory/strings/word.h"
      23                 :            : #include "util/rational.h"
      24                 :            : 
      25                 :            : using namespace std;
      26                 :            : using namespace cvc5::context;
      27                 :            : using namespace cvc5::internal::kind;
      28                 :            : 
      29                 :            : namespace cvc5::internal {
      30                 :            : namespace theory {
      31                 :            : namespace strings {
      32                 :            : 
      33                 :      50196 : InferenceManager::InferenceManager(Env& env,
      34                 :            :                                    Theory& t,
      35                 :            :                                    SolverState& s,
      36                 :            :                                    TermRegistry& tr,
      37                 :            :                                    ExtTheory& e,
      38                 :      50196 :                                    SequencesStatistics& statistics)
      39                 :            :     : InferenceManagerBuffered(env, t, s, "theory::strings::"),
      40                 :            :       d_state(s),
      41                 :            :       d_termReg(tr),
      42                 :            :       d_extt(e),
      43                 :            :       d_statistics(statistics),
      44                 :      10068 :       d_ipc(isProofEnabled() ? new InferProofCons(env, context()) : nullptr),
      45 [ +  + ][ +  + ]:      60264 :       d_ipcl(isProofEnabled() ? new InferProofCons(env, context()) : nullptr)
      46                 :            : {
      47                 :      50196 :   NodeManager* nm = nodeManager();
      48                 :      50196 :   d_zero = nm->mkConstInt(Rational(0));
      49                 :      50196 :   d_one = nm->mkConstInt(Rational(1));
      50                 :      50196 :   d_true = nm->mkConst(true);
      51                 :      50196 :   d_false = nm->mkConst(false);
      52                 :      50196 : }
      53                 :            : 
      54                 :      98446 : void InferenceManager::doPending()
      55                 :            : {
      56                 :      98446 :   doPendingFacts();
      57         [ +  + ]:      98446 :   if (d_state.isInConflict())
      58                 :            :   {
      59                 :            :     // just clear the pending vectors, nothing else to do
      60                 :       5915 :     clearPendingLemmas();
      61                 :       5915 :     clearPendingPhaseRequirements();
      62                 :       5915 :     return;
      63                 :            :   }
      64                 :      92531 :   doPendingLemmas();
      65                 :      92531 :   doPendingPhaseRequirements();
      66                 :            : }
      67                 :            : 
      68                 :      36569 : bool InferenceManager::sendInternalInference(std::vector<Node>& exp,
      69                 :            :                                              Node conc,
      70                 :            :                                              InferenceId infer)
      71                 :            : {
      72                 :     109707 :   if (conc.getKind() == Kind::AND
      73 [ +  + ][ +  + ]:      36569 :       || (conc.getKind() == Kind::NOT && conc[0].getKind() == Kind::OR))
         [ +  + ][ +  + ]
         [ +  + ][ -  - ]
      74                 :            :   {
      75         [ +  + ]:       1933 :     Node conj = conc.getKind() == Kind::AND ? conc : conc[0];
      76                 :       1933 :     bool pol = conc.getKind() == Kind::AND;
      77                 :       1933 :     bool ret = true;
      78         [ +  + ]:       7494 :     for (const Node& cc : conj)
      79                 :            :     {
      80         [ +  + ]:       5561 :       bool retc = sendInternalInference(exp, pol ? cc : cc.negate(), infer);
      81 [ +  + ][ +  + ]:       5561 :       ret = ret && retc;
      82                 :            :     }
      83                 :       1933 :     return ret;
      84                 :            :   }
      85                 :      34636 :   bool pol = conc.getKind() != Kind::NOT;
      86         [ +  + ]:      69272 :   Node lit = pol ? conc : conc[0];
      87         [ +  + ]:      34636 :   if (lit.getKind() == Kind::EQUAL)
      88                 :            :   {
      89         [ +  + ]:      13212 :     for (unsigned i = 0; i < 2; i++)
      90                 :            :     {
      91                 :       9262 :       if (!lit[i].isConst() && !d_state.hasTerm(lit[i]))
      92                 :            :       {
      93                 :            :         // introduces a new non-constant term, do not infer
      94                 :        702 :         return false;
      95                 :            :       }
      96                 :            :     }
      97                 :            :     // does it already hold?
      98                 :       7900 :     if (pol ? d_state.areEqual(lit[0], lit[1])
      99                 :       3950 :             : d_state.areDisequal(lit[0], lit[1]))
     100                 :            :     {
     101                 :       3054 :       return true;
     102                 :            :     }
     103                 :            :   }
     104         [ +  + ]:      29984 :   else if (lit.isConst())
     105                 :            :   {
     106         [ -  + ]:        719 :     if (lit.getConst<bool>())
     107                 :            :     {
     108                 :          0 :       Assert(pol);
     109                 :            :       // trivially holds
     110                 :          0 :       return true;
     111                 :            :     }
     112                 :            :   }
     113         [ +  + ]:      29265 :   else if (!d_state.hasTerm(lit))
     114                 :            :   {
     115                 :            :     // introduces a new non-constant term, do not infer
     116                 :      26916 :     return false;
     117                 :            :   }
     118 [ +  + ][ +  + ]:       2349 :   else if (d_state.areEqual(lit, pol ? d_true : d_false))
     119                 :            :   {
     120                 :            :     // already holds
     121                 :       2322 :     return true;
     122                 :            :   }
     123                 :       1642 :   sendInference(exp, conc, infer);
     124                 :       1642 :   return true;
     125                 :            : }
     126                 :            : 
     127                 :     121264 : bool InferenceManager::sendInference(const std::vector<Node>& exp,
     128                 :            :                                      const std::vector<Node>& noExplain,
     129                 :            :                                      Node eq,
     130                 :            :                                      InferenceId infer,
     131                 :            :                                      bool isRev,
     132                 :            :                                      bool asLemma)
     133                 :            : {
     134         [ +  + ]:     121264 :   if (eq.isNull())
     135                 :            :   {
     136                 :         96 :     eq = d_false;
     137                 :            :   }
     138         [ +  + ]:     121168 :   else if (rewrite(eq) == d_true)
     139                 :            :   {
     140                 :            :     // if trivial, return
     141                 :          2 :     return false;
     142                 :            :   }
     143                 :            :   // wrap in infer info and send below
     144                 :     121262 :   InferInfo ii(infer);
     145                 :     121262 :   ii.d_idRev = isRev;
     146                 :     121262 :   ii.d_conc = eq;
     147                 :     121262 :   ii.d_premises = exp;
     148                 :     121262 :   ii.d_noExplain = noExplain;
     149                 :     121262 :   sendInference(ii, asLemma);
     150                 :     121262 :   return true;
     151                 :            : }
     152                 :            : 
     153                 :     118837 : bool InferenceManager::sendInference(const std::vector<Node>& exp,
     154                 :            :                                      Node eq,
     155                 :            :                                      InferenceId infer,
     156                 :            :                                      bool isRev,
     157                 :            :                                      bool asLemma)
     158                 :            : {
     159                 :     118837 :   std::vector<Node> noExplain;
     160                 :     237674 :   return sendInference(exp, noExplain, eq, infer, isRev, asLemma);
     161                 :            : }
     162                 :            : 
     163                 :     131881 : void InferenceManager::sendInference(InferInfo& ii, bool asLemma)
     164                 :            : {
     165 [ -  + ][ -  + ]:     131881 :   Assert(!ii.isTrivial());
                 [ -  - ]
     166                 :            :   // This inference manager will be processing the side effects of this
     167                 :            :   // inferences if the inference manager has not been marked already.
     168         [ +  + ]:     131881 :   if (ii.d_sim == nullptr)
     169                 :            :   {
     170                 :     121262 :     ii.d_sim = this;
     171                 :            :   }
     172         [ +  - ]:     263762 :   Trace("strings-infer-debug")
     173                 :     131881 :       << "sendInference: " << ii << ", asLemma = " << asLemma << std::endl;
     174                 :            :   // check if we should send a conflict, lemma or a fact
     175         [ +  + ]:     131881 :   if (ii.isConflict())
     176                 :            :   {
     177         [ +  - ]:       2305 :     Trace("strings-infer-debug") << "...as conflict" << std::endl;
     178         [ +  - ]:       4610 :     Trace("strings-lemma") << "Strings::Conflict: " << ii.d_premises << " by "
     179                 :       2305 :                            << ii.getId() << std::endl;
     180         [ +  - ]:       2305 :     Trace("strings-conflict") << "CONFLICT: inference conflict " << ii.d_premises << " by " << ii.getId() << std::endl;
     181                 :       2305 :     ++(d_statistics.d_conflictsInfer);
     182                 :            :     // process the conflict immediately
     183                 :       2305 :     processConflict(ii);
     184                 :       2305 :     return;
     185                 :            :   }
     186 [ +  + ][ +  - ]:     129576 :   else if (asLemma || options().strings.stringInferAsLemmas || !ii.isFact())
         [ +  + ][ +  + ]
     187                 :            :   {
     188         [ +  - ]:      39157 :     Trace("strings-infer-debug") << "...as lemma" << std::endl;
     189                 :      39157 :     addPendingLemma(std::unique_ptr<InferInfo>(new InferInfo(ii)));
     190                 :      39157 :     return;
     191                 :            :   }
     192         [ +  - ]:      90419 :   if (options().strings.stringInferSym)
     193                 :            :   {
     194                 :      90419 :     std::vector<Node> unproc;
     195         [ +  + ]:     296773 :     for (const Node& ac : ii.d_premises)
     196                 :            :     {
     197                 :     206354 :       d_termReg.removeProxyEqs(ac, unproc);
     198                 :            :     }
     199         [ +  + ]:      90419 :     if (unproc.empty())
     200                 :            :     {
     201                 :          4 :       Node eqs = ii.d_conc;
     202                 :            :       // keep the same id for now, since we are transforming the form of the
     203                 :            :       // inference, not the root reason.
     204                 :          2 :       InferInfo iiSubsLem(ii.getId());
     205                 :          2 :       iiSubsLem.d_sim = this;
     206                 :          2 :       iiSubsLem.d_conc = eqs;
     207         [ -  + ]:          2 :       if (TraceIsOn("strings-lemma-debug"))
     208                 :            :       {
     209         [ -  - ]:          0 :         Trace("strings-lemma-debug")
     210                 :          0 :             << "Strings::Infer " << iiSubsLem << std::endl;
     211         [ -  - ]:          0 :         Trace("strings-lemma-debug")
     212                 :          0 :             << "Strings::Infer Alternate : " << eqs << std::endl;
     213                 :            :       }
     214         [ +  - ]:          2 :       Trace("strings-infer-debug") << "...as symbolic lemma" << std::endl;
     215                 :          2 :       addPendingLemma(std::unique_ptr<InferInfo>(new InferInfo(iiSubsLem)));
     216                 :          2 :       return;
     217                 :            :     }
     218         [ -  + ]:      90417 :     if (TraceIsOn("strings-lemma-debug"))
     219                 :            :     {
     220         [ -  - ]:          0 :       for (const Node& u : unproc)
     221                 :            :       {
     222         [ -  - ]:          0 :         Trace("strings-lemma-debug")
     223                 :          0 :             << "  non-trivial explanation : " << u << std::endl;
     224                 :            :       }
     225                 :            :     }
     226                 :            :   }
     227         [ +  - ]:      90417 :   Trace("strings-infer-debug") << "...as fact" << std::endl;
     228                 :            :   // add to pending to be processed as a fact
     229                 :      90417 :   addPendingFact(std::unique_ptr<InferInfo>(new InferInfo(ii)));
     230                 :            : }
     231                 :            : 
     232                 :       4789 : bool InferenceManager::sendSplit(Node a, Node b, InferenceId infer, bool preq)
     233                 :            : {
     234                 :       9578 :   Node eq = a.eqNode(b);
     235                 :       4789 :   eq = rewrite(eq);
     236         [ -  + ]:       4789 :   if (eq.isConst())
     237                 :            :   {
     238                 :          0 :     return false;
     239                 :            :   }
     240                 :       4789 :   NodeManager* nm = nodeManager();
     241                 :       4789 :   InferInfo iiSplit(infer);
     242                 :       4789 :   iiSplit.d_sim = this;
     243                 :       4789 :   iiSplit.d_conc = nm->mkNode(Kind::OR, eq, nm->mkNode(Kind::NOT, eq));
     244                 :       4789 :   addPendingPhaseRequirement(eq, preq);
     245                 :       4789 :   addPendingLemma(std::unique_ptr<InferInfo>(new InferInfo(iiSplit)));
     246                 :       4789 :   return true;
     247                 :            : }
     248                 :            : 
     249                 :    1426960 : void InferenceManager::addToExplanation(Node a,
     250                 :            :                                         Node b,
     251                 :            :                                         std::vector<Node>& exp) const
     252                 :            : {
     253         [ +  + ]:    1426960 :   if (a != b)
     254                 :            :   {
     255                 :            :     // prefer having constants on the RHS, which helps proof reconstruction
     256 [ +  + ][ +  - ]:     620689 :     if (a.isConst() && !b.isConst())
                 [ +  + ]
     257                 :            :     {
     258                 :       4396 :       Node tmp = a;
     259                 :       2198 :       a = b;
     260                 :       2198 :       b = tmp;
     261                 :            :     }
     262         [ +  - ]:    1241380 :     Trace("strings-explain")
     263                 :     620689 :         << "Add to explanation : " << a << " == " << b << std::endl;
     264 [ -  + ][ -  + ]:     620689 :     Assert(d_state.areEqual(a, b));
                 [ -  - ]
     265                 :     620689 :     exp.push_back(a.eqNode(b));
     266                 :            :   }
     267                 :    1426960 : }
     268                 :            : 
     269                 :          0 : void InferenceManager::addToExplanation(Node lit, std::vector<Node>& exp) const
     270                 :            : {
     271         [ -  - ]:          0 :   if (!lit.isNull())
     272                 :            :   {
     273                 :          0 :     Assert(!lit.isConst());
     274                 :          0 :     exp.push_back(lit);
     275                 :            :   }
     276                 :          0 : }
     277                 :            : 
     278                 :    8595180 : bool InferenceManager::hasProcessed() const
     279                 :            : {
     280 [ +  + ][ +  + ]:    8595180 :   return d_state.isInConflict() || hasPending();
     281                 :            : }
     282                 :            : 
     283                 :        132 : void InferenceManager::markInactive(Node n, ExtReducedId id, bool contextDepend)
     284                 :            : {
     285                 :        132 :   d_extt.markInactive(n, id, contextDepend);
     286                 :        132 : }
     287                 :            : 
     288                 :       3442 : void InferenceManager::processConflict(const InferInfo& ii)
     289                 :            : {
     290 [ -  + ][ -  + ]:       3442 :   Assert(!d_state.isInConflict());
                 [ -  - ]
     291         [ +  + ]:       3442 :   if (ii.getId() == InferenceId::STRINGS_PREFIX_CONFLICT)
     292                 :            :   {
     293                 :        740 :     bool isSuf = ii.d_idRev;
     294                 :            :     // The shape of prefix conflicts is P1? ^ P2? ^ (= x y)?
     295                 :            :     // where if applicable:
     296                 :            :     //   P1 implies a prefix on string x,
     297                 :            :     //   P2 implies a (conflicting) prefix on string y.
     298                 :            :     // See EqcInfo::mkMergeConflict.
     299         [ +  - ]:       1480 :     Trace("strings-prefix-min") << "Minimize prefix conflict " << ii.d_premises
     300                 :        740 :                                 << ", isSuf=" << isSuf << std::endl;
     301                 :        740 :     size_t npremises = ii.d_premises.size();
     302                 :        740 :     Node eq = ii.d_premises[npremises - 1];
     303                 :            :     // if we included an equality, we will try to minimize its explanation
     304         [ +  - ]:        740 :     if (eq.getKind() == Kind::EQUAL)
     305                 :            :     {
     306                 :        740 :       InferInfo iim(InferenceId::STRINGS_PREFIX_CONFLICT_MIN);
     307         [ -  - ]:       2220 :       Node pft[2] = {eq[0], eq[1]};
     308         [ +  + ]:       1103 :       for (size_t i = 0; i < (npremises - 1); i++)
     309                 :            :       {
     310         [ +  - ]:        363 :         if (ii.d_premises[i].getKind() == Kind::STRING_IN_REGEXP)
     311                 :            :         {
     312         [ +  + ]:        363 :           size_t eindex = ii.d_premises[i][0] == eq[0] ? 0 : 1;
     313 [ -  + ][ -  + ]:        363 :           Assert(ii.d_premises[i][0] == eq[eindex]);
                 [ -  - ]
     314                 :            :           // the basis of prefix for eq[eindex] is the RE of this premise
     315                 :        363 :           pft[eindex] = ii.d_premises[i][1];
     316                 :            :         }
     317                 :            :         // include it in the explanation
     318                 :        363 :         iim.d_premises.push_back(ii.d_premises[i]);
     319                 :            :       }
     320         [ +  - ]:       1480 :       Trace("strings-prefix-min")
     321                 :        740 :           << "Prefix terms: " << pft[0] << " / " << pft[1] << std::endl;
     322 [ +  + ][ -  - ]:       3700 :       Node pfv[2];
     323         [ +  + ]:       2220 :       for (size_t i = 0; i < 2; i++)
     324                 :            :       {
     325                 :       1480 :         pfv[i] = utils::getConstantEndpoint(pft[i], isSuf);
     326                 :            :       }
     327         [ +  - ]:       1480 :       Trace("strings-prefix-min")
     328                 :        740 :           << "Prefixes: " << pfv[0] << " / " << pfv[1] << std::endl;
     329         [ +  + ]:       2076 :       for (size_t i = 0; i < 2; i++)
     330                 :            :       {
     331                 :       1408 :         if (pft[1 - i] == eq[1 - i] && pft[i] != eq[i])
     332                 :            :         {
     333                 :            :           // if the other side is justified by itself and we are justified
     334                 :            :           // externally, we can try to minimize the explanation of this
     335                 :            :           // get the minimal conflicting prefix
     336                 :        359 :           std::vector<TNode> assumptions;
     337                 :        359 :           explain(eq, assumptions);
     338                 :        359 :           std::map<TNode, TNode> emap = getExplanationMap(assumptions);
     339                 :            :           Node mexp =
     340                 :        718 :               mkPrefixExplainMin(eq[i], pfv[i], assumptions, emap, isSuf);
     341                 :            :           // if we minimized the conflict, process it
     342         [ +  + ]:        359 :           if (!mexp.isNull())
     343                 :            :           {
     344                 :            :             // must flatten here
     345                 :         72 :             utils::flattenOp(Kind::AND, mexp, iim.d_premises);
     346                 :         72 :             iim.d_conc = ii.d_conc;
     347                 :         72 :             processConflict(iim);
     348                 :         72 :             return;
     349                 :            :           }
     350                 :            :         }
     351                 :            :       }
     352                 :            :     }
     353                 :            :     // otherwise if we fail to minimize, process the original
     354                 :            :   }
     355                 :            :   // setup the fact to reproduce the proof in the call below
     356         [ +  + ]:       3370 :   if (d_ipcl != nullptr)
     357                 :            :   {
     358                 :       1590 :     d_ipcl->notifyLemma(ii);
     359                 :            :   }
     360                 :            :   // make the trust node
     361         [ +  + ]:       3370 :   TrustNode tconf = mkConflictExp(ii.d_premises, d_ipcl.get());
     362 [ -  + ][ -  + ]:       3370 :   Assert(tconf.getKind() == TrustNodeKind::CONFLICT);
                 [ -  - ]
     363 [ +  - ][ -  - ]:       6740 :   Trace("strings-assert") << "(assert (not " << tconf.getNode()
     364         [ -  + ]:       3370 :                           << ")) ; conflict " << ii.getId() << std::endl;
     365                 :            :   // send the trusted conflict
     366                 :       3370 :   trustedConflict(tconf, ii.getId());
     367                 :            : }
     368                 :            : 
     369                 :      87841 : void InferenceManager::processFact(InferInfo& ii, ProofGenerator*& pg)
     370                 :            : {
     371 [ +  - ][ -  - ]:     175682 :   Trace("strings-assert") << "(assert (=> " << ii.getPremises(nodeManager())
     372         [ -  + ]:      87841 :                           << " " << ii.d_conc << ")) ; fact " << ii.getId()
     373                 :      87841 :                           << std::endl;
     374         [ +  - ]:     175682 :   Trace("strings-lemma") << "Strings::Fact: " << ii.d_conc << " from "
     375         [ -  - ]:      87841 :                          << ii.getPremises(nodeManager()) << " by "
     376         [ -  + ]:      87841 :                          << ii.getId() << std::endl;
     377         [ +  + ]:      87841 :   if (d_ipc != nullptr)
     378                 :            :   {
     379                 :            :     // ensure the proof generator is ready to explain this fact in the
     380                 :            :     // current SAT context
     381                 :      37507 :     d_ipc->notifyFact(ii);
     382         [ +  - ]:      37507 :     pg = d_ipc.get();
     383                 :            :   }
     384                 :            :   // ensure facts are for rewritten terms
     385         [ +  - ]:      87841 :   if (Configuration::isAssertionBuild())
     386                 :            :   {
     387         [ +  + ]:     175682 :     Node atom = ii.d_conc.getKind() == Kind::NOT ? ii.d_conc[0] : ii.d_conc;
     388         [ +  + ]:      87841 :     if (atom.getKind() == Kind::EQUAL)
     389                 :            :     {
     390 [ -  + ][ -  + ]:      87643 :       Assert(rewrite(atom[0])==atom[0]);
                 [ -  - ]
     391 [ -  + ][ -  + ]:      87643 :       Assert(rewrite(atom[1])==atom[1]);
                 [ -  - ]
     392                 :            :     }
     393                 :            :     else
     394                 :            :     {
     395 [ -  + ][ -  + ]:        198 :       Assert(rewrite(atom)==atom);
                 [ -  - ]
     396                 :            :     }
     397                 :            :   }
     398                 :      87841 : }
     399                 :            : 
     400                 :      43554 : TrustNode InferenceManager::processLemma(InferInfo& ii, LemmaProperty& p)
     401                 :            : {
     402 [ -  + ][ -  + ]:      43554 :   Assert(!ii.isTrivial());
                 [ -  - ]
     403 [ -  + ][ -  + ]:      43554 :   Assert(!ii.isConflict());
                 [ -  - ]
     404                 :            :   // set up the explanation and no-explanation
     405                 :      87108 :   std::vector<Node> exp;
     406         [ +  + ]:     137684 :   for (const Node& ec : ii.d_premises)
     407                 :            :   {
     408                 :      94130 :     utils::flattenOp(Kind::AND, ec, exp);
     409                 :            :   }
     410                 :      87108 :   std::vector<Node> noExplain;
     411         [ -  + ]:      43554 :   if (!options().strings.stringRExplainLemmas)
     412                 :            :   {
     413                 :            :     // if we aren't regressing the explanation, we add all literals to
     414                 :            :     // noExplain and ignore ii.d_ant.
     415                 :          0 :     noExplain.insert(noExplain.end(), exp.begin(), exp.end());
     416                 :            :   }
     417                 :            :   else
     418                 :            :   {
     419                 :            :     // otherwise, the no-explain literals are those provided
     420         [ +  + ]:      47577 :     for (const Node& ecn : ii.d_noExplain)
     421                 :            :     {
     422                 :       4023 :       utils::flattenOp(Kind::AND, ecn, noExplain);
     423                 :            :     }
     424                 :            :   }
     425                 :            :   // ensure that the proof generator is ready to explain the final conclusion
     426                 :            :   // of the lemma (ii.d_conc).
     427         [ +  + ]:      43554 :   if (d_ipcl != nullptr)
     428                 :            :   {
     429                 :      16650 :     d_ipcl->notifyLemma(ii);
     430                 :            :   }
     431         [ +  + ]:      43554 :   TrustNode tlem = mkLemmaExp(ii.d_conc, exp, noExplain, d_ipcl.get());
     432 [ +  - ][ -  + ]:      87108 :   Trace("strings-pending") << "Process pending lemma : " << tlem.getNode()
                 [ -  - ]
     433                 :      43554 :                            << std::endl;
     434                 :            : 
     435                 :            :   // Process the side effects of the inference info.
     436                 :            :   // Register the new skolems from this inference. We register them here
     437                 :            :   // (lazily), since this is the moment when we have decided to process the
     438                 :            :   // inference.
     439                 :       2576 :   for (const std::pair<const LengthStatus, std::vector<Node> >& sks :
     440         [ +  + ]:      48706 :        ii.d_skolems)
     441                 :            :   {
     442         [ +  + ]:       5152 :     for (const Node& n : sks.second)
     443                 :            :     {
     444                 :       2576 :       d_termReg.registerTermAtomic(n, sks.first);
     445                 :            :     }
     446                 :            :   }
     447         [ +  + ]:      43554 :   if (ii.getId() == InferenceId::STRINGS_REDUCTION)
     448                 :            :   {
     449                 :       4442 :     p |= LemmaProperty::NEEDS_JUSTIFY;
     450                 :            :   }
     451                 :            :   // send phase requirements
     452         [ +  + ]:      45515 :   for (const std::pair<const Node, bool>& pp : ii.d_pendingPhase)
     453                 :            :   {
     454                 :       1961 :     Node ppr = rewrite(pp.first);
     455                 :       1961 :     addPendingPhaseRequirement(ppr, pp.second);
     456                 :            :   }
     457 [ +  - ][ -  - ]:      87108 :   Trace("strings-assert") << "(assert " << tlem.getNode() << ") ; lemma "
     458         [ -  + ]:      43554 :                           << ii.getId() << std::endl;
     459 [ +  - ][ -  - ]:      87108 :   Trace("strings-lemma") << "Strings::Lemma: " << tlem.getNode() << " by "
     460         [ -  + ]:      43554 :                          << ii.getId() << std::endl;
     461                 :      87108 :   return tlem;
     462                 :            : }
     463                 :            : 
     464                 :        359 : std::map<TNode, TNode> InferenceManager::getExplanationMap(
     465                 :            :     const std::vector<TNode>& assumptions)
     466                 :            : {
     467                 :        359 :   std::map<TNode, TNode> emap;
     468         [ +  + ]:       1286 :   for (TNode e : assumptions)
     469                 :            :   {
     470         [ +  + ]:        927 :     if (e.getKind() != Kind::EQUAL)
     471                 :            :     {
     472                 :            :       // skip non-equalities, which could be included if we internally
     473                 :            :       // concluded an equality as a fact from a non-equality
     474                 :         49 :       continue;
     475                 :            :     }
     476         [ +  + ]:       2634 :     for (size_t i = 0; i < 2; i++)
     477                 :            :     {
     478                 :       1756 :       emap[e[i]] = e;
     479                 :            :     }
     480                 :            :   }
     481                 :        359 :   return emap;
     482                 :            : }
     483                 :        359 : Node InferenceManager::mkPrefixExplainMin(Node x,
     484                 :            :                                           Node prefix,
     485                 :            :                                           const std::vector<TNode>& assumptions,
     486                 :            :                                           const std::map<TNode, TNode>& emap,
     487                 :            :                                           bool isSuf)
     488                 :            : {
     489 [ -  + ][ -  + ]:        359 :   Assert(prefix.isConst());
                 [ -  - ]
     490         [ +  - ]:        718 :   Trace("strings-prefix-min")
     491         [ -  - ]:          0 :       << "mkPrefixExplainMin: " << x << " for " << (isSuf ? "suffix" : "prefix")
     492                 :        359 :       << " " << prefix << std::endl;
     493         [ +  - ]:        359 :   Trace("strings-prefix-min") << "- via: " << assumptions << std::endl;
     494                 :        718 :   std::vector<TNode> minAssumptions;
     495                 :            :   // the current node(s) we are looking at
     496                 :        718 :   std::vector<TNode> cc;
     497                 :        359 :   cc.push_back(x);
     498                 :        359 :   size_t pindex = 0;
     499                 :        718 :   std::vector<Node> pchars = Word::getChars(prefix);
     500                 :        359 :   std::map<TNode, TNode>::const_iterator it;
     501                 :        359 :   bool isConflict = false;
     502 [ +  - ][ +  + ]:       1419 :   while (pindex < pchars.size() && !cc.empty())
                 [ +  + ]
     503                 :            :   {
     504         [ +  - ]:       2564 :     Trace("strings-prefix-min")
     505                 :       1282 :         << "  " << pindex << "/" << pchars.size() << ", " << cc << std::endl;
     506                 :       1282 :     TNode c = cc.back();
     507                 :       1282 :     cc.pop_back();
     508         [ +  + ]:       1282 :     if (c.isConst())
     509                 :            :     {
     510                 :            :       // check for conflict
     511                 :        337 :       std::vector<Node> cchars = Word::getChars(c);
     512                 :        337 :       size_t cindex = 0;
     513 [ +  - ][ +  + ]:        347 :       while (pindex < pchars.size() && cindex < cchars.size())
                 [ +  + ]
     514                 :            :       {
     515         [ +  + ]:        150 :         size_t pii = isSuf ? (pchars.size() - 1) - pindex : pindex;
     516         [ +  + ]:        150 :         size_t cii = isSuf ? (cchars.size() - 1) - cindex : cindex;
     517         [ +  + ]:        150 :         if (cchars[cii] != pchars[pii])
     518                 :            :         {
     519         [ +  - ]:        280 :           Trace("strings-prefix-min") << "...conflict at " << pindex
     520                 :        140 :                                       << " while processing " << c << std::endl;
     521                 :        140 :           isConflict = true;
     522                 :        140 :           break;
     523                 :            :         }
     524                 :         10 :         pindex++;
     525                 :         10 :         cindex++;
     526                 :            :       }
     527         [ +  + ]:        337 :       if (isConflict)
     528                 :            :       {
     529                 :        140 :         break;
     530                 :            :       }
     531                 :        197 :       continue;
     532                 :            :     }
     533                 :        945 :     it = emap.find(c);
     534         [ +  + ]:        945 :     if (it != emap.end())
     535                 :            :     {
     536                 :        857 :       TNode ceq = it->second;
     537                 :            :       // do not continue if not already processed, which also avoids
     538                 :            :       // non-termination
     539                 :        857 :       if (std::find(minAssumptions.begin(), minAssumptions.end(), ceq)
     540         [ +  + ]:       1714 :           == minAssumptions.end())
     541                 :            :       {
     542 [ -  + ][ -  + ]:        560 :         Assert(ceq.getKind() == Kind::EQUAL);
                 [ -  - ]
     543                 :        560 :         Assert(ceq[0] == c || ceq[1] == c);
     544                 :            :         // add to explanation and look at the term it is equal to
     545                 :        560 :         minAssumptions.push_back(ceq);
     546         [ +  + ]:       1120 :         TNode oc = ceq[ceq[0] == c ? 1 : 0];
     547                 :        560 :         cc.push_back(oc);
     548                 :        560 :         continue;
     549                 :            :       }
     550                 :            :     }
     551                 :            :     // we don't know what it is equal to
     552                 :            :     // if it is a concatenation, try to recurse into children
     553         [ +  + ]:        385 :     if (c.getKind() == Kind::STRING_CONCAT)
     554                 :            :     {
     555         [ +  + ]:       1093 :       for (size_t i = 0, nchild = c.getNumChildren(); i < nchild; i++)
     556                 :            :       {
     557                 :            :         // reverse if it is a prefix
     558         [ +  + ]:        790 :         size_t ii = isSuf ? i : (nchild - 1) - i;
     559                 :        790 :         cc.push_back(c[ii]);
     560                 :            :       }
     561                 :        303 :       continue;
     562                 :            :     }
     563         [ +  - ]:         82 :     Trace("strings-prefix-min") << "-> no explanation for " << c << std::endl;
     564                 :         82 :     break;
     565                 :            :   }
     566 [ +  + ][ +  + ]:        359 :   if (isConflict && minAssumptions.size() < assumptions.size())
                 [ +  + ]
     567                 :            :   {
     568         [ +  - ]:        144 :     Trace("strings-prefix-min")
     569                 :         72 :         << "-> min-explained: " << minAssumptions << std::endl;
     570         [ +  - ]:        144 :     Trace("strings-exp-min-stats")
     571                 :          0 :         << "Min-explain (prefix) " << minAssumptions.size() << " / "
     572                 :         72 :         << assumptions.size() << std::endl;
     573                 :         72 :     return nodeManager()->mkAnd(minAssumptions);
     574                 :            :   }
     575                 :        287 :   return Node::null();
     576                 :            : }
     577                 :            : 
     578                 :            : }  // namespace strings
     579                 :            : }  // namespace theory
     580                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14