Provide API version of some SMT Commands. (#5222)
authorAbdalrhman Mohamed <32971963+abdoo8080@users.noreply.github.com>
Sat, 10 Oct 2020 13:25:24 +0000 (08:25 -0500)
committerGitHub <noreply@github.com>
Sat, 10 Oct 2020 13:25:24 +0000 (08:25 -0500)
This PR provides an SMT version of the following SMT commands:

get-instantiations
block-model
block-model-values
get-qe
get-qe-disjunct
command.cpp now calls those functions instead of their SmtEngine counterparts. In addition, SEXPR is now an API kind.

src/api/cvc4cpp.cpp
src/api/cvc4cpp.h
src/api/cvc4cppkind.h
src/smt/command.cpp
src/smt/command.h
src/smt/smt_engine.cpp
test/unit/api/solver_black.h

index 16585447669ad222d0cca1a0189ed9eb201ad567..4dda6098ebe65b6fc100d4a2bd7b4a04d757bb94 100644 (file)
@@ -79,6 +79,7 @@ const static std::unordered_map<Kind, CVC4::Kind, KindHashFunction> s_kinds{
     {DISTINCT, CVC4::Kind::DISTINCT},
     {CONSTANT, CVC4::Kind::VARIABLE},
     {VARIABLE, CVC4::Kind::BOUND_VARIABLE},
+    {SEXPR, CVC4::Kind::SEXPR},
     {LAMBDA, CVC4::Kind::LAMBDA},
     {WITNESS, CVC4::Kind::WITNESS},
     /* Boolean ------------------------------------------------------------- */
@@ -2321,11 +2322,9 @@ Term DatatypeConstructor::getSpecializedConstructorTerm(Sort retSort) const
       d_ctor->getConstructor());
   (void)ret.getType(true); /* kick off type checking */
   // apply type ascription to the operator
-  Term sctor =
-      api::Term(d_solver,
-                ret);
+  Term sctor = api::Term(d_solver, ret);
   return sctor;
-  
+
   CVC4_API_SOLVER_TRY_CATCH_END;
 }
 
@@ -5176,6 +5175,28 @@ std::vector<Term> Solver::getValue(const std::vector<Term>& terms) const
   CVC4_API_SOLVER_TRY_CATCH_END;
 }
 
+Term Solver::getQuantifierElimination(api::Term q) const
+{
+  CVC4_API_SOLVER_TRY_CATCH_BEGIN;
+  CVC4_API_ARG_CHECK_NOT_NULL(q);
+  CVC4_API_SOLVER_CHECK_TERM(q);
+  CVC4::ExprManagerScope exmgrs(*(d_exprMgr.get()));
+  return Term(this,
+              d_smtEngine->getQuantifierElimination(q.getNode(), true, true));
+  CVC4_API_SOLVER_TRY_CATCH_END;
+}
+
+Term Solver::getQuantifierEliminationDisjunct(api::Term q) const
+{
+  CVC4_API_SOLVER_TRY_CATCH_BEGIN;
+  CVC4_API_ARG_CHECK_NOT_NULL(q);
+  CVC4_API_SOLVER_CHECK_TERM(q);
+  CVC4::ExprManagerScope exmgrs(*(d_exprMgr.get()));
+  return Term(
+      this, d_smtEngine->getQuantifierElimination(q.getNode(), false, true));
+  CVC4_API_SOLVER_TRY_CATCH_END;
+}
+
 Term Solver::getSeparationHeap() const
 {
   CVC4_API_SOLVER_TRY_CATCH_BEGIN;
@@ -5315,6 +5336,51 @@ void Solver::printModel(std::ostream& out) const
   CVC4_API_SOLVER_TRY_CATCH_END;
 }
 
+void Solver::blockModel() const
+{
+  CVC4_API_SOLVER_TRY_CATCH_BEGIN;
+  CVC4::ExprManagerScope exmgrs(*(d_exprMgr.get()));
+  CVC4_API_CHECK(d_smtEngine->getOptions()[options::produceModels])
+      << "Cannot get value unless model generation is enabled "
+         "(try --produce-models)";
+  CVC4_API_CHECK(d_smtEngine->getSmtMode() != SmtMode::UNSAT)
+      << "Cannot get value when in unsat mode.";
+  d_smtEngine->blockModel();
+  CVC4_API_SOLVER_TRY_CATCH_END;
+}
+
+void Solver::blockModelValues(const std::vector<Term>& terms) const
+{
+  CVC4_API_SOLVER_TRY_CATCH_BEGIN;
+  CVC4_API_CHECK(d_smtEngine->getOptions()[options::produceModels])
+      << "Cannot get value unless model generation is enabled "
+         "(try --produce-models)";
+  CVC4_API_CHECK(d_smtEngine->getSmtMode() != SmtMode::UNSAT)
+      << "Cannot get value when in unsat mode.";
+  CVC4_API_ARG_SIZE_CHECK_EXPECTED(!terms.empty(), terms)
+      << "a non-empty set of terms";
+  for (int i = 0; i < terms.size(); ++i)
+  {
+    CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED(
+        !terms[i].isNull(), "term", terms[i], i)
+        << "a non-null term";
+    CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED(
+        this == terms[i].d_solver, "term", terms[i], i)
+        << "a term associated to this solver object";
+  }
+  CVC4::ExprManagerScope exmgrs(*(d_exprMgr.get()));
+  d_smtEngine->blockModelValues(termVectorToExprs(terms));
+  CVC4_API_SOLVER_TRY_CATCH_END;
+}
+
+void Solver::printInstantiations(std::ostream& out) const
+{
+  CVC4_API_SOLVER_TRY_CATCH_BEGIN;
+  CVC4::ExprManagerScope exmgrs(*(d_exprMgr.get()));
+  d_smtEngine->printInstantiations(out);
+  CVC4_API_SOLVER_TRY_CATCH_END;
+}
+
 /**
  *  ( push <numeral> )
  */
index 679f397507b2f25b9ef531dbb10882d9034031a8..5c6b01d5928c1eb7f92808ca03904e8ac61dbe31 100644 (file)
@@ -3094,6 +3094,48 @@ class CVC4_PUBLIC Solver
    */
   std::vector<Term> getValue(const std::vector<Term>& terms) const;
 
+  /**
+   * Do quantifier elimination.
+   * SMT-LIB: ( get-qe <q> )
+   * Requires a logic that supports quantifier elimination. Currently, the only
+   * logics supported by quantifier elimination is LRA and LIA.
+   * @param q a quantified formula of the form:
+   *   Q x1...xn. P( x1...xn, y1...yn )
+   * where P( x1...xn, y1...yn ) is a quantifier-free formula
+   * @return a formula ret such that, given the current set of formulas A
+   * asserted to this solver:
+   *   - ( A ^ q ) and ( A ^ ret ) are equivalent
+   *   - ret is quantifier-free formula containing only free variables in
+   *     y1...yn.
+   */
+  Term getQuantifierElimination(api::Term q) const;
+
+  /**
+   * Do partial quantifier elimination, which can be used for incrementally
+   * computing the result of a quantifier elimination.
+   * SMT-LIB: ( get-qe-disjunct <q> )
+   * Requires a logic that supports quantifier elimination. Currently, the only
+   * logics supported by quantifier elimination is LRA and LIA.
+   * @param q a quantified formula of the form:
+   *   Q x1...xn. P( x1...xn, y1...yn )
+   * where P( x1...xn, y1...yn ) is a quantifier-free formula
+   * @return a formula ret such that, given the current set of formulas A
+   * asserted to this solver:
+   *   - (A ^ q) => (A ^ ret) if Q is forall or (A ^ ret) => (A ^ q) if Q is
+   *     exists,
+   *   - ret is quantifier-free formula containing only free variables in
+   *     y1...yn,
+   *   - If Q is exists, let A^Q_n be the formula
+   *       A ^ ~ret^Q_1 ^ ... ^ ~ret^Q_n
+   *     where for each i=1,...n, formula ret^Q_i is the result of calling
+   *     getQuantifierEliminationDisjunct for q with the set of assertions
+   *     A^Q_{i-1}. Similarly, if Q is forall, then let A^Q_n be
+   *       A ^ ret^Q_1 ^ ... ^ ret^Q_n
+   *     where ret^Q_i is the same as above. In either case, we have
+   *     that ret^Q_j will eventually be true or false, for some finite j.
+   */
+  Term getQuantifierEliminationDisjunct(api::Term q) const;
+
   /**
    * When using separation logic, obtain the term for the heap.
    * @return The term for the heap
@@ -3168,6 +3210,30 @@ class CVC4_PUBLIC Solver
    */
   void printModel(std::ostream& out) const;
 
+  /**
+   * Block the current model. Can be called only if immediately preceded by a
+   * SAT or INVALID query.
+   * SMT-LIB: ( block-model )
+   * Requires enabling 'produce-models' option and setting 'block-models' option
+   * to a mode other than "none".
+   */
+  void blockModel() const;
+
+  /**
+   * Block the current model values of (at least) the values in terms. Can be
+   * called only if immediately preceded by a SAT or NOT_ENTAILED query.
+   * SMT-LIB: ( block-model-values ( <terms>+ ) )
+   * Requires enabling 'produce-models' option and setting 'block-models' option
+   * to a mode other than "none".
+   */
+  void blockModelValues(const std::vector<Term>& terms) const;
+
+  /**
+   * Print all instantiations made by the quantifiers module.
+   * @param out the output stream
+   */
+  void printInstantiations(std::ostream& out) const;
+
   /**
    * Push (a) level(s) to the assertion stack.
    * SMT-LIB: ( push <numeral> )
index 6f84a9959484604ff1658faf9f68dd28fe6afd08..913a4a993519f6ba7a7f40130b5356b1329baf2f 100644 (file)
@@ -120,9 +120,18 @@ enum CVC4_PUBLIC Kind : int32_t
 #if 0
   /* Skolem variable (internal only) */
   SKOLEM,
-  /* Symbolic expression (any arity) */
-  SEXPR,
 #endif
+  /*
+   * Symbolic expression.
+   * Parameters: n > 0
+   *   -[1]..[n]: terms
+   * Create with:
+   *   mkTerm(Kind kind, Term child)
+   *   mkTerm(Kind kind, Term child1, Term child2)
+   *   mkTerm(Kind kind, Term child1, Term child2, Term child3)
+   *   mkTerm(Kind kind, const std::vector<Term>& children)
+   */
+  SEXPR,
   /**
    * Lambda expression.
    * Parameters: 2
index 5edbe51e04e4b07db5332b58bd9ea1640513cae1..73553a31cef312c809aca9d29fa39268b50fcd95 100644 (file)
@@ -282,7 +282,7 @@ void AssertCommand::invoke(api::Solver* solver)
 {
   try
   {
-    solver->getSmtEngine()->assertFormula(d_term.getExpr(), d_inUnsatCore);
+    solver->getSmtEngine()->assertFormula(d_term.getNode(), d_inUnsatCore);
     d_commandStatus = CommandSuccess::instance();
   }
   catch (UnsafeInterruptException& e)
@@ -1594,9 +1594,16 @@ ExpandDefinitionsCommand::ExpandDefinitionsCommand(api::Term term)
 api::Term ExpandDefinitionsCommand::getTerm() const { return d_term; }
 void ExpandDefinitionsCommand::invoke(api::Solver* solver)
 {
-  Node t = d_term.getNode();
-  d_result = api::Term(solver, solver->getSmtEngine()->expandDefinitions(t));
-  d_commandStatus = CommandSuccess::instance();
+  try
+  {
+    d_result = api::Term(
+        solver, solver->getSmtEngine()->expandDefinitions(d_term.getNode()));
+    d_commandStatus = CommandSuccess::instance();
+  }
+  catch (exception& e)
+  {
+    d_commandStatus = new CommandFailure(e.what());
+  }
 }
 
 api::Term ExpandDefinitionsCommand::getResult() const { return d_result; }
@@ -1659,29 +1666,23 @@ void GetValueCommand::invoke(api::Solver* solver)
 {
   try
   {
-    NodeManager* nm = solver->getNodeManager();
-    smt::SmtScope scope(solver->getSmtEngine());
-    std::vector<Node> result =
-        api::termVectorToNodes(solver->getValue(d_terms));
+    std::vector<api::Term> result = solver->getValue(d_terms);
     Assert(result.size() == d_terms.size());
     for (int i = 0, size = d_terms.size(); i < size; i++)
     {
-      api::Term t = d_terms[i];
-      Node tNode = t.getNode();
-      Node request = options::expandDefinitions()
-                         ? solver->getSmtEngine()->expandDefinitions(tNode)
-                         : tNode;
-      Node value = result[i];
-      if (value.getType().isInteger() && request.getType() == nm->realType())
+      api::Term request = d_terms[i];
+      api::Term value = result[i];
+      if (value.getSort().isInteger()
+          && request.getSort() == solver->getRealSort())
       {
         // Need to wrap in division-by-one so that output printers know this
         // is an integer-looking constant that really should be output as
         // a rational.  Necessary for SMT-LIB standards compliance.
-        value = nm->mkNode(kind::DIVISION, value, nm->mkConst(Rational(1)));
+        value = solver->mkTerm(api::DIVISION, value, solver->mkReal(1));
       }
-      result[i] = nm->mkNode(kind::SEXPR, request, value);
+      result[i] = solver->mkTerm(api::SEXPR, request, value);
     }
-    d_result = api::Term(solver, nm->mkNode(kind::SEXPR, result));
+    d_result = solver->mkTerm(api::SEXPR, result);
     d_commandStatus = CommandSuccess::instance();
   }
   catch (api::CVC4ApiRecoverableException& e)
@@ -1873,10 +1874,10 @@ void BlockModelCommand::invoke(api::Solver* solver)
 {
   try
   {
-    solver->getSmtEngine()->blockModel();
+    solver->blockModel();
     d_commandStatus = CommandSuccess::instance();
   }
-  catch (RecoverableModalException& e)
+  catch (api::CVC4ApiRecoverableException& e)
   {
     d_commandStatus = new CommandRecoverableFailure(e.what());
   }
@@ -1928,7 +1929,7 @@ void BlockModelValuesCommand::invoke(api::Solver* solver)
 {
   try
   {
-    solver->getSmtEngine()->blockModelValues(api::termVectorToExprs(d_terms));
+    solver->blockModelValues(d_terms);
     d_commandStatus = CommandSuccess::instance();
   }
   catch (RecoverableModalException& e)
@@ -1997,12 +1998,12 @@ void GetProofCommand::toStream(std::ostream& out,
 /* class GetInstantiationsCommand                                             */
 /* -------------------------------------------------------------------------- */
 
-GetInstantiationsCommand::GetInstantiationsCommand() : d_smtEngine(nullptr) {}
+GetInstantiationsCommand::GetInstantiationsCommand() : d_solver(nullptr) {}
 void GetInstantiationsCommand::invoke(api::Solver* solver)
 {
   try
   {
-    d_smtEngine = solver->getSmtEngine();
+    d_solver = solver;
     d_commandStatus = CommandSuccess::instance();
   }
   catch (exception& e)
@@ -2020,7 +2021,7 @@ void GetInstantiationsCommand::printResult(std::ostream& out,
   }
   else
   {
-    d_smtEngine->printInstantiations(out);
+    d_solver->printInstantiations(out);
   }
 }
 
@@ -2028,7 +2029,7 @@ Command* GetInstantiationsCommand::clone() const
 {
   GetInstantiationsCommand* c = new GetInstantiationsCommand();
   // c->d_result = d_result;
-  c->d_smtEngine = d_smtEngine;
+  c->d_solver = d_solver;
   return c;
 }
 
@@ -2301,9 +2302,14 @@ void GetQuantifierEliminationCommand::invoke(api::Solver* solver)
 {
   try
   {
-    d_result = api::Term(solver,
-                         solver->getSmtEngine()->getQuantifierElimination(
-                             d_term.getNode(), d_doFull));
+    if (d_doFull)
+    {
+      d_result = solver->getQuantifierElimination(d_term);
+    }
+    else
+    {
+      d_result = solver->getQuantifierEliminationDisjunct(d_term);
+    }
     d_commandStatus = CommandSuccess::instance();
   }
   catch (exception& e)
index 81a736407dde3faf50e1e5b5228be3734ffcd40f..b823b57302da0e51853003bdff45f192b75d74a4 100644 (file)
@@ -1070,7 +1070,7 @@ class CVC4_PUBLIC GetInstantiationsCommand : public Command
       OutputLanguage language = language::output::LANG_AUTO) const override;
 
  protected:
-  SmtEngine* d_smtEngine;
+  api::Solver* d_solver;
 }; /* class GetInstantiationsCommand */
 
 class CVC4_PUBLIC GetSynthSolutionCommand : public Command
index 25643200fcf82e68955bda965e25f6bfefd6da42..ff45ce0ceb8f2de05bd595390af6943c3f0f4c86 100644 (file)
@@ -1444,9 +1444,6 @@ Result SmtEngine::blockModelValues(const std::vector<Expr>& exprs)
 
   finishInit();
 
-  PrettyCheckArgument(
-      !exprs.empty(),
-      "block model values must be called on non-empty set of terms");
   if (Dump.isOn("benchmark"))
   {
     getOutputManager().getPrinter().toStreamCmdBlockModelValues(
index 62531daf7ae8919d288ffe3439f574e376643dfb..aa4289ef30506ab48012d6e0646066b7f99142a8 100644 (file)
@@ -115,6 +115,8 @@ class SolverBlack : public CxxTest::TestSuite
   void testGetValue1();
   void testGetValue2();
   void testGetValue3();
+  void testGetQuantifierElimination();
+  void testGetQuantifierEliminationDisjunct();
   void testGetSeparationHeapTerm1();
   void testGetSeparationHeapTerm2();
   void testGetSeparationHeapTerm3();
@@ -141,6 +143,16 @@ class SolverBlack : public CxxTest::TestSuite
   void testPrintModel2();
   void testPrintModel3();
 
+  void testBlockModel1();
+  void testBlockModel2();
+  void testBlockModel3();
+  void testBlockModel4();
+  void testBlockModelValues1();
+  void testBlockModelValues2();
+  void testBlockModelValues3();
+  void testBlockModelValues4();
+  void testBlockModelValues5();
+
   void testSimplify();
 
   void testAssertFormula();
@@ -1613,6 +1625,36 @@ void SolverBlack::testGetValue3()
   TS_ASSERT_THROWS(slv.getValue(x), CVC4ApiException&);
 }
 
+void SolverBlack::testGetQuantifierElimination()
+{
+  Term x = d_solver->mkVar(d_solver->getBooleanSort(), "x");
+  Term forall =
+      d_solver->mkTerm(FORALL,
+                       d_solver->mkTerm(BOUND_VAR_LIST, x),
+                       d_solver->mkTerm(OR, x, d_solver->mkTerm(NOT, x)));
+  TS_ASSERT_THROWS(d_solver->getQuantifierElimination(Term()),
+                   CVC4ApiException&);
+  TS_ASSERT_THROWS(
+      d_solver->getQuantifierElimination(Solver().mkBoolean(false)),
+      CVC4ApiException&);
+  TS_ASSERT_THROWS_NOTHING(d_solver->getQuantifierElimination(forall));
+}
+
+void SolverBlack::testGetQuantifierEliminationDisjunct()
+{
+  Term x = d_solver->mkVar(d_solver->getBooleanSort(), "x");
+  Term forall =
+      d_solver->mkTerm(FORALL,
+                       d_solver->mkTerm(BOUND_VAR_LIST, x),
+                       d_solver->mkTerm(OR, x, d_solver->mkTerm(NOT, x)));
+  TS_ASSERT_THROWS(d_solver->getQuantifierEliminationDisjunct(Term()),
+                   CVC4ApiException&);
+  TS_ASSERT_THROWS(
+      d_solver->getQuantifierEliminationDisjunct(Solver().mkBoolean(false)),
+      CVC4ApiException&);
+  TS_ASSERT_THROWS_NOTHING(d_solver->getQuantifierEliminationDisjunct(forall));
+}
+
 namespace {
 /**
  * Helper function for testGetSeparation{Heap,Nil}TermX. Asserts and checks
@@ -1793,6 +1835,93 @@ void SolverBlack::testPrintModel3()
   TS_ASSERT_THROWS_NOTHING(d_solver->printModel(std::cout));
 }
 
+void SolverBlack::testBlockModel1()
+{
+  d_solver->setOption("produce-models", "true");
+  Term x = d_solver->mkConst(d_solver->getBooleanSort(), "x");
+  d_solver->assertFormula(x.eqTerm(x));
+  d_solver->checkSat();
+  TS_ASSERT_THROWS(d_solver->blockModel(), CVC4ApiException&);
+}
+
+void SolverBlack::testBlockModel2()
+{
+  d_solver->setOption("block-models", "literals");
+  Term x = d_solver->mkConst(d_solver->getBooleanSort(), "x");
+  d_solver->assertFormula(x.eqTerm(x));
+  d_solver->checkSat();
+  TS_ASSERT_THROWS(d_solver->blockModel(), CVC4ApiException&);
+}
+
+void SolverBlack::testBlockModel3()
+{
+  d_solver->setOption("produce-models", "true");
+  d_solver->setOption("block-models", "literals");
+  Term x = d_solver->mkConst(d_solver->getBooleanSort(), "x");
+  d_solver->assertFormula(x.eqTerm(x));
+  TS_ASSERT_THROWS(d_solver->blockModel(), CVC4ApiException&);
+}
+
+void SolverBlack::testBlockModel4()
+{
+  d_solver->setOption("produce-models", "true");
+  d_solver->setOption("block-models", "literals");
+  Term x = d_solver->mkConst(d_solver->getBooleanSort(), "x");
+  d_solver->assertFormula(x.eqTerm(x));
+  d_solver->checkSat();
+  TS_ASSERT_THROWS_NOTHING(d_solver->blockModel());
+}
+
+void SolverBlack::testBlockModelValues1()
+{
+  d_solver->setOption("produce-models", "true");
+  d_solver->setOption("block-models", "literals");
+  Term x = d_solver->mkConst(d_solver->getBooleanSort(), "x");
+  d_solver->assertFormula(x.eqTerm(x));
+  d_solver->checkSat();
+  TS_ASSERT_THROWS(d_solver->blockModelValues({}), CVC4ApiException&);
+  TS_ASSERT_THROWS(d_solver->blockModelValues({Term()}), CVC4ApiException&);
+  TS_ASSERT_THROWS(d_solver->blockModelValues({Solver().mkBoolean(false)}),
+                   CVC4ApiException&);
+}
+
+void SolverBlack::testBlockModelValues2()
+{
+  d_solver->setOption("produce-models", "true");
+  Term x = d_solver->mkConst(d_solver->getBooleanSort(), "x");
+  d_solver->assertFormula(x.eqTerm(x));
+  d_solver->checkSat();
+  TS_ASSERT_THROWS(d_solver->blockModelValues({x}), CVC4ApiException&);
+}
+
+void SolverBlack::testBlockModelValues3()
+{
+  d_solver->setOption("block-models", "literals");
+  Term x = d_solver->mkConst(d_solver->getBooleanSort(), "x");
+  d_solver->assertFormula(x.eqTerm(x));
+  d_solver->checkSat();
+  TS_ASSERT_THROWS(d_solver->blockModelValues({x}), CVC4ApiException&);
+}
+
+void SolverBlack::testBlockModelValues4()
+{
+  d_solver->setOption("produce-models", "true");
+  d_solver->setOption("block-models", "literals");
+  Term x = d_solver->mkConst(d_solver->getBooleanSort(), "x");
+  d_solver->assertFormula(x.eqTerm(x));
+  TS_ASSERT_THROWS(d_solver->blockModelValues({x}), CVC4ApiException&);
+}
+
+void SolverBlack::testBlockModelValues5()
+{
+  d_solver->setOption("produce-models", "true");
+  d_solver->setOption("block-models", "literals");
+  Term x = d_solver->mkConst(d_solver->getBooleanSort(), "x");
+  d_solver->assertFormula(x.eqTerm(x));
+  d_solver->checkSat();
+  TS_ASSERT_THROWS_NOTHING(d_solver->blockModelValues({x}));
+}
+
 void SolverBlack::testSetInfo()
 {
   TS_ASSERT_THROWS(d_solver->setInfo("cvc4-lagic", "QF_BV"), CVC4ApiException&);