Add missing methods to Solver API for models (#7052)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Fri, 27 Aug 2021 15:36:27 +0000 (10:36 -0500)
committerGitHub <noreply@github.com>
Fri, 27 Aug 2021 15:36:27 +0000 (15:36 +0000)
This adds the last two remaining API methods required for implementing the text output of get-model on the API side.

A followup PR will implement GetModelCommand on the API side and eliminate the (last remaining) call to getSmtEngine() from the command layer.

This PR does some minor reorganization of the model cores code to account for the new interface. It also removes a deprecated interface from TheoryModel.

15 files changed:
src/api/cpp/cvc5.cpp
src/api/cpp/cvc5.h
src/api/python/cvc5.pxd
src/api/python/cvc5.pxi
src/printer/cvc/cvc_printer.cpp
src/printer/smt2/smt2_printer.cpp
src/smt/model_core_builder.cpp
src/smt/preprocessor.cpp
src/smt/preprocessor.h
src/smt/smt_engine.cpp
src/smt/smt_engine.h
src/theory/sep/theory_sep.cpp
src/theory/theory_model.cpp
src/theory/theory_model.h
test/unit/api/solver_black.cpp

index f85286acbe202a2362d0ab38e0ab30122d3f7acf..1eaae87617c217783361283c6f8be6c1b7574d87 100644 (file)
@@ -7131,6 +7131,51 @@ std::vector<Term> Solver::getValue(const std::vector<Term>& terms) const
   CVC5_API_TRY_CATCH_END;
 }
 
+std::vector<Term> Solver::getModelDomainElements(const Sort& s) const
+{
+  CVC5_API_TRY_CATCH_BEGIN;
+  NodeManagerScope scope(getNodeManager());
+  CVC5_API_RECOVERABLE_CHECK(d_smtEngine->getOptions().smt.produceModels)
+      << "Cannot get domain elements unless model generation is enabled "
+         "(try --produce-models)";
+  CVC5_API_RECOVERABLE_CHECK(d_smtEngine->isSmtModeSat())
+      << "Cannot get domain elements unless after a SAT or unknown response.";
+  CVC5_API_SOLVER_CHECK_SORT(s);
+  CVC5_API_RECOVERABLE_CHECK(s.isUninterpretedSort())
+      << "Expecting an uninterpreted sort as argument to "
+         "getModelDomainElements.";
+  //////// all checks before this line
+  std::vector<Term> res;
+  std::vector<Node> elements =
+      d_smtEngine->getModelDomainElements(s.getTypeNode());
+  for (const Node& n : elements)
+  {
+    res.push_back(Term(this, n));
+  }
+  return res;
+  ////////
+  CVC5_API_TRY_CATCH_END;
+}
+
+bool Solver::isModelCoreSymbol(const Term& v) const
+{
+  CVC5_API_TRY_CATCH_BEGIN;
+  NodeManagerScope scope(getNodeManager());
+  CVC5_API_RECOVERABLE_CHECK(d_smtEngine->getOptions().smt.produceModels)
+      << "Cannot check if model core symbol unless model generation is enabled "
+         "(try --produce-models)";
+  CVC5_API_RECOVERABLE_CHECK(d_smtEngine->isSmtModeSat())
+      << "Cannot check if model core symbol unless after a SAT or unknown "
+         "response.";
+  CVC5_API_SOLVER_CHECK_TERM(v);
+  CVC5_API_RECOVERABLE_CHECK(v.getKind() == CONSTANT)
+      << "Expecting a free constant as argument to isModelCoreSymbol.";
+  //////// all checks before this line
+  return d_smtEngine->isModelCoreSymbol(v.getNode());
+  ////////
+  CVC5_API_TRY_CATCH_END;
+}
+
 Term Solver::getQuantifierElimination(const Term& q) const
 {
   NodeManagerScope scope(getNodeManager());
index 3c024131160b49d5067057073e54aca89dd30a7b..9f805fa65440c0a65289b035ef56dd7937b28720 100644 (file)
@@ -3782,7 +3782,7 @@ class CVC5_EXPORT Solver
   std::string getProof() const;
 
   /**
-   * Get the value of the given term.
+   * Get the value of the given term in the current model.
    * SMT-LIB:
    * \verbatim
    * ( get-value ( <term> ) )
@@ -3791,8 +3791,9 @@ class CVC5_EXPORT Solver
    * @return the value of the given term
    */
   Term getValue(const Term& term) const;
+
   /**
-   * Get the values of the given terms.
+   * Get the values of the given terms in the current model.
    * SMT-LIB:
    * \verbatim
    * ( get-value ( <term>+ ) )
@@ -3802,6 +3803,27 @@ class CVC5_EXPORT Solver
    */
   std::vector<Term> getValue(const std::vector<Term>& terms) const;
 
+  /**
+   * Get the domain elements of uninterpreted sort s in the current model. The
+   * current model interprets s as the finite sort whose domain elements are
+   * given in the return value of this method.
+   *
+   * @param s The uninterpreted sort in question
+   * @return the domain elements of s in the current model
+   */
+  std::vector<Term> getModelDomainElements(const Sort& s) const;
+
+  /**
+   * This returns false if the model value of free constant v was not essential
+   * for showing the satisfiability of the last call to checkSat using the
+   * current model. This method will only return false (for any v) if
+   * the model-cores option has been set.
+   *
+   * @param v The term in question
+   * @return true if v is a model core symbol
+   */
+  bool isModelCoreSymbol(const Term& v) const;
+
   /**
    * Do quantifier elimination.
    * SMT-LIB:
index 2665706c0f3d1dd45ac7c2e86c8230ec3c6a2c27..ef9971c20f94299797484af1b87f36107f5e8ffd 100644 (file)
@@ -278,6 +278,8 @@ cdef extern from "api/cpp/cvc5.h" namespace "cvc5::api":
         vector[Term] getUnsatCore() except +
         Term getValue(Term term) except +
         vector[Term] getValue(const vector[Term]& terms) except +
+        vector[Term] getModelDomainElements(Sort sort) except +
+        bint isModelCoreSymbol(Term v) except +
         void declareSeparationHeap(Sort locSort, Sort dataSort) except +
         Term getSeparationHeap() except +
         Term getSeparationNilTerm() except +
index 36dcc066e62139310c5e80c7eeb1ac7280d97e9e..68998d521c1b5f43237a4fe7b3d7ca90a4c2547a 100644 (file)
@@ -1296,6 +1296,18 @@ cdef class Solver:
         term.cterm = self.csolver.getValue(t.cterm)
         return term
 
+    def getModelDomainElements(self, Sort s):
+        result = []
+        cresult = self.csolver.getModelDomainElements(s.csort)
+        for e in cresult:
+            term = Term(self)
+            term.cterm = e
+            result.append(term)
+        return result
+
+    def isModelCoreSymbol(self, Term v):
+        return self.csolver.isModelCoreSymbol(v.cterm)
+
     def getSeparationHeap(self):
         cdef Term term = Term(self)
         term.cterm = self.csolver.getSeparationHeap()
index b42304ecc4fc5741239022c834ec29e8af56f737..1f1296b7f0c8ece7ce953fb9ca3d9bb9b4a2b060 100644 (file)
@@ -1095,16 +1095,6 @@ void CvcPrinter::toStreamModelTerm(std::ostream& out,
 
 void CvcPrinter::toStream(std::ostream& out, const smt::Model& m) const
 {
-  const theory::TheoryModel* tm = m.getTheoryModel();
-  // print the model comments
-  std::stringstream c;
-  tm->getComments(c);
-  std::string ln;
-  while (std::getline(c, ln))
-  {
-    out << "; " << ln << std::endl;
-  }
-
   // print the model
   out << "MODEL BEGIN" << std::endl;
   this->Printer::toStream(out, m);
index b7f32123d0beb3d7fd1c7601ef2fd7c08b90c497..523b3efa9944afdb017969442903d80dcef96146 100644 (file)
@@ -1262,13 +1262,6 @@ void Smt2Printer::toStream(std::ostream& out, const UnsatCore& core) const
 void Smt2Printer::toStream(std::ostream& out, const smt::Model& m) const
 {
   const theory::TheoryModel* tm = m.getTheoryModel();
-  //print the model comments
-  std::stringstream c;
-  tm->getComments(c);
-  std::string ln;
-  while( std::getline( c, ln ) ){
-    out << "; " << ln << std::endl;
-  }
   //print the model
   out << "(" << endl;
   // don't need to print approximations since they are built into choice
index 0395a32fde501ba1d17c076cc6fd07bcdb7a193a..9e9eb5f230d1968ea198b13ebc4de5d6a7415d75 100644 (file)
@@ -25,6 +25,11 @@ bool ModelCoreBuilder::setModelCore(const std::vector<Node>& assertions,
                                     theory::TheoryModel* m,
                                     options::ModelCoresMode mode)
 {
+  if (m->isUsingModelCore())
+  {
+    // already computed
+    return true;
+  }
   if (Trace.isOn("model-core"))
   {
     Trace("model-core") << "Compute model core, assertions:" << std::endl;
index 3c0a4ac5b4eccd1cf4054597c84fe22850d1a943..c36ff1bce89b5f7a7f357b1f4bccbb3b980573d3 100644 (file)
@@ -135,6 +135,15 @@ Node Preprocessor::expandDefinitions(const Node& node,
   return n;
 }
 
+void Preprocessor::expandDefinitions(std::vector<Node>& ns)
+{
+  std::unordered_map<Node, Node> cache;
+  for (size_t i = 0, nasserts = ns.size(); i < nasserts; i++)
+  {
+    ns[i] = expandDefinitions(ns[i], cache);
+  }
+}
+
 Node Preprocessor::simplify(const Node& node)
 {
   Trace("smt") << "SMT simplify(" << node << ")" << endl;
index 3a1f9f08066ba55fa371509c8fe8f93f28465678..03246ed6721a2adb949933da21e1d420f86542da 100644 (file)
@@ -90,6 +90,8 @@ class Preprocessor
   Node expandDefinitions(const Node& n);
   /** Same as above, with a cache of previous results. */
   Node expandDefinitions(const Node& n, std::unordered_map<Node, Node>& cache);
+  /** Same as above, for a list of assertions, updating in place */
+  void expandDefinitions(std::vector<Node>& ns);
   /**
    * Set proof node manager. Enables proofs in this preprocessor.
    */
index ea253e4ef8f5884134330b1b9a42f2f303c2aa04..be5c75af05f851ee7bddf1e3d7ee532ddf053136 100644 (file)
@@ -1166,7 +1166,7 @@ Node SmtEngine::getValue(const Node& ex) const
   return resultNode;
 }
 
-std::vector<Node> SmtEngine::getValues(const std::vector<Node>& exprs)
+std::vector<Node> SmtEngine::getValues(const std::vector<Node>& exprs) const
 {
   std::vector<Node> result;
   for (const Node& e : exprs)
@@ -1176,6 +1176,39 @@ std::vector<Node> SmtEngine::getValues(const std::vector<Node>& exprs)
   return result;
 }
 
+std::vector<Node> SmtEngine::getModelDomainElements(TypeNode tn) const
+{
+  Assert(tn.isSort());
+  Model* m = getAvailableModel("getModelDomainElements");
+  return m->getTheoryModel()->getDomainElements(tn);
+}
+
+bool SmtEngine::isModelCoreSymbol(Node n)
+{
+  SmtScope smts(this);
+  Assert(n.isVar());
+  const Options& opts = d_env->getOptions();
+  if (opts.smt.modelCoresMode == options::ModelCoresMode::NONE)
+  {
+    // if the model core mode is none, we are always a model core symbol
+    return true;
+  }
+  Model* m = getAvailableModel("isModelCoreSymbol");
+  TheoryModel* tm = m->getTheoryModel();
+  // compute the model core if not done so already
+  if (!tm->isUsingModelCore())
+  {
+    // If we enabled model cores, we compute a model core for m based on our
+    // (expanded) assertions using the model core builder utility. Notice that
+    // we get the assertions using the getAssertionsInternal, which does not
+    // impact whether we are in "sat" mode
+    std::vector<Node> asserts = getAssertionsInternal();
+    d_pp->expandDefinitions(asserts);
+    ModelCoreBuilder::setModelCore(asserts, tm, opts.smt.modelCoresMode);
+  }
+  return tm->isModelCoreSymbol(n);
+}
+
 // TODO(#1108): Simplify the error reporting of this method.
 Model* SmtEngine::getModel() {
   Trace("smt") << "SMT getModel()" << endl;
@@ -1201,10 +1234,10 @@ Model* SmtEngine::getModel() {
   {
     // If we enabled model cores, we compute a model core for m based on our
     // (expanded) assertions using the model core builder utility
-    std::vector<Node> eassertsProc = getExpandedAssertions();
-    ModelCoreBuilder::setModelCore(eassertsProc,
-                                   m->getTheoryModel(),
-                                   d_env->getOptions().smt.modelCoresMode);
+    std::vector<Node> asserts = getAssertionsInternal();
+    d_pp->expandDefinitions(asserts);
+    ModelCoreBuilder::setModelCore(
+        asserts, m->getTheoryModel(), d_env->getOptions().smt.modelCoresMode);
   }
   // set the information on the SMT-level model
   Assert(m != nullptr);
@@ -1294,18 +1327,25 @@ std::pair<Node, Node> SmtEngine::getSepHeapAndNilExpr(void)
   return std::make_pair(heap, nil);
 }
 
+std::vector<Node> SmtEngine::getAssertionsInternal()
+{
+  Assert(d_state->isFullyInited());
+  context::CDList<Node>* al = d_asserts->getAssertionList();
+  Assert(al != nullptr);
+  std::vector<Node> res;
+  for (const Node& n : *al)
+  {
+    res.emplace_back(n);
+  }
+  return res;
+}
+
 std::vector<Node> SmtEngine::getExpandedAssertions()
 {
   std::vector<Node> easserts = getAssertions();
   // must expand definitions
-  std::vector<Node> eassertsProc;
-  std::unordered_map<Node, Node> cache;
-  for (const Node& e : easserts)
-  {
-    Node eae = d_pp->expandDefinitions(e, cache);
-    eassertsProc.push_back(eae);
-  }
-  return eassertsProc;
+  d_pp->expandDefinitions(easserts);
+  return easserts;
 }
 Env& SmtEngine::getEnv() { return *d_env.get(); }
 
@@ -1787,15 +1827,7 @@ std::vector<Node> SmtEngine::getAssertions()
       "Cannot query the current assertion list when not in produce-assertions mode.";
     throw ModalException(msg);
   }
-  context::CDList<Node>* al = d_asserts->getAssertionList();
-  Assert(al != nullptr);
-  std::vector<Node> res;
-  for (const Node& n : *al)
-  {
-    res.emplace_back(n);
-  }
-  // copy the result out
-  return res;
+  return getAssertionsInternal();
 }
 
 void SmtEngine::push()
index d9786f6a17cbfd766754406131f4830bdddabb6b..a250e2a7f193d382aebdaa8ead9cd74c5c3fe6e9 100644 (file)
@@ -532,7 +532,17 @@ class CVC5_EXPORT SmtEngine
   /**
    * Same as getValue but for a vector of expressions
    */
-  std::vector<Node> getValues(const std::vector<Node>& exprs);
+  std::vector<Node> getValues(const std::vector<Node>& exprs) const;
+
+  /**
+   * @return the domain elements for uninterpreted sort tn.
+   */
+  std::vector<Node> getModelDomainElements(TypeNode tn) const;
+
+  /**
+   * @return true if v is a model core symbol
+   */
+  bool isModelCoreSymbol(Node v);
 
   /** print instantiations
    *
@@ -1022,7 +1032,13 @@ class CVC5_EXPORT SmtEngine
    * element is the nil expression.
    */
   std::pair<Node, Node> getSepHeapAndNilExpr();
-
+  /**
+   * Get assertions internal, which is only called after initialization. This
+   * should be used internally to get the assertions instead of getAssertions
+   * or getExpandedAssertions, which may trigger initialization and SMT state
+   * changes.
+   */
+  std::vector<Node> getAssertionsInternal();
   /* Members -------------------------------------------------------------- */
 
   /** Solver instance that owns this SmtEngine instance. */
index 92d15653e328e3c8dc932fe94d60e3398f54759e..ba1680c6b9c81914787727328a5c17720f06d2db 100644 (file)
@@ -198,7 +198,6 @@ void TheorySep::postProcessModel( TheoryModel* m ){
         Trace("sep-model") << " " << l << " -> ";
         if( d_pto_model[l].isNull() ){
           Trace("sep-model") << "_";
-          //m->d_comment_str << "_";
           TypeEnumerator te_range( data_type );
           if (d_state.isFiniteType(data_type))
           {
index 3e902463cc2513daf5d63916d0d1da14d05a5bf9..02a83789910900115c24b8ceac0d426b5ecffe57 100644 (file)
@@ -74,7 +74,6 @@ void TheoryModel::finishInit(eq::EqualityEngine* ee)
 
 void TheoryModel::reset(){
   d_modelCache.clear();
-  d_comment_str.clear();
   d_sep_heap = Node::null();
   d_sep_nil_eq = Node::null();
   d_approximations.clear();
@@ -91,11 +90,6 @@ void TheoryModel::reset(){
   d_model_core.clear();
 }
 
-void TheoryModel::getComments(std::ostream& out) const {
-  Trace("model-builder") << "get comments..." << std::endl;
-  out << d_comment_str.str();
-}
-
 void TheoryModel::setHeapModel( Node h, Node neq ) { 
   d_sep_heap = h;
   d_sep_nil_eq = neq;
@@ -610,6 +604,7 @@ void TheoryModel::recordApproximation(TNode n, TNode pred, Node witness)
   Node predDisj = NodeManager::currentNM()->mkNode(OR, n.eqNode(witness), pred);
   recordApproximation(n, predDisj);
 }
+bool TheoryModel::isUsingModelCore() const { return d_using_model_core; }
 void TheoryModel::setUsingModelCore()
 {
   d_using_model_core = true;
index 4034e43bda90aa536c22b8da422e925e0636b8fd..6841a11d857fa9ae511ccdf2199a0fbef1e855e9 100644 (file)
@@ -296,8 +296,6 @@ class TheoryModel
    * has called buildModel(...) on this model.
    */
   Node getValue(TNode n) const;
-  /** get comments */
-  void getComments(std::ostream& out) const;
 
   //---------------------------- separation logic
   /** set the heap and value sep.nil is equal to */
@@ -318,6 +316,8 @@ class TheoryModel
   RepSet* getRepSetPtr() { return &d_rep_set; }
 
   //---------------------------- model cores
+  /** True if a model core has been computed for this model. */
+  bool isUsingModelCore() const;
   /** set using model core */
   void setUsingModelCore();
   /** record model core symbol */
@@ -394,8 +394,6 @@ class TheoryModel
   /** true/false nodes */
   Node d_true;
   Node d_false;
-  /** comment stream to include in printing */
-  std::stringstream d_comment_str;
   /** are we using model cores? */
   bool d_using_model_core;
   /** symbols that are in the model core */
index 99a60aa76e1c7748c94777f159d53bfa9b4961e4..312d57319fda54dfb0259f89b193967f0ec95a9b 100644 (file)
@@ -1473,6 +1473,57 @@ TEST_F(TestApiBlackSolver, getValue3)
   ASSERT_THROW(slv.getValue(x), CVC5ApiException);
 }
 
+TEST_F(TestApiBlackSolver, getModelDomainElements)
+{
+  d_solver.setOption("produce-models", "true");
+  Sort uSort = d_solver.mkUninterpretedSort("u");
+  Sort intSort = d_solver.getIntegerSort();
+  Term x = d_solver.mkConst(uSort, "x");
+  Term y = d_solver.mkConst(uSort, "y");
+  Term z = d_solver.mkConst(uSort, "z");
+  Term f = d_solver.mkTerm(DISTINCT, x, y, z);
+  d_solver.assertFormula(f);
+  d_solver.checkSat();
+  ASSERT_NO_THROW(d_solver.getModelDomainElements(uSort));
+  ASSERT_TRUE(d_solver.getModelDomainElements(uSort).size() >= 3);
+  ASSERT_THROW(d_solver.getModelDomainElements(intSort), CVC5ApiException);
+}
+
+TEST_F(TestApiBlackSolver, getModelDomainElements2)
+{
+  d_solver.setOption("produce-models", "true");
+  d_solver.setOption("finite-model-find", "true");
+  Sort uSort = d_solver.mkUninterpretedSort("u");
+  Term x = d_solver.mkVar(uSort, "x");
+  Term y = d_solver.mkVar(uSort, "y");
+  Term eq = d_solver.mkTerm(EQUAL, x, y);
+  Term bvl = d_solver.mkTerm(BOUND_VAR_LIST, x, y);
+  Term f = d_solver.mkTerm(FORALL, bvl, eq);
+  d_solver.assertFormula(f);
+  d_solver.checkSat();
+  ASSERT_NO_THROW(d_solver.getModelDomainElements(uSort));
+  // a model for the above must interpret u as size 1
+  ASSERT_TRUE(d_solver.getModelDomainElements(uSort).size() == 1);
+}
+
+TEST_F(TestApiBlackSolver, isModelCoreSymbol)
+{
+  d_solver.setOption("produce-models", "true");
+  d_solver.setOption("model-cores", "simple");
+  Sort uSort = d_solver.mkUninterpretedSort("u");
+  Term x = d_solver.mkConst(uSort, "x");
+  Term y = d_solver.mkConst(uSort, "y");
+  Term z = d_solver.mkConst(uSort, "z");
+  Term zero = d_solver.mkInteger(0);
+  Term f = d_solver.mkTerm(NOT, d_solver.mkTerm(EQUAL, x, y));
+  d_solver.assertFormula(f);
+  d_solver.checkSat();
+  ASSERT_TRUE(d_solver.isModelCoreSymbol(x));
+  ASSERT_TRUE(d_solver.isModelCoreSymbol(y));
+  ASSERT_FALSE(d_solver.isModelCoreSymbol(z));
+  ASSERT_THROW(d_solver.isModelCoreSymbol(zero), CVC5ApiException);
+}
+
 TEST_F(TestApiBlackSolver, getQuantifierElimination)
 {
   Term x = d_solver.mkVar(d_solver.getBooleanSort(), "x");