Add instantiation pool feature to the API (#6358)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Tue, 20 Apr 2021 21:40:12 +0000 (16:40 -0500)
committerGitHub <noreply@github.com>
Tue, 20 Apr 2021 21:40:12 +0000 (21:40 +0000)
This adds the command declare-pool to the public API. It also adds parsing support for this feature, lifts the internal kinds to public kinds, adds an example regression, and a unit test for the new declare-pool solver method.

14 files changed:
src/CMakeLists.txt
src/api/cpp/cvc5.cpp
src/api/cpp/cvc5.h
src/api/cpp/cvc5_kind.h
src/api/python/cvc4.pxd
src/api/python/cvc4.pxi
src/parser/smt2/Smt2.g
src/printer/printer.cpp
src/printer/printer.h
src/smt/command.cpp
src/smt/command.h
test/regress/CMakeLists.txt
test/regress/regress1/quantifiers/pool-example.smt2 [new file with mode: 0644]
test/unit/api/solver_black.cpp

index b86435eb770435b712f2185c3643c44298b55a7f..c24ab31ec877a4639b9cd3643bca4bb2ccd16a90 100644 (file)
@@ -869,6 +869,8 @@ libcvc4_add_sources(
   theory/quantifiers/term_database.h
   theory/quantifiers/term_enumeration.cpp
   theory/quantifiers/term_enumeration.h
+  theory/quantifiers/term_pools.cpp
+  theory/quantifiers/term_pools.h
   theory/quantifiers/term_registry.cpp
   theory/quantifiers/term_registry.h
   theory/quantifiers/term_util.cpp
index a32ff4caa1a15d00f47a4c1d19868b0f4a84bf5e..aafc5331a47a69f95c04334039a062bd34e15430 100644 (file)
@@ -350,6 +350,9 @@ const static std::unordered_map<Kind, cvc5::Kind, KindHashFunction> s_kinds{
     {BOUND_VAR_LIST, cvc5::Kind::BOUND_VAR_LIST},
     {INST_PATTERN, cvc5::Kind::INST_PATTERN},
     {INST_NO_PATTERN, cvc5::Kind::INST_NO_PATTERN},
+    {INST_POOL, cvc5::Kind::INST_POOL},
+    {INST_ADD_TO_POOL, cvc5::Kind::INST_ADD_TO_POOL},
+    {SKOLEM_ADD_TO_POOL, cvc5::Kind::SKOLEM_ADD_TO_POOL},
     {INST_ATTRIBUTE, cvc5::Kind::INST_ATTRIBUTE},
     {INST_PATTERN_LIST, cvc5::Kind::INST_PATTERN_LIST},
     {LAST_KIND, cvc5::Kind::LAST_KIND},
@@ -644,6 +647,9 @@ const static std::unordered_map<cvc5::Kind, Kind, cvc5::kind::KindHashFunction>
         {cvc5::Kind::BOUND_VAR_LIST, BOUND_VAR_LIST},
         {cvc5::Kind::INST_PATTERN, INST_PATTERN},
         {cvc5::Kind::INST_NO_PATTERN, INST_NO_PATTERN},
+        {cvc5::Kind::INST_POOL, INST_POOL},
+        {cvc5::Kind::INST_ADD_TO_POOL, INST_ADD_TO_POOL},
+        {cvc5::Kind::SKOLEM_ADD_TO_POOL, SKOLEM_ADD_TO_POOL},
         {cvc5::Kind::INST_ATTRIBUTE, INST_ATTRIBUTE},
         {cvc5::Kind::INST_PATTERN_LIST, INST_PATTERN_LIST},
         /* ----------------------------------------------------------------- */
@@ -6596,6 +6602,24 @@ Term Solver::getSeparationNilTerm() const
   CVC5_API_TRY_CATCH_END;
 }
 
+Term Solver::declarePool(const std::string& symbol,
+                         const Sort& sort,
+                         const std::vector<Term>& initValue) const
+{
+  NodeManagerScope scope(getNodeManager());
+  CVC5_API_TRY_CATCH_BEGIN;
+  CVC5_API_SOLVER_CHECK_SORT(sort);
+  CVC5_API_SOLVER_CHECK_TERMS(initValue);
+  //////// all checks before this line
+  TypeNode setType = getNodeManager()->mkSetType(*sort.d_type);
+  Node pool = getNodeManager()->mkBoundVar(symbol, setType);
+  std::vector<Node> initv = Term::termVectorToNodes(initValue);
+  d_smtEngine->declarePool(pool, initv);
+  return Term(this, pool);
+  ////////
+  CVC5_API_TRY_CATCH_END;
+}
+
 void Solver::pop(uint32_t nscopes) const
 {
   NodeManagerScope scope(getNodeManager());
index dc834d8b5a7d21176e179d7706a9f58ac6d8b668..aaf19c30b19d06af061d156d0360ac0d7e8e1ffe 100644 (file)
@@ -3566,6 +3566,19 @@ class CVC4_EXPORT Solver
    */
   Term getSeparationNilTerm() const;
 
+  /**
+   * Declare a symbolic pool of terms with the given initial value.
+   * SMT-LIB:
+   * \verbatim
+   * ( declare-pool <symbol> <sort> ( <term>* ) )
+   * \endverbatim
+   * @param symbol The name of the pool
+   * @param sort The sort of the elements of the pool.
+   * @param initValue The initial value of the pool
+   */
+  Term declarePool(const std::string& symbol,
+                   const Sort& sort,
+                   const std::vector<Term>& initValue) const;
   /**
    * Pop (a) level(s) from the assertion stack.
    * SMT-LIB:
index 6cb4be3c8aa5daebbb918ef95ccd935ae1e1fc56..86e6d676dab102ad897f89d383fa95043f2751d1 100644 (file)
@@ -3331,6 +3331,34 @@ enum CVC4_EXPORT Kind : int32_t
    *   - `Solver::mkTerm(Kind kind, const std::vector<Term>& children) const`
    */
   INST_NO_PATTERN,
+  /*
+   * An instantiation pool.
+   * Specifies an annotation for pool based instantiation.
+   * Parameters: n > 1
+   *   - 1..n: Terms that comprise the pools, which are one-to-one with
+   * the variables of the quantified formula to be instantiated.
+   * Create with:
+   *   - `mkTerm(Kind kind, Term child1, Term child2)
+   *   - `mkTerm(Kind kind, Term child1, Term child2, Term child3)
+   *   - `mkTerm(Kind kind, const std::vector<Term>& children)
+   */
+  INST_POOL,
+  /*
+   * A instantantiation-add-to-pool annotation.
+   * Parameters: n = 1
+   *   - 1: The pool to add to.
+   * Create with:
+   *   - `mkTerm(Kind kind, Term child)
+   */
+  INST_ADD_TO_POOL,
+  /*
+   * A skolemization-add-to-pool annotation.
+   * Parameters: n = 1
+   *   - 1: The pool to add to.
+   * Create with:
+   *   - `mkTerm(Kind kind, Term child)
+   */
+  SKOLEM_ADD_TO_POOL,
   /**
    * An instantiation attribute
    * Specifies a custom property for a quantified formula given by a
index abc23ea4be805cc06d0ffffe87a5236ae1130036..336def3dcb87d752e41b1925ee8e9031a25299f2 100644 (file)
@@ -240,6 +240,7 @@ cdef extern from "api/cpp/cvc5.h" namespace "cvc5::api":
         void declareSeparationHeap(Sort locSort, Sort dataSort) except +
         Term getSeparationHeap() except +
         Term getSeparationNilTerm() except +
+        Term declarePool(const string& name, Sort sort, vector[Term]& initValue) except +
         void pop(uint32_t nscopes) except +
         void push(uint32_t nscopes) except +
         void reset() except +
index 3156b08825b91283677e8e3a3485c569ef6152b1..48921dc879874828a9c46cdf99d91591fc881fb7 100644 (file)
@@ -1136,6 +1136,14 @@ cdef class Solver:
         term.cterm = self.csolver.getSeparationNilTerm()
         return term
 
+    def declarePool(self, str symbol, Sort sort, initValue):
+        cdef Term term = Term(self)
+        cdef vector[c_Term] niv
+        for v in initValue:
+            niv.push_back((<Term?> v).cterm)
+        term.cterm = self.csolver.declarePool(symbol.encode(), sort.csort, niv)
+        return term
+
     def pop(self, nscopes=1):
         self.csolver.pop(nscopes)
 
index c347dc0db0a4b85ba19d9d8fd346b2065dd5b452..4a612ce6f595c8dc0d7f56c1390297fa2eace364 100644 (file)
@@ -1059,6 +1059,19 @@ extendedCommand[std::unique_ptr<cvc5::Command>* cmd]
     sortSymbol[s, CHECK_DECLARED]
     { cmd->reset(new DeclareHeapCommand(t, s)); }
     RPAREN_TOK
+  | DECLARE_POOL { PARSER_STATE->checkThatLogicIsSet(); }
+    symbol[name,CHECK_NONE,SYM_VARIABLE]
+    { PARSER_STATE->checkUserSymbol(name); }
+    sortSymbol[t,CHECK_DECLARED]
+    LPAREN_TOK
+    ( term[e, e2]
+      { terms.push_back( e ); }
+    )* RPAREN_TOK
+    { Debug("parser") << "declare pool: '" << name << "'" << std::endl;
+      api::Term pool = SOLVER->declarePool(name, t, terms);
+      PARSER_STATE->defineVar(name, pool);
+      cmd->reset(new DeclarePoolCommand(name, pool, t, terms));
+    }
   | BLOCK_MODEL_TOK { PARSER_STATE->checkThatLogicIsSet(); }
     { cmd->reset(new BlockModelCommand()); }
 
@@ -1468,7 +1481,7 @@ termNonVariable[cvc5::api::Term& expr, cvc5::api::Term& expr2]
 
     /* attributed expressions */
   | LPAREN_TOK ATTRIBUTE_TOK term[expr, f2]
-    ( attribute[expr, attexpr, attr]
+    ( attribute[expr, attexpr]
       { if( ! attexpr.isNull()) {
           patexprs.push_back( attexpr );
         }
@@ -1478,14 +1491,7 @@ termNonVariable[cvc5::api::Term& expr, cvc5::api::Term& expr2]
       if(! patexprs.empty()) {
         if( !f2.isNull() && f2.getKind()==api::INST_PATTERN_LIST ){
           for( size_t i=0; i<f2.getNumChildren(); i++ ){
-            if( f2[i].getKind()==api::INST_PATTERN ){
-              patexprs.push_back( f2[i] );
-            }else{
-              std::stringstream ss;
-              ss << "warning: rewrite rules do not support " << f2[i]
-                 << " within instantiation pattern list";
-              PARSER_STATE->warning(ss.str());
-            }
+            patexprs.push_back( f2[i] );
           }
         }
         expr2 = MK_TERM(api::INST_PATTERN_LIST, patexprs);
@@ -1745,7 +1751,7 @@ termAtomic[cvc5::api::Term& atomTerm]
 /**
  * Read attribute
  */
-attribute[cvc5::api::Term& expr, cvc5::api::Term& retExpr, std::string& attr]
+attribute[cvc5::api::Term& expr, cvc5::api::Term& retExpr]
 @init {
   api::Term sexpr;
   std::string s;
@@ -1753,23 +1759,26 @@ attribute[cvc5::api::Term& expr, cvc5::api::Term& retExpr, std::string& attr]
   std::vector<cvc5::api::Term> patexprs;
   cvc5::api::Term e2;
   bool hasValue = false;
+  api::Kind k;
 }
   : KEYWORD ( simpleSymbolicExprNoKeyword[s] { hasValue = true; } )?
   {
-    attr = AntlrInput::tokenText($KEYWORD);
-    PARSER_STATE->attributeNotSupported(attr);
+    PARSER_STATE->attributeNotSupported(AntlrInput::tokenText($KEYWORD));
   }
-  | ATTRIBUTE_PATTERN_TOK LPAREN_TOK
+  | ( ATTRIBUTE_PATTERN_TOK { k = api::INST_PATTERN; } |
+      ATTRIBUTE_POOL_TOK { k = api::INST_POOL; }  |
+      ATTRIBUTE_INST_ADD_TO_POOL_TOK { k = api::INST_ADD_TO_POOL; }  |
+      ATTRIBUTE_SKOLEM_ADD_TO_POOL_TOK{ k = api::SKOLEM_ADD_TO_POOL; } 
+    )
+    LPAREN_TOK
     ( term[patexpr, e2]
       { patexprs.push_back( patexpr ); }
     )+ RPAREN_TOK
     {
-      attr = std::string(":pattern");
-      retExpr = MK_TERM(api::INST_PATTERN, patexprs);
+      retExpr = MK_TERM(k, patexprs);
     }
   | ATTRIBUTE_NO_PATTERN_TOK term[patexpr, e2]
     {
-      attr = std::string(":no-pattern");
       retExpr = MK_TERM(api::INST_NO_PATTERN, patexpr);
     }
   | tok=( ATTRIBUTE_INST_LEVEL ) INTEGER_LITERAL
@@ -1792,7 +1801,6 @@ attribute[cvc5::api::Term& expr, cvc5::api::Term& retExpr, std::string& attr]
     {
       api::Sort boolType = SOLVER->getBooleanSort();
       api::Term avar = SOLVER->mkConst(boolType, sexprToString(sexpr));
-      attr = std::string(":qid");
       retExpr = MK_TERM(api::INST_ATTRIBUTE, avar);
       Command* c = new SetUserAttributeCommand("qid", avar);
       c->setMuted(true);
@@ -1800,7 +1808,6 @@ attribute[cvc5::api::Term& expr, cvc5::api::Term& retExpr, std::string& attr]
     }
   | ATTRIBUTE_NAMED_TOK symbolicExpr[sexpr]
     {
-      attr = std::string(":named");
       // notify that expression was given a name
       PARSER_STATE->notifyNamedExpression(expr, sexprToString(sexpr));
     }
@@ -2237,6 +2244,7 @@ GET_QE_DISJUNCT_TOK : 'get-qe-disjunct';
 GET_ABDUCT_TOK : 'get-abduct';
 GET_INTERPOL_TOK : 'get-interpol';
 DECLARE_HEAP : 'declare-heap';
+DECLARE_POOL : 'declare-pool';
 
 // SyGuS commands
 SYNTH_FUN_TOK : { PARSER_STATE->sygus() }?'synth-fun';
@@ -2252,6 +2260,9 @@ SYGUS_VARIABLE_TOK : { PARSER_STATE->sygus() }? 'Variable';
 // attributes
 ATTRIBUTE_PATTERN_TOK : ':pattern';
 ATTRIBUTE_NO_PATTERN_TOK : ':no-pattern';
+ATTRIBUTE_POOL_TOK : ':pool';
+ATTRIBUTE_INST_ADD_TO_POOL_TOK : ':inst-add-to-pool';
+ATTRIBUTE_SKOLEM_ADD_TO_POOL_TOK : ':skolem-add-to-pool';
 ATTRIBUTE_NAMED_TOK : ':named';
 ATTRIBUTE_INST_LEVEL : ':quant-inst-max-level';
 ATTRIBUTE_QUANTIFIER_ID_TOK : ':qid';
index 6ab419b8526c4d70a9265cad40628f6796d16ded..19d14eb0984ba2ee80cdc435a5c4ce036171407d 100644 (file)
@@ -190,6 +190,14 @@ void Printer::toStreamCmdDeclareFunction(std::ostream& out,
   printUnknownCommand(out, "declare-fun");
 }
 
+void Printer::toStreamCmdDeclarePool(std::ostream& out,
+                                     const std::string& id,
+                                     TypeNode type,
+                                     const std::vector<Node>& initValue) const
+{
+  printUnknownCommand(out, "declare-pool");
+}
+
 void Printer::toStreamCmdDeclareType(std::ostream& out,
                                      TypeNode type) const
 {
index aeffc76eb021d2ba4d16a34d9140425db2cf72a6..86f3dbb2b3cd33e32a056420e94e9a32e80d70fd 100644 (file)
@@ -87,6 +87,11 @@ class Printer
   virtual void toStreamCmdDeclareFunction(std::ostream& out,
                                           const std::string& id,
                                           TypeNode type) const;
+  /** Print declare-pool command */
+  virtual void toStreamCmdDeclarePool(std::ostream& out,
+                                      const std::string& id,
+                                      TypeNode type,
+                                      const std::vector<Node>& initValue) const;
 
   /** Print declare-sort command */
   virtual void toStreamCmdDeclareType(std::ostream& out,
index f27ed6e1f81ee4d22c3bee9a60b242e1c004bc2d..b2a1590b01db499c86df38609f8751ca1cb30dde 100644 (file)
@@ -1167,6 +1167,60 @@ void DeclareFunctionCommand::toStream(std::ostream& out,
       out, d_func.toString(), sortToTypeNode(d_sort));
 }
 
+/* -------------------------------------------------------------------------- */
+/* class DeclareFunctionCommand                                               */
+/* -------------------------------------------------------------------------- */
+
+DeclarePoolCommand::DeclarePoolCommand(const std::string& id,
+                                       api::Term func,
+                                       api::Sort sort,
+                                       const std::vector<api::Term>& initValue)
+    : DeclarationDefinitionCommand(id),
+      d_func(func),
+      d_sort(sort),
+      d_initValue(initValue)
+{
+}
+
+api::Term DeclarePoolCommand::getFunction() const { return d_func; }
+api::Sort DeclarePoolCommand::getSort() const { return d_sort; }
+const std::vector<api::Term>& DeclarePoolCommand::getInitialValue() const
+{
+  return d_initValue;
+}
+
+void DeclarePoolCommand::invoke(api::Solver* solver, SymbolManager* sm)
+{
+  // Notice that the pool is already declared by the parser so that it the
+  // symbol is bound eagerly. This is analogous to DeclareSygusVarCommand.
+  // Hence, we do nothing here.
+  d_commandStatus = CommandSuccess::instance();
+}
+
+Command* DeclarePoolCommand::clone() const
+{
+  DeclarePoolCommand* dfc =
+      new DeclarePoolCommand(d_symbol, d_func, d_sort, d_initValue);
+  return dfc;
+}
+
+std::string DeclarePoolCommand::getCommandName() const
+{
+  return "declare-pool";
+}
+
+void DeclarePoolCommand::toStream(std::ostream& out,
+                                  int toDepth,
+                                  size_t dag,
+                                  OutputLanguage language) const
+{
+  Printer::getPrinter(language)->toStreamCmdDeclarePool(
+      out,
+      d_func.toString(),
+      sortToTypeNode(d_sort),
+      termVectorToNodes(d_initValue));
+}
+
 /* -------------------------------------------------------------------------- */
 /* class DeclareSortCommand                                                   */
 /* -------------------------------------------------------------------------- */
index f14d52e94e54cfa83690a092a3e6b409c2daff1d..07dfa2b30fbf090df7023c283beab76f93ee2ee9 100644 (file)
@@ -425,6 +425,32 @@ class CVC4_EXPORT DeclareFunctionCommand : public DeclarationDefinitionCommand
       OutputLanguage language = language::output::LANG_AUTO) const override;
 }; /* class DeclareFunctionCommand */
 
+class CVC4_EXPORT DeclarePoolCommand : public DeclarationDefinitionCommand
+{
+ protected:
+  api::Term d_func;
+  api::Sort d_sort;
+  std::vector<api::Term> d_initValue;
+
+ public:
+  DeclarePoolCommand(const std::string& id,
+                     api::Term func,
+                     api::Sort sort,
+                     const std::vector<api::Term>& initValue);
+  api::Term getFunction() const;
+  api::Sort getSort() const;
+  const std::vector<api::Term>& getInitialValue() const;
+
+  void invoke(api::Solver* solver, SymbolManager* sm) override;
+  Command* clone() const override;
+  std::string getCommandName() const override;
+  void toStream(
+      std::ostream& out,
+      int toDepth = -1,
+      size_t dag = 1,
+      OutputLanguage language = language::output::LANG_AUTO) const override;
+}; /* class DeclarePoolCommand */
+
 class CVC4_EXPORT DeclareSortCommand : public DeclarationDefinitionCommand
 {
  protected:
index 12e0c568dee7f891384145f6bbab6de00fed5ba4..24d6602e99bb3753bee7ea161888ecf023244661 100644 (file)
@@ -1805,6 +1805,7 @@ set(regress_1_tests
   regress1/quantifiers/nra-interleave-inst.smt2
   regress1/quantifiers/opisavailable-12.smt2
   regress1/quantifiers/parametric-lists.smt2
+  regress1/quantifiers/pool-example.smt2
   regress1/quantifiers/prenex-scholl-smt08_RNDPRE_RNDPRE_4_6.smt2
   regress1/quantifiers/psyco-001-bv.smt2
   regress1/quantifiers/psyco-107-bv.smt2
diff --git a/test/regress/regress1/quantifiers/pool-example.smt2 b/test/regress/regress1/quantifiers/pool-example.smt2
new file mode 100644 (file)
index 0000000..8344f54
--- /dev/null
@@ -0,0 +1,17 @@
+; COMMAND-LINE: --pool-inst
+; EXPECT: unsat
+(set-logic ALL)
+(declare-pool L Int ())
+
+(declare-fun P (Int) Bool)
+
+(assert (not (=
+(forall ((x Int)) (! (!
+  (P x)
+  :skolem-add-to-pool ((- x 100) L)) :pool (L) )) 
+(forall ((x Int)) (! (!
+  (P (+ x 100))
+  :skolem-add-to-pool ((+ x 100) L)) :pool (L) ) 
+))))
+
+(check-sat)
index 361bfa8aa398dde4cc70a97496741ecc9e3151b0..db950dd7ea76e76c91dca490357bc1b57ac02824 100644 (file)
@@ -1299,6 +1299,22 @@ TEST_F(TestApiBlackSolver, getInterpolant)
   ASSERT_TRUE(output.getSort().isBoolean());
 }
 
+TEST_F(TestApiBlackSolver, declarePool)
+{
+  Sort intSort = d_solver.getIntegerSort();
+  Sort setSort = d_solver.mkSetSort(intSort);
+  Term zero = d_solver.mkInteger(0);
+  Term x = d_solver.mkConst(intSort, "x");
+  Term y = d_solver.mkConst(intSort, "y");
+  // declare a pool with initial value { 0, x, y }
+  Term p = d_solver.declarePool("p", intSort, {zero, x, y});
+  // pool should have the same sort
+  ASSERT_TRUE(p.getSort() == setSort);
+  // cannot pass null sort
+  Sort nullSort;
+  ASSERT_THROW(d_solver.declarePool("i", nullSort, {}), CVC5ApiException);
+}
+
 TEST_F(TestApiBlackSolver, getOp)
 {
   Sort bv32 = d_solver.mkBitVectorSort(32);