Improve pre-skolemization, move quantifiers preprocess to own file (#7153)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Wed, 8 Sep 2021 20:54:22 +0000 (15:54 -0500)
committerGitHub <noreply@github.com>
Wed, 8 Sep 2021 20:54:22 +0000 (20:54 +0000)
This also heavily refactors the preskolemization method (now in QuantifiersPreprocess), in preparation for it being enabled by default. This method previously was doing a tree traversal, it now maintains a visited cache.

It makes minor cleanup to the dependencies of this method.

18 files changed:
src/CMakeLists.txt
src/preprocessing/passes/quantifiers_preprocess.cpp
src/preprocessing/passes/sygus_inference.cpp
src/proof/proof_rule.h
src/smt/env_obj.cpp
src/smt/env_obj.h
src/theory/quantifiers/instantiate.cpp
src/theory/quantifiers/quant_util.cpp
src/theory/quantifiers/quant_util.h
src/theory/quantifiers/quantifiers_preprocess.cpp [new file with mode: 0644]
src/theory/quantifiers/quantifiers_preprocess.h [new file with mode: 0644]
src/theory/quantifiers/quantifiers_registry.cpp
src/theory/quantifiers/quantifiers_registry.h
src/theory/quantifiers/quantifiers_rewriter.cpp
src/theory/quantifiers/quantifiers_rewriter.h
src/theory/quantifiers/skolemize.cpp
src/theory/quantifiers/skolemize.h
src/theory/quantifiers/theory_quantifiers.cpp

index ba88080b8bfc47f1e720afcbf7318bafffcbc1c9..5c898b654e5692dcbd196e85cd74f7579c0f405d 100644 (file)
@@ -855,6 +855,8 @@ libcvc5_add_sources(
   theory/quantifiers/quantifiers_macros.h
   theory/quantifiers/quantifiers_modules.cpp
   theory/quantifiers/quantifiers_modules.h
+  theory/quantifiers/quantifiers_preprocess.cpp
+  theory/quantifiers/quantifiers_preprocess.h
   theory/quantifiers/quantifiers_registry.cpp
   theory/quantifiers/quantifiers_registry.h
   theory/quantifiers/quantifiers_rewriter.cpp
index a9363c549d54d6242a7d5ca35d882850510dd986..c0bb0ea7f28eec948e257bbdb3af3602a8ceb9f2 100644 (file)
@@ -20,7 +20,7 @@
 
 #include "base/output.h"
 #include "preprocessing/assertion_pipeline.h"
-#include "theory/quantifiers/quantifiers_rewriter.h"
+#include "theory/quantifiers/quantifiers_preprocess.h"
 #include "theory/rewriter.h"
 
 namespace cvc5 {
@@ -37,10 +37,11 @@ PreprocessingPassResult QuantifiersPreprocess::applyInternal(
     AssertionPipeline* assertionsToPreprocess)
 {
   size_t size = assertionsToPreprocess->size();
+  quantifiers::QuantifiersPreprocess qp(d_env);
   for (size_t i = 0; i < size; ++i)
   {
     Node prev = (*assertionsToPreprocess)[i];
-    TrustNode trn = quantifiers::QuantifiersRewriter::preprocess(prev);
+    TrustNode trn = qp.preprocess(prev);
     if (!trn.isNull())
     {
       Node next = trn.getNode();
index d105c436a110b58afc7e657cc081770e943bd21a..8194f9f52ad793f6e5e997219717a409cafb5b16 100644 (file)
@@ -21,7 +21,7 @@
 #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/quantifiers_preprocess.h"
 #include "theory/quantifiers/sygus/sygus_grammar_cons.h"
 #include "theory/quantifiers/sygus/sygus_utils.h"
 #include "theory/rewriter.h"
@@ -118,6 +118,7 @@ bool SygusInference::solveSygus(const std::vector<Node>& assertions,
 
   // process eassertions
   std::vector<Node> processed_assertions;
+  quantifiers::QuantifiersPreprocess qp(d_env);
   for (const Node& as : eassertions)
   {
     // substitution for this assertion
@@ -131,7 +132,7 @@ bool SygusInference::solveSygus(const std::vector<Node>& assertions,
     if (pas.getKind() == FORALL)
     {
       // preprocess the quantified formula
-      TrustNode trn = quantifiers::QuantifiersRewriter::preprocess(pas);
+      TrustNode trn = qp.preprocess(pas);
       if (!trn.isNull())
       {
         pas = trn.getNode();
index bc2b6437bf42266015c0efe31b25cb7e911f58df..7b93e3a55b8ccefd842122d91cf5348d2beab3fc 100644 (file)
@@ -840,7 +840,7 @@ enum class PfRule : uint32_t
   // Arguments: (F)
   // ---------------------------------------------------------------
   // Conclusion: F
-  // where F is an equality of the form t = QuantifiersRewriter::preprocess(t)
+  // where F is an equality of the form t = QuantifiersPreprocess::preprocess(t)
   QUANTIFIERS_PREPROCESS,
 
   //================================================= String rules
index fc50a359bab1dd0e5351c691e879ef6990969264..32c6e4b027bb08d0801477d0399d0b807633fca7 100644 (file)
@@ -24,9 +24,12 @@ namespace cvc5 {
 
 EnvObj::EnvObj(Env& env) : d_env(env) {}
 
-Node EnvObj::rewrite(TNode node) { return d_env.getRewriter()->rewrite(node); }
+Node EnvObj::rewrite(TNode node) const
+{
+  return d_env.getRewriter()->rewrite(node);
+}
 
-Node EnvObj::extendedRewrite(TNode node, bool aggr)
+Node EnvObj::extendedRewrite(TNode node, bool aggr) const
 {
   return d_env.getRewriter()->extendedRewrite(node, aggr);
 }
index ebe304dcf8e6ebdd871bc66420ec14cd4402b9b0..4b907c27b10f32431e7159cdd0e698d5699f852b 100644 (file)
@@ -48,12 +48,12 @@ class EnvObj
    * Rewrite a node.
    * This is a wrapper around theory::Rewriter::rewrite via Env.
    */
-  Node rewrite(TNode node);
+  Node rewrite(TNode node) const;
   /**
    * Extended rewrite a node.
    * This is a wrapper around theory::Rewriter::extendedRewrite via Env.
    */
-  Node extendedRewrite(TNode node, bool aggr = true);
+  Node extendedRewrite(TNode node, bool aggr = true) const;
 
   /** Get the current logic information. */
   const LogicInfo& logicInfo() const;
index 0daf53d2d61dbd5a6e94934a7fff87cdb5231089..9010d4fe17162108cd98c74f0d5e72ee5a09cbd8 100644 (file)
@@ -28,7 +28,7 @@
 #include "theory/quantifiers/cegqi/inst_strategy_cegqi.h"
 #include "theory/quantifiers/first_order_model.h"
 #include "theory/quantifiers/quantifiers_attributes.h"
-#include "theory/quantifiers/quantifiers_rewriter.h"
+#include "theory/quantifiers/quantifiers_preprocess.h"
 #include "theory/quantifiers/term_database.h"
 #include "theory/quantifiers/term_enumeration.h"
 #include "theory/quantifiers/term_registry.h"
@@ -252,7 +252,7 @@ bool Instantiate::addInstantiation(Node q,
       q, d_qreg.d_vars[q], terms, id, pfArg, doVts, pfTmp.get());
   Node orig_body = body;
   // now preprocess, storing the trust node for the rewrite
-  TrustNode tpBody = QuantifiersRewriter::preprocess(body, true);
+  TrustNode tpBody = d_qreg.getPreprocess().preprocess(body, true);
   if (!tpBody.isNull())
   {
     Assert(tpBody.getKind() == TrustNodeKind::REWRITE);
index 34235a193e7fcbf7e32553a06045f41574c3eb3a..64a816975e20b083eea5884ac55ce179a880351d 100644 (file)
@@ -93,7 +93,9 @@ void QuantPhaseReq::computePhaseReqs( Node n, bool polarity, std::map< Node, int
   }
 }
 
-void QuantPhaseReq::getPolarity( Node n, int child, bool hasPol, bool pol, bool& newHasPol, bool& newPol ) {
+void QuantPhaseReq::getPolarity(
+    Node n, size_t child, bool hasPol, bool pol, bool& newHasPol, bool& newPol)
+{
   if( n.getKind()==AND || n.getKind()==OR || n.getKind()==SEP_STAR ){
     newHasPol = hasPol;
     newPol = pol;
@@ -115,7 +117,9 @@ void QuantPhaseReq::getPolarity( Node n, int child, bool hasPol, bool pol, bool&
   }
 }
 
-void QuantPhaseReq::getEntailPolarity( Node n, int child, bool hasPol, bool pol, bool& newHasPol, bool& newPol ) {
+void QuantPhaseReq::getEntailPolarity(
+    Node n, size_t child, bool hasPol, bool pol, bool& newHasPol, bool& newPol)
+{
   if( n.getKind()==AND || n.getKind()==OR || n.getKind()==SEP_STAR ){
     newHasPol = hasPol && pol!=( n.getKind()==OR );
     newPol = pol;
index 5f91a9488b0757f28e36b19195d92abc883898ed..7ca17b6ad29fb1ca2713c22bb75bc8a282b4b374 100644 (file)
@@ -78,8 +78,35 @@ public:
   std::map< Node, bool > d_phase_reqs_equality;
   std::map< Node, Node > d_phase_reqs_equality_term;
 
-  static void getPolarity( Node n, int child, bool hasPol, bool pol, bool& newHasPol, bool& newPol );
-  static void getEntailPolarity( Node n, int child, bool hasPol, bool pol, bool& newHasPol, bool& newPol );
+  /**
+   * Get the polarity of the child^th child of n, assuming its polarity
+   * is given by (hasPol, pol). A term has polarity if it is only relevant
+   * if asserted with one polarity. Its polarity is (typically) the number
+   * of negations it is beneath.
+   */
+  static void getPolarity(Node n,
+                          size_t child,
+                          bool hasPol,
+                          bool pol,
+                          bool& newHasPol,
+                          bool& newPol);
+
+  /**
+   * Get the entailed polarity of the child^th child of n, assuming its
+   * entailed polarity is given by (hasPol, pol). A term has entailed polarity
+   * if it must be asserted with a polarity. Its polarity is (typically) the
+   * number of negations it is beneath.
+   *
+   * Entailed polarity and polarity above differ, e.g.:
+   *   (and A B): A and B have true polarity and true entailed polarity
+   *   (or A B): A and B have true polarity and no entailed polarity
+   */
+  static void getEntailPolarity(Node n,
+                                size_t child,
+                                bool hasPol,
+                                bool pol,
+                                bool& newHasPol,
+                                bool& newPol);
 };
 
 }
diff --git a/src/theory/quantifiers/quantifiers_preprocess.cpp b/src/theory/quantifiers/quantifiers_preprocess.cpp
new file mode 100644 (file)
index 0000000..19487bc
--- /dev/null
@@ -0,0 +1,267 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2021 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.
+ * ****************************************************************************
+ *
+ * Preprocessor for the theory of quantifiers.
+ */
+
+#include "theory/quantifiers/quantifiers_preprocess.h"
+
+#include "expr/node_algorithm.h"
+#include "options/quantifiers_options.h"
+#include "theory/quantifiers/quant_util.h"
+#include "theory/quantifiers/quantifiers_rewriter.h"
+#include "theory/quantifiers/skolemize.h"
+
+using namespace cvc5::kind;
+
+namespace cvc5 {
+namespace theory {
+namespace quantifiers {
+
+QuantifiersPreprocess::QuantifiersPreprocess(Env& env) : EnvObj(env) {}
+
+Node QuantifiersPreprocess::computePrenexAgg(
+    Node n, std::map<Node, Node>& visited) const
+{
+  std::map<Node, Node>::iterator itv = visited.find(n);
+  if (itv != visited.end())
+  {
+    return itv->second;
+  }
+  if (!expr::hasClosure(n))
+  {
+    // trivial
+    return n;
+  }
+  NodeManager* nm = NodeManager::currentNM();
+  Node ret = n;
+  if (n.getKind() == NOT)
+  {
+    ret = computePrenexAgg(n[0], visited).negate();
+  }
+  else if (n.getKind() == FORALL)
+  {
+    std::vector<Node> children;
+    children.push_back(computePrenexAgg(n[1], visited));
+    std::vector<Node> args;
+    args.insert(args.end(), n[0].begin(), n[0].end());
+    // for each child, strip top level quant
+    for (unsigned i = 0; i < children.size(); i++)
+    {
+      if (children[i].getKind() == FORALL)
+      {
+        args.insert(args.end(), children[i][0].begin(), children[i][0].end());
+        children[i] = children[i][1];
+      }
+    }
+    // keep the pattern
+    std::vector<Node> iplc;
+    if (n.getNumChildren() == 3)
+    {
+      iplc.insert(iplc.end(), n[2].begin(), n[2].end());
+    }
+    Node nb = nm->mkOr(children);
+    ret = QuantifiersRewriter::mkForall(args, nb, iplc, true);
+  }
+  else
+  {
+    std::unordered_set<Node> argsSet;
+    std::unordered_set<Node> nargsSet;
+    Node q;
+    Node nn =
+        QuantifiersRewriter::computePrenex(q, n, argsSet, nargsSet, true, true);
+    Assert(n != nn || argsSet.empty());
+    Assert(n != nn || nargsSet.empty());
+    if (n != nn)
+    {
+      Node nnn = computePrenexAgg(nn, visited);
+      // merge prenex
+      if (nnn.getKind() == FORALL)
+      {
+        argsSet.insert(nnn[0].begin(), nnn[0].end());
+        nnn = nnn[1];
+        // pos polarity variables are inner
+        if (!argsSet.empty())
+        {
+          nnn = QuantifiersRewriter::mkForall(
+              {argsSet.begin(), argsSet.end()}, nnn, true);
+        }
+        argsSet.clear();
+      }
+      else if (nnn.getKind() == NOT && nnn[0].getKind() == FORALL)
+      {
+        nargsSet.insert(nnn[0][0].begin(), nnn[0][0].end());
+        nnn = nnn[0][1].negate();
+      }
+      if (!nargsSet.empty())
+      {
+        nnn = QuantifiersRewriter::mkForall(
+                  {nargsSet.begin(), nargsSet.end()}, nnn.negate(), true)
+                  .negate();
+      }
+      if (!argsSet.empty())
+      {
+        nnn = QuantifiersRewriter::mkForall(
+            {argsSet.begin(), argsSet.end()}, nnn, true);
+      }
+      ret = nnn;
+    }
+  }
+  visited[n] = ret;
+  return ret;
+}
+
+Node QuantifiersPreprocess::preSkolemizeQuantifiers(
+    Node n,
+    bool polarity,
+    std::vector<TNode>& fvs,
+    std::unordered_map<std::pair<Node, bool>, Node, NodePolPairHashFunction>&
+        visited) const
+{
+  std::pair<Node, bool> key(n, polarity);
+  std::unordered_map<std::pair<Node, bool>, Node, NodePolPairHashFunction>::
+      iterator it = visited.find(key);
+  if (it != visited.end())
+  {
+    return it->second;
+  }
+  NodeManager* nm = NodeManager::currentNM();
+  Trace("pre-sk") << "Pre-skolem " << n << " " << polarity << " " << fvs.size()
+                  << std::endl;
+  if (n.getKind() == FORALL)
+  {
+    Node ret = n;
+    if (n.getNumChildren() == 3)
+    {
+      // Do not pre-skolemize quantified formulas with three children.
+      // This includes non-standard quantified formulas
+      // like recursive function definitions, or sygus conjectures, and
+      // quantified formulas with triggers.
+    }
+    else if (polarity)
+    {
+      if (options().quantifiers.preSkolemQuant
+          && options().quantifiers.preSkolemQuantNested)
+      {
+        std::vector<Node> children;
+        children.push_back(n[0]);
+        // add children to current scope
+        std::vector<TNode> fvss;
+        fvss.insert(fvss.end(), fvs.begin(), fvs.end());
+        fvss.insert(fvss.end(), n[0].begin(), n[0].end());
+        // process body in a new context
+        std::unordered_map<std::pair<Node, bool>, Node, NodePolPairHashFunction>
+            visitedSub;
+        Node pbody = preSkolemizeQuantifiers(n[1], polarity, fvss, visitedSub);
+        children.push_back(pbody);
+        // return processed quantifier
+        ret = nm->mkNode(FORALL, children);
+      }
+    }
+    else
+    {
+      // will skolemize current, process body
+      Node nn = preSkolemizeQuantifiers(n[1], polarity, fvs, visited);
+      std::vector<Node> sk;
+      Node sub;
+      std::vector<unsigned> sub_vars;
+      // return skolemized body
+      ret = Skolemize::mkSkolemizedBody(n, nn, fvs, sk, sub, sub_vars);
+    }
+    visited[key] = ret;
+    return ret;
+  }
+  // check if it contains a quantifier as a subterm
+  // if so, we may preprocess this node
+  if (!expr::hasClosure(n))
+  {
+    visited[key] = n;
+    return n;
+  }
+  Kind k = n.getKind();
+  Node ret = n;
+  Assert(n.getType().isBoolean());
+  if (k == ITE || (k == EQUAL && n[0].getType().isBoolean()))
+  {
+    if (options().quantifiers.preSkolemQuantAgg)
+    {
+      Node nn;
+      // must remove structure
+      if (k == ITE)
+      {
+        nn = nm->mkNode(AND,
+                        nm->mkNode(OR, n[0].notNode(), n[1]),
+                        nm->mkNode(OR, n[0], n[2]));
+      }
+      else if (k == EQUAL)
+      {
+        nn = nm->mkNode(AND,
+                        nm->mkNode(OR, n[0].notNode(), n[1]),
+                        nm->mkNode(OR, n[0], n[1].notNode()));
+      }
+      ret = preSkolemizeQuantifiers(nn, polarity, fvs, visited);
+    }
+  }
+  else if (k == AND || k == OR || k == NOT || k == IMPLIES)
+  {
+    std::vector<Node> children;
+    for (size_t i = 0, nchild = n.getNumChildren(); i < nchild; i++)
+    {
+      bool newHasPol;
+      bool newPol;
+      QuantPhaseReq::getPolarity(n, i, true, polarity, newHasPol, newPol);
+      Assert(newHasPol);
+      children.push_back(preSkolemizeQuantifiers(n[i], newPol, fvs, visited));
+    }
+    ret = nm->mkNode(k, children);
+  }
+  visited[key] = ret;
+  return ret;
+}
+
+TrustNode QuantifiersPreprocess::preprocess(Node n, bool isInst) const
+{
+  Node prev = n;
+  if (options().quantifiers.preSkolemQuant)
+  {
+    if (!isInst || !options().quantifiers.preSkolemQuantNested)
+    {
+      Trace("quantifiers-preprocess-debug")
+          << "Pre-skolemize " << n << "..." << std::endl;
+      // apply pre-skolemization to existential quantifiers
+      std::vector<TNode> fvs;
+      std::unordered_map<std::pair<Node, bool>, Node, NodePolPairHashFunction>
+          visited;
+      n = preSkolemizeQuantifiers(prev, true, fvs, visited);
+    }
+  }
+  // pull all quantifiers globally
+  if (options().quantifiers.prenexQuant == options::PrenexQuantMode::NORMAL)
+  {
+    Trace("quantifiers-prenex") << "Prenexing : " << n << std::endl;
+    std::map<Node, Node> visited;
+    n = computePrenexAgg(n, visited);
+    n = rewrite(n);
+    Trace("quantifiers-prenex") << "Prenexing returned : " << n << std::endl;
+  }
+  if (n != prev)
+  {
+    Trace("quantifiers-preprocess") << "Preprocess " << prev << std::endl;
+    Trace("quantifiers-preprocess") << "..returned " << n << std::endl;
+    return TrustNode::mkTrustRewrite(prev, n, nullptr);
+  }
+  return TrustNode::null();
+}
+
+}  // namespace quantifiers
+}  // namespace theory
+}  // namespace cvc5
diff --git a/src/theory/quantifiers/quantifiers_preprocess.h b/src/theory/quantifiers/quantifiers_preprocess.h
new file mode 100644 (file)
index 0000000..45b7ad0
--- /dev/null
@@ -0,0 +1,78 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2021 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.
+ * ****************************************************************************
+ *
+ * Preprocessor for the theory of quantifiers.
+ */
+
+#include "cvc5_private.h"
+
+#ifndef CVC5__THEORY__QUANTIFIERS__QUANTIFIERS_PREPROCESS_H
+#define CVC5__THEORY__QUANTIFIERS__QUANTIFIERS_PREPROCESS_H
+
+#include "proof/trust_node.h"
+#include "smt/env_obj.h"
+
+namespace cvc5 {
+namespace theory {
+namespace quantifiers {
+
+/**
+ * Module for doing preprocessing that is pertinent to quantifiers. These
+ * operations cannot be done in the rewriter since e.g. preskolemization
+ * depends on knowing the polarity of the position in which quantifiers occur.
+ */
+class QuantifiersPreprocess : protected EnvObj
+{
+ public:
+  QuantifiersPreprocess(Env& env);
+  /** preprocess
+   *
+   * This returns the result of applying simple quantifiers-specific
+   * pre-processing to n, including but not limited to:
+   * - pre-skolemization,
+   * - aggressive prenexing.
+   * The argument isInst is set to true if n is an instance of a previously
+   * registered quantified formula. If this flag is true, we do not apply
+   * certain steps like pre-skolemization since we know they will have no
+   * effect.
+   *
+   * The result is wrapped in a trust node of kind TrustNodeKind::REWRITE.
+   */
+  TrustNode preprocess(Node n, bool isInst = false) const;
+
+ private:
+  using NodePolPairHashFunction = PairHashFunction<Node, bool, std::hash<Node>>;
+  /**
+   * Pre-skolemize quantifiers. Return the pre-skolemized form of n.
+   *
+   * @param n The formula to preskolemize.
+   * @param polarity The polarity of n in the input.
+   * @param fvs The free variables
+   * @param visited Cache of visited (node, polarity) pairs.
+   */
+  Node preSkolemizeQuantifiers(
+      Node n,
+      bool polarity,
+      std::vector<TNode>& fvs,
+      std::unordered_map<std::pair<Node, bool>, Node, NodePolPairHashFunction>&
+          visited) const;
+  /**
+   * Apply prenexing aggressively. Returns the prenex normal form of n.
+   */
+  Node computePrenexAgg(Node n, std::map<Node, Node>& visited) const;
+};
+
+}  // namespace quantifiers
+}  // namespace theory
+}  // namespace cvc5
+
+#endif /* CVC5__THEORY__QUANTIFIERS__QUANTIFIERS_REWRITER_H */
index 7e3aebbb8bce67d04ec1c9456f91c826090ebfcc..4dc329d8ee9006865d0275f1dcac7d3c9a922554 100644 (file)
@@ -23,10 +23,11 @@ namespace cvc5 {
 namespace theory {
 namespace quantifiers {
 
-QuantifiersRegistry::QuantifiersRegistry()
+QuantifiersRegistry::QuantifiersRegistry(Env& env)
     : d_quantAttr(),
       d_quantBoundInf(options::fmfTypeCompletionThresh(),
-                      options::finiteModelFind())
+                      options::finiteModelFind()),
+      d_quantPreproc(env)
 {
 }
 
@@ -189,6 +190,10 @@ QuantifiersBoundInference& QuantifiersRegistry::getQuantifiersBoundInference()
 {
   return d_quantBoundInf;
 }
+QuantifiersPreprocess& QuantifiersRegistry::getPreprocess()
+{
+  return d_quantPreproc;
+}
 
 Node QuantifiersRegistry::getNameForQuant(Node q) const
 {
index ee99a284a6f2473409cab256bd178e04f1739b25..559939bbee8389c8918383448f9f533e688994f9 100644 (file)
@@ -22,6 +22,7 @@
 #include "theory/quantifiers/quant_bound_inference.h"
 #include "theory/quantifiers/quant_util.h"
 #include "theory/quantifiers/quantifiers_attributes.h"
+#include "theory/quantifiers/quantifiers_preprocess.h"
 
 namespace cvc5 {
 namespace theory {
@@ -42,7 +43,7 @@ class QuantifiersRegistry : public QuantifiersUtil
   friend class Instantiate;
 
  public:
-  QuantifiersRegistry();
+  QuantifiersRegistry(Env& env);
   ~QuantifiersRegistry() {}
   /**
    * Register quantifier, which allocates the instantiation constants for q.
@@ -91,6 +92,8 @@ class QuantifiersRegistry : public QuantifiersUtil
   QuantAttributes& getQuantAttributes();
   /** Get quantifiers bound inference utility */
   QuantifiersBoundInference& getQuantifiersBoundInference();
+  /** Get the preprocess utility */
+  QuantifiersPreprocess& getPreprocess();
   /**
    * Get quantifiers name, which returns a variable corresponding to the name of
    * quantified formula q if q has a name, or otherwise returns q itself.
@@ -126,6 +129,8 @@ class QuantifiersRegistry : public QuantifiersUtil
   QuantAttributes d_quantAttr;
   /** The quantifiers bound inference class */
   QuantifiersBoundInference d_quantBoundInf;
+  /** The quantifiers preprocessor utility */
+  QuantifiersPreprocess d_quantPreproc;
 };
 
 }  // namespace quantifiers
index e5662cdc6cd0c8016c6b7700aca00b950f5fd146..c66324445ad87ef15f32f92a622589ac226be43d 100644 (file)
@@ -1406,92 +1406,6 @@ Node QuantifiersRewriter::computePrenex(Node q,
   return body;
 }
 
-Node QuantifiersRewriter::computePrenexAgg(Node n,
-                                           std::map<Node, Node>& visited)
-{
-  std::map< Node, Node >::iterator itv = visited.find( n );
-  if( itv!=visited.end() ){
-    return itv->second;
-  }
-  if (!expr::hasClosure(n))
-  {
-    // trivial
-    return n;
-  }
-  NodeManager* nm = NodeManager::currentNM();
-  Node ret = n;
-  if (n.getKind() == NOT)
-  {
-    ret = computePrenexAgg(n[0], visited).negate();
-  }
-  else if (n.getKind() == FORALL)
-  {
-    std::vector<Node> children;
-    children.push_back(computePrenexAgg(n[1], visited));
-    std::vector<Node> args;
-    args.insert(args.end(), n[0].begin(), n[0].end());
-    // for each child, strip top level quant
-    for (unsigned i = 0; i < children.size(); i++)
-    {
-      if (children[i].getKind() == FORALL)
-      {
-        args.insert(args.end(), children[i][0].begin(), children[i][0].end());
-        children[i] = children[i][1];
-      }
-    }
-    // keep the pattern
-    std::vector<Node> iplc;
-    if (n.getNumChildren() == 3)
-    {
-      iplc.insert(iplc.end(), n[2].begin(), n[2].end());
-    }
-    Node nb = children.size() == 1 ? children[0] : nm->mkNode(OR, children);
-    ret = mkForall(args, nb, iplc, true);
-  }
-  else
-  {
-    std::unordered_set<Node> argsSet;
-    std::unordered_set<Node> nargsSet;
-    Node q;
-    Node nn = computePrenex(q, n, argsSet, nargsSet, true, true);
-    Assert(n != nn || argsSet.empty());
-    Assert(n != nn || nargsSet.empty());
-    if (n != nn)
-    {
-      Node nnn = computePrenexAgg(nn, visited);
-      // merge prenex
-      if (nnn.getKind() == FORALL)
-      {
-        argsSet.insert(nnn[0].begin(), nnn[0].end());
-        nnn = nnn[1];
-        // pos polarity variables are inner
-        if (!argsSet.empty())
-        {
-          nnn = mkForall({argsSet.begin(), argsSet.end()}, nnn, true);
-        }
-        argsSet.clear();
-      }
-      else if (nnn.getKind() == NOT && nnn[0].getKind() == FORALL)
-      {
-        nargsSet.insert(nnn[0][0].begin(), nnn[0][0].end());
-        nnn = nnn[0][1].negate();
-      }
-      if (!nargsSet.empty())
-      {
-        nnn = mkForall({nargsSet.begin(), nargsSet.end()}, nnn.negate(), true)
-                  .negate();
-      }
-      if (!argsSet.empty())
-      {
-        nnn = mkForall({argsSet.begin(), argsSet.end()}, nnn, true);
-      }
-      ret = nnn;
-    }
-  }
-  visited[n] = ret;
-  return ret;
-}
-
 Node QuantifiersRewriter::computeSplit( std::vector< Node >& args, Node body, QAttributes& qa ) {
   Assert(body.getKind() == OR);
   size_t var_found_count = 0;
@@ -1999,112 +1913,6 @@ bool QuantifiersRewriter::isPrenexNormalForm( Node n ) {
   }
 }
 
-Node QuantifiersRewriter::preSkolemizeQuantifiers( Node n, bool polarity, std::vector< TypeNode >& fvTypes, std::vector< TNode >& fvs ){
-  Trace("pre-sk") << "Pre-skolem " << n << " " << polarity << " " << fvs.size() << endl;
-  if( n.getKind()==kind::NOT ){
-    Node nn = preSkolemizeQuantifiers( n[0], !polarity, fvTypes, fvs );
-    return nn.negate();
-  }else if( n.getKind()==kind::FORALL ){
-    if (n.getNumChildren() == 3)
-    {
-      // Do not pre-skolemize quantified formulas with three children.
-      // This includes non-standard quantified formulas
-      // like recursive function definitions, or sygus conjectures, and
-      // quantified formulas with triggers.
-      return n;
-    }
-    else if (polarity)
-    {
-      if( options::preSkolemQuant() && options::preSkolemQuantNested() ){
-        vector< Node > children;
-        children.push_back( n[0] );
-        //add children to current scope
-        std::vector< TypeNode > fvt;
-        std::vector< TNode > fvss;
-        fvt.insert( fvt.begin(), fvTypes.begin(), fvTypes.end() );
-        fvss.insert( fvss.begin(), fvs.begin(), fvs.end() );
-        for( int i=0; i<(int)n[0].getNumChildren(); i++ ){
-          fvt.push_back( n[0][i].getType() );
-          fvss.push_back( n[0][i] );
-        }
-        //process body
-        children.push_back( preSkolemizeQuantifiers( n[1], polarity, fvt, fvss ) );
-        //return processed quantifier
-        return NodeManager::currentNM()->mkNode( kind::FORALL, children );
-      }
-    }else{
-      //process body
-      Node nn = preSkolemizeQuantifiers( n[1], polarity, fvTypes, fvs );
-      std::vector< Node > sk;
-      Node sub;
-      std::vector< unsigned > sub_vars;
-      //return skolemized body
-      return Skolemize::mkSkolemizedBody(
-          n, nn, fvTypes, fvs, sk, sub, sub_vars);
-    }
-  }else{
-    //check if it contains a quantifier as a subterm
-    //if so, we will write this node
-    if (expr::hasClosure(n))
-    {
-      if( ( n.getKind()==kind::ITE && n.getType().isBoolean() ) || ( n.getKind()==kind::EQUAL && n[0].getType().isBoolean() ) ){
-        if( options::preSkolemQuantAgg() ){
-          Node nn;
-          //must remove structure
-          if( n.getKind()==kind::ITE ){
-            nn = NodeManager::currentNM()->mkNode( kind::AND,
-                  NodeManager::currentNM()->mkNode( kind::OR, n[0].notNode(), n[1] ),
-                  NodeManager::currentNM()->mkNode( kind::OR, n[0], n[2] ) );
-          }else if( n.getKind()==kind::EQUAL ){
-            nn = NodeManager::currentNM()->mkNode( kind::AND,
-                  NodeManager::currentNM()->mkNode( kind::OR, n[0].notNode(), n.getKind()==kind::XOR ? n[1].notNode() : n[1] ),
-                  NodeManager::currentNM()->mkNode( kind::OR, n[0], n.getKind()==kind::XOR ? n[1] : n[1].notNode() ) );
-          }
-          return preSkolemizeQuantifiers( nn, polarity, fvTypes, fvs );
-        }
-      }else if( n.getKind()==kind::AND || n.getKind()==kind::OR ){
-        vector< Node > children;
-        for( int i=0; i<(int)n.getNumChildren(); i++ ){
-          children.push_back( preSkolemizeQuantifiers( n[i], polarity, fvTypes, fvs ) );
-        }
-        return NodeManager::currentNM()->mkNode( n.getKind(), children );
-      }
-    }
-  }
-  return n;
-}
-
-TrustNode QuantifiersRewriter::preprocess(Node n, bool isInst)
-{
-  Node prev = n;
-
-  if( options::preSkolemQuant() ){
-    if( !isInst || !options::preSkolemQuantNested() ){
-      Trace("quantifiers-preprocess-debug") << "Pre-skolemize " << n << "..." << std::endl;
-      //apply pre-skolemization to existential quantifiers
-      std::vector< TypeNode > fvTypes;
-      std::vector< TNode > fvs;
-      n = preSkolemizeQuantifiers( prev, true, fvTypes, fvs );
-    }
-  }
-  //pull all quantifiers globally
-  if (options::prenexQuant() == options::PrenexQuantMode::NORMAL)
-  {
-    Trace("quantifiers-prenex") << "Prenexing : " << n << std::endl;
-    std::map<Node, Node> visited;
-    n = computePrenexAgg(n, visited);
-    n = Rewriter::rewrite( n );
-    Trace("quantifiers-prenex") << "Prenexing returned : " << n << std::endl;
-    //Assert( isPrenexNormalForm( n ) );
-  }
-  if( n!=prev ){       
-    Trace("quantifiers-preprocess") << "Preprocess " << prev << std::endl;
-    Trace("quantifiers-preprocess") << "..returned " << n << std::endl;
-    return TrustNode::mkTrustRewrite(prev, n, nullptr);
-  }
-  return TrustNode::null();
-}
-
 }  // namespace quantifiers
 }  // namespace theory
 }  // namespace cvc5
index a7f1075730558d81242459226046d697b267d7dd..de5c0b0a4ed4fe5107fc5d231673b8f2c9ebbe2f 100644 (file)
@@ -273,10 +273,6 @@ class QuantifiersRewriter : public TheoryRewriter
                             std::unordered_set<Node>& nargs,
                             bool pol,
                             bool prenexAgg);
-  /**
-   * Apply prenexing aggressively. Returns the prenex normal form of n.
-   */
-  static Node computePrenexAgg(Node n, std::map<Node, Node>& visited);
   static Node computeSplit( std::vector< Node >& args, Node body, QAttributes& qa );
 private:
  static Node computeOperation(Node f,
@@ -291,25 +287,8 @@ private:
   /** options */
  static bool doOperation(Node f, RewriteStep computeOption, QAttributes& qa);
 
-private:
-  static Node preSkolemizeQuantifiers(Node n, bool polarity, std::vector< TypeNode >& fvTypes, std::vector<TNode>& fvs);
 public:
   static bool isPrenexNormalForm( Node n );
-  /** preprocess
-   *
-   * This returns the result of applying simple quantifiers-specific
-   * preprocessing to n, including but not limited to:
-   * - rewrite rule elimination,
-   * - pre-skolemization,
-   * - aggressive prenexing.
-   * The argument isInst is set to true if n is an instance of a previously
-   * registered quantified formula. If this flag is true, we do not apply
-   * certain steps like pre-skolemization since we know they will have no
-   * effect.
-   *
-   * The result is wrapped in a trust node of kind TrustNodeKind::REWRITE.
-   */
-  static TrustNode preprocess(Node n, bool isInst = false);
   static Node mkForAll(const std::vector<Node>& args,
                        Node body,
                        QAttributes& qa);
index fa91b17826ed08bc52a57c30197c20770a3077db..23b66b1de9fdda07793c0c51e05f2d6242b6eec2 100644 (file)
@@ -178,13 +178,18 @@ void Skolemize::getSelfSel(const DType& dt,
 
 Node Skolemize::mkSkolemizedBody(Node f,
                                  Node n,
-                                 std::vector<TypeNode>& argTypes,
                                  std::vector<TNode>& fvs,
                                  std::vector<Node>& sk,
                                  Node& sub,
                                  std::vector<unsigned>& sub_vars)
 {
   NodeManager* nm = NodeManager::currentNM();
+  // compute the argument types from the free variables
+  std::vector<TypeNode> argTypes;
+  for (TNode v : fvs)
+  {
+    argTypes.push_back(v.getType());
+  }
   SkolemManager* sm = nm->getSkolemManager();
   Assert(sk.empty() || sk.size() == f[0].getNumChildren());
   // calculate the variables and substitution
@@ -329,12 +334,11 @@ Node Skolemize::getSkolemizedBody(Node f)
   std::unordered_map<Node, Node>::iterator it = d_skolem_body.find(f);
   if (it == d_skolem_body.end())
   {
-    std::vector<TypeNode> fvTypes;
     std::vector<TNode> fvs;
     Node sub;
     std::vector<unsigned> sub_vars;
-    Node ret = mkSkolemizedBody(
-        f, f[1], fvTypes, fvs, d_skolem_constants[f], sub, sub_vars);
+    Node ret =
+        mkSkolemizedBody(f, f[1], fvs, d_skolem_constants[f], sub, sub_vars);
     d_skolem_body[f] = ret;
     // store sub quantifier information
     if (!sub.isNull())
index c8e6ec7dd8ab7b11810b5ac7b6858583b0becd59..fbb79f5d2f1a4b0746a391350f77e761b01c0088 100644 (file)
@@ -89,10 +89,10 @@ class Skolemize
    * The skolem constants/functions we generate by this
    * skolemization are added to sk.
    *
-   * The arguments fvTypes and fvs are used if we are
+   * The argument fvs are used if we are
    * performing skolemization within a nested quantified
    * formula. In this case, skolem constants we introduce
-   * must be parameterized based on fvTypes and must be
+   * must be parameterized based on the types of fvs and must be
    * applied to fvs.
    *
    * The last two arguments sub and sub_vars are used for
@@ -103,7 +103,6 @@ class Skolemize
    */
   static Node mkSkolemizedBody(Node q,
                                Node n,
-                               std::vector<TypeNode>& fvTypes,
                                std::vector<TNode>& fvs,
                                std::vector<Node>& sk,
                                Node& sub,
index 137e25c89cf02ed4cf6f3e6edf246c268135e6dd..76696c32ffc301e3e6ae6d58c4cc1aed8ec82aea 100644 (file)
@@ -35,7 +35,7 @@ TheoryQuantifiers::TheoryQuantifiers(Env& env,
                                      Valuation valuation)
     : Theory(THEORY_QUANTIFIERS, env, out, valuation),
       d_qstate(env, valuation, logicInfo()),
-      d_qreg(),
+      d_qreg(env),
       d_treg(env, d_qstate, d_qreg),
       d_qim(env, *this, d_qstate, d_qreg, d_treg, d_pnm),
       d_qengine(nullptr)