Support get-abduct-next (#7850)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Tue, 21 Dec 2021 18:21:40 +0000 (12:21 -0600)
committerGitHub <noreply@github.com>
Tue, 21 Dec 2021 18:21:40 +0000 (18:21 +0000)
Adds support for incremental abduction, adds regression for using push/pop in combination with get-abduct, and a use of get-abduct-next.

Adds this method to C++, java API.

Interpolation/abduction not yet supported in Python API, I'm waiting for @yoni206 to add this; getAbductNext will be added to Python API in followup PR.

22 files changed:
src/api/cpp/cvc5.cpp
src/api/cpp/cvc5.h
src/api/java/io/github/cvc5/api/Solver.java
src/api/java/jni/solver.cpp
src/expr/symbol_manager.cpp
src/expr/symbol_manager.h
src/parser/smt2/Smt2.g
src/printer/printer.cpp
src/printer/printer.h
src/printer/smt2/smt2_printer.cpp
src/printer/smt2/smt2_printer.h
src/smt/abduction_solver.cpp
src/smt/abduction_solver.h
src/smt/command.cpp
src/smt/command.h
src/smt/solver_engine.cpp
src/smt/solver_engine.h
test/regress/CMakeLists.txt
test/regress/regress1/abduction/simple-incremental-push-pop.smt2 [new file with mode: 0644]
test/regress/regress1/abduction/simple-incremental.smt2 [new file with mode: 0644]
test/unit/api/cpp/solver_black.cpp
test/unit/api/java/SolverTest.java

index bdaef769e0cc1dee97fce548ad01b68b88afa411..4c43dbd5b343bc196d32a0ea2253d855973a8897 100644 (file)
@@ -7618,7 +7618,8 @@ bool Solver::getAbduct(const Term& conj, Term& output) const
       << "Cannot get abduct unless abducts are enabled (try --produce-abducts)";
   //////// all checks before this line
   Node result;
-  bool success = d_slv->getAbduct(*conj.d_node, result);
+  TypeNode nullType;
+  bool success = d_slv->getAbduct(*conj.d_node, nullType, result);
   if (success)
   {
     output = Term(this, result);
@@ -7647,6 +7648,27 @@ bool Solver::getAbduct(const Term& conj, Grammar& grammar, Term& output) const
   CVC5_API_TRY_CATCH_END;
 }
 
+bool Solver::getAbductNext(Term& output) const
+{
+  CVC5_API_TRY_CATCH_BEGIN;
+  CVC5_API_CHECK(d_slv->getOptions().smt.produceAbducts)
+      << "Cannot get next abduct unless abducts are enabled (try "
+         "--produce-abducts)";
+  CVC5_API_CHECK(d_slv->getOptions().base.incrementalSolving)
+      << "Cannot get next abduct when not solving incrementally (try "
+         "--incremental)";
+  //////// all checks before this line
+  Node result;
+  bool success = d_slv->getAbductNext(result);
+  if (success)
+  {
+    output = Term(this, result);
+  }
+  return success;
+  ////////
+  CVC5_API_TRY_CATCH_END;
+}
+
 void Solver::blockModel() const
 {
   CVC5_API_TRY_CATCH_BEGIN;
index 3b22f0c6f205a774f1af5012246b19a097198169..7e4dc3cc8c7fc91e127a7a576f978af81cf96a55 100644 (file)
@@ -4507,6 +4507,30 @@ class CVC5_EXPORT Solver
    */
   bool getAbduct(const Term& conj, Grammar& grammar, Term& output) const;
 
+  /**
+   * Get the next abduct. Can only be called immediately after a successful
+   * call to get-abduct or get-abduct-next. Is guaranteed to produce a
+   * syntactically different abduct wrt the last returned abduct if successful.
+   *
+   * SMT-LIB:
+   *
+   * \verbatim embed:rst:leading-asterisk
+   * .. code:: smtlib
+   *
+   *     (get-abduct-next)
+   *
+   * Requires to enable incremental mode, and option
+   * :ref:`produce-abducts <lbl-option-produce-abducts>`.
+   * \endverbatim
+   *
+   * @param output a term C such that @f$(A \wedge C)@f$ is satisfiable, and
+   *        @f$(A \wedge \neg B \wedge C)@f$ is unsatisfiable, where @f$A@f$ is
+   *        the current set of assertions and @f$B@f$ is given in the input by
+   *        the last call to getAbduct.
+   * @return true if it gets abduct @f$C@f$ successfully, false otherwise
+   */
+  bool getAbductNext(Term& output) const;
+
   /**
    * Block the current model. Can be called only if immediately preceded by a
    * SAT or INVALID query.
index 64bef09c90a8a13b604ef0fd273fb51225b9f76d..32ddf069166edddb0dfacc757aef2efb8b6d3fdc 100644 (file)
@@ -2258,6 +2258,26 @@ public class Solver implements IPointer, AutoCloseable
   private native boolean getAbduct(
       long pointer, long conjPointer, long grammarPointer, long outputPointer);
 
+  /**
+   * Get the next abduct. Can only be called immediately after a successful
+   * call to get-abduct or get-abduct-next. Is guaranteed to produce a
+   * syntactically different abduct wrt the last returned abduct if successful.
+   * SMT-LIB:
+   * {@code
+   * ( get-abduct-next )
+   * }
+   * Requires enabling incremental mode and option 'produce-abducts'
+   * @param output a term C such that A^C is satisfiable, and A^~B^C is
+   *        unsatisfiable, where A is the current set of assertions and B is
+   *        given in the input by conj in the last call to getAbduct.
+   * @return true if it gets C successfully, false otherwise
+   */
+  public boolean getAbductNext(Term output) {
+    return getAbductNext(pointer, output.getPointer());
+  }
+
+  private native boolean getAbductNext(long pointer, long outputPointer);
+
   /**
    * Block the current model. Can be called only if immediately preceded by a
    * SAT or INVALID query.
index 73e481b49756277295d820b49046ac8c6f00e1b6..524c527964f0ad80c016840fd45dd30708aecff3 100644 (file)
@@ -2281,6 +2281,21 @@ Java_io_github_cvc5_api_Solver_getAbduct__JJJJ(JNIEnv* env,
   CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, 0);
 }
 
+/*
+ * Class:     io_github_cvc5_api_Solver
+ * Method:    getAbductNext
+ * Signature: (JJ)Z
+ */
+JNIEXPORT jboolean JNICALL Java_io_github_cvc5_api_Solver_getAbductNext
+    (JNIEnv * env, jobject, jlong pointer, jlong outputPointer)
+{
+  CVC5_JAVA_API_TRY_CATCH_BEGIN;
+  Solver* solver = reinterpret_cast<Solver*>(pointer);
+  Term* output = reinterpret_cast<Term*>(outputPointer);
+  return (jboolean)solver->getAbductNext(*output);
+  CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, 0);
+}
+
 /*
  * Class:     io_github_cvc5_api_Solver
  * Method:    blockModel
index 687f4963ff0f75f0fc92ef476489becfed03dc5b..240e043c626d1878de2206e720caae37266bf51b 100644 (file)
@@ -41,7 +41,8 @@ class SymbolManager::Implementation
         d_declareSorts(&d_context),
         d_declareTerms(&d_context),
         d_funToSynth(&d_context),
-        d_hasPushedScope(&d_context, false)
+        d_hasPushedScope(&d_context, false),
+        d_lastSynthName(&d_context)
   {
     // use an outermost push, to be able to clear all definitions
     d_context.push();
@@ -84,6 +85,10 @@ class SymbolManager::Implementation
   void popScope();
   /** Have we pushed a scope (e.g. let or quantifier) in the current context? */
   bool hasPushedScope() const;
+  /** Set the last abduct-to-synthesize had the given name. */
+  void setLastSynthName(const std::string& name);
+  /** Get the name of the last abduct-to-synthesize */
+  const std::string& getLastSynthName() const;
 
  private:
   /** The context manager for the scope maps. */
@@ -102,6 +107,8 @@ class SymbolManager::Implementation
    * Have we pushed a scope (e.g. a let or quantifier) in the current context?
    */
   CDO<bool> d_hasPushedScope;
+  /** The last abduct or interpolant to synthesize name */
+  CDO<std::string> d_lastSynthName;
 };
 
 NamingResult SymbolManager::Implementation::setExpressionName(
@@ -256,6 +263,16 @@ bool SymbolManager::Implementation::hasPushedScope() const
   return d_hasPushedScope.get();
 }
 
+void SymbolManager::Implementation::setLastSynthName(const std::string& name)
+{
+  d_lastSynthName = name;
+}
+
+const std::string& SymbolManager::Implementation::getLastSynthName() const
+{
+  return d_lastSynthName.get();
+}
+
 void SymbolManager::Implementation::reset()
 {
   Trace("sym-manager") << "SymbolManager: reset" << std::endl;
@@ -388,6 +405,16 @@ bool SymbolManager::getGlobalDeclarations() const
   return d_globalDeclarations;
 }
 
+void SymbolManager::setLastSynthName(const std::string& name)
+{
+  d_implementation->setLastSynthName(name);
+}
+
+const std::string& SymbolManager::getLastSynthName() const
+{
+  return d_implementation->getLastSynthName();
+}
+
 void SymbolManager::reset()
 {
   d_symtabAllocated.reset();
index d8b7e23bd47ab7eb225c6a237e0f63e08d485b18..e3084904fdf8cbb41c4be0e437831d99c60e2305 100644 (file)
@@ -160,6 +160,17 @@ class CVC5_EXPORT SymbolManager
   void setGlobalDeclarations(bool flag);
   /** Get global declarations flag. */
   bool getGlobalDeclarations() const;
+  /**
+   * Set the last abduct or interpolant to synthesize had the given name. This
+   * is required since e.g. get-abduct-next must know the name of the
+   * abduct-to-synthesize to print its result. For example, the sequence:
+   *   (get-abduct A <conjecture>)
+   *   (get-abduct-next)
+   * The latter command must know the symbol "A".
+   */
+  void setLastSynthName(const std::string& name);
+  /** Get the name of the last abduct or interpolant to synthesize */
+  const std::string& getLastSynthName() const;
 
  private:
   /** The API Solver object. */
index 7f95368b026f97e8c566819938134a6ea814b6dc..31171b2788ee025008ff788fbc673a8a316281f9 100644 (file)
@@ -1007,6 +1007,10 @@ extendedCommand[std::unique_ptr<cvc5::Command>* cmd]
     {
       cmd->reset(new GetAbductCommand(name, e, g));
     }
+  | GET_ABDUCT_NEXT_TOK {
+      PARSER_STATE->checkThatLogicIsSet();
+      cmd->reset(new GetAbductNextCommand);
+    }
   | GET_INTERPOL_TOK {
       PARSER_STATE->checkThatLogicIsSet();
     }
@@ -2238,6 +2242,7 @@ INCLUDE_TOK : 'include';
 GET_QE_TOK : 'get-qe';
 GET_QE_DISJUNCT_TOK : 'get-qe-disjunct';
 GET_ABDUCT_TOK : 'get-abduct';
+GET_ABDUCT_NEXT_TOK : 'get-abduct-next';
 GET_INTERPOL_TOK : 'get-interpol';
 DECLARE_HEAP : 'declare-heap';
 DECLARE_POOL : 'declare-pool';
index d1c2c58a5d2dd95be1360e0025db8c58878c1fb8..a4ff10014640e6899f1415a8b3e5fffe0deeddc5 100644 (file)
@@ -413,6 +413,11 @@ void Printer::toStreamCmdGetAbduct(std::ostream& out,
   printUnknownCommand(out, "get-abduct");
 }
 
+void Printer::toStreamCmdGetAbductNext(std::ostream& out) const
+{
+  printUnknownCommand(out, "get-abduct-next");
+}
+
 void Printer::toStreamCmdGetQuantifierElimination(std::ostream& out,
                                                   Node n,
                                                   bool doFull) const
index d55723443042ff320c245a4a7e68011766b57dc4..74eb8970b1c6e333e87aa44600eb711e9fc511e7 100644 (file)
@@ -206,6 +206,9 @@ class Printer
                                     Node conj,
                                     TypeNode sygusType) const;
 
+  /** Print get-abduct-next command */
+  virtual void toStreamCmdGetAbductNext(std::ostream& out) const;
+
   /** Print get-quantifier-elimination command */
   virtual void toStreamCmdGetQuantifierElimination(std::ostream& out,
                                                    Node n,
index 2a20bba36eb52618c9a8ac9e56bf28ce6b1b47c2..e6c99c7070e8fd4fa32ffd419ffc65ec1a497edd 100644 (file)
@@ -1934,6 +1934,11 @@ void Smt2Printer::toStreamCmdGetAbduct(std::ostream& out,
   out << ')' << std::endl;
 }
 
+void Smt2Printer::toStreamCmdGetAbductNext(std::ostream& out) const
+{
+  out << "(get-abduct-next)" << std::endl;
+}
+
 void Smt2Printer::toStreamCmdGetQuantifierElimination(std::ostream& out,
                                                       Node n,
                                                       bool doFull) const
index b97ad2ffb1c928bd8ed5644fc8cd0092af7636c3..aad3948feb552b721de3eec6869497919cb8f97d 100644 (file)
@@ -180,6 +180,9 @@ class Smt2Printer : public cvc5::Printer
                             Node conj,
                             TypeNode sygusType) const override;
 
+  /** Print get-abduct-next command */
+  void toStreamCmdGetAbductNext(std::ostream& out) const override;
+
   /** Print get-quantifier-elimination command */
   void toStreamCmdGetQuantifierElimination(std::ostream& out,
                                            Node n,
index f17e768c9ac5df05fede01254c2e3ee5f42cfb52..3e5912837e1b422c82862c2853f7b12cdc22be07 100644 (file)
@@ -73,19 +73,23 @@ bool AbductionSolver::getAbduct(const std::vector<Node>& axioms,
   d_subsolver->setLogic(l);
   // assert the abduction query
   d_subsolver->assertFormula(aconj);
-  return getAbductInternal(axioms, abd);
+  d_axioms = axioms;
+  return getAbductInternal(abd);
 }
 
-bool AbductionSolver::getAbduct(const std::vector<Node>& axioms,
-                                const Node& goal,
-                                Node& abd)
+bool AbductionSolver::getAbductNext(Node& abd)
 {
-  TypeNode grammarType;
-  return getAbduct(axioms, goal, grammarType, abd);
+  // Since we are using the subsolver's check-sat interface directly, we
+  // simply call getAbductInternal again here. We assert that the subsolver
+  // is already initialized, which must be the case or else we are not in the
+  // proper SMT mode to make this call. Due to the default behavior of
+  // subsolvers having synthesis conjectures, this is guaranteed to produce
+  // a new solution.
+  Assert(d_subsolver != nullptr);
+  return getAbductInternal(abd);
 }
 
-bool AbductionSolver::getAbductInternal(const std::vector<Node>& axioms,
-                                        Node& abd)
+bool AbductionSolver::getAbductInternal(Node& abd)
 {
   // should have initialized the subsolver by now
   Assert(d_subsolver != nullptr);
@@ -133,7 +137,7 @@ bool AbductionSolver::getAbductInternal(const std::vector<Node>& axioms,
       // if check abducts option is set, we check the correctness
       if (options().smt.checkAbducts)
       {
-        checkAbduct(axioms, abd);
+        checkAbduct(abd);
       }
       return true;
     }
@@ -144,13 +148,13 @@ bool AbductionSolver::getAbductInternal(const std::vector<Node>& axioms,
   return false;
 }
 
-void AbductionSolver::checkAbduct(const std::vector<Node>& axioms, Node a)
+void AbductionSolver::checkAbduct(Node a)
 {
   Assert(a.getType().isBoolean());
   Trace("check-abduct") << "SolverEngine::checkAbduct: get expanded assertions"
                         << std::endl;
 
-  std::vector<Node> asserts(axioms.begin(), axioms.end());
+  std::vector<Node> asserts(d_axioms.begin(), d_axioms.end());
   asserts.push_back(a);
 
   // two checks: first, consistent with assertions, second, implies negated goal
index 32671341847c960ae64770acca0d60ac4115cf7b..309b6c971509f590abc1014abbe288015b3482f1 100644 (file)
@@ -53,6 +53,7 @@ class AbductionSolver : protected EnvObj
    * @param abd This argument is updated to contain the solution C to the
    * abduction problem. Notice that this is a formula whose free symbols
    * are contained in goal + the parent's current assertion stack.
+   * @return true if the abduct was successfully computed
    *
    * This method invokes a separate copy of the SMT engine for solving the
    * corresponding sygus problem for generating such a solution.
@@ -63,23 +64,14 @@ class AbductionSolver : protected EnvObj
                  Node& abd);
 
   /**
-   * Same as above, but without user-provided grammar restrictions. A default
-   * grammar is chosen internally using the sygus grammar constructor utility.
-   */
-  bool getAbduct(const std::vector<Node>& axioms, const Node& goal, Node& abd);
-
-  /**
-   * Check that a solution to an abduction conjecture is indeed a solution.
-   *
-   * The check is made by determining that the assertions conjoined with the
-   * solution to the abduction problem (a) is SAT, and the assertions conjoined
-   * with the abduct and the goal is UNSAT. If these criteria are not met, an
-   * internal error is thrown.
-   *
-   * @param axioms The expanded assertions of the parent SMT engine
-   * @param a The abduct to check.
+   * Get the next abduct, return true if successful and store the result
+   * in abd if so.
+   * 
+   * @param abd This argument is updated to contain the solution C to the
+   * abduction problem.
+   * @return true if the abduct was successfully computed
    */
-  void checkAbduct(const std::vector<Node>& axioms, Node a);
+  bool getAbductNext(Node& abd);
 
  private:
   /**
@@ -91,7 +83,19 @@ class AbductionSolver : protected EnvObj
    * This method assumes d_subsolver has been initialized to do abduction
    * problems.
    */
-  bool getAbductInternal(const std::vector<Node>& axioms, Node& abd);
+  bool getAbductInternal(Node& abd);
+  /**
+   * Check that a solution to an abduction conjecture is indeed a solution.
+   *
+   * The check is made by determining that the assertions conjoined with the
+   * solution to the abduction problem (a) is SAT, and the assertions conjoined
+   * with the abduct and the goal is UNSAT. If these criteria are not met, an
+   * internal error is thrown. We use the expanded assertions of the parent SMT
+   * engine, which are stored in d_axioms.
+   *
+   * @param a The abduct to check.
+   */
+  void checkAbduct(Node a);
   /** The SMT engine subsolver
    *
    * This is a separate copy of the SMT engine which is used for making
@@ -119,6 +123,8 @@ class AbductionSolver : protected EnvObj
    * for. This is used for the get-abduct command.
    */
   Node d_sssf;
+  /** The list of axioms for the abduction query */
+  std::vector<Node> d_axioms;
 };
 
 }  // namespace smt
index ac393d568bcd18b27b9d845f8b597e59a466a698..8161dfb49acb9539cca8868c97d0215b7ceae7fd 100644 (file)
@@ -2101,6 +2101,9 @@ void GetAbductCommand::invoke(api::Solver* solver, SymbolManager* sm)
 {
   try
   {
+    // we must remember the name of the abduct, in case get-abduct-next is
+    // called later.
+    sm->setLastSynthName(d_name);
     if (d_sygus_grammar == nullptr)
     {
       d_resultStatus = solver->getAbduct(d_conj, d_result);
@@ -2158,6 +2161,72 @@ void GetAbductCommand::toStream(std::ostream& out,
       out, d_name, termToNode(d_conj), grammarToTypeNode(d_sygus_grammar));
 }
 
+/* -------------------------------------------------------------------------- */
+/* class GetAbductNextCommand */
+/* -------------------------------------------------------------------------- */
+
+GetAbductNextCommand::GetAbductNextCommand() : d_resultStatus(false) {}
+
+api::Term GetAbductNextCommand::getResult() const { return d_result; }
+
+void GetAbductNextCommand::invoke(api::Solver* solver, SymbolManager* sm)
+{
+  try
+  {
+    // Get the name of the abduct from the symbol manager
+    d_name = sm->getLastSynthName();
+    d_resultStatus = solver->getAbductNext(d_result);
+    d_commandStatus = CommandSuccess::instance();
+  }
+  catch (exception& e)
+  {
+    d_commandStatus = new CommandFailure(e.what());
+  }
+}
+
+void GetAbductNextCommand::printResult(std::ostream& out) const
+{
+  if (!ok())
+  {
+    this->Command::printResult(out);
+  }
+  else
+  {
+    options::ioutils::Scope scope(out);
+    options::ioutils::applyDagThresh(out, 0);
+    if (d_resultStatus)
+    {
+      out << "(define-fun " << d_name << " () Bool " << d_result << ")"
+          << std::endl;
+    }
+    else
+    {
+      out << "none" << std::endl;
+    }
+  }
+}
+
+Command* GetAbductNextCommand::clone() const
+{
+  GetAbductNextCommand* c = new GetAbductNextCommand;
+  c->d_result = d_result;
+  c->d_resultStatus = d_resultStatus;
+  return c;
+}
+
+std::string GetAbductNextCommand::getCommandName() const
+{
+  return "get-abduct-next";
+}
+
+void GetAbductNextCommand::toStream(std::ostream& out,
+                                    int toDepth,
+                                    size_t dag,
+                                    Language language) const
+{
+  Printer::getPrinter(language)->toStreamCmdGetAbductNext(out);
+}
+
 /* -------------------------------------------------------------------------- */
 /* class GetQuantifierEliminationCommand                                      */
 /* -------------------------------------------------------------------------- */
index 1a00c2adaceeb7c9fad28a7de8abd6f506b52374..68ff7ffab21eb968af477e00c44d0ac9be30e74e 100644 (file)
@@ -1108,6 +1108,34 @@ class CVC5_EXPORT GetAbductCommand : public Command
   api::Term d_result;
 }; /* class GetAbductCommand */
 
+/** The command (get-abduct-next) */
+class CVC5_EXPORT GetAbductNextCommand : public Command
+{
+ public:
+  GetAbductNextCommand();
+  /**
+   * Get the result of the query, which is the solution to the abduction query.
+   */
+  api::Term getResult() const;
+
+  void invoke(api::Solver* solver, SymbolManager* sm) override;
+  void printResult(std::ostream& out) const override;
+  Command* clone() const override;
+  std::string getCommandName() const override;
+  void toStream(std::ostream& out,
+                int toDepth = -1,
+                size_t dag = 1,
+                Language language = Language::LANG_AUTO) const override;
+
+ protected:
+  /** The name of the abduction predicate */
+  std::string d_name;
+  /** the return status of the command */
+  bool d_resultStatus;
+  /** the return expression of the command */
+  api::Term d_result;
+}; /* class GetAbductCommand */
+
 class CVC5_EXPORT GetQuantifierEliminationCommand : public Command
 {
  protected:
index 09814c3008acdedf107f21e9537de79ad9eecaad..2df3eafa1b20f001e6fb92dceb96cd6969a01945 100644 (file)
@@ -942,7 +942,7 @@ Result SolverEngine::checkSynth(bool isNext)
   {
     throw RecoverableModalException(
         "Cannot check-synth-next unless immediately preceded by a successful "
-        "call to check-synth.");
+        "call to check-synth(-next).");
   }
   Result r = d_sygusSolver->checkSynth(*d_asserts, isNext);
   d_state->notifyCheckSynthResult(r);
@@ -1661,16 +1661,26 @@ bool SolverEngine::getAbduct(const Node& conj,
   finishInit();
   std::vector<Node> axioms = getExpandedAssertions();
   bool success = d_abductSolver->getAbduct(axioms, conj, grammarType, abd);
-  // notify the state of whether the get-abduct call was successfuly, which
+  // notify the state of whether the get-abduct call was successful, which
   // impacts the SMT mode.
   d_state->notifyGetAbduct(success);
   return success;
 }
 
-bool SolverEngine::getAbduct(const Node& conj, Node& abd)
+bool SolverEngine::getAbductNext(Node& abd)
 {
-  TypeNode grammarType;
-  return getAbduct(conj, grammarType, abd);
+  SolverEngineScope smts(this);
+  finishInit();
+  if (d_state->getMode() != SmtMode::ABDUCT)
+  {
+    throw RecoverableModalException(
+        "Cannot get-abduct-next unless immediately preceded by a successful "
+        "call to get-abduct(-next).");
+  }
+  bool success = d_abductSolver->getAbductNext(abd);
+  // notify the state of whether the get-abduct-next call was successful
+  d_state->notifyGetAbduct(success);
+  return success;
 }
 
 void SolverEngine::getInstantiatedQuantifiedFormulas(std::vector<Node>& qs)
index ecfb521a01d176d944f9cb75c1eb6f85e3c1acce..165424594814b9397f6cd4d1a5b57d8cadcc804a 100644 (file)
@@ -658,8 +658,13 @@ class CVC5_EXPORT SolverEngine
    */
   bool getAbduct(const Node& conj, const TypeNode& grammarType, Node& abd);
 
-  /** Same as above, but without user-provided grammar restrictions */
-  bool getAbduct(const Node& conj, Node& abd);
+  /**
+   * Get next abduct. This can only be called immediately after a successful
+   * call to getAbduct or getAbductNext.
+   *
+   * Returns true if an abduct was found, and sets abd to the abduct.
+   */
+  bool getAbductNext(Node& abd);
 
   /**
    * Get list of quantified formulas that were instantiated on the last call
index 4a54e2fcb5961f83996923ef32e3e84c6776eac1..5c4cdcd4a078409e3cd5e2f8b76df989e318f3b8 100644 (file)
@@ -1534,23 +1534,25 @@ set(regress_0_tests
 # Regression level 1 tests
 
 set(regress_1_tests
+  regress1/abduction/abduction_1255.corecstrs.readable.smt2
+  regress1/abduction/abduction_streq.readable.smt2
+  regress1/abduction/abduction-no-pbe-sym-break.smt2
+  regress1/abduction/abduct-dt.smt2
   regress1/abduction/abd-real-const.smt2
-  regress1/abduction/sygus-abduct-test-user.smt2
-  regress1/abduction/issue5848-4.smt2
+  regress1/abduction/abd-simple-conj-4.smt2
   regress1/abduction/issue5848-2.smt2
+  regress1/abduction/issue5848-3-trivial-no-abduct.smt2
+  regress1/abduction/issue5848-4.smt2
   regress1/abduction/issue5848.smt2
   regress1/abduction/issue6605-1.smt2
-  regress1/abduction/abduct-dt.smt2
-  regress1/abduction/sygus-abduct-test-ccore.smt2
+  regress1/abduction/simple-incremental.smt2
+  regress1/abduction/simple-incremental-push-pop.smt2
+  regress1/abduction/sygus-abduct-ex1-grammar.smt2
   regress1/abduction/sygus-abduct-test.smt2
-  regress1/abduction/abd-simple-conj-4.smt2
-  regress1/abduction/abduction_streq.readable.smt2
-  regress1/abduction/yoni-true-sol.smt2
+  regress1/abduction/sygus-abduct-test-ccore.smt2
+  regress1/abduction/sygus-abduct-test-user.smt2
   regress1/abduction/uf-abduct.smt2
-  regress1/abduction/abduction_1255.corecstrs.readable.smt2
-  regress1/abduction/abduction-no-pbe-sym-break.smt2
-  regress1/abduction/issue5848-3-trivial-no-abduct.smt2
-  regress1/abduction/sygus-abduct-ex1-grammar.smt2
+  regress1/abduction/yoni-true-sol.smt2
   regress1/arith/arith-brab-test.smt2
   regress1/arith/arith-int-004.cvc.smt2
   regress1/arith/arith-int-011.cvc.smt2
diff --git a/test/regress/regress1/abduction/simple-incremental-push-pop.smt2 b/test/regress/regress1/abduction/simple-incremental-push-pop.smt2
new file mode 100644 (file)
index 0000000..2c22158
--- /dev/null
@@ -0,0 +1,15 @@
+; COMMAND-LINE: --produce-abducts
+; SCRUBBER: grep -v -E '(\(define-fun)'
+; EXIT: 0
+(set-logic QF_LIA)
+(set-option :produce-abducts true)
+(set-option :incremental true)
+(declare-fun x () Int)
+(declare-fun y () Int)
+(push)
+(assert (>= x 0))
+(get-abduct A (>= (+ x y) 2))
+(pop)
+
+(assert (<= x 0))
+(get-abduct A (<= (+ x y) 2))
diff --git a/test/regress/regress1/abduction/simple-incremental.smt2 b/test/regress/regress1/abduction/simple-incremental.smt2
new file mode 100644 (file)
index 0000000..50d2385
--- /dev/null
@@ -0,0 +1,12 @@
+; COMMAND-LINE: --produce-abducts
+; SCRUBBER: grep -v -E '(\(define-fun)'
+; EXIT: 0
+(set-logic QF_LIA)
+(set-option :produce-abducts true)
+(set-option :incremental true)
+(declare-fun x () Int)
+(declare-fun y () Int)
+(assert (>= x 0))
+(get-abduct A (>= (+ x y) 2))
+(get-abduct-next)
+(get-abduct-next)
index 960d4bb6977a9d4445e48bd598f0df4b1555d2e8..934e0da0da8d8d090ba0743698bb7418b1d51f2e 100644 (file)
@@ -1395,6 +1395,30 @@ TEST_F(TestApiBlackSolver, getAbduct2)
   ASSERT_THROW(d_solver.getAbduct(conj, output), CVC5ApiException);
 }
 
+TEST_F(TestApiBlackSolver, getAbductNext)
+{
+  d_solver.setLogic("QF_LIA");
+  d_solver.setOption("produce-abducts", "true");
+  d_solver.setOption("incremental", "true");
+
+  Sort intSort = d_solver.getIntegerSort();
+  Term zero = d_solver.mkInteger(0);
+  Term x = d_solver.mkConst(intSort, "x");
+  Term y = d_solver.mkConst(intSort, "y");
+
+  // Assumptions for abduction: x > 0
+  d_solver.assertFormula(d_solver.mkTerm(GT, x, zero));
+  // Conjecture for abduction: y > 0
+  Term conj = d_solver.mkTerm(GT, y, zero);
+  Term output;
+  // Call the abduction api, while the resulting abduct is the output
+  ASSERT_TRUE(d_solver.getAbduct(conj, output));
+  Term output2;
+  ASSERT_TRUE(d_solver.getAbductNext(output2));
+  // should produce a different output
+  ASSERT_TRUE(output != output2);
+}
+
 TEST_F(TestApiBlackSolver, getInterpolant)
 {
   d_solver.setLogic("QF_LIA");
index 128141bfa895737f01a8731969f69fc4c35693f6..3fb9ca50eb2b4a30b1fa76376dc2bdce2b281d0e 100644 (file)
@@ -1335,6 +1335,84 @@ class SolverTest
     assertThrows(CVC5ApiException.class, () -> d_solver.getInfo("asdf"));
   }
 
+  @Test void getAbduct() throws CVC5ApiException
+  {
+    d_solver.setLogic("QF_LIA");
+    d_solver.setOption("produce-abducts", "true");
+    d_solver.setOption("incremental", "false");
+
+    Sort intSort = d_solver.getIntegerSort();
+    Term zero = d_solver.mkInteger(0);
+    Term x = d_solver.mkConst(intSort, "x");
+    Term y = d_solver.mkConst(intSort, "y");
+
+    // Assumptions for abduction: x > 0
+    d_solver.assertFormula(d_solver.mkTerm(GT, x, zero));
+    // Conjecture for abduction: y > 0
+    Term conj = d_solver.mkTerm(GT, y, zero);
+    Term output = d_solver.getNullTerm();
+    // Call the abduction api, while the resulting abduct is the output
+    assertTrue(d_solver.getAbduct(conj, output));
+    // We expect the resulting output to be a boolean formula
+    assertTrue(!output.isNull() && output.getSort().isBoolean());
+
+    // try with a grammar, a simple grammar admitting true
+    Sort bsort = d_solver.getBooleanSort();
+    Term truen = d_solver.mkBoolean(true);
+    Term start = d_solver.mkVar(bsort);
+    Term output2  = d_solver.getNullTerm();
+    Grammar g = d_solver.mkSygusGrammar(new Term[] {}, new Term[] {start});
+    Term conj2 = d_solver.mkTerm(GT, x, zero);
+    assertDoesNotThrow(() -> g.addRule(start, truen));
+    // Call the abduction api, while the resulting abduct is the output
+    assertTrue(d_solver.getAbduct(conj2, g, output2));
+    // abduct must be true
+    assertEquals(output2, truen);
+  }
+
+  @Test void getAbduct2() throws CVC5ApiException
+  {
+    d_solver.setLogic("QF_LIA");
+    d_solver.setOption("incremental", "false");
+    Sort intSort = d_solver.getIntegerSort();
+    Term zero = d_solver.mkInteger(0);
+    Term x = d_solver.mkConst(intSort, "x");
+    Term y = d_solver.mkConst(intSort, "y");
+    // Assumptions for abduction: x > 0
+    d_solver.assertFormula(d_solver.mkTerm(GT, x, zero));
+    // Conjecture for abduction: y > 0
+    Term conj = d_solver.mkTerm(GT, y, zero);
+    Term output  = d_solver.getNullTerm();
+    // Fails due to option not set
+    assertThrows(
+        CVC5ApiException.class, () -> d_solver.getAbduct(conj, output));
+  }
+
+  @Test void getAbductNext() throws CVC5ApiException
+  {
+    d_solver.setLogic("QF_LIA");
+    d_solver.setOption("produce-abducts", "true");
+    d_solver.setOption("incremental", "true");
+
+    Sort intSort = d_solver.getIntegerSort();
+    Term zero = d_solver.mkInteger(0);
+    Term x = d_solver.mkConst(intSort, "x");
+    Term y = d_solver.mkConst(intSort, "y");
+
+    // Assumptions for abduction: x > 0
+    d_solver.assertFormula(d_solver.mkTerm(GT, x, zero));
+    // Conjecture for abduction: y > 0
+    Term conj = d_solver.mkTerm(GT, y, zero);
+    Term output = d_solver.getNullTerm();
+    // Call the abduction api, while the resulting abduct is the output
+    assertTrue(d_solver.getAbduct(conj, output));
+    Term output2 = d_solver.getNullTerm();
+    assertTrue(d_solver.getAbductNext(output2));
+    // should produce a different output
+    assertNotEquals(output, output2);
+  }
+
+
   @Test void getInterpolant() throws CVC5ApiException
   {
     d_solver.setLogic("QF_LIA");