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: 293 313 93.6 %
Date: 2026-08-03 10:34:52 Functions: 13 14 92.9 %
Branches: 236 358 65.9 %

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

Generated by: LCOV version 1.14