New grammar construction modes for SyGuS (#3486)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Wed, 4 Dec 2019 23:18:06 +0000 (17:18 -0600)
committerGitHub <noreply@github.com>
Wed, 4 Dec 2019 23:18:06 +0000 (17:18 -0600)
14 files changed:
src/options/options_handler.cpp
src/options/options_handler.h
src/options/quantifiers_modes.h
src/options/quantifiers_options.toml
src/theory/datatypes/theory_datatypes_utils.cpp
src/theory/datatypes/theory_datatypes_utils.h
src/theory/quantifiers/sygus/cegis.cpp
src/theory/quantifiers/sygus/sygus_explain.cpp
src/theory/quantifiers/sygus/sygus_grammar_cons.cpp
test/regress/CMakeLists.txt
test/regress/regress1/sygus/int-any-const.sy [new file with mode: 0644]
test/regress/regress1/sygus/real-any-const.sy [new file with mode: 0644]
test/regress/regress1/sygus/strings-any-term1.sy [new file with mode: 0644]
test/regress/regress3/strings-any-term.sy [new file with mode: 0644]

index d4194d456e07eb4957eeea50e5f859acaabe809f..faf35857374042bdcaf302b517ac6cd4d9020a8a 100644 (file)
@@ -569,6 +569,29 @@ auto (default) \n\
 \n\
 ";
 
+const std::string OptionsHandler::s_sygusGrammarConsHelp =
+    "\
+Modes for default SyGuS grammars, supported by --sygus-grammar-cons:\n\
+\n\
+simple (default) \n\
++ Use simple grammar construction (no symbolic terms or constants).\n\
+\n\
+any-const  \n\
++ Use symoblic constant constructors.\n\
+\n\
+any-term  \n\
++ When applicable, use constructors corresponding to any symbolic term.\n\
+This option enables a sum-of-monomials grammar for arithmetic. For all\n\
+other types, it enables symbolic constant constructors.\n\
+\n\
+any-term-concise  \n\
++ When applicable, use constructors corresponding to any symbolic term,\n\
+favoring conciseness over generality. This option is equivalent to any-term\n\
+but enables a polynomial grammar for arithmetic when not in a combined\n\
+theory.\n\
+\n\
+";
+
 const std::string OptionsHandler::s_macrosQuantHelp = "\
 Modes for quantifiers macro expansion, supported by --macros-quant-mode:\n\
 \n\
@@ -1085,6 +1108,38 @@ OptionsHandler::stringToSygusActiveGenMode(std::string option,
                           + optarg + "'.  Try --sygus-inv-templ help.");
   }
 }
+theory::quantifiers::SygusGrammarConsMode
+OptionsHandler::stringToSygusGrammarConsMode(std::string option,
+                                             std::string optarg)
+{
+  if (optarg == "simple")
+  {
+    return theory::quantifiers::SYGUS_GCONS_SIMPLE;
+  }
+  else if (optarg == "any-const")
+  {
+    return theory::quantifiers::SYGUS_GCONS_ANY_CONST;
+  }
+  else if (optarg == "any-term")
+  {
+    return theory::quantifiers::SYGUS_GCONS_ANY_TERM;
+  }
+  else if (optarg == "any-term-concise")
+  {
+    return theory::quantifiers::SYGUS_GCONS_ANY_TERM_CONCISE;
+  }
+  else if (optarg == "help")
+  {
+    puts(s_sygusGrammarConsHelp.c_str());
+    exit(1);
+  }
+  else
+  {
+    throw OptionException(
+        std::string("unknown option for --sygus-grammar-cons: `") + optarg
+        + "'.  Try --sygus-grammar-cons help.");
+  }
+}
 
 theory::quantifiers::MacrosQuantMode OptionsHandler::stringToMacrosQuantMode(
     std::string option, std::string optarg)
index 84b7002e3016c0e8a401760a292ac8f96c59c8b9..eae61c5b20afcc8b50c9fb7416f59b167ca15e30 100644 (file)
@@ -122,6 +122,8 @@ public:
       std::string option, std::string optarg);
   theory::quantifiers::SygusActiveGenMode stringToSygusActiveGenMode(
       std::string option, std::string optarg);
+  theory::quantifiers::SygusGrammarConsMode stringToSygusGrammarConsMode(
+      std::string option, std::string optarg);
   theory::quantifiers::MacrosQuantMode stringToMacrosQuantMode(
       std::string option, std::string optarg);
   theory::quantifiers::QuantDSplitMode stringToQuantDSplitMode(
@@ -275,6 +277,7 @@ public:
   static const std::string s_sygusFilterSolHelp;
   static const std::string s_sygusInvTemplHelp;
   static const std::string s_sygusActiveGenHelp;
+  static const std::string s_sygusGrammarConsHelp;
   static const std::string s_termDbModeHelp;
   static const std::string s_theoryOfModeHelp;
   static const std::string s_triggerSelModeHelp;
index 049cdef1c201f8a237239bcafae2f24a5b92ad1f..9ff2ac19658f6df5c566662372294f07eecef0cf 100644 (file)
@@ -292,6 +292,29 @@ enum SygusFilterSolMode
   SYGUS_FILTER_SOL_WEAK,
 };
 
+enum SygusGrammarConsMode
+{
+  /**
+   * Use simple default SyGuS grammar construction (no symbolic terms or
+   * constants).
+   */
+  SYGUS_GCONS_SIMPLE,
+  /** Use "any constant" constructors in default SyGuS grammar construction. */
+  SYGUS_GCONS_ANY_CONST,
+  /**
+   * When applicable, use constructors that encode any term using "any constant"
+   * constructors. This construction uses sum-of-monomials for arithmetic
+   * grammars.
+   */
+  SYGUS_GCONS_ANY_TERM,
+  /**
+   * When applicable, use constructors that encode any term using "any constant"
+   * constructors in a way that prefers conciseness over generality. This
+   * construction uses polynomials for arithmetic grammars.
+   */
+  SYGUS_GCONS_ANY_TERM_CONCISE,
+};
+
 enum MacrosQuantMode {
   /** infer all definitions */
   MACROS_QUANT_MODE_ALL,
index 019b052bc13027c41ab191cfa0071e19c0bde19e..171af1e47efc4951d3e0c3d5cbd42c44996ec3b3 100644 (file)
@@ -1076,6 +1076,16 @@ header = "options/quantifiers_options.h"
   read_only  = true
   help       = "embed sygus templates into grammars"
 
+[[option]]
+  name       = "sygusGrammarConsMode"
+  category   = "regular"
+  long       = "sygus-grammar-cons=MODE"
+  type       = "CVC4::theory::quantifiers::SygusGrammarConsMode"
+  default    = "CVC4::theory::quantifiers::SYGUS_GCONS_SIMPLE"
+  handler    = "stringToSygusGrammarConsMode"
+  includes   = ["options/quantifiers_modes.h"]
+  help       = "mode for SyGuS grammar construction"
+
 [[option]]
   name       = "sygusInvTemplMode"
   category   = "regular"
index 5bf0ca03661ecd3a962d300abf747c625dcd7678..43d23b523cd80bb6b16c81571d0cc88159cd2201 100644 (file)
@@ -125,8 +125,14 @@ Node mkSygusTerm(const Datatype& dt,
   Assert(i < dt.getNumConstructors());
   Assert(dt.isSygus());
   Assert(!dt[i].getSygusOp().isNull());
-  std::vector<Node> schildren;
   Node op = Node::fromExpr(dt[i].getSygusOp());
+  return mkSygusTerm(op, children, doBetaReduction);
+}
+
+Node mkSygusTerm(Node op,
+                 const std::vector<Node>& children,
+                 bool doBetaReduction)
+{
   Trace("dt-sygus-util") << "Operator is " << op << std::endl;
   if (children.empty())
   {
@@ -140,6 +146,7 @@ Node mkSygusTerm(const Datatype& dt,
     Assert(children.size() == 1);
     return children[0];
   }
+  std::vector<Node> schildren;
   // get the kind of the operator
   Kind ok = op.getKind();
   if (ok != BUILTIN)
index 2ac288c4c3b3349f5c8a8d1c41466ee0bf1aa155..5f74a4bee693f6141fcfb57bed04318cd3d6f62a 100644 (file)
@@ -147,6 +147,13 @@ Node mkSygusTerm(const Datatype& dt,
                  unsigned i,
                  const std::vector<Node>& children,
                  bool doBetaReduction = true);
+/**
+ * Same as above, but we already have the sygus operator op. The above method
+ * is syntax sugar for calling this method on dt[i].getSygusOp().
+ */
+Node mkSygusTerm(Node op,
+                 const std::vector<Node>& children,
+                 bool doBetaReduction = true);
 /**
  * n is a builtin term that is an application of operator op.
  *
index c806bb1e7466344429d1db6ff7f041487e1f8225..7a9a9ca21fe7e07007910bec8ffecfa4fc3203e2 100644 (file)
@@ -77,8 +77,11 @@ bool Cegis::processInitialize(Node conj,
   for (unsigned i = 0; i < csize; i++)
   {
     Trace("cegis") << "...register enumerator " << candidates[i];
+    // We use symbolic constants if we are doing repair constants or if the
+    // grammar construction was not simple.
     bool useSymCons = false;
-    if (options::sygusRepairConst())
+    if (options::sygusRepairConst()
+        || options::sygusGrammarConsMode() != SYGUS_GCONS_SIMPLE)
     {
       TypeNode ctn = candidates[i].getType();
       d_tds->registerSygusType(ctn);
index b1baed9cb6936a8eb881da9fc5a8eda91f4612a5..cf1993efb78ac9838e568542559d5d8fafc86765 100644 (file)
@@ -189,8 +189,13 @@ void SygusExplain::getExplanationFor(TermRecBuild& trb,
   TypeNode ntn = n.getType();
   if (!ntn.isDatatype())
   {
-    // sygus datatype fields that are not sygus datatypes are treated as
-    // abstractions only, hence we disregard this field
+    // SyGuS datatype fields that are not sygus datatypes are treated as
+    // abstractions only, hence we disregard this field. It is important
+    // that users of this method pay special attention to any constants,
+    // otherwise the explanation n.eqNode(vn) is necessary here. For example,
+    // any lemma schema that blocks the current value of an enumerator should
+    // not make any assumptions about the value of the arguments of its any
+    // constant constructors, since their explanation is not included here.
     return;
   }
   Assert(vn.getKind() == APPLY_CONSTRUCTOR);
index d00df38c5ae50939a92d9a4f66d17510f78ffb65..5e4513ff3653f5e32ea267439c4f44091cef19ba 100644 (file)
@@ -20,6 +20,7 @@
 #include "options/quantifiers_options.h"
 #include "printer/sygus_print_callback.h"
 #include "theory/bv/theory_bv_utils.h"
+#include "theory/datatypes/theory_datatypes_utils.h"
 #include "theory/quantifiers/sygus/sygus_grammar_norm.h"
 #include "theory/quantifiers/sygus/sygus_process_conj.h"
 #include "theory/quantifiers/sygus/synth_conjecture.h"
@@ -515,10 +516,11 @@ void CegGrammarConstructor::mkSygusDefaultGrammar(
   std::map<TypeNode, TypeNode> sygus_to_builtin;
 
   std::vector<TypeNode> types;
-  // collect connected types for each of the variables
+  // Collect connected types for each of the variables.
   for (unsigned i = 0, size = sygus_vars.size(); i < size; ++i)
   {
-    collectSygusGrammarTypesFor(sygus_vars[i].getType(), types);
+    TypeNode tni = sygus_vars[i].getType();
+    collectSygusGrammarTypesFor(tni, types);
   }
   // collect connected types to range
   collectSygusGrammarTypesFor(range, types);
@@ -534,6 +536,9 @@ void CegGrammarConstructor::mkSygusDefaultGrammar(
   std::map<TypeNode, TypeNode> type_to_unres;
   std::map<TypeNode, std::unordered_set<Node, NodeHashFunction>>::const_iterator
       itc;
+  // maps types to the index of its "any term" grammar construction
+  std::map<TypeNode, unsigned> typeToGAnyTerm;
+  SygusGrammarConsMode sgcm = options::sygusGrammarConsMode();
   for (unsigned i = 0, size = types.size(); i < size; ++i)
   {
     std::stringstream ss;
@@ -556,10 +561,40 @@ void CegGrammarConstructor::mkSygusDefaultGrammar(
     type_to_unres[types[i]] = unres_t;
     sygus_to_builtin[unres_t] = types[i];
   }
-  for (unsigned i = 0, size = types.size(); i < size; ++i)
+  // We ensure an ordering on types such that parametric types are processed
+  // before their consitituents. Since parametric types were added before their
+  // arguments in collectSygusGrammarTypesFor above, we will construct the
+  // sygus grammars by iterating on types in reverse order. This ensures
+  // that we know all constructors coming from other types (e.g. select(A,i))
+  // by the time we process the type.
+  for (int i = (types.size() - 1); i >= 0; --i)
   {
     Trace("sygus-grammar-def") << "Make grammar for " << types[i] << " " << unres_types[i] << std::endl;
     TypeNode unres_t = unres_types[i];
+    SygusGrammarConsMode tsgcm = sgcm;
+    if (tsgcm == SYGUS_GCONS_ANY_TERM || tsgcm == SYGUS_GCONS_ANY_TERM_CONCISE)
+    {
+      // If the type does not support any term, we do any constant instead.
+      // We also fall back on any constant construction if the type has no
+      // constructors at this point (e.g. it simply encodes all constants).
+      if (!types[i].isReal())
+      {
+        tsgcm = SYGUS_GCONS_ANY_CONST;
+      }
+      else
+      {
+        // Add a placeholder for the "any term" version of this datatype, to be
+        // constructed later.
+        typeToGAnyTerm[types[i]] = sdts.size();
+        std::stringstream ssat;
+        ssat << sdts[i].d_sdt.getName() << "_any_term";
+        sdts.push_back(SygusDatatypeGenerator(ssat.str()));
+        TypeNode unresAnyTerm = mkUnresolvedType(ssat.str(), unres);
+        unres_types.push_back(unresAnyTerm);
+      }
+    }
+    Trace("sygus-grammar-def")
+        << "Grammar constructor mode for this type is " << tsgcm << std::endl;
     //add variables
     for (const Node& sv : sygus_vars)
     {
@@ -592,29 +627,44 @@ void CegGrammarConstructor::mkSygusDefaultGrammar(
       }
     }
     //add constants
-    std::vector< Node > consts;
-    mkSygusConstantsForType( types[i], consts );
-    std::map<TypeNode, std::unordered_set<Node, NodeHashFunction>>::iterator
-        itec = extra_cons.find(types[i]);
-    if( itec!=extra_cons.end() ){
-      for (std::unordered_set<Node, NodeHashFunction>::iterator set_it =
-               itec->second.begin();
-           set_it != itec->second.end();
-           ++set_it)
+    std::vector<Node> consts;
+    mkSygusConstantsForType(types[i], consts);
+    if (tsgcm == SYGUS_GCONS_ANY_CONST)
+    {
+      // Use the any constant constructor. Notice that for types that don't
+      // have constants (e.g. uninterpreted or function types), we don't add
+      // this constructor.
+      if (!consts.empty())
       {
-        if (std::find(consts.begin(), consts.end(), *set_it) == consts.end())
-        {
-          consts.push_back(*set_it);
-        }
+        sdts[i].d_sdt.addAnyConstantConstructor(types[i]);
       }
     }
-    for (unsigned j = 0, size_j = consts.size(); j < size_j; ++j)
+    else
     {
-      std::stringstream ss;
-      ss << consts[j];
-      Trace("sygus-grammar-def") << "...add for constant " << ss.str() << std::endl;
-      std::vector<TypeNode> cargsEmpty;
-      sdts[i].addConstructor(consts[j], ss.str(), cargsEmpty);
+      std::map<TypeNode, std::unordered_set<Node, NodeHashFunction>>::iterator
+          itec = extra_cons.find(types[i]);
+      if (itec != extra_cons.end())
+      {
+        for (std::unordered_set<Node, NodeHashFunction>::iterator set_it =
+                 itec->second.begin();
+             set_it != itec->second.end();
+             ++set_it)
+        {
+          if (std::find(consts.begin(), consts.end(), *set_it) == consts.end())
+          {
+            consts.push_back(*set_it);
+          }
+        }
+      }
+      for (unsigned j = 0, size_j = consts.size(); j < size_j; ++j)
+      {
+        std::stringstream ss;
+        ss << consts[j];
+        Trace("sygus-grammar-def")
+            << "...add for constant " << ss.str() << std::endl;
+        std::vector<TypeNode> cargsEmpty;
+        sdts[i].addConstructor(consts[j], ss.str(), cargsEmpty);
+      }
     }
     // ITE
     Kind k = ITE;
@@ -641,12 +691,14 @@ void CegGrammarConstructor::mkSygusDefaultGrammar(
       {
         Trace("sygus-grammar-def")
             << "  ...create auxiliary Positive Integers grammar\n";
-        /* Creating type for positive integers */
+        // Creating type for positive integers. Notice we can't use the any
+        // constant constructor here, since it admits zero.
         std::stringstream ss;
         ss << fun << "_PosInt";
         std::string pos_int_name = ss.str();
         // make unresolved type
-        TypeNode unres_pos_int_t = mkUnresolvedType(pos_int_name, unres);
+        TypeNode unresPosInt = mkUnresolvedType(pos_int_name, unres);
+        unres_types.push_back(unresPosInt);
         // make data type for positive constant integers
         sdts.push_back(SygusDatatypeGenerator(pos_int_name));
         /* Add operator 1 */
@@ -657,8 +709,8 @@ void CegGrammarConstructor::mkSygusDefaultGrammar(
         Kind k = PLUS;
         Trace("sygus-grammar-def") << "\t...add for PLUS to Pos_Int\n";
         std::vector<TypeNode> cargsPlus;
-        cargsPlus.push_back(unres_pos_int_t);
-        cargsPlus.push_back(unres_pos_int_t);
+        cargsPlus.push_back(unresPosInt);
+        cargsPlus.push_back(unresPosInt);
         sdts.back().addConstructor(k, cargsPlus);
         sdts.back().d_sdt.initializeDatatype(types[i], bvl, true, true);
         Trace("sygus-grammar-def")
@@ -668,7 +720,7 @@ void CegGrammarConstructor::mkSygusDefaultGrammar(
         Trace("sygus-grammar-def") << "\t...add for " << k << std::endl;
         std::vector<TypeNode> cargsDiv;
         cargsDiv.push_back(unres_t);
-        cargsDiv.push_back(unres_pos_int_t);
+        cargsDiv.push_back(unresPosInt);
         sdts[i].addConstructor(k, cargsDiv);
       }
     }
@@ -812,7 +864,8 @@ void CegGrammarConstructor::mkSygusDefaultGrammar(
           << types[i] << std::endl;
     }
   }
-  // make datatypes
+  std::map<TypeNode, unsigned>::iterator itgat;
+  // initialize the datatypes
   for (unsigned i = 0, size = types.size(); i < size; ++i)
   {
     sdts[i].d_sdt.initializeDatatype(types[i], bvl, true, true);
@@ -822,8 +875,227 @@ void CegGrammarConstructor::mkSygusDefaultGrammar(
     if( types[i]==range ){
       startIndex = i;
     }
-  }
+    itgat = typeToGAnyTerm.find(types[i]);
+    if (itgat == typeToGAnyTerm.end())
+    {
+      // no any term datatype, we are done
+      continue;
+    }
+    Trace("sygus-grammar-def")
+        << "Build any-term datatype for " << types[i] << "..." << std::endl;
+    unsigned iat = itgat->second;
+    // for now, only real has any term construction
+    Assert(types[i].isReal());
+    // We have initialized the given type sdts[i], which should now contain
+    // a constructor for each relevant arithmetic term/variable. We now
+    // construct a sygus datatype of one of the following two forms.
+    //
+    // (1) The "sum of monomials" grammar:
+    //   I -> C*x1 | ... | C*xn | C | I + I | ite( B, I, I )
+    //   C -> any_constant
+    // where x1, ..., xn are the arithmetic terms/variables (non-arithmetic
+    // builtin operators) terms we have considered thus far.
+    //
+    // (2) The "polynomial" grammar:
+    //   I -> C*x1 + ... + C*xn + C | ite( B, I, I )
+    //   C -> any_constant
+    //
+    // The advantage of the first is that it allows for sums of terms
+    // constructible from other theories that share sorts with arithmetic, e.g.
+    //   c1*str.len(x) + c2*str.len(y)
+    // The advantage of the second is that there are fewer constructors, and
+    // hence may be more efficient.
 
+    // Before proceeding, we build the any constant datatype
+    Trace("sygus-grammar-def")
+        << "Build any-constant datatype for " << types[i] << std::endl;
+    std::stringstream ss;
+    ss << fun << "_AnyConst";
+    // Make sygus datatype for any constant.
+    TypeNode unresAnyConst = mkUnresolvedType(ss.str(), unres);
+    unres_types.push_back(unresAnyConst);
+    sdts.push_back(SygusDatatypeGenerator(ss.str()));
+    sdts.back().d_sdt.addAnyConstantConstructor(types[i]);
+    sdts.back().d_sdt.initializeDatatype(types[i], bvl, true, true);
+
+    // Now get the reference to the sygus datatype at position i (important that
+    // this comes after the modification to sdts above, which may modify
+    // the references).
+    const SygusDatatype& sdti = sdts[i].d_sdt;
+    // whether we will use the polynomial grammar
+    bool polynomialGrammar = sgcm == SYGUS_GCONS_ANY_TERM_CONCISE;
+    // A set of constructor indices that will be used in the overall sum we
+    // are constructing; indices of constructors corresponding to builtin
+    // arithmetic operators will be excluded from this set.
+    std::set<unsigned> useConstructor;
+    Trace("sygus-grammar-def")
+        << "Look at operators, num = " << sdti.getNumConstructors() << "..."
+        << std::endl;
+    for (unsigned k = 0, ncons = sdti.getNumConstructors(); k < ncons; k++)
+    {
+      const SygusDatatypeConstructor& sdc = sdti.getConstructor(k);
+      Node sop = sdc.d_op;
+      bool isBuiltinArithOp = (sop.getKind() == CONST_RATIONAL);
+      bool hasExternalType = false;
+      for (unsigned j = 0, nargs = sdc.d_argTypes.size(); j < nargs; j++)
+      {
+        // Since we are accessing the fields of the sygus datatype, this
+        // already corresponds to the correct sygus datatype type.
+        TypeNode atype = sdc.d_argTypes[j];
+        if (atype == unres_types[i])
+        {
+          // It is recursive, thus is (likely) a builtin arithmetic operator
+          // as constructed above. It may also be an operator from another
+          // theory that has both an arithmetic return type and an arithmetic
+          // argument (e.g. str.indexof). In either case, we ignore it for the
+          // sake of well-foundedness.
+          isBuiltinArithOp = true;
+          break;
+        }
+        else if (atype != unres_bt)
+        {
+          // It is an external type. This is the case of an operator of another
+          // theory whose return type is arithmetic, e.g. select.
+          hasExternalType = true;
+        }
+      }
+      if (!isBuiltinArithOp)
+      {
+        useConstructor.insert(k);
+        if (hasExternalType)
+        {
+          // If we have an external term in the sum, e.g. select(A,i), we
+          // cannot use a fixed polynomial template. As mentioned above, we
+          // cannot use a polynomial grammar when external terms (those built
+          // from the symbols of other theories) are involved.
+          Trace("sygus-grammar-def")
+              << "Cannot use polynomial grammar due to " << sop << std::endl;
+          polynomialGrammar = false;
+        }
+      }
+    }
+    Trace("sygus-grammar-def")
+        << "Done look at operators, num = " << sdti.getNumConstructors()
+        << "..." << std::endl;
+    // we have now decided whether we will use sum-of-monomials or polynomial
+    // Now, extract the terms and set up the polynomial
+    std::vector<Node> sumChildren;
+    std::vector<TypeNode> cargsAnyTerm;
+    std::vector<Node> lambdaVars;
+    for (unsigned k = 0, ncons = sdti.getNumConstructors(); k < ncons; k++)
+    {
+      Trace("sygus-grammar-def") << "Process #" << k << std::endl;
+      if (useConstructor.find(k) == useConstructor.end())
+      {
+        Trace("sygus-grammar-def") << "Skip variable #" << k << std::endl;
+        // builtin operator, as computed above, we skip
+        continue;
+      }
+      const SygusDatatypeConstructor& sdc = sdti.getConstructor(k);
+      Node sop = sdc.d_op;
+      Trace("sygus-grammar-def")
+          << "Monomial variable: #" << k << ": " << sop << std::endl;
+      unsigned nargs = sdc.d_argTypes.size();
+      std::vector<TypeNode> opCArgs;
+      std::vector<Node> opLArgs;
+      if (nargs > 0)
+      {
+        // Take its arguments. For example, if we are building a polynomial
+        // over str.len(s), then our any term constructor would include an
+        // argument of string type, e.g.:
+        //   (lambda s : String, c1, c2 : Int. c1*len(s) + c2)
+        for (unsigned j = 0; j < nargs; j++)
+        {
+          // this is already corresponds to the correct sygus datatype type
+          TypeNode atype = sdc.d_argTypes[j];
+          opCArgs.push_back(atype);
+          // get the builtin type
+          TypeNode btype = sygus_to_builtin[atype];
+          opLArgs.push_back(nm->mkBoundVar(btype));
+        }
+        // Do beta reduction on the operator so that its arguments match the
+        // fresh variables of the lambda (op) we are constructing below.
+        sop = datatypes::utils::mkSygusTerm(sop, opLArgs);
+        sop = Rewriter::rewrite(sop);
+      }
+      opCArgs.push_back(unresAnyConst);
+      Node coeff = nm->mkBoundVar(types[i]);
+      opLArgs.push_back(coeff);
+      Node monomial = nm->mkNode(MULT, coeff, sop);
+      if (polynomialGrammar)
+      {
+        // add the monomial c*t to the sum
+        sumChildren.push_back(monomial);
+        lambdaVars.insert(lambdaVars.end(), opLArgs.begin(), opLArgs.end());
+        cargsAnyTerm.insert(cargsAnyTerm.end(), opCArgs.begin(), opCArgs.end());
+      }
+      else
+      {
+        Node op =
+            nm->mkNode(LAMBDA, nm->mkNode(BOUND_VAR_LIST, opLArgs), monomial);
+        // use a print callback since we do not want to print the lambda
+        std::shared_ptr<SygusPrintCallback> spc;
+        std::vector<Expr> opLArgsExpr;
+        for (unsigned i = 0, nvars = opLArgs.size(); i < nvars; i++)
+        {
+          opLArgsExpr.push_back(opLArgs[i].toExpr());
+        }
+        spc = std::make_shared<printer::SygusExprPrintCallback>(
+            monomial.toExpr(), opLArgsExpr);
+        // add it as a constructor
+        std::stringstream ssop;
+        ssop << "monomial_" << sdc.d_name;
+        sdts[iat].d_sdt.addConstructor(op, ssop.str(), opCArgs, spc);
+      }
+    }
+    if (polynomialGrammar)
+    {
+      // add the constant
+      Node coeff = nm->mkBoundVar(types[i]);
+      lambdaVars.push_back(coeff);
+      cargsAnyTerm.push_back(unresAnyConst);
+      // make the sygus operator lambda X. c1*t1 + ... + cn*tn + c
+      Assert(sumChildren.size() > 1);
+      Node ops = nm->mkNode(PLUS, sumChildren);
+      Node op = nm->mkNode(LAMBDA, nm->mkNode(BOUND_VAR_LIST, lambdaVars), ops);
+      std::shared_ptr<SygusPrintCallback> spc;
+      std::vector<Expr> lambdaVarsExpr;
+      for (unsigned i = 0, nvars = lambdaVars.size(); i < nvars; i++)
+      {
+        lambdaVarsExpr.push_back(lambdaVars[i].toExpr());
+      }
+      spc = std::make_shared<printer::SygusExprPrintCallback>(ops.toExpr(),
+                                                              lambdaVarsExpr);
+      Trace("sygus-grammar-def") << "any term operator is " << op << std::endl;
+      // make the any term datatype, add to back
+      // do not consider the exclusion criteria of the generator
+      sdts[iat].d_sdt.addConstructor(op, "polynomial", cargsAnyTerm, spc);
+    }
+    else
+    {
+      // add the any constant constructor as a separate constructor
+      sdts[iat].d_sdt.addAnyConstantConstructor(types[i]);
+      // add plus
+      std::vector<TypeNode> cargsPlus;
+      cargsPlus.push_back(unres_types[iat]);
+      cargsPlus.push_back(unres_types[iat]);
+      sdts[iat].d_sdt.addConstructor(PLUS, cargsPlus);
+    }
+    // add the ITE, regardless of sum-of-monomials vs polynomial
+    std::vector<TypeNode> cargsIte;
+    cargsIte.push_back(unres_bt);
+    cargsIte.push_back(unres_types[iat]);
+    cargsIte.push_back(unres_types[iat]);
+    sdts[iat].d_sdt.addConstructor(ITE, cargsIte);
+    sdts[iat].d_sdt.initializeDatatype(types[i], bvl, true, true);
+    Trace("sygus-grammar-def")
+        << "...built datatype " << sdts[iat].d_sdt.getDatatype() << std::endl;
+    // if the type is range, use it as the default type
+    if (types[i] == range)
+    {
+      startIndex = iat;
+    }
+  }
   //------ make Boolean type
   TypeNode btype = nm->booleanType();
   sdts.push_back(SygusDatatypeGenerator(dbname));
@@ -860,6 +1132,13 @@ void CegGrammarConstructor::mkSygusDefaultGrammar(
     {
       continue;
     }
+    unsigned iuse = i;
+    // use the any-term type if it exists
+    itgat = typeToGAnyTerm.find(types[i]);
+    if (itgat != typeToGAnyTerm.end())
+    {
+      iuse = itgat->second;
+    }
     Trace("sygus-grammar-def") << "...add predicates for " << types[i] << std::endl;
     //add equality per type
     Kind k = EQUAL;
@@ -867,8 +1146,8 @@ void CegGrammarConstructor::mkSygusDefaultGrammar(
     std::stringstream ss;
     ss << kindToString(k) << "_" << types[i];
     std::vector<TypeNode> cargsBinary;
-    cargsBinary.push_back(unres_types[i]);
-    cargsBinary.push_back(unres_types[i]);
+    cargsBinary.push_back(unres_types[iuse]);
+    cargsBinary.push_back(unres_types[iuse]);
     sdtBool.addConstructor(nm->operatorOf(k), ss.str(), cargsBinary);
     // type specific predicates
     if (types[i].isReal())
@@ -889,7 +1168,7 @@ void CegGrammarConstructor::mkSygusDefaultGrammar(
       Trace("sygus-grammar-def") << "...add for testers" << std::endl;
       const Datatype& dt = types[i].getDatatype();
       std::vector<TypeNode> cargsTester;
-      cargsTester.push_back(unres_types[i]);
+      cargsTester.push_back(unres_types[iuse]);
       for (unsigned k = 0, size_k = dt.getNumConstructors(); k < size_k; ++k)
       {
         Trace("sygus-grammar-def") << "...for " << dt[k].getTesterName() << std::endl;
index cf06d2e90a926d4a49409f692b0b0491bad0909a..7d3fb2d5cd1788fcde6af88adf3e81644bed3896 100644 (file)
@@ -1713,6 +1713,7 @@ set(regress_1_tests
   regress1/sygus/icfp_14_12_diff_types.sy
   regress1/sygus/icfp_28_10.sy
   regress1/sygus/icfp_easy-ite.sy
+  regress1/sygus/int-any-const.sy
   regress1/sygus/inv-example.sy
   regress1/sygus/inv-missed-sol-true.sy
   regress1/sygus/inv-unused.sy
@@ -1750,6 +1751,7 @@ set(regress_1_tests
   regress1/sygus/process-10-vars.sy
   regress1/sygus/qe.sy
   regress1/sygus/qf_abv.smt2
+  regress1/sygus/real-any-const.sy
   regress1/sygus/real-grammar.sy
   regress1/sygus/rec-fun-swap.sy
   regress1/sygus/rec-fun-sygus.sy
@@ -1760,6 +1762,7 @@ set(regress_1_tests
   regress1/sygus/repair-const-rl.sy
   regress1/sygus/simple-regexp.sy
   regress1/sygus/stopwatch-bt.sy
+  regress1/sygus/strings-any-term1.sy
   regress1/sygus/strings-no-syntax.sy
   regress1/sygus/strings-concat-3-args.sy
   regress1/sygus/strings-double-rec.sy
@@ -1924,6 +1927,7 @@ set(regress_3_tests
   regress3/pp-regfile.smtv1.smt2
   regress3/qwh.35.405.shuffled-as.sat03-1651.smtv1.smt2
   regress3/sixfuncs.sy
+  regress3/strings-any-term.sy
   regress3/strings/extf_d_perf.smt2
 )
 
diff --git a/test/regress/regress1/sygus/int-any-const.sy b/test/regress/regress1/sygus/int-any-const.sy
new file mode 100644 (file)
index 0000000..b5de575
--- /dev/null
@@ -0,0 +1,11 @@
+; EXPECT: unsat
+; COMMAND-LINE: --sygus-out=status --no-sygus-pbe --cegqi-si=none --sygus-grammar-cons=any-term-concise
+
+(set-logic LIA)
+(synth-fun f ((x Int) (y Int)) Int)
+(constraint (= (f 1 7) 15))
+(constraint (= (f 0 27) 27))
+(constraint (= (f 2 27) 43))
+; this example does not fit the polynomial solution to the above example, thus, we expect to construct an ITE
+(constraint (= (f 3 27) 43))
+(check-synth)
diff --git a/test/regress/regress1/sygus/real-any-const.sy b/test/regress/regress1/sygus/real-any-const.sy
new file mode 100644 (file)
index 0000000..2b59b73
--- /dev/null
@@ -0,0 +1,9 @@
+; EXPECT: unsat
+; COMMAND-LINE: --sygus-out=status --no-sygus-pbe --cegqi-si=none --sygus-grammar-cons=any-term-concise
+
+(set-logic LRA)
+(synth-fun f ((x Real) (y Real)) Real)
+(constraint (= (f 1.5 7.5) 59.7))
+(constraint (= (f 0.5 27.5) 174.9))
+(constraint (= (f 0.8 20.0) 131.04))
+(check-synth)
diff --git a/test/regress/regress1/sygus/strings-any-term1.sy b/test/regress/regress1/sygus/strings-any-term1.sy
new file mode 100644 (file)
index 0000000..3d8fd75
--- /dev/null
@@ -0,0 +1,10 @@
+; EXPECT: unsat
+; COMMAND-LINE: --sygus-out=status --no-sygus-pbe --cegqi-si=none --sygus-grammar-cons=any-term
+(set-logic ALL)
+(synth-fun f ((x String) (y String)) Int)
+(declare-var x String)
+(constraint (= (f "A" "BC") 11))
+(constraint (= (f "BB" "CC") 18))
+(constraint (= (f "BCB" "") 25))
+(constraint (= (f "BCBD" "") 32))
+(check-synth)
diff --git a/test/regress/regress3/strings-any-term.sy b/test/regress/regress3/strings-any-term.sy
new file mode 100644 (file)
index 0000000..88b30b2
--- /dev/null
@@ -0,0 +1,10 @@
+; EXPECT: unsat
+; COMMAND-LINE: --sygus-out=status --no-sygus-pbe --cegqi-si=none --sygus-grammar-cons=any-term --sygus-active-gen=none
+(set-logic ALL)
+(synth-fun f ((x String) (y String)) Int)
+(declare-var x String)
+(constraint (= (f "A" "BC") 207))
+(constraint (= (f "BB" "CC") 214))
+(constraint (= (f "BCB" "") 21))
+(constraint (= (f "BCBD" "") 28))
+(check-synth)