LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/smt - check_models.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 69 118 58.5 %
Date: 2026-02-10 13:58:09 Functions: 2 3 66.7 %
Branches: 28 60 46.7 %

           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                 :            :  * Utility for constructing and maintaining abstract values.
      14                 :            :  */
      15                 :            : 
      16                 :            : #include "smt/check_models.h"
      17                 :            : 
      18                 :            : #include "base/modal_exception.h"
      19                 :            : #include "expr/non_closed_node_converter.h"
      20                 :            : #include "options/quantifiers_options.h"
      21                 :            : #include "options/smt_options.h"
      22                 :            : #include "smt/env.h"
      23                 :            : #include "smt/expand_definitions.h"
      24                 :            : #include "smt/preprocessor.h"
      25                 :            : #include "smt/set_defaults.h"
      26                 :            : #include "smt/smt_solver.h"
      27                 :            : #include "theory/rewriter.h"
      28                 :            : #include "theory/smt_engine_subsolver.h"
      29                 :            : #include "theory/theory_model.h"
      30                 :            : #include "theory/trust_substitutions.h"
      31                 :            : 
      32                 :            : using namespace cvc5::internal::theory;
      33                 :            : 
      34                 :            : namespace cvc5::internal {
      35                 :            : namespace smt {
      36                 :            : 
      37                 :          0 : void getTheoriesOf(Env& env, const Node& n, std::vector<TheoryId>& theories)
      38                 :            : {
      39                 :          0 :   std::unordered_set<TNode> visited;
      40                 :          0 :   std::vector<TNode> visit;
      41                 :          0 :   TNode cur;
      42                 :          0 :   visit.push_back(n);
      43                 :          0 :   do
      44                 :            :   {
      45                 :          0 :     cur = visit.back();
      46                 :          0 :     visit.pop_back();
      47         [ -  - ]:          0 :     if (visited.find(cur) == visited.end())
      48                 :            :     {
      49                 :          0 :       visited.insert(cur);
      50                 :            :       // get the theories of the term and its type
      51                 :          0 :       TheoryId tid = env.theoryOf(cur);
      52         [ -  - ]:          0 :       if (std::find(theories.begin(), theories.end(), tid) == theories.end())
      53                 :            :       {
      54                 :          0 :         theories.push_back(tid);
      55                 :            :       }
      56                 :          0 :       TheoryId ttid = env.theoryOf(cur.getType());
      57         [ -  - ]:          0 :       if (ttid!=tid)
      58                 :            :       {
      59         [ -  - ]:          0 :         if (std::find(theories.begin(), theories.end(), ttid) == theories.end())
      60                 :            :         {
      61                 :          0 :           theories.push_back(ttid);
      62                 :            :         }
      63                 :            :       }
      64                 :          0 :       visit.insert(visit.end(), cur.begin(), cur.end());
      65                 :            :     }
      66         [ -  - ]:          0 :   } while (!visit.empty());
      67                 :          0 : }
      68                 :            : 
      69                 :       2411 : CheckModels::CheckModels(Env& e) : EnvObj(e) {}
      70                 :            : 
      71                 :       3077 : void CheckModels::checkModel(TheoryModel* m,
      72                 :            :                              const context::CDList<Node>& al,
      73                 :            :                              bool hardFailure)
      74                 :            : {
      75                 :            :   // Throughout, we use verbose(1) to give diagnostic output.
      76                 :            :   //
      77                 :            :   // If this function is running, the user gave --check-model (or equivalent),
      78                 :            :   // and if verbose(1) is on, the user gave --verbose (or equivalent).
      79                 :            : 
      80                 :       3077 :   Node sepHeap, sepNeq;
      81         [ -  + ]:       3077 :   if (m->getHeapModel(sepHeap, sepNeq))
      82                 :            :   {
      83                 :            :     throw RecoverableModalException(
      84                 :          0 :         "Cannot run check-model on a model with a separation logic heap.");
      85                 :            :   }
      86         [ +  + ]:       3077 :   if (options().quantifiers.fmfFunWellDefined)
      87                 :            :   {
      88                 :          4 :     warning() << "Running check-model is not guaranteed to pass when fmf-fun "
      89                 :          4 :                  "is enabled."
      90                 :          4 :               << std::endl;
      91                 :            :     // only throw warning
      92                 :          4 :     hardFailure = false;
      93                 :            :   }
      94                 :            :   // expand definitions module and substitutions
      95                 :       3077 :   std::unordered_map<Node, Node> ecache;
      96                 :       3077 :   ExpandDefs expDef(d_env);
      97                 :            : 
      98                 :       3077 :   theory::SubstitutionMap& sm = d_env.getTopLevelSubstitutions().get();
      99         [ +  - ]:       3077 :   Trace("check-model") << "checkModel: Check assertions..." << std::endl;
     100                 :       3077 :   std::unordered_map<Node, Node> cache;
     101                 :            :   // the list of assertions that did not rewrite to true
     102                 :       3077 :   std::vector<Node> noCheckList;
     103                 :            :   // Now go through all our user assertions checking if they're satisfied.
     104         [ +  + ]:      27338 :   for (const Node& assertion : al)
     105                 :            :   {
     106                 :      48522 :     verbose(1) << "SolverEngine::checkModel(): checking assertion " << assertion
     107                 :      24261 :                << std::endl;
     108                 :            : 
     109                 :            :     // Apply any define-funs from the problem. We do not expand theory symbols
     110                 :            :     // like integer division here. Hence, the code below is not able to properly
     111                 :            :     // evaluate e.g. divide-by-zero. This is intentional since the evaluation
     112                 :            :     // is not trustworthy, since the UF introduced by expanding definitions may
     113                 :            :     // not be properly constrained.
     114                 :      24261 :     Node n = sm.apply(assertion);
     115                 :      48522 :     verbose(1) << "SolverEngine::checkModel(): -- substitutes to " << n
     116                 :      24261 :                << std::endl;
     117                 :            : 
     118                 :            :     // Expand definitions, which is required for being accurate for operators
     119                 :            :     // that expand involving skolems during preprocessing. Not doing this will
     120                 :            :     // increase the spurious warnings raised by this class.
     121                 :      24261 :     n = expDef.expandDefinitions(n, cache);
     122                 :      24261 :     bool checkAgain = false;
     123                 :      24261 :     bool processed = false;
     124                 :      24261 :     Node nval;
     125         [ +  - ]:         15 :     do
     126                 :            :     {
     127                 :      24276 :       checkAgain = false;
     128                 :      48552 :       verbose(1) << "SolverEngine::checkModel(): -- expands to " << n
     129                 :      24276 :                  << std::endl;
     130                 :            : 
     131                 :      24276 :       n = rewrite(n);
     132                 :      48552 :       verbose(1) << "SolverEngine::checkModel(): -- rewrites to " << n
     133                 :      24276 :                  << std::endl;
     134                 :            : 
     135                 :      24276 :       nval = m->getValue(n);
     136                 :      48552 :       verbose(1) << "SolverEngine::checkModel(): -- get value : " << n
     137                 :      24276 :                  << std::endl;
     138                 :            : 
     139 [ +  + ][ +  - ]:      24276 :       if (nval.isConst() && nval.getConst<bool>())
                 [ +  + ]
     140                 :            :       {
     141                 :            :         // assertion is true, everything is fine
     142                 :      24178 :         processed = true;
     143                 :      24178 :         break;
     144                 :            :       }
     145                 :            : 
     146                 :            :       // Otherwise, we did not succeed in showing the current assertion to be
     147                 :            :       // true. This may either indicate that our model is wrong, or that we
     148                 :            :       // cannot check it. The latter may be the case for several reasons. One
     149                 :            :       // example is the occurrence of partial operators. Another example are
     150                 :            :       // quantified formulas, which are not checkable, although we assign them
     151                 :            :       // to true/false based on the satisfying assignment. However, quantified
     152                 :            :       // formulas can be modified during preprocess, so they may not correspond
     153                 :            :       // to those in the satisfying assignment. Hence we throw warnings for
     154                 :            :       // assertions that do not simplify to either true or false. Other theories
     155                 :            :       // such as non-linear arithmetic (in particular, transcendental functions)
     156                 :            :       // also have the property of not being able to be checked precisely here.
     157                 :            :       // Note that warnings like these can be avoided for quantified formulas
     158                 :            :       // by making preprocessing passes explicitly record how they
     159                 :            :       // rewrite quantified formulas (see cvc4-wishues#43).
     160         [ +  - ]:         98 :       if (!nval.isConst())
     161                 :            :       {
     162                 :         98 :         n = expDef.expandDefinitions(nval, cache);
     163         [ +  + ]:         98 :         if (n != nval)
     164                 :            :         {
     165                 :            :           // It could be that we can expand again after simplifying. This is
     166                 :            :           // the case e.g. if a quantified formula with division is simplified
     167                 :            :           // to a quantifier-free formula.
     168                 :         15 :           checkAgain = true;
     169                 :            :         }
     170                 :            :         else
     171                 :            :         {
     172                 :            :           // Note that we must be a "closed" term, i.e. one that can be
     173                 :            :           // given in an assertion.
     174                 :         83 :           if (options().smt.checkModelSubsolver
     175 [ +  - ][ +  + ]:         83 :               && NonClosedNodeConverter::isClosed(d_env, nval))
                 [ +  + ]
     176                 :            :           {
     177         [ +  - ]:         66 :             Trace("check-model-subsolver") << "Query is " << nval << std::endl;
     178                 :            :             // satisfiability call
     179                 :         66 :             Options subOptions;
     180                 :         66 :             subOptions.copyValues(options());
     181                 :         66 :             smt::SetDefaults::disableChecking(subOptions);
     182                 :            :             // initialize the subsolver
     183                 :         66 :             SubsolverSetupInfo ssi(d_env, subOptions);
     184                 :          0 :             std::unique_ptr<SolverEngine> checkModelChecker;
     185                 :         66 :             initializeSubsolver(nodeManager(), checkModelChecker, ssi);
     186                 :         66 :             checkModelChecker->assertFormula(nval);
     187                 :         66 :             Result r = checkModelChecker->checkSat();
     188         [ +  - ]:         66 :             Trace("check-model-subsolver") << "..result is " << r << std::endl;
     189         [ +  + ]:         66 :             if (r == Result::SAT)
     190                 :            :             {
     191                 :         63 :               processed = true;
     192                 :         63 :               break;
     193                 :            :             }
     194                 :            :           }
     195                 :            :           // Not constant, print a less severe warning message here.
     196                 :         20 :           warning() << "Warning : SolverEngine::checkModel(): cannot check "
     197                 :            :                        "simplified "
     198                 :         20 :                        "assertion : "
     199                 :         20 :                     << nval << std::endl;
     200                 :         20 :           noCheckList.push_back(nval);
     201                 :         20 :           processed = true;
     202                 :         20 :           break;
     203                 :            :         }
     204                 :            :       }
     205                 :            :     } while (checkAgain);
     206                 :            :     // If processed in the loop above, we go to the next term
     207         [ +  - ]:      24261 :     if (processed)
     208                 :            :     {
     209                 :      24261 :       continue;
     210                 :            :     }
     211                 :            :     // Assertions that simplify to false result in an InternalError or
     212                 :            :     // Warning being thrown below (when hardFailure is false).
     213                 :          0 :     verbose(1) << "SolverEngine::checkModel(): *** PROBLEM: EXPECTED `TRUE' ***"
     214                 :          0 :                << std::endl;
     215                 :          0 :     std::stringstream ss;
     216                 :            :     ss << "SolverEngine::checkModel(): "
     217                 :          0 :        << "ERRORS SATISFYING ASSERTIONS WITH MODEL";
     218                 :          0 :     std::stringstream ssdet;
     219                 :          0 :     ssdet << ":" << std::endl
     220                 :          0 :           << "assertion:     " << assertion << std::endl
     221                 :          0 :           << "simplifies to: " << nval << std::endl
     222                 :          0 :           << "expected `true'." << std::endl
     223                 :          0 :           << "Run with `--check-models -v' for additional diagnostics.";
     224         [ -  - ]:          0 :     if (hardFailure)
     225                 :            :     {
     226                 :            :       // compute the theories involved, e.g. for the sake of issue tracking.
     227                 :            :       // to ensure minimality, if this is a topmost AND, miniscope
     228                 :          0 :       Node nmin = n;
     229         [ -  - ]:          0 :       while (nmin.getKind() == Kind::AND)
     230                 :            :       {
     231         [ -  - ]:          0 :         for (const Node& nc : nmin)
     232                 :            :         {
     233         [ -  - ]:          0 :           if (m->getValue(nc) == nval)
     234                 :            :           {
     235                 :          0 :             nmin = nc;
     236                 :          0 :             break;
     237                 :            :           }
     238                 :            :         }
     239                 :            :       }
     240                 :            :       // collect the theories of the assertion
     241                 :          0 :       std::vector<TheoryId> theories;
     242                 :          0 :       getTheoriesOf(d_env, nmin, theories);
     243                 :          0 :       std::sort(theories.begin(), theories.end());
     244                 :          0 :       ss << " {";
     245         [ -  - ]:          0 :       for (TheoryId tid : theories)
     246                 :            :       {
     247         [ -  - ]:          0 :         if (tid != THEORY_BOOL)
     248                 :            :         {
     249                 :          0 :           ss << " " << tid;
     250                 :            :         }
     251                 :            :       }
     252                 :          0 :       ss << " }";
     253                 :            :       // internal error if hardFailure is true
     254                 :          0 :       InternalError() << ss.str() << ssdet.str();
     255                 :            :     }
     256                 :            :     else
     257                 :            :     {
     258                 :          0 :       warning() << ss.str() << ssdet.str() << std::endl;
     259                 :            :     }
     260                 :            :   }
     261         [ +  + ]:       3077 :   if (noCheckList.empty())
     262                 :            :   {
     263                 :       3060 :     verbose(1) << "SolverEngine::checkModel(): all assertions checked out OK !"
     264                 :       3060 :                << std::endl;
     265                 :       3060 :     return;
     266                 :            :   }
     267                 :            :   // if the noCheckList is non-empty, we could expand definitions on this list
     268                 :            :   // and check satisfiability
     269                 :            : 
     270         [ +  - ]:         17 :   Trace("check-model") << "checkModel: Finish" << std::endl;
     271                 :            : }
     272                 :            : 
     273                 :            : }  // namespace smt
     274                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14