Support get-abduct smt2 command (#3122)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Mon, 29 Jul 2019 18:58:09 +0000 (13:58 -0500)
committerGitHub <noreply@github.com>
Mon, 29 Jul 2019 18:58:09 +0000 (13:58 -0500)
21 files changed:
src/CMakeLists.txt
src/bindings/java/CMakeLists.txt
src/options/quantifiers_options.toml
src/options/smt_options.toml
src/parser/smt2/Smt2.g
src/parser/smt2/smt2.cpp
src/preprocessing/passes/sygus_abduct.cpp [deleted file]
src/preprocessing/passes/sygus_abduct.h [deleted file]
src/preprocessing/preprocessing_pass_registry.cpp
src/smt/command.cpp
src/smt/command.h
src/smt/smt_engine.cpp
src/smt/smt_engine.h
src/theory/logic_info.cpp
src/theory/logic_info.h
src/theory/quantifiers/expr_miner.cpp
src/theory/quantifiers/sygus/sygus_abduct.cpp [new file with mode: 0644]
src/theory/quantifiers/sygus/sygus_abduct.h [new file with mode: 0644]
test/regress/CMakeLists.txt
test/regress/regress1/sygus-abduct-test-user.smt2 [new file with mode: 0644]
test/regress/regress1/sygus-abduct-test.smt2

index 9da9bd60353b37fc69f97f9bb01686ed90308ee3..86184163320fe8df0559a6ed42eaed27fe761bda 100644 (file)
@@ -90,8 +90,6 @@ libcvc4_add_sources(
   preprocessing/passes/sort_infer.h
   preprocessing/passes/static_learning.cpp
   preprocessing/passes/static_learning.h
-  preprocessing/passes/sygus_abduct.cpp
-  preprocessing/passes/sygus_abduct.h
   preprocessing/passes/sygus_inference.cpp
   preprocessing/passes/sygus_inference.h
   preprocessing/passes/symmetry_breaker.cpp
@@ -551,6 +549,8 @@ libcvc4_add_sources(
   theory/quantifiers/sygus/cegis_unif.h
   theory/quantifiers/sygus/enum_stream_substitution.cpp
   theory/quantifiers/sygus/enum_stream_substitution.h
+  theory/quantifiers/sygus/sygus_abduct.cpp
+  theory/quantifiers/sygus/sygus_abduct.h
   theory/quantifiers/sygus/sygus_enumerator.cpp
   theory/quantifiers/sygus/sygus_enumerator.h
   theory/quantifiers/sygus/sygus_eval_unfold.cpp
index a94afc0a01ae5e6ae7527e2c7b6cbb2df525db7d..555ab2d6f93901afab5ef8a8ad8193b88dd23749 100644 (file)
@@ -111,6 +111,7 @@ set(gen_java_files
   ${CMAKE_CURRENT_BINARY_DIR}/FloatingPointToUBV.java
   ${CMAKE_CURRENT_BINARY_DIR}/FloatingPointType.java
   ${CMAKE_CURRENT_BINARY_DIR}/FunctionType.java
+  ${CMAKE_CURRENT_BINARY_DIR}/GetAbductCommand.java
   ${CMAKE_CURRENT_BINARY_DIR}/GetAssertionsCommand.java
   ${CMAKE_CURRENT_BINARY_DIR}/GetAssignmentCommand.java
   ${CMAKE_CURRENT_BINARY_DIR}/GetInfoCommand.java
index 03ffade464b6791a895168aa193a3c2e3468a316..8792fb56b439202c5fe99203d4f4d96f05fdc13d 100644 (file)
@@ -885,15 +885,6 @@ header = "options/quantifiers_options.h"
   read_only  = false
   help       = "attempt to preprocess arbitrary inputs to sygus conjectures"
 
-[[option]]
-  name       = "sygusAbduct"
-  category   = "regular"
-  long       = "sygus-abduct"
-  type       = "bool"
-  default    = "false"
-  read_only  = false
-  help       = "compute abductions using sygus"
-
 [[option]]
   name       = "ceGuidedInst"
   category   = "regular"
index 72dfdd7f8e6119af08dcd2301d2de98fd456f708..ef4e19654f936ed71f9d908a6e5743492f938014 100644 (file)
@@ -614,3 +614,12 @@ header = "options/smt_options.h"
   default    = "false"
   read_only  = true
   help       = "attempt to solve a pure real satisfiable problem as an integer problem (for non-linear)"
+
+[[option]]
+  name       = "produceAbducts"
+  category   = "undocumented"
+  long       = "produce-abducts"
+  type       = "bool"
+  default    = "false"
+  read_only  = true
+  help       = "support the get-abduct command"
index d72188c6c253fa962b0b1028f6366700d58540d9..a95689f1ce128c7d291e19ddb9fc17d6b448b388 100644 (file)
@@ -1529,8 +1529,19 @@ extendedCommand[std::unique_ptr<CVC4::Command>* cmd]
   | GET_QE_DISJUNCT_TOK { PARSER_STATE->checkThatLogicIsSet(); }
     term[e,e2]
     { cmd->reset(new GetQuantifierEliminationCommand(e, false)); }
-  | DECLARE_HEAP LPAREN_TOK
-    sortSymbol[t,CHECK_DECLARED]
+  | GET_ABDUCT_TOK { 
+      PARSER_STATE->checkThatLogicIsSet();
+    }
+    symbol[name,CHECK_UNDECLARED,SYM_VARIABLE]
+    term[e,e2]
+    (
+      sygusGrammar[t, terms, name]
+    )? 
+    {
+      cmd->reset(new GetAbductCommand(name,e, t));
+    }
+  | DECLARE_HEAP LPAREN_TOK 
+    sortSymbol[t,CHECK_DECLARED] 
     sortSymbol[t, CHECK_DECLARED]
     // We currently do nothing with the type information declared for the heap.
     { cmd->reset(new EmptyCommand()); }
@@ -3055,6 +3066,7 @@ SIMPLIFY_TOK : 'simplify';
 INCLUDE_TOK : 'include';
 GET_QE_TOK : 'get-qe';
 GET_QE_DISJUNCT_TOK : 'get-qe-disjunct';
+GET_ABDUCT_TOK : 'get-abduct';
 DECLARE_HEAP : 'declare-heap';
 
 // SyGuS commands
index 278f2bdfd1d529805970a17f96124a247d9939d6..752bf58b403850bbb6ffe6764163c314914df378 100644 (file)
@@ -674,11 +674,8 @@ Command* Smt2::setLogic(std::string name, bool fromCommand)
     }
     // get unlocked copy, modify, copy and relock
     LogicInfo log(d_logic.getUnlockedCopy());
-    log.enableQuantifiers();
-    log.enableTheory(theory::THEORY_UF);
-    log.enableTheory(theory::THEORY_DATATYPES);
-    log.enableIntegers();
-    log.enableHigherOrder();
+    // enable everything needed for sygus
+    log.enableSygus();
     d_logic = log;
     d_logic.lock();
   }
diff --git a/src/preprocessing/passes/sygus_abduct.cpp b/src/preprocessing/passes/sygus_abduct.cpp
deleted file mode 100644 (file)
index b2dc872..0000000
+++ /dev/null
@@ -1,360 +0,0 @@
-/*********************                                                        */
-/*! \file sygus_abduct.cpp
- ** \verbatim
- ** Top contributors (to current version):
- **   Andrew Reynolds
- ** This file is part of the CVC4 project.
- ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS
- ** in the top-level source directory) and their institutional affiliations.
- ** All rights reserved.  See the file COPYING in the top-level source
- ** directory for licensing information.\endverbatim
- **
- ** \brief Implementation of sygus abduction preprocessing pass, which
- ** transforms an arbitrary input into an abduction problem.
- **/
-
-#include "preprocessing/passes/sygus_abduct.h"
-
-#include "expr/datatype.h"
-#include "expr/node_algorithm.h"
-#include "printer/sygus_print_callback.h"
-#include "smt/smt_engine.h"
-#include "smt/smt_engine_scope.h"
-#include "smt/smt_statistics_registry.h"
-#include "theory/quantifiers/quantifiers_attributes.h"
-#include "theory/quantifiers/quantifiers_rewriter.h"
-#include "theory/quantifiers/term_util.h"
-#include "theory/rewriter.h"
-
-using namespace std;
-using namespace CVC4::kind;
-
-namespace CVC4 {
-namespace preprocessing {
-namespace passes {
-
-SygusAbduct::SygusAbduct(PreprocessingPassContext* preprocContext)
-    : PreprocessingPass(preprocContext, "sygus-abduct"){};
-
-PreprocessingPassResult SygusAbduct::applyInternal(
-    AssertionPipeline* assertionsToPreprocess)
-{
-  Trace("sygus-abduct") << "Run sygus abduct..." << std::endl;
-
-  Trace("sygus-abduct-debug") << "Collect symbols..." << std::endl;
-  std::vector<Node>& asserts = assertionsToPreprocess->ref();
-  // do we have any assumptions, e.g. via check-sat-assuming?
-  bool usingAssumptions = (assertionsToPreprocess->getNumAssumptions() > 0);
-  // The following is our set of "axioms". We construct this set only when the
-  // usingAssumptions (above) is true. In this case, our input formula is
-  // partitioned into Fa ^ Fc as described in the header of this class, where:
-  // - The conjunction of assertions marked as assumptions are the negated
-  // conjecture Fc, and
-  // - The conjunction of all other assertions are the axioms Fa.
-  std::vector<Node> axioms;
-  if (usingAssumptions)
-  {
-    for (size_t i = 0, astart = assertionsToPreprocess->getAssumptionsStart();
-         i < astart;
-         i++)
-    {
-      // if we are not an assumption, add it to the set of axioms
-      axioms.push_back(asserts[i]);
-    }
-  }
-
-  // the abduction grammar type we are using (null for now, until a future
-  // commit)
-  TypeNode abdGType;
-
-  Node res = mkAbductionConjecture(asserts, axioms, abdGType);
-
-  Node trueNode = NodeManager::currentNM()->mkConst(true);
-
-  assertionsToPreprocess->replace(0, res);
-  for (size_t i = 1, size = assertionsToPreprocess->size(); i < size; ++i)
-  {
-    assertionsToPreprocess->replace(i, trueNode);
-  }
-
-  return PreprocessingPassResult::NO_CONFLICT;
-}
-
-Node SygusAbduct::mkAbductionConjecture(const std::vector<Node>& asserts,
-                                        const std::vector<Node>& axioms,
-                                        TypeNode abdGType)
-{
-  NodeManager* nm = NodeManager::currentNM();
-  std::unordered_set<Node, NodeHashFunction> symset;
-  for (size_t i = 0, size = asserts.size(); i < size; i++)
-  {
-    expr::getSymbols(asserts[i], symset);
-  }
-  Trace("sygus-abduct-debug")
-      << "...finish, got " << symset.size() << " symbols." << std::endl;
-
-  Trace("sygus-abduct-debug") << "Setup symbols..." << std::endl;
-  std::vector<Node> syms;
-  std::vector<Node> vars;
-  std::vector<Node> varlist;
-  std::vector<TypeNode> varlistTypes;
-  for (const Node& s : symset)
-  {
-    TypeNode tn = s.getType();
-    if (tn.isFirstClass())
-    {
-      std::stringstream ss;
-      ss << s;
-      Node var = nm->mkBoundVar(tn);
-      syms.push_back(s);
-      vars.push_back(var);
-      Node vlv = nm->mkBoundVar(ss.str(), tn);
-      varlist.push_back(vlv);
-      varlistTypes.push_back(tn);
-    }
-  }
-  // make the sygus variable list
-  Node abvl = nm->mkNode(BOUND_VAR_LIST, varlist);
-  Trace("sygus-abduct-debug") << "...finish" << std::endl;
-
-  Trace("sygus-abduct-debug") << "Make abduction predicate..." << std::endl;
-  // make the abduction predicate to synthesize
-  TypeNode abdType = varlistTypes.empty() ? nm->booleanType()
-                                          : nm->mkPredicateType(varlistTypes);
-  Node abd = nm->mkBoundVar("A", abdType);
-  Trace("sygus-abduct-debug") << "...finish" << std::endl;
-
-  // if provided, we will associate it with the function-to-synthesize
-  if (!abdGType.isNull())
-  {
-    Assert(abdGType.isDatatype() && abdGType.getDatatype().isSygus());
-    // must convert all constructors to version with bound variables in "vars"
-    std::vector<Datatype> datatypes;
-    std::set<Type> unres;
-
-    Trace("sygus-abduct-debug") << "Process abduction type:" << std::endl;
-    Trace("sygus-abduct-debug") << abdGType.getDatatype() << std::endl;
-
-    // datatype types we need to process
-    std::vector<TypeNode> dtToProcess;
-    // datatype types we have processed
-    std::map<TypeNode, TypeNode> dtProcessed;
-    dtToProcess.push_back(abdGType);
-    std::stringstream ssutn0;
-    ssutn0 << abdGType.getDatatype().getName() << "_s";
-    TypeNode abdTNew =
-        nm->mkSort(ssutn0.str(), ExprManager::SORT_FLAG_PLACEHOLDER);
-    unres.insert(abdTNew.toType());
-    dtProcessed[abdGType] = abdTNew;
-
-    // We must convert all symbols in the sygus datatype type abdGType to
-    // apply the substitution { syms -> varlist }, where syms is the free
-    // variables of the input problem, and varlist is the formal argument list
-    // of the abduct-to-synthesize. For example, given user-provided sygus
-    // grammar:
-    //   G -> a | +( b, G )
-    // we synthesize a abduct A with two arguments x_a and x_b corresponding to
-    // a and b, and reconstruct the grammar:
-    //   G' -> x_a | +( x_b, G' )
-    // In this way, x_a and x_b are treated as bound variables and handled as
-    // arguments of the abduct-to-synthesize instead of as free variables with
-    // no relation to A. We additionally require that x_a, when printed, prints
-    // "a", which we do with a custom sygus callback below.
-
-    // We are traversing over the subfield types of the datatype to convert
-    // them into the form described above.
-    while (!dtToProcess.empty())
-    {
-      std::vector<TypeNode> dtNextToProcess;
-      for (const TypeNode& curr : dtToProcess)
-      {
-        Assert(curr.isDatatype() && curr.getDatatype().isSygus());
-        const Datatype& dtc = curr.getDatatype();
-        std::stringstream ssdtn;
-        ssdtn << dtc.getName() << "_s";
-        datatypes.push_back(Datatype(ssdtn.str()));
-        Trace("sygus-abduct-debug")
-            << "Process datatype " << datatypes.back().getName() << "..."
-            << std::endl;
-        for (unsigned j = 0, ncons = dtc.getNumConstructors(); j < ncons; j++)
-        {
-          Node op = Node::fromExpr(dtc[j].getSygusOp());
-          // apply the substitution to the argument
-          Node ops = op.substitute(
-              syms.begin(), syms.end(), varlist.begin(), varlist.end());
-          Trace("sygus-abduct-debug") << "  Process constructor " << op << " / "
-                                      << ops << "..." << std::endl;
-          std::vector<Type> cargs;
-          for (unsigned k = 0, nargs = dtc[j].getNumArgs(); k < nargs; k++)
-          {
-            TypeNode argt = TypeNode::fromType(dtc[j].getArgType(k));
-            std::map<TypeNode, TypeNode>::iterator itdp =
-                dtProcessed.find(argt);
-            TypeNode argtNew;
-            if (itdp == dtProcessed.end())
-            {
-              std::stringstream ssutn;
-              ssutn << argt.getDatatype().getName() << "_s";
-              argtNew =
-                  nm->mkSort(ssutn.str(), ExprManager::SORT_FLAG_PLACEHOLDER);
-              Trace("sygus-abduct-debug")
-                  << "    ...unresolved type " << argtNew << " for " << argt
-                  << std::endl;
-              unres.insert(argtNew.toType());
-              dtProcessed[argt] = argtNew;
-              dtNextToProcess.push_back(argt);
-            }
-            else
-            {
-              argtNew = itdp->second;
-            }
-            Trace("sygus-abduct-debug")
-                << "    Arg #" << k << ": " << argtNew << std::endl;
-            cargs.push_back(argtNew.toType());
-          }
-          // callback prints as the expression
-          std::shared_ptr<SygusPrintCallback> spc;
-          std::vector<Expr> args;
-          if (op.getKind() == LAMBDA)
-          {
-            Node opBody = op[1];
-            for (const Node& v : op[0])
-            {
-              args.push_back(v.toExpr());
-            }
-            spc = std::make_shared<printer::SygusExprPrintCallback>(
-                opBody.toExpr(), args);
-          }
-          else if (cargs.empty())
-          {
-            spc = std::make_shared<printer::SygusExprPrintCallback>(op.toExpr(),
-                                                                    args);
-          }
-          std::stringstream ss;
-          ss << ops.getKind();
-          Trace("sygus-abduct-debug")
-              << "Add constructor : " << ops << std::endl;
-          datatypes.back().addSygusConstructor(
-              ops.toExpr(), ss.str(), cargs, spc);
-        }
-        Trace("sygus-abduct-debug")
-            << "Set sygus : " << dtc.getSygusType() << " " << abvl << std::endl;
-        datatypes.back().setSygus(dtc.getSygusType(),
-                                  abvl.toExpr(),
-                                  dtc.getSygusAllowConst(),
-                                  dtc.getSygusAllowAll());
-      }
-      dtToProcess.clear();
-      dtToProcess.insert(
-          dtToProcess.end(), dtNextToProcess.begin(), dtNextToProcess.end());
-    }
-    Trace("sygus-abduct-debug")
-        << "Make " << datatypes.size() << " datatype types..." << std::endl;
-    // make the datatype types
-    std::vector<DatatypeType> datatypeTypes =
-        nm->toExprManager()->mkMutualDatatypeTypes(
-            datatypes, unres, ExprManager::DATATYPE_FLAG_PLACEHOLDER);
-    TypeNode abdGTypeS = TypeNode::fromType(datatypeTypes[0]);
-    if (Trace.isOn("sygus-abduct-debug"))
-    {
-      Trace("sygus-abduct-debug") << "Made datatype types:" << std::endl;
-      for (unsigned j = 0, ndts = datatypeTypes.size(); j < ndts; j++)
-      {
-        const Datatype& dtj = datatypeTypes[j].getDatatype();
-        Trace("sygus-abduct-debug") << "#" << j << ": " << dtj << std::endl;
-        for (unsigned k = 0, ncons = dtj.getNumConstructors(); k < ncons; k++)
-        {
-          for (unsigned l = 0, nargs = dtj[k].getNumArgs(); l < nargs; l++)
-          {
-            if (!dtj[k].getArgType(l).isDatatype())
-            {
-              Trace("sygus-abduct-debug")
-                  << "Argument " << l << " of " << dtj[k]
-                  << " is not datatype : " << dtj[k].getArgType(l) << std::endl;
-              AlwaysAssert(false);
-            }
-          }
-        }
-      }
-    }
-
-    Trace("sygus-abduct-debug")
-        << "Make sygus grammar attribute..." << std::endl;
-    Node sym = nm->mkBoundVar("sfproxy_abduct", abdGTypeS);
-    // Set the sygus grammar attribute to indicate that abdGTypeS encodes the
-    // grammar for abd.
-    theory::SygusSynthGrammarAttribute ssg;
-    abd.setAttribute(ssg, sym);
-    Trace("sygus-abduct-debug") << "Finished setting up grammar." << std::endl;
-  }
-
-  Trace("sygus-abduct-debug") << "Make abduction predicate app..." << std::endl;
-  std::vector<Node> achildren;
-  achildren.push_back(abd);
-  achildren.insert(achildren.end(), vars.begin(), vars.end());
-  Node abdApp = vars.empty() ? abd : nm->mkNode(APPLY_UF, achildren);
-  Trace("sygus-abduct-debug") << "...finish" << std::endl;
-
-  Trace("sygus-abduct-debug") << "Set attributes..." << std::endl;
-  // set the sygus bound variable list
-  abd.setAttribute(theory::SygusSynthFunVarListAttribute(), abvl);
-  Trace("sygus-abduct-debug") << "...finish" << std::endl;
-
-  Trace("sygus-abduct-debug") << "Make conjecture body..." << std::endl;
-  Node input = asserts.size() == 1 ? asserts[0] : nm->mkNode(AND, asserts);
-  input = input.substitute(syms.begin(), syms.end(), vars.begin(), vars.end());
-  // A(x) => ~input( x )
-  input = nm->mkNode(OR, abdApp.negate(), input.negate());
-  Trace("sygus-abduct-debug") << "...finish" << std::endl;
-
-  Trace("sygus-abduct-debug") << "Make conjecture..." << std::endl;
-  Node res = input.negate();
-  if (!vars.empty())
-  {
-    Node bvl = nm->mkNode(BOUND_VAR_LIST, vars);
-    // exists x. ~( A( x ) => ~input( x ) )
-    res = nm->mkNode(EXISTS, bvl, res);
-  }
-  // sygus attribute
-  Node sygusVar = nm->mkSkolem("sygus", nm->booleanType());
-  theory::SygusAttribute ca;
-  sygusVar.setAttribute(ca, true);
-  Node instAttr = nm->mkNode(INST_ATTRIBUTE, sygusVar);
-  std::vector<Node> iplc;
-  iplc.push_back(instAttr);
-  if (!axioms.empty())
-  {
-    Node aconj = axioms.size() == 1 ? axioms[0] : nm->mkNode(AND, axioms);
-    aconj =
-        aconj.substitute(syms.begin(), syms.end(), vars.begin(), vars.end());
-    Trace("sygus-abduct") << "---> Assumptions: " << aconj << std::endl;
-    Node sc = nm->mkNode(AND, aconj, abdApp);
-    Node vbvl = nm->mkNode(BOUND_VAR_LIST, vars);
-    sc = nm->mkNode(EXISTS, vbvl, sc);
-    Node sygusScVar = nm->mkSkolem("sygus_sc", nm->booleanType());
-    sygusScVar.setAttribute(theory::SygusSideConditionAttribute(), sc);
-    instAttr = nm->mkNode(INST_ATTRIBUTE, sygusScVar);
-    // build in the side condition
-    //   exists x. A( x ) ^ input_axioms( x )
-    // as an additional annotation on the sygus conjecture. In other words,
-    // the abducts A we procedure must be consistent with our axioms.
-    iplc.push_back(instAttr);
-  }
-  Node instAttrList = nm->mkNode(INST_PATTERN_LIST, iplc);
-
-  Node fbvl = nm->mkNode(BOUND_VAR_LIST, abd);
-
-  // forall A. exists x. ~( A( x ) => ~input( x ) )
-  res = nm->mkNode(FORALL, fbvl, res, instAttrList);
-  Trace("sygus-abduct-debug") << "...finish" << std::endl;
-
-  res = theory::Rewriter::rewrite(res);
-
-  Trace("sygus-abduct") << "Generate: " << res << std::endl;
-
-  return res;
-}
-
-}  // namespace passes
-}  // namespace preprocessing
-}  // namespace CVC4
diff --git a/src/preprocessing/passes/sygus_abduct.h b/src/preprocessing/passes/sygus_abduct.h
deleted file mode 100644 (file)
index db40b96..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*********************                                                        */
-/*! \file sygus_abduct.h
- ** \verbatim
- ** Top contributors (to current version):
- **   Andrew Reynolds
- ** This file is part of the CVC4 project.
- ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS
- ** in the top-level source directory) and their institutional affiliations.
- ** All rights reserved.  See the file COPYING in the top-level source
- ** directory for licensing information.\endverbatim
- **
- ** \brief Sygus abduction preprocessing pass, which transforms an arbitrary
- ** input into an abduction problem.
- **/
-
-#ifndef CVC4__PREPROCESSING__PASSES__SYGUS_ABDUCT_H
-#define CVC4__PREPROCESSING__PASSES__SYGUS_ABDUCT_H
-
-#include "preprocessing/preprocessing_pass.h"
-#include "preprocessing/preprocessing_pass_context.h"
-
-namespace CVC4 {
-namespace preprocessing {
-namespace passes {
-
-/** SygusAbduct
- *
- * A preprocessing utility that turns a set of quantifier-free assertions into
- * a sygus conjecture that encodes an abduction problem. In detail, if our
- * input formula is F( x ) for free symbols x, then we construct the sygus
- * conjecture:
- *
- * exists A. forall x. ( A( x ) => ~F( x ) )
- *
- * where A( x ) is a predicate over the free symbols of our input. In other
- * words, A( x ) is a sufficient condition for showing ~F( x ).
- *
- * Another way to view this is A( x ) is any condition such that A( x ) ^ F( x )
- * is unsatisfiable.
- *
- * A common use case is to find the weakest such A that meets the above
- * specification. We do this by streaming solutions (sygus-stream) for A
- * while filtering stronger solutions (sygus-filter-sol=strong). These options
- * are enabled by default when this preprocessing class is used (sygus-abduct).
- *
- * If the input F( x ) is partitioned into axioms Fa and negated conjecture Fc
- * Fa( x ) ^ Fc( x ), then the sygus conjecture we construct is:
- *
- * exists A. ( exists y. A( y ) ^ Fa( y ) ) ^ forall x. ( A( x ) => ~F( x ) )
- *
- * In other words, A( y ) must be consistent with our axioms Fa and imply
- * ~F( x ). We encode this conjecture using SygusSideConditionAttribute.
- */
-class SygusAbduct : public PreprocessingPass
-{
- public:
-  SygusAbduct(PreprocessingPassContext* preprocContext);
-
-  /**
-   * Returns the sygus conjecture corresponding to the abduction problem for
-   * input problem (F above) given by asserts, and axioms (Fa above) given by
-   * axioms. Note that axioms is expected to be a subset of asserts.
-   *
-   * The type abdGType (if non-null) is a sygus datatype type that encodes the
-   * grammar that should be used for solutions of the abduction conjecture.
-   */
-  static Node mkAbductionConjecture(const std::vector<Node>& asserts,
-                                    const std::vector<Node>& axioms,
-                                    TypeNode abdGType);
-
- protected:
-  /**
-   * Replaces the set of assertions by an abduction sygus problem described
-   * above.
-   */
-  PreprocessingPassResult applyInternal(
-      AssertionPipeline* assertionsToPreprocess) override;
-};
-
-}  // namespace passes
-}  // namespace preprocessing
-}  // namespace CVC4
-
-#endif /* CVC4__PREPROCESSING__PASSES__SYGUS_ABDUCT_H_ */
index 15618f5759ea7f42e1ac63dd1a954a0cc6c4b78e..34bae12248151f4a5bc6140ad133fc708a6b00f4 100644 (file)
@@ -48,7 +48,6 @@
 #include "preprocessing/passes/sep_skolem_emp.h"
 #include "preprocessing/passes/sort_infer.h"
 #include "preprocessing/passes/static_learning.h"
-#include "preprocessing/passes/sygus_abduct.h"
 #include "preprocessing/passes/sygus_inference.h"
 #include "preprocessing/passes/symmetry_breaker.h"
 #include "preprocessing/passes/symmetry_detect.h"
@@ -127,7 +126,6 @@ PreprocessingPassRegistry::PreprocessingPassRegistry()
   registerPassInfo("synth-rr", callCtor<SynthRewRulesPass>);
   registerPassInfo("real-to-int", callCtor<RealToInt>);
   registerPassInfo("sygus-infer", callCtor<SygusInference>);
-  registerPassInfo("sygus-abduct", callCtor<SygusAbduct>);
   registerPassInfo("bv-to-bool", callCtor<BVToBool>);
   registerPassInfo("bv-intro-pow2", callCtor<BvIntroPow2>);
   registerPassInfo("sort-inference", callCtor<SortInferencePass>);
index b1936d8cc95bba5bd37b73e57065c8902bc0c0ea..044062f770387066d9276395660f16e0156e10d5 100644 (file)
@@ -2033,6 +2033,90 @@ std::string GetSynthSolutionCommand::getCommandName() const
   return "get-instantiations";
 }
 
+GetAbductCommand::GetAbductCommand() {}
+GetAbductCommand::GetAbductCommand(const std::string& name, Expr conj)
+    : d_name(name), d_conj(conj), d_resultStatus(false)
+{
+}
+GetAbductCommand::GetAbductCommand(const std::string& name,
+                                   Expr conj,
+                                   const Type& gtype)
+    : d_name(name),
+      d_conj(conj),
+      d_sygus_grammar_type(gtype),
+      d_resultStatus(false)
+{
+}
+
+Expr GetAbductCommand::getConjecture() const { return d_conj; }
+Type GetAbductCommand::getGrammarType() const { return d_sygus_grammar_type; }
+Expr GetAbductCommand::getResult() const { return d_result; }
+
+void GetAbductCommand::invoke(SmtEngine* smtEngine)
+{
+  try
+  {
+    if (d_sygus_grammar_type.isNull())
+    {
+      d_resultStatus = smtEngine->getAbduct(d_conj, d_result);
+    }
+    else
+    {
+      d_resultStatus =
+          smtEngine->getAbduct(d_conj, d_sygus_grammar_type, d_result);
+    }
+    d_commandStatus = CommandSuccess::instance();
+  }
+  catch (exception& e)
+  {
+    d_commandStatus = new CommandFailure(e.what());
+  }
+}
+
+void GetAbductCommand::printResult(std::ostream& out, uint32_t verbosity) const
+{
+  if (!ok())
+  {
+    this->Command::printResult(out, verbosity);
+  }
+  else
+  {
+    expr::ExprDag::Scope scope(out, false);
+    if (d_resultStatus)
+    {
+      out << "(define-fun " << d_name << " () Bool " << d_result << ")"
+          << std::endl;
+    }
+    else
+    {
+      out << "none" << std::endl;
+    }
+  }
+}
+
+Command* GetAbductCommand::exportTo(ExprManager* exprManager,
+                                    ExprManagerMapCollection& variableMap)
+{
+  GetAbductCommand* c =
+      new GetAbductCommand(d_name, d_conj.exportTo(exprManager, variableMap));
+  c->d_sygus_grammar_type =
+      d_sygus_grammar_type.exportTo(exprManager, variableMap);
+  c->d_result = d_result.exportTo(exprManager, variableMap);
+  c->d_resultStatus = d_resultStatus;
+  return c;
+}
+
+Command* GetAbductCommand::clone() const
+{
+  GetAbductCommand* c = new GetAbductCommand(d_name, d_conj);
+  c->d_sygus_grammar_type = d_sygus_grammar_type;
+  c->d_result = d_result;
+  c->d_resultStatus = d_resultStatus;
+  return c;
+}
+
+std::string GetAbductCommand::getCommandName() const { return "get-abduct"; }
+
 /* -------------------------------------------------------------------------- */
 /* class GetQuantifierEliminationCommand                                      */
 /* -------------------------------------------------------------------------- */
index 68f9d18815bcedc114d97653ed040cf394704b19..eb319994473ba30088f5e6e57e1ee41010334c3a 100644 (file)
@@ -1009,6 +1009,56 @@ class CVC4_PUBLIC GetSynthSolutionCommand : public Command
   SmtEngine* d_smtEngine;
 }; /* class GetSynthSolutionCommand */
 
+/** The command (get-abduct s B (G)?)
+ *
+ * This command asks for an abduct from the current set of assertions and
+ * conjecture (goal) given by the argument B.
+ *
+ * The symbol s is the name for the abduction predicate. If we successfully
+ * find a predicate P, then the output response of this command is:
+ *   (define-fun s () Bool P)
+ *
+ * A grammar type G can be optionally provided to indicate the syntactic
+ * restrictions on the possible solutions returned.
+ */
+class CVC4_PUBLIC GetAbductCommand : public Command
+{
+ public:
+  GetAbductCommand();
+  GetAbductCommand(const std::string& name, Expr conj);
+  GetAbductCommand(const std::string& name, Expr conj, const Type& gtype);
+
+  /** Get the conjecture of the abduction query */
+  Expr getConjecture() const;
+  /** Get the grammar type given for the abduction query */
+  Type getGrammarType() const;
+  /** Get the result of the query, which is the solution to the abduction query.
+   */
+  Expr getResult() const;
+
+  void invoke(SmtEngine* smtEngine) override;
+  void printResult(std::ostream& out, uint32_t verbosity = 2) const override;
+  Command* exportTo(ExprManager* exprManager,
+                    ExprManagerMapCollection& variableMap) override;
+  Command* clone() const override;
+  std::string getCommandName() const override;
+
+ protected:
+  /** The name of the abduction predicate */
+  std::string d_name;
+  /** The conjecture of the abduction query */
+  Expr d_conj;
+  /**
+   * The (optional) grammar of the abduction query, expressed as a sygus
+   * datatype type.
+   */
+  Type d_sygus_grammar_type;
+  /** the return status of the command */
+  bool d_resultStatus;
+  /** the return expression of the command */
+  Expr d_result;
+}; /* class GetAbductCommand */
+
 class CVC4_PUBLIC GetQuantifierEliminationCommand : public Command
 {
  protected:
index 5e85403481233001f558da8a47fe953203748e80..5db3fc43d8153ace69edcc331e593310c116b8f8 100644 (file)
@@ -98,6 +98,7 @@
 #include "theory/quantifiers/fun_def_process.h"
 #include "theory/quantifiers/quantifiers_rewriter.h"
 #include "theory/quantifiers/single_inv_partition.h"
+#include "theory/quantifiers/sygus/sygus_abduct.h"
 #include "theory/quantifiers/sygus/synth_engine.h"
 #include "theory/quantifiers/term_util.h"
 #include "theory/rewriter.h"
@@ -1258,21 +1259,25 @@ void SmtEngine::setDefaults() {
   // sygus inference may require datatypes
   if (!d_isInternalSubsolver)
   {
-    if (options::sygusInference() || options::sygusRewSynthInput()
-        || options::sygusAbduct())
+    if (options::produceAbducts())
+    {
+      // we may invoke a sygus conjecture, hence we need options related to
+      // sygus
+      is_sygus = true;
+    }
+    if (options::sygusInference() || options::sygusRewSynthInput())
     {
-      d_logic = d_logic.getUnlockedCopy();
-      // sygus requires arithmetic, datatypes and quantifiers
-      d_logic.enableTheory(THEORY_ARITH);
-      d_logic.enableTheory(THEORY_DATATYPES);
-      d_logic.enableTheory(THEORY_QUANTIFIERS);
-      d_logic.lock();
       // since we are trying to recast as sygus, we assume the input is sygus
       is_sygus = true;
+      // we change the logic to incorporate sygus immediately
+      d_logic = d_logic.getUnlockedCopy();
+      d_logic.enableSygus();
+      d_logic.lock();
     }
   }
 
   if ((options::checkModels() || options::checkSynthSol()
+       || options::produceAbducts()
        || options::modelCoresMode() != MODEL_CORES_NONE)
       && !options::produceAssertions())
   {
@@ -1954,16 +1959,24 @@ void SmtEngine::setDefaults() {
         options::sygusExtRew.set(false);
       }
     }
-    if (options::sygusAbduct())
+    // Whether we must use "basic" sygus algorithms. A non-basic sygus algorithm
+    // is one that is specialized for returning a single solution. Non-basic
+    // sygus algorithms currently include the PBE solver, static template
+    // inference for invariant synthesis, and single invocation techniques.
+    bool reqBasicSygus = false;
+    if (options::produceAbducts())
     {
       // if doing abduction, we should filter strong solutions
       if (!options::sygusFilterSolMode.wasSetByUser())
       {
         options::sygusFilterSolMode.set(quantifiers::SYGUS_FILTER_SOL_STRONG);
       }
+      // we must use basic sygus algorithms, since e.g. we require checking
+      // a sygus side condition for consistency with axioms.
+      reqBasicSygus = true;
     }
     if (options::sygusRewSynth() || options::sygusRewVerify()
-        || options::sygusQueryGen() || options::sygusAbduct())
+        || options::sygusQueryGen())
     {
       // rewrite rule synthesis implies that sygus stream must be true
       options::sygusStream.set(true);
@@ -1971,9 +1984,12 @@ void SmtEngine::setDefaults() {
     if (options::sygusStream())
     {
       // Streaming is incompatible with techniques that focus the search towards
-      // finding a single solution. This currently includes the PBE solver,
-      // static template inference for invariant synthesis, and single
-      // invocation techniques.
+      // finding a single solution.
+      reqBasicSygus = true;
+    }
+    // Now, disable options for non-basic sygus algorithms, if necessary.
+    if (reqBasicSygus)
+    {
       if (!options::sygusUnifPbe.wasSetByUser())
       {
         options::sygusUnifPbe.set(false);
@@ -2291,11 +2307,11 @@ void SmtEngine::setDefaults() {
           "--sygus-expr-miner-check-timeout=N requires "
           "--sygus-expr-miner-check-use-export");
     }
-    if (options::sygusRewSynthInput() || options::sygusAbduct())
+    if (options::sygusRewSynthInput() || options::produceAbducts())
     {
       std::stringstream ss;
       ss << (options::sygusRewSynthInput() ? "--sygus-rr-synth-input"
-                                           : "--sygus-abduct");
+                                           : "--produce-abducts");
       ss << "requires --sygus-expr-miner-check-use-export";
       throw OptionException(ss.str());
     }
@@ -3325,10 +3341,6 @@ void SmtEnginePrivate::processAssertions() {
     {
       d_passes["sygus-infer"]->apply(&d_assertions);
     }
-    else if (options::sygusAbduct())
-    {
-      d_passes["sygus-abduct"]->apply(&d_assertions);
-    }
     else if (options::sygusRewSynthInput())
     {
       // do candidate rewrite rule synthesis
@@ -4961,6 +4973,96 @@ Expr SmtEngine::doQuantifierElimination(const Expr& e, bool doFull, bool strict)
   }
 }
 
+bool SmtEngine::getAbduct(const Expr& conj, const Type& grammarType, Expr& abd)
+{
+  SmtScope smts(this);
+
+  if (!options::produceAbducts())
+  {
+    const char* msg = "Cannot get abduct when produce-abducts options is off.";
+    throw ModalException(msg);
+  }
+  Trace("sygus-abduct") << "SmtEngine::getAbduct: conjecture " << conj
+                        << std::endl;
+  std::vector<Expr> easserts = getAssertions();
+  std::vector<Node> axioms;
+  for (unsigned i = 0, size = easserts.size(); i < size; i++)
+  {
+    axioms.push_back(Node::fromExpr(easserts[i]));
+  }
+  std::vector<Node> asserts(axioms.begin(), axioms.end());
+  asserts.push_back(Node::fromExpr(conj));
+  d_sssfVarlist.clear();
+  d_sssfSyms.clear();
+  std::string name("A");
+  Node aconj = theory::quantifiers::SygusAbduct::mkAbductionConjecture(
+      name,
+      asserts,
+      axioms,
+      TypeNode::fromType(grammarType),
+      d_sssfVarlist,
+      d_sssfSyms);
+  // should be a quantified conjecture with one function-to-synthesize
+  Assert(aconj.getKind() == kind::FORALL && aconj[0].getNumChildren() == 1);
+  // remember the abduct-to-synthesize
+  d_sssf = aconj[0][0].toExpr();
+  Trace("sygus-abduct") << "SmtEngine::getAbduct: made conjecture : " << aconj
+                        << ", solving for " << d_sssf << std::endl;
+  // we generate a new smt engine to do the abduction query
+  d_subsolver.reset(new SmtEngine(NodeManager::currentNM()->toExprManager()));
+  d_subsolver->setIsInternalSubsolver();
+  // get the logic
+  LogicInfo l = d_logic.getUnlockedCopy();
+  // enable everything needed for sygus
+  l.enableSygus();
+  d_subsolver->setLogic(l);
+  // assert the abduction query
+  d_subsolver->assertFormula(aconj.toExpr());
+  Trace("sygus-abduct") << "  SmtEngine::getAbduct check sat..." << std::endl;
+  Result r = d_subsolver->checkSat();
+  Trace("sygus-abduct") << "  SmtEngine::getAbduct result: " << r << std::endl;
+  if (r.asSatisfiabilityResult().isSat() == Result::UNSAT)
+  {
+    // get the synthesis solution
+    std::map<Expr, Expr> sols;
+    d_subsolver->getSynthSolutions(sols);
+    Assert(sols.size() == 1);
+    std::map<Expr, Expr>::iterator its = sols.find(d_sssf);
+    if (its != sols.end())
+    {
+      Node abdn = Node::fromExpr(its->second);
+      Trace("sygus-abduct")
+          << "SmtEngine::getAbduct: solution is " << abdn << std::endl;
+      if (abdn.getKind() == kind::LAMBDA)
+      {
+        abdn = abdn[1];
+      }
+      // convert back to original
+      // must replace formal arguments of abd with the free variables in the
+      // input problem that they correspond to.
+      abdn = abdn.substitute(d_sssfVarlist.begin(),
+                             d_sssfVarlist.end(),
+                             d_sssfSyms.begin(),
+                             d_sssfSyms.end());
+
+      // convert to expression
+      abd = abdn.toExpr();
+
+      return true;
+    }
+    Trace("sygus-abduct") << "SmtEngine::getAbduct: could not find solution!"
+                          << std::endl;
+    throw RecoverableModalException("Could not find solution for get-abduct.");
+  }
+  return false;
+}
+
+bool SmtEngine::getAbduct(const Expr& conj, Expr& abd)
+{
+  Type grammarType;
+  return getAbduct(conj, grammarType, abd);
+}
+
 void SmtEngine::getInstantiatedQuantifiedFormulas( std::vector< Expr >& qs ) {
   SmtScope smts(this);
   if( d_theoryEngine ){
index 5913716e606f472834faca248c48bc8db8be522a..566777a89b1fcac8001ebcbe5729dbc34dfe8bd9 100644 (file)
@@ -147,6 +147,34 @@ class CVC4_PUBLIC SmtEngine {
   ProofManager* d_proofManager;
   /** An index of our defined functions */
   DefinedFunctionMap* d_definedFunctions;
+  /** The SMT engine subsolver
+   *
+   * This is a separate copy of the SMT engine which is used for making
+   * calls that cannot be answered by this copy of the SMT engine. An example
+   * of invoking this subsolver is the get-abduct command, where we wish to
+   * solve a sygus conjecture based on the current assertions. In particular,
+   * consider the input:
+   *   (assert A)
+   *   (get-abduct B)
+   * In the copy of the SMT engine where these commands are issued, we maintain
+   * A in the assertion stack. To solve the abduction problem, instead of
+   * modifying the assertion stack to remove A and add the sygus conjecture
+   * (exists I. ...), we invoke a fresh copy of the SMT engine and leave the
+   * assertion stack unchaged. This copy of the SMT engine can be further
+   * queried for information regarding further solutions.
+   */
+  std::unique_ptr<SmtEngine> d_subsolver;
+  /**
+   * If applicable, the function-to-synthesize that the subsolver is solving
+   * for. This is used for the get-abduct command.
+   */
+  Expr d_sssf;
+  /**
+   * The substitution to apply to the solutions from the subsolver, used for
+   * the get-abduct command.
+   */
+  std::vector<Node> d_sssfVarlist;
+  std::vector<Node> d_sssfSyms;
   /** recursive function definition abstractions for --fmf-fun */
   std::map< Node, TypeNode > d_fmfRecFunctionsAbs;
   std::map< Node, std::vector< Node > > d_fmfRecFunctionsConcrete;
@@ -849,6 +877,21 @@ class CVC4_PUBLIC SmtEngine {
   Expr doQuantifierElimination(const Expr& e,
                                bool doFull,
                                bool strict = true) /* throw(Exception) */;
+  /**
+   * This method asks this SMT engine to find an abduct with respect to the
+   * current assertion stack (call it A) and the conjecture (call it B).
+   * If this method returns true, then abd is set to a formula C such that
+   * A ^ C is satisfiable, and A ^ B ^ C is unsatisfiable.
+   *
+   * The argument grammarType is a sygus datatype type that encodes the syntax
+   * restrictions on the shape of possible solutions.
+   *
+   * This method invokes a separate copy of the SMT engine for solving the
+   * corresponding sygus problem for generating such a solution.
+   */
+  bool getAbduct(const Expr& conj, const Type& grammarType, Expr& abd);
+  /** Same as above, but without user-provided grammar restrictions */
+  bool getAbduct(const Expr& conj, Expr& abd);
 
   /**
    * Get list of quantified formulas that were instantiated
index e21d1f6309a18743d9dccf92b7304ade8a71f4b0..37b25163a4e5cf3af3a4b3fb7722d905b98f65bc 100644 (file)
@@ -579,6 +579,15 @@ void LogicInfo::disableTheory(theory::TheoryId theory) {
   }
 }
 
+void LogicInfo::enableSygus()
+{
+  enableQuantifiers();
+  enableTheory(THEORY_UF);
+  enableTheory(THEORY_DATATYPES);
+  enableIntegers();
+  enableHigherOrder();
+}
+
 void LogicInfo::enableIntegers() {
   PrettyCheckArgument(!d_locked, *this, "This LogicInfo is locked, and cannot be modified");
   d_logicString = "";
index a765472ddd58cb758613a0ca468afd7905e5d697..969810a6f705916ed9f26b6eb2c7af4c462793ef 100644 (file)
@@ -201,6 +201,12 @@ public:
     disableTheory(theory::THEORY_QUANTIFIERS);
   }
 
+  /**
+   * Enable everything that is needed for sygus with respect to this logic info.
+   * This means enabling quantifiers, datatypes, UF, integers, and higher order.
+   */
+  void enableSygus();
+
   // these are for arithmetic
 
   /** Enable the use of integers in this logic. */
index 98d07fdea7c618f812756e03378b233e4bd859c0..6b042e29494a91e773e21d917a6a7785b0640991 100644 (file)
@@ -91,7 +91,6 @@ void ExprMiner::initializeChecker(std::unique_ptr<SmtEngine>& checker,
       checker->setTimeLimit(options::sygusExprMinerCheckTimeout(), true);
       checker->setLogic(smt::currentSmtEngine()->getLogicInfo());
       checker->setOption("sygus-rr-synth-input", false);
-      checker->setOption("sygus-abduct", false);
       checker->setOption("input-language", "smt2");
       Expr equery = squery.toExpr().exportTo(&em, varMap);
       checker->assertFormula(equery);
diff --git a/src/theory/quantifiers/sygus/sygus_abduct.cpp b/src/theory/quantifiers/sygus/sygus_abduct.cpp
new file mode 100644 (file)
index 0000000..52fb60c
--- /dev/null
@@ -0,0 +1,313 @@
+/*********************                                                        */
+/*! \file sygus_abduct.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ **   Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved.  See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief Implementation of sygus abduction utility, which
+ ** transforms an arbitrary input into an abduction problem.
+ **/
+
+#include "theory/quantifiers/sygus/sygus_abduct.h"
+
+#include "expr/datatype.h"
+#include "expr/node_algorithm.h"
+#include "printer/sygus_print_callback.h"
+#include "theory/quantifiers/quantifiers_attributes.h"
+#include "theory/quantifiers/quantifiers_rewriter.h"
+#include "theory/quantifiers/term_util.h"
+#include "theory/rewriter.h"
+
+using namespace std;
+using namespace CVC4::kind;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+SygusAbduct::SygusAbduct() {}
+
+Node SygusAbduct::mkAbductionConjecture(const std::string& name,
+                                        const std::vector<Node>& asserts,
+                                        const std::vector<Node>& axioms,
+                                        TypeNode abdGType,
+                                        std::vector<Node>& varlist,
+                                        std::vector<Node>& syms)
+{
+  NodeManager* nm = NodeManager::currentNM();
+  std::unordered_set<Node, NodeHashFunction> symset;
+  for (size_t i = 0, size = asserts.size(); i < size; i++)
+  {
+    expr::getSymbols(asserts[i], symset);
+  }
+  Trace("sygus-abduct-debug")
+      << "...finish, got " << symset.size() << " symbols." << std::endl;
+
+  Trace("sygus-abduct-debug") << "Setup symbols..." << std::endl;
+  std::vector<Node> vars;
+  std::vector<TypeNode> varlistTypes;
+  for (const Node& s : symset)
+  {
+    TypeNode tn = s.getType();
+    if (tn.isFirstClass())
+    {
+      std::stringstream ss;
+      ss << s;
+      Node var = nm->mkBoundVar(tn);
+      syms.push_back(s);
+      vars.push_back(var);
+      Node vlv = nm->mkBoundVar(ss.str(), tn);
+      varlist.push_back(vlv);
+      varlistTypes.push_back(tn);
+    }
+  }
+  // make the sygus variable list
+  Node abvl = nm->mkNode(BOUND_VAR_LIST, varlist);
+  Trace("sygus-abduct-debug") << "...finish" << std::endl;
+
+  Trace("sygus-abduct-debug") << "Make abduction predicate..." << std::endl;
+  // make the abduction predicate to synthesize
+  TypeNode abdType = varlistTypes.empty() ? nm->booleanType()
+                                          : nm->mkPredicateType(varlistTypes);
+  Node abd = nm->mkBoundVar(name.c_str(), abdType);
+  Trace("sygus-abduct-debug") << "...finish" << std::endl;
+
+  // if provided, we will associate it with the function-to-synthesize
+  if (!abdGType.isNull())
+  {
+    Assert(abdGType.isDatatype() && abdGType.getDatatype().isSygus());
+    // must convert all constructors to version with bound variables in "vars"
+    std::vector<Datatype> datatypes;
+    std::set<Type> unres;
+
+    Trace("sygus-abduct-debug") << "Process abduction type:" << std::endl;
+    Trace("sygus-abduct-debug") << abdGType.getDatatype() << std::endl;
+
+    // datatype types we need to process
+    std::vector<TypeNode> dtToProcess;
+    // datatype types we have processed
+    std::map<TypeNode, TypeNode> dtProcessed;
+    dtToProcess.push_back(abdGType);
+    std::stringstream ssutn0;
+    ssutn0 << abdGType.getDatatype().getName() << "_s";
+    TypeNode abdTNew =
+        nm->mkSort(ssutn0.str(), ExprManager::SORT_FLAG_PLACEHOLDER);
+    unres.insert(abdTNew.toType());
+    dtProcessed[abdGType] = abdTNew;
+
+    // We must convert all symbols in the sygus datatype type abdGType to
+    // apply the substitution { syms -> varlist }, where syms is the free
+    // variables of the input problem, and varlist is the formal argument list
+    // of the abduct-to-synthesize. For example, given user-provided sygus
+    // grammar:
+    //   G -> a | +( b, G )
+    // we synthesize a abduct A with two arguments x_a and x_b corresponding to
+    // a and b, and reconstruct the grammar:
+    //   G' -> x_a | +( x_b, G' )
+    // In this way, x_a and x_b are treated as bound variables and handled as
+    // arguments of the abduct-to-synthesize instead of as free variables with
+    // no relation to A. We additionally require that x_a, when printed, prints
+    // "a", which we do with a custom sygus callback below.
+
+    // We are traversing over the subfield types of the datatype to convert
+    // them into the form described above.
+    while (!dtToProcess.empty())
+    {
+      std::vector<TypeNode> dtNextToProcess;
+      for (const TypeNode& curr : dtToProcess)
+      {
+        Assert(curr.isDatatype() && curr.getDatatype().isSygus());
+        const Datatype& dtc = curr.getDatatype();
+        std::stringstream ssdtn;
+        ssdtn << dtc.getName() << "_s";
+        datatypes.push_back(Datatype(ssdtn.str()));
+        Trace("sygus-abduct-debug")
+            << "Process datatype " << datatypes.back().getName() << "..."
+            << std::endl;
+        for (unsigned j = 0, ncons = dtc.getNumConstructors(); j < ncons; j++)
+        {
+          Node op = Node::fromExpr(dtc[j].getSygusOp());
+          // apply the substitution to the argument
+          Node ops = op.substitute(
+              syms.begin(), syms.end(), varlist.begin(), varlist.end());
+          Trace("sygus-abduct-debug") << "  Process constructor " << op << " / "
+                                      << ops << "..." << std::endl;
+          std::vector<Type> cargs;
+          for (unsigned k = 0, nargs = dtc[j].getNumArgs(); k < nargs; k++)
+          {
+            TypeNode argt = TypeNode::fromType(dtc[j].getArgType(k));
+            std::map<TypeNode, TypeNode>::iterator itdp =
+                dtProcessed.find(argt);
+            TypeNode argtNew;
+            if (itdp == dtProcessed.end())
+            {
+              std::stringstream ssutn;
+              ssutn << argt.getDatatype().getName() << "_s";
+              argtNew =
+                  nm->mkSort(ssutn.str(), ExprManager::SORT_FLAG_PLACEHOLDER);
+              Trace("sygus-abduct-debug")
+                  << "    ...unresolved type " << argtNew << " for " << argt
+                  << std::endl;
+              unres.insert(argtNew.toType());
+              dtProcessed[argt] = argtNew;
+              dtNextToProcess.push_back(argt);
+            }
+            else
+            {
+              argtNew = itdp->second;
+            }
+            Trace("sygus-abduct-debug")
+                << "    Arg #" << k << ": " << argtNew << std::endl;
+            cargs.push_back(argtNew.toType());
+          }
+          // callback prints as the expression
+          std::shared_ptr<SygusPrintCallback> spc;
+          std::vector<Expr> args;
+          if (op.getKind() == LAMBDA)
+          {
+            Node opBody = op[1];
+            for (const Node& v : op[0])
+            {
+              args.push_back(v.toExpr());
+            }
+            spc = std::make_shared<printer::SygusExprPrintCallback>(
+                opBody.toExpr(), args);
+          }
+          else if (cargs.empty())
+          {
+            spc = std::make_shared<printer::SygusExprPrintCallback>(op.toExpr(),
+                                                                    args);
+          }
+          std::stringstream ss;
+          ss << ops.getKind();
+          Trace("sygus-abduct-debug")
+              << "Add constructor : " << ops << std::endl;
+          datatypes.back().addSygusConstructor(
+              ops.toExpr(), ss.str(), cargs, spc);
+        }
+        Trace("sygus-abduct-debug")
+            << "Set sygus : " << dtc.getSygusType() << " " << abvl << std::endl;
+        datatypes.back().setSygus(dtc.getSygusType(),
+                                  abvl.toExpr(),
+                                  dtc.getSygusAllowConst(),
+                                  dtc.getSygusAllowAll());
+      }
+      dtToProcess.clear();
+      dtToProcess.insert(
+          dtToProcess.end(), dtNextToProcess.begin(), dtNextToProcess.end());
+    }
+    Trace("sygus-abduct-debug")
+        << "Make " << datatypes.size() << " datatype types..." << std::endl;
+    // make the datatype types
+    std::vector<DatatypeType> datatypeTypes =
+        nm->toExprManager()->mkMutualDatatypeTypes(
+            datatypes, unres, ExprManager::DATATYPE_FLAG_PLACEHOLDER);
+    TypeNode abdGTypeS = TypeNode::fromType(datatypeTypes[0]);
+    if (Trace.isOn("sygus-abduct-debug"))
+    {
+      Trace("sygus-abduct-debug") << "Made datatype types:" << std::endl;
+      for (unsigned j = 0, ndts = datatypeTypes.size(); j < ndts; j++)
+      {
+        const Datatype& dtj = datatypeTypes[j].getDatatype();
+        Trace("sygus-abduct-debug") << "#" << j << ": " << dtj << std::endl;
+        for (unsigned k = 0, ncons = dtj.getNumConstructors(); k < ncons; k++)
+        {
+          for (unsigned l = 0, nargs = dtj[k].getNumArgs(); l < nargs; l++)
+          {
+            if (!dtj[k].getArgType(l).isDatatype())
+            {
+              Trace("sygus-abduct-debug")
+                  << "Argument " << l << " of " << dtj[k]
+                  << " is not datatype : " << dtj[k].getArgType(l) << std::endl;
+              AlwaysAssert(false);
+            }
+          }
+        }
+      }
+    }
+
+    Trace("sygus-abduct-debug")
+        << "Make sygus grammar attribute..." << std::endl;
+    Node sym = nm->mkBoundVar("sfproxy_abduct", abdGTypeS);
+    // Set the sygus grammar attribute to indicate that abdGTypeS encodes the
+    // grammar for abd.
+    theory::SygusSynthGrammarAttribute ssg;
+    abd.setAttribute(ssg, sym);
+    Trace("sygus-abduct-debug") << "Finished setting up grammar." << std::endl;
+  }
+
+  Trace("sygus-abduct-debug") << "Make abduction predicate app..." << std::endl;
+  std::vector<Node> achildren;
+  achildren.push_back(abd);
+  achildren.insert(achildren.end(), vars.begin(), vars.end());
+  Node abdApp = vars.empty() ? abd : nm->mkNode(APPLY_UF, achildren);
+  Trace("sygus-abduct-debug") << "...finish" << std::endl;
+
+  Trace("sygus-abduct-debug") << "Set attributes..." << std::endl;
+  // set the sygus bound variable list
+  abd.setAttribute(theory::SygusSynthFunVarListAttribute(), abvl);
+  Trace("sygus-abduct-debug") << "...finish" << std::endl;
+
+  Trace("sygus-abduct-debug") << "Make conjecture body..." << std::endl;
+  Node input = asserts.size() == 1 ? asserts[0] : nm->mkNode(AND, asserts);
+  input = input.substitute(syms.begin(), syms.end(), vars.begin(), vars.end());
+  // A(x) => ~input( x )
+  input = nm->mkNode(OR, abdApp.negate(), input.negate());
+  Trace("sygus-abduct-debug") << "...finish" << std::endl;
+
+  Trace("sygus-abduct-debug") << "Make conjecture..." << std::endl;
+  Node res = input.negate();
+  if (!vars.empty())
+  {
+    Node bvl = nm->mkNode(BOUND_VAR_LIST, vars);
+    // exists x. ~( A( x ) => ~input( x ) )
+    res = nm->mkNode(EXISTS, bvl, res);
+  }
+  // sygus attribute
+  Node sygusVar = nm->mkSkolem("sygus", nm->booleanType());
+  theory::SygusAttribute ca;
+  sygusVar.setAttribute(ca, true);
+  Node instAttr = nm->mkNode(INST_ATTRIBUTE, sygusVar);
+  std::vector<Node> iplc;
+  iplc.push_back(instAttr);
+  if (!axioms.empty())
+  {
+    Node aconj = axioms.size() == 1 ? axioms[0] : nm->mkNode(AND, axioms);
+    aconj =
+        aconj.substitute(syms.begin(), syms.end(), vars.begin(), vars.end());
+    Trace("sygus-abduct") << "---> Assumptions: " << aconj << std::endl;
+    Node sc = nm->mkNode(AND, aconj, abdApp);
+    Node vbvl = nm->mkNode(BOUND_VAR_LIST, vars);
+    sc = nm->mkNode(EXISTS, vbvl, sc);
+    Node sygusScVar = nm->mkSkolem("sygus_sc", nm->booleanType());
+    sygusScVar.setAttribute(theory::SygusSideConditionAttribute(), sc);
+    instAttr = nm->mkNode(INST_ATTRIBUTE, sygusScVar);
+    // build in the side condition
+    //   exists x. A( x ) ^ input_axioms( x )
+    // as an additional annotation on the sygus conjecture. In other words,
+    // the abducts A we procedure must be consistent with our axioms.
+    iplc.push_back(instAttr);
+  }
+  Node instAttrList = nm->mkNode(INST_PATTERN_LIST, iplc);
+
+  Node fbvl = nm->mkNode(BOUND_VAR_LIST, abd);
+
+  // forall A. exists x. ~( A( x ) => ~input( x ) )
+  res = nm->mkNode(FORALL, fbvl, res, instAttrList);
+  Trace("sygus-abduct-debug") << "...finish" << std::endl;
+
+  res = theory::Rewriter::rewrite(res);
+
+  Trace("sygus-abduct") << "Generate: " << res << std::endl;
+
+  return res;
+}
+
+}  // namespace quantifiers
+}  // namespace theory
+}  // namespace CVC4
diff --git a/src/theory/quantifiers/sygus/sygus_abduct.h b/src/theory/quantifiers/sygus/sygus_abduct.h
new file mode 100644 (file)
index 0000000..d6bbd0e
--- /dev/null
@@ -0,0 +1,90 @@
+/*********************                                                        */
+/*! \file sygus_abduct.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ **   Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved.  See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief Sygus abduction utility, which transforms an arbitrary input into an
+ ** abduction problem.
+ **/
+
+#ifndef CVC4__THEORY__QUANTIFIERS__SYGUS_ABDUCT_H
+#define CVC4__THEORY__QUANTIFIERS__SYGUS_ABDUCT_H
+
+#include <string>
+#include <vector>
+#include "expr/node.h"
+#include "expr/type.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+/** SygusAbduct
+ *
+ * A utility that turns a set of quantifier-free assertions into
+ * a sygus conjecture that encodes an abduction problem. In detail, if our
+ * input formula is F( x ) for free symbols x, then we construct the sygus
+ * conjecture:
+ *
+ * exists A. forall x. ( A( x ) => ~F( x ) )
+ *
+ * where A( x ) is a predicate over the free symbols of our input. In other
+ * words, A( x ) is a sufficient condition for showing ~F( x ).
+ *
+ * Another way to view this is A( x ) is any condition such that A( x ) ^ F( x )
+ * is unsatisfiable.
+ *
+ * A common use case is to find the weakest such A that meets the above
+ * specification. We do this by streaming solutions (sygus-stream) for A
+ * while filtering stronger solutions (sygus-filter-sol=strong). These options
+ * are enabled by default when this preprocessing class is used (sygus-abduct).
+ *
+ * If the input F( x ) is partitioned into axioms Fa and negated conjecture Fc
+ * Fa( x ) ^ Fc( x ), then the sygus conjecture we construct is:
+ *
+ * exists A. ( exists y. A( y ) ^ Fa( y ) ) ^ forall x. ( A( x ) => ~F( x ) )
+ *
+ * In other words, A( y ) must be consistent with our axioms Fa and imply
+ * ~F( x ). We encode this conjecture using SygusSideConditionAttribute.
+ */
+class SygusAbduct
+{
+ public:
+  SygusAbduct();
+
+  /**
+   * Returns the sygus conjecture corresponding to the abduction problem for
+   * input problem (F above) given by asserts, and axioms (Fa above) given by
+   * axioms. Note that axioms is expected to be a subset of asserts.
+   *
+   * The argument name is the name for the abduct-to-synthesize.
+   *
+   * The type abdGType (if non-null) is a sygus datatype type that encodes the
+   * grammar that should be used for solutions of the abduction conjecture.
+   *
+   * The arguments varlist and syms specify the relationship between the free
+   * variables of asserts and the formal argument list of the
+   * abduct-to-synthesize. In particular, solutions to the synthesis conjecture
+   * will be in the form of a closed term (lambda varlist. t). The intended
+   * solution, which is a term whose free variables are a subset of asserts,
+   * is the term t { varlist -> syms }.
+   */
+  static Node mkAbductionConjecture(const std::string& name,
+                                    const std::vector<Node>& asserts,
+                                    const std::vector<Node>& axioms,
+                                    TypeNode abdGType,
+                                    std::vector<Node>& varlist,
+                                    std::vector<Node>& syms);
+};
+
+}  // namespace quantifiers
+}  // namespace theory
+}  // namespace CVC4
+
+#endif /* CVC4__THEORY__QUANTIFIERS__SYGUS_ABDUCT_H */
index 62672878ed090370f4370a48ad8339a7a59d9127..5ecacd4ab310ed8bf3dd34b22b80bfa4aaca5ea9 100644 (file)
@@ -1618,6 +1618,7 @@ set(regress_1_tests
   regress1/strings/type003.smt2
   regress1/strings/username_checker_min.smt2
   regress1/sygus-abduct-test.smt2
+  regress1/sygus-abduct-test-user.smt2
   regress1/sygus/VC22_a.sy
   regress1/sygus/abv.sy
   regress1/sygus/array_search_2.sy
diff --git a/test/regress/regress1/sygus-abduct-test-user.smt2 b/test/regress/regress1/sygus-abduct-test-user.smt2
new file mode 100644 (file)
index 0000000..bdb6806
--- /dev/null
@@ -0,0 +1,28 @@
+; COMMAND-LINE: --produce-abducts
+; SCRUBBER: grep -v -E '(\(define-fun)'
+; EXIT: 0
+(set-logic QF_UFLIRA)
+(set-option :produce-abducts true)
+(declare-fun n () Int)
+(declare-fun m () Int)
+(declare-fun x () Int)
+(declare-fun y () Int)
+
+(assert (>= n 1))
+(assert (and (<= n x)(<= x (+ n 5))))
+(assert (and (<= 1 y)(<= y m)))
+
+; Generate a predicate A that is consistent with the above axioms (i.e.
+; their conjunction is SAT), and is such that the conjunction of the above
+; axioms, A and the conjecture below are UNSAT.
+; The signature of A is below grammar.
+(get-abduct A (< x y)
+
+; the grammar for the abduct-to-synthesize
+((Start Bool) (StartInt Int))
+(
+(Start Bool ((< StartInt StartInt)))
+(StartInt Int (n m (+ StartInt StartInt) 0 1))
+)
+
+)
index 4ac90870c0036eb49f7cac36825f1348d37e2cde..d01f5f5ff45a922356696487928fc952c87edcb0 100644 (file)
@@ -1,4 +1,4 @@
-; COMMAND-LINE: --sygus-abduct --sygus-abort-size=2 
+; COMMAND-LINE: --produce-abducts --sygus-stream --sygus-abort-size=2
 ; EXPECT: (error "Maximum term size (2) for enumerative SyGuS exceeded.")
 ; SCRUBBER: grep -v -E '(\(define-fun)'
 ; EXIT: 1
@@ -13,4 +13,4 @@
 (assert (and (<= n x)(<= x (+ n 5))))
 (assert (and (<= 1 y)(<= y m)))
 
-(check-sat-assuming ((< x y)))
+(get-abduct A (< x y))