bv: Assert input facts on user-level 0. (#6515)
authorMathias Preiner <mathias.preiner@gmail.com>
Fri, 14 May 2021 00:29:52 +0000 (17:29 -0700)
committerGitHub <noreply@github.com>
Fri, 14 May 2021 00:29:52 +0000 (00:29 +0000)
The bitblast solver currently uses solving under assumptions for all facts that are sent to the bit-vector solver. For input facts on user-level 0 we can however assert the fact to the SAT solver, which allows the SAT solver to do more preprocessing. This PR adds the option to assert user-level 0 input facts, which is disabled by default.

src/options/bv_options.toml
src/prop/minisat/core/Solver.h
src/prop/minisat/minisat.cpp
src/prop/minisat/minisat.h
src/prop/prop_engine.cpp
src/prop/prop_engine.h
src/prop/sat_solver.h
src/theory/bv/bv_solver_bitblast.cpp
src/theory/bv/bv_solver_bitblast.h
src/theory/valuation.cpp
src/theory/valuation.h

index 3172f8d5f2a19a653d09749398f477154f8dee7e..34bdfcdaad093231fd5f550d0d64849e48f7e028 100644 (file)
@@ -243,3 +243,11 @@ name   = "Bitvector theory"
   name = "simple"
   help = "Enables simple bitblasting solver with proof support."
 
+[[option]]
+  name       = "bvAssertInput"
+  category   = "regular"
+  long       = "bv-assert-input"
+  type       = "bool"
+  default    = "false"
+  help       = "assert input assertions on user-level 0 instead of assuming them in the bit-vector SAT solver"
+
index a9ee25c113d518c78fe124e0e2b2b38ae98fba93..f4aa04e4dc0214a6d359b64927820ab8954132d5 100644 (file)
@@ -531,12 +531,12 @@ protected:
     bool     isPropagated     (Var x) const; // Does the variable have a propagated variables
     bool     isPropagatedBy   (Var x, const Clause& c) const; // Is the value of the variable propagated by the clause Clause C
 
-    int      level            (Var x) const;
-    int      user_level       (Var x) const; // User level at which this variable was asserted
-    int      intro_level      (Var x) const; // User level at which this variable was created
     int      trail_index      (Var x) const; // Index in the trail
     double   progressEstimate ()      const; // DELETE THIS ?? IT'S NOT VERY USEFUL ...
 public:
+    int      level            (Var x) const;
+    int      user_level       (Var x) const; // User level at which this variable was asserted
+    int      intro_level      (Var x) const; // User level at which this variable was created
     bool     withinBudget     (uint64_t amount)      const;
     bool withinBudget(Resource r) const;
 
index b09ffd685d5364829d7df6e2a26c3ac68a6a6527..293e7175b368a0df903b1958c2979750c0cc956b 100644 (file)
@@ -259,6 +259,16 @@ bool MinisatSatSolver::isDecision(SatVariable decn) const {
   return d_minisat->isDecision( decn );
 }
 
+int32_t MinisatSatSolver::getDecisionLevel(SatVariable v) const
+{
+  return d_minisat->level(v);
+}
+
+int32_t MinisatSatSolver::getIntroLevel(SatVariable v) const
+{
+  return d_minisat->intro_level(v);
+}
+
 SatProofManager* MinisatSatSolver::getProofManager()
 {
   return d_minisat->getProofManager();
index a4bd1e7a0800f2cf22199ddc66b7dc14de068f7c..0e6ca28b83f94aaf649a7e0e8284e1e68a045121 100644 (file)
@@ -85,6 +85,10 @@ class MinisatSatSolver : public CDCLTSatSolverInterface
 
   bool isDecision(SatVariable decn) const override;
 
+  int32_t getDecisionLevel(SatVariable v) const override;
+
+  int32_t getIntroLevel(SatVariable v) const override;
+
   /** Retrieve a pointer to the unerlying solver. */
   Minisat::SimpSolver* getSolver() { return d_minisat; }
 
index 70401c56d09a93b30492a893c2993a16a169dd11..63591e74bce8e4569ffb043186571769a6980c22 100644 (file)
@@ -340,6 +340,20 @@ bool PropEngine::isDecision(Node lit) const {
   return d_satSolver->isDecision(d_cnfStream->getLiteral(lit).getSatVariable());
 }
 
+int32_t PropEngine::getDecisionLevel(Node lit) const
+{
+  Assert(isSatLiteral(lit));
+  return d_satSolver->getDecisionLevel(
+      d_cnfStream->getLiteral(lit).getSatVariable());
+}
+
+int32_t PropEngine::getIntroLevel(Node lit) const
+{
+  Assert(isSatLiteral(lit));
+  return d_satSolver->getIntroLevel(
+      d_cnfStream->getLiteral(lit).getSatVariable());
+}
+
 void PropEngine::printSatisfyingAssignment(){
   const CnfStream::NodeToLiteralMap& transCache =
     d_cnfStream->getTranslationCache();
index 631317c2d675b8c21d80228fdcd7ffc6ac020e35..a128169060da0b08b466f939847d5a1a6432be04 100644 (file)
@@ -156,6 +156,22 @@ class PropEngine
    */
   bool isDecision(Node lit) const;
 
+  /**
+   * Return the current decision level of `lit`.
+   *
+   * @param lit: The node in question, must have an associated SAT literal.
+   * @return Decision level of the SAT variable of `lit` (phase is disregarded),
+   *         or -1 if `lit` has not been assigned yet.
+   */
+  int32_t getDecisionLevel(Node lit) const;
+
+  /**
+   * Return the user-context level when `lit` was introduced..
+   *
+   * @return User-context level or -1 if not yet introduced.
+   */
+  int32_t getIntroLevel(Node lit) const;
+
   /**
    * Checks the current context for satisfiability.
    *
index 1c64c92c25ef3852566b99ec71c150a84215f3c0..963810594ca7e4817a22f7ffbdf1fe5d41f2b959 100644 (file)
@@ -168,6 +168,16 @@ class CDCLTSatSolverInterface : public SatSolver
 
   virtual bool isDecision(SatVariable decn) const = 0;
 
+  /**
+   * Return the current decision level of `lit`.
+   */
+  virtual int32_t getDecisionLevel(SatVariable v) const { return -1; }
+
+  /**
+   * Return the user-context level when `lit` was introduced..
+   */
+  virtual int32_t getIntroLevel(SatVariable v) const { return -1; }
+
   virtual std::shared_ptr<ProofNode> getProof() = 0;
 
 }; /* class CDCLTSatSolverInterface */
index ecf2bafb68dad6751d97bcc8a4c0c65d37ef5224..b01ba2580a8bed47bcc3928fdd52ea3b3df400f1 100644 (file)
@@ -34,7 +34,9 @@ BVSolverBitblast::BVSolverBitblast(TheoryState* s,
       d_nullRegistrar(new prop::NullRegistrar()),
       d_nullContext(new context::Context()),
       d_bbFacts(s->getSatContext()),
+      d_bbInputFacts(s->getSatContext()),
       d_assumptions(s->getSatContext()),
+      d_assertions(s->getSatContext()),
       d_invalidateModelCache(s->getSatContext(), true),
       d_inSatMode(s->getSatContext(), false),
       d_epg(pnm ? new EagerProofGenerator(pnm, s->getUserContext(), "")
@@ -76,6 +78,23 @@ void BVSolverBitblast::postCheck(Theory::Effort level)
     }
   }
 
+  NodeManager* nm = NodeManager::currentNM();
+
+  /* Process input assertions bit-blast queue. */
+  while (!d_bbInputFacts.empty())
+  {
+    Node fact = d_bbInputFacts.front();
+    d_bbInputFacts.pop();
+    /* Bit-blast fact and cache literal. */
+    if (d_factLiteralCache.find(fact) == d_factLiteralCache.end())
+    {
+      d_bitblaster->bbAtom(fact);
+      Node bb_fact = d_bitblaster->getStoredBBAtom(fact);
+      d_cnfStream->convertAndAssert(bb_fact, false, false);
+    }
+    d_assertions.push_back(fact);
+  }
+
   /* Process bit-blast queue and store SAT literals. */
   while (!d_bbFacts.empty())
   {
@@ -87,7 +106,6 @@ void BVSolverBitblast::postCheck(Theory::Effort level)
       d_bitblaster->bbAtom(fact);
       Node bb_fact = d_bitblaster->getStoredBBAtom(fact);
       d_cnfStream->ensureLiteral(bb_fact);
-
       prop::SatLiteral lit = d_cnfStream->getLiteral(bb_fact);
       d_factLiteralCache[fact] = lit;
       d_literalFactCache[lit] = fact;
@@ -106,25 +124,51 @@ void BVSolverBitblast::postCheck(Theory::Effort level)
   {
     std::vector<prop::SatLiteral> unsat_assumptions;
     d_satSolver->getUnsatAssumptions(unsat_assumptions);
-    Assert(unsat_assumptions.size() > 0);
 
-    std::vector<Node> conflict;
-    for (const prop::SatLiteral& lit : unsat_assumptions)
+    Node conflict;
+    // Unsat assumptions produce conflict.
+    if (unsat_assumptions.size() > 0)
     {
-      conflict.push_back(d_literalFactCache[lit]);
-      Debug("bv-bitblast") << "unsat assumption (" << lit
-                           << "): " << conflict.back() << std::endl;
+      std::vector<Node> conf;
+      for (const prop::SatLiteral& lit : unsat_assumptions)
+      {
+        conf.push_back(d_literalFactCache[lit]);
+        Debug("bv-bitblast")
+            << "unsat assumption (" << lit << "): " << conf.back() << std::endl;
+      }
+      conflict = nm->mkAnd(conf);
     }
-
-    NodeManager* nm = NodeManager::currentNM();
-    d_im.conflict(nm->mkAnd(conflict), InferenceId::BV_BITBLAST_CONFLICT);
+    else  // Input assertions produce conflict.
+    {
+      std::vector<Node> assertions(d_assertions.begin(), d_assertions.end());
+      conflict = nm->mkAnd(assertions);
+    }
+    d_im.conflict(conflict, InferenceId::BV_BITBLAST_CONFLICT);
   }
 }
 
 bool BVSolverBitblast::preNotifyFact(
     TNode atom, bool pol, TNode fact, bool isPrereg, bool isInternal)
 {
-  d_bbFacts.push_back(fact);
+  Valuation& val = d_state.getValuation();
+
+  /**
+   * Check whether `fact` is an input assertion on user-level 0.
+   *
+   * If this is the case we can assert `fact` to the SAT solver instead of
+   * using assumptions.
+   */
+  if (options::bvAssertInput() && val.isSatLiteral(fact)
+      && !val.isDecision(fact) && val.getDecisionLevel(fact) == 0
+      && val.getIntroLevel(fact) == 0)
+  {
+    d_bbInputFacts.push_back(fact);
+  }
+  else
+  {
+    d_bbFacts.push_back(fact);
+  }
+
   return false;  // Return false to enable equality engine reasoning in Theory.
 }
 
index 36c06209ad8e654e35915ff3b319c2b3d71b7555..f98121a63b7c26baf94eccc23e48563a107f3ca8 100644 (file)
@@ -33,6 +33,8 @@ namespace cvc5 {
 namespace theory {
 namespace bv {
 
+class BBRegistrar;
+
 /**
  * Bit-blasting solver with support for different SAT back ends.
  */
@@ -94,6 +96,7 @@ class BVSolverBitblast : public BVSolver
 
   /** Used for initializing `d_cnfStream`. */
   std::unique_ptr<prop::NullRegistrar> d_nullRegistrar;
+
   std::unique_ptr<context::Context> d_nullContext;
 
   /** SAT solver back end (configured via options::bvSatSolver. */
@@ -108,9 +111,19 @@ class BVSolverBitblast : public BVSolver
    */
   context::CDQueue<Node> d_bbFacts;
 
+  /**
+   * Bit-blast queue for user-level 0 input facts sent to this solver.
+   *
+   * Get populated on preNotifyFact().
+   */
+  context::CDQueue<Node> d_bbInputFacts;
+
   /** Corresponds to the SAT literals of the currently asserted facts. */
   context::CDList<prop::SatLiteral> d_assumptions;
 
+  /** Stores the current input assertions. */
+  context::CDList<Node> d_assertions;
+
   /** Flag indicating whether `d_modelCache` should be invalidated. */
   context::CDO<bool> d_invalidateModelCache;
 
index 937fcd5fabf8b3e56bf5694824591be2aa725c7e..621f89539820d42b9dbf2646ff0d05d22586374c 100644 (file)
@@ -188,6 +188,18 @@ bool Valuation::isDecision(Node lit) const {
   return d_engine->getPropEngine()->isDecision(lit);
 }
 
+int32_t Valuation::getDecisionLevel(Node lit) const
+{
+  Assert(d_engine != nullptr);
+  return d_engine->getPropEngine()->getDecisionLevel(lit);
+}
+
+int32_t Valuation::getIntroLevel(Node lit) const
+{
+  Assert(d_engine != nullptr);
+  return d_engine->getPropEngine()->getIntroLevel(lit);
+}
+
 unsigned Valuation::getAssertionLevel() const{
   Assert(d_engine != nullptr);
   return d_engine->getPropEngine()->getAssertionLevel();
index a1a3db706e5b421902bf139972a2559ddf32c51a..7438279b1c7f3d76f81e9ceb440e02322558c3d9 100644 (file)
@@ -184,6 +184,22 @@ public:
    */
   bool isDecision(Node lit) const;
 
+  /**
+   * Return the current decision level of `lit`.
+   *
+   * @param lit: The node in question, must have an associated SAT literal.
+   * @return Decision level of the SAT variable of `lit` (phase is disregarded),
+   *         or -1 if `lit` has not been assigned yet.
+   */
+  int32_t getDecisionLevel(Node lit) const;
+
+  /**
+   * Return the user-context level when `lit` was introduced..
+   *
+   * @return User-context level or -1 if not yet introduced.
+   */
+  int32_t getIntroLevel(Node lit) const;
+
   /**
    * Get the assertion level of the SAT solver.
    */