Support context-(in)dependent decision strategies. (#3281)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Thu, 19 Sep 2019 14:44:04 +0000 (09:44 -0500)
committerGitHub <noreply@github.com>
Thu, 19 Sep 2019 14:44:04 +0000 (09:44 -0500)
src/theory/arrays/theory_arrays.cpp
src/theory/arrays/theory_arrays.h
src/theory/decision_manager.cpp
src/theory/decision_manager.h
src/theory/strings/theory_strings.cpp
src/theory/theory_engine.cpp
src/theory/uf/cardinality_extension.cpp
test/regress/CMakeLists.txt
test/regress/regress2/strings/repl-repl-i-no-push.smt2 [new file with mode: 0644]

index 9f06950bd6902fa7041fb189d8872a3ca0eb5699..003bb0d684ec942c92861e81513a53beb81815af 100644 (file)
@@ -112,7 +112,8 @@ TheoryArrays::TheoryArrays(context::Context* c,
       d_arrayMerges(c),
       d_inCheckModel(false),
       d_proofReconstruction(&d_equalityEngine),
-      d_dstrat(new TheoryArraysDecisionStrategy(this))
+      d_dstrat(new TheoryArraysDecisionStrategy(this)),
+      d_dstratInit(false)
 {
   smtStatisticsRegistry()->registerStat(&d_numRow);
   smtStatisticsRegistry()->registerStat(&d_numExt);
@@ -1252,9 +1253,15 @@ bool TheoryArrays::collectModelInfo(TheoryModel* m)
 void TheoryArrays::presolve()
 {
   Trace("arrays")<<"Presolving \n";
-  // add the decision strategy
-  getDecisionManager()->registerStrategy(DecisionManager::STRAT_ARRAYS,
-                                         d_dstrat.get());
+  if (!d_dstratInit)
+  {
+    d_dstratInit = true;
+    // add the decision strategy, which is user-context-independent
+    getDecisionManager()->registerStrategy(
+        DecisionManager::STRAT_ARRAYS,
+        d_dstrat.get(),
+        DecisionManager::STRAT_SCOPE_CTX_INDEPENDENT);
+  }
 }
 
 
index be80a081df1ffe887d8231c7c87b4051c27438c6..3d6d0692ecd9aa08a7457020e8d7cd502b010213 100644 (file)
@@ -474,6 +474,8 @@ class TheoryArrays : public Theory {
   };
   /** an instance of the above decision strategy */
   std::unique_ptr<TheoryArraysDecisionStrategy> d_dstrat;
+  /** Have we registered the above strategy? (context-independent) */
+  bool d_dstratInit;
   /** get the next decision request
    *
    * If the "arrays-eager-index" option is enabled, then whenever a
index 3eda45b00500cd3f0429e4f4992045ebdc834d40..a2aee5d40981e9960203de8b1046110045ee7229 100644 (file)
@@ -22,27 +22,68 @@ using namespace CVC4::kind;
 namespace CVC4 {
 namespace theory {
 
-DecisionManager::DecisionManager(context::Context* satContext) {}
+DecisionManager::DecisionManager(context::Context* userContext)
+    : d_strategyCacheC(userContext)
+{
+}
 
-void DecisionManager::reset()
+void DecisionManager::presolve()
 {
-  Trace("dec-manager") << "DecisionManager: reset." << std::endl;
+  Trace("dec-manager") << "DecisionManager: presolve." << std::endl;
+  // remove the strategies that are not in this user context
+  std::unordered_set<DecisionStrategy*> active;
+  for (DecisionStrategyList::const_iterator i = d_strategyCacheC.begin();
+       i != d_strategyCacheC.end();
+       ++i)
+  {
+    active.insert(*i);
+  }
+  active.insert(d_strategyCache.begin(), d_strategyCache.end());
+  std::map<StrategyId, std::vector<DecisionStrategy*> > tmp = d_reg_strategy;
   d_reg_strategy.clear();
+  for (std::pair<const StrategyId, std::vector<DecisionStrategy*> >& rs : tmp)
+  {
+    for (DecisionStrategy* ds : rs.second)
+    {
+      if (active.find(ds) != active.end())
+      {
+        // if its active, we keep it
+        d_reg_strategy[rs.first].push_back(ds);
+      }
+    }
+  }
 }
 
-void DecisionManager::registerStrategy(StrategyId id, DecisionStrategy* ds)
+void DecisionManager::registerStrategy(StrategyId id,
+                                       DecisionStrategy* ds,
+                                       StrategyScope sscope)
 {
   Trace("dec-manager") << "DecisionManager: Register strategy : "
                        << ds->identify() << ", id = " << id << std::endl;
   ds->initialize();
   d_reg_strategy[id].push_back(ds);
+  if (sscope == STRAT_SCOPE_USER_CTX_DEPENDENT)
+  {
+    // store it in the user-context-dependent list
+    d_strategyCacheC.push_back(ds);
+  }
+  else if (sscope == STRAT_SCOPE_CTX_INDEPENDENT)
+  {
+    // it is context independent
+    d_strategyCache.insert(ds);
+  }
+  else
+  {
+    // it is local to this call, we don't cache it
+    Assert(sscope == STRAT_SCOPE_LOCAL_SOLVE);
+  }
 }
 
 Node DecisionManager::getNextDecisionRequest()
 {
   Trace("dec-manager-debug")
       << "DecisionManager: Get next decision..." << std::endl;
-  for (const std::pair<StrategyId, std::vector<DecisionStrategy*> >& rs :
+  for (const std::pair<const StrategyId, std::vector<DecisionStrategy*> >& rs :
        d_reg_strategy)
   {
     for (unsigned i = 0, size = rs.second.size(); i < size; i++)
index 6825c1cf46667dda06b8b1b19dbdf63a05ae2475..da040a7f7ff3bda3d821c5be27fbe1fa378d66c6 100644 (file)
@@ -19,6 +19,7 @@
 #define CVC4__THEORY__DECISION_MANAGER__H
 
 #include <map>
+#include "context/cdlist.h"
 #include "theory/decision_strategy.h"
 
 namespace CVC4 {
@@ -46,6 +47,8 @@ namespace theory {
  */
 class DecisionManager
 {
+  typedef context::CDList<DecisionStrategy*> DecisionStrategyList;
+
  public:
   enum StrategyId
   {
@@ -83,21 +86,42 @@ class DecisionManager
 
     STRAT_LAST
   };
-  DecisionManager(context::Context* satContext);
+  /** The scope of a strategy, used in registerStrategy below */
+  enum StrategyScope
+  {
+    // The strategy is user-context dependent, that is, it is cleared when
+    // the user context is popped.
+    STRAT_SCOPE_USER_CTX_DEPENDENT,
+    // The strategy is local to a check-sat call, that is, it is cleared on
+    // a call to presolve.
+    STRAT_SCOPE_LOCAL_SOLVE,
+    // The strategy is context-independent.
+    STRAT_SCOPE_CTX_INDEPENDENT,
+  };
+  DecisionManager(context::Context* userContext);
   ~DecisionManager() {}
-  /** reset the strategy
+  /** presolve
    *
-   * This clears all decision strategies that are registered to this manager.
+   * This clears all decision strategies that are registered to this manager
+   * that no longer exist in the current user context.
    * We require that each satisfiability check beyond the first calls this
-   * function exactly once. Currently, it is called during
-   * TheoryEngine::postSolve.
+   * function exactly once. It is called during TheoryEngine::presolve.
    */
-  void reset();
+  void presolve();
   /**
    * Registers the strategy ds with this manager. The id specifies when the
-   * strategy should be run.
+   * strategy should be run. The argument sscope indicates the scope of the
+   * strategy, i.e. how long it persists.
+   *
+   * Typically, strategies that are user-context-dependent are those that are
+   * in response to an assertion (e.g. a strategy that decides that a sygus
+   * conjecture is feasible). An example of a strategy that is context
+   * independent is the combined cardinality decision strategy for finite model
+   * finding for UF, which is not specific to any formula/type.
    */
-  void registerStrategy(StrategyId id, DecisionStrategy* ds);
+  void registerStrategy(StrategyId id,
+                        DecisionStrategy* ds,
+                        StrategyScope sscope = STRAT_SCOPE_USER_CTX_DEPENDENT);
   /** Get the next decision request
    *
    * If this method returns a non-null node n, then n is a literal corresponding
@@ -111,6 +135,10 @@ class DecisionManager
  private:
   /** Map containing all strategies registered to this manager */
   std::map<StrategyId, std::vector<DecisionStrategy*> > d_reg_strategy;
+  /** Set of decision strategies in this user context */
+  DecisionStrategyList d_strategyCacheC;
+  /** Set of decision strategies that are context independent */
+  std::unordered_set<DecisionStrategy*> d_strategyCache;
 };
 
 }  // namespace theory
index 65f06c0083cde35f518c430dae5c3e2970081b3c..0f9a78516b3b394be6c979fc0060817d00166166 100644 (file)
@@ -570,8 +570,12 @@ void TheoryStrings::presolve() {
       inputVars.push_back(*itr);
     }
     d_sslds->initialize(inputVars);
+    // This strategy is local to a check-sat call, since we refresh the strategy
+    // on every call to presolve.
     getDecisionManager()->registerStrategy(
-        DecisionManager::STRAT_STRINGS_SUM_LENGTHS, d_sslds.get());
+        DecisionManager::STRAT_STRINGS_SUM_LENGTHS,
+        d_sslds.get(),
+        DecisionManager::STRAT_SCOPE_LOCAL_SOLVE);
   }
 }
 
index 8348c24d59abd3b3b2c7f424138824a118996fc1..5d185ad9da17070664bc15fd246a7827df201483 100644 (file)
@@ -274,7 +274,7 @@ TheoryEngine::TheoryEngine(context::Context* context,
       d_masterEqualityEngine(nullptr),
       d_masterEENotify(*this),
       d_quantEngine(nullptr),
-      d_decManager(new DecisionManager(context)),
+      d_decManager(new DecisionManager(userContext)),
       d_curr_model(nullptr),
       d_aloc_curr_model(false),
       d_curr_model_builder(nullptr),
@@ -910,6 +910,10 @@ bool TheoryEngine::presolve() {
   // Reset the interrupt flag
   d_interrupted = false;
 
+  // Reset the decision manager. This clears its decision strategies that are
+  // no longer valid in this user context.
+  d_decManager->presolve();
+
   try {
     // Definition of the statement that is to be run by every theory
 #ifdef CVC4_FOR_EACH_THEORY_STATEMENT
@@ -933,9 +937,6 @@ bool TheoryEngine::presolve() {
 }/* TheoryEngine::presolve() */
 
 void TheoryEngine::postsolve() {
-  // Reset the decision manager. This clears its decision strategies, which are
-  // user-context-dependent.
-  d_decManager->reset();
   // no longer in SAT mode
   d_inSatMode = false;
   // Reset the interrupt flag
index 94e5f67c12dee7b2d532df5f9af521e8d34fb68a..87696ef5f7c62cd2358904f48fc1f3c67a8cf78e 100644 (file)
@@ -504,6 +504,8 @@ void SortModel::initialize( OutputChannel* out ){
   if (d_c_dec_strat.get() != nullptr && !d_initialized)
   {
     d_initialized = true;
+    // Strategy is user-context-dependent, since it is in sync with
+    // user-context-dependent flag d_initialized.
     d_thss->getTheory()->getDecisionManager()->registerStrategy(
         DecisionManager::STRAT_UF_CARD, d_c_dec_strat.get());
   }
index c456ed2e8c85f96078d705a6709fb3f3528d9ebb..a9767044667f9680b66fbafe27553684a8637db2 100644 (file)
@@ -1820,6 +1820,7 @@ set(regress_2_tests
   regress2/strings/norn-dis-0707-3.smt2
   regress2/strings/range-perf.smt2
   regress2/strings/repl-repl.smt2
+  regress2/strings/repl-repl-i-no-push.smt2
   regress2/strings/replaceall-diffrange.smt2
   regress2/strings/replaceall-len-c.smt2
   regress2/strings/small-1.smt2
diff --git a/test/regress/regress2/strings/repl-repl-i-no-push.smt2 b/test/regress/regress2/strings/repl-repl-i-no-push.smt2
new file mode 100644 (file)
index 0000000..0076af3
--- /dev/null
@@ -0,0 +1,26 @@
+; COMMAND-LINE: --strings-exp --strings-fmf --incremental
+; EXPECT: sat
+; EXPECT: unsat
+; EXPECT: sat
+; EXPECT: sat
+(set-logic SLIA)
+(declare-const x String)
+(declare-const y String)
+(declare-const z String)
+
+(assert (not (= (str.replace (str.replace x "B" x) x "AB") (str.replace (str.replace x "B" "AB") x "AB"))))
+(check-sat)
+
+(push 1)
+(assert (not (= (str.replace (str.replace x "B" x) x "A") (str.replace (str.replace x "B" "A") x "A"))))
+(check-sat)
+(pop 1)
+
+(push 1)
+(assert (not (= (str.replace (str.replace x z x) x y) (str.replace (str.replace x z y) x y))))
+(check-sat)
+(pop 1)
+
+(assert (not (= (str.replace (str.replace x z x) x y) (str.replace (str.replace x z y) x y))))
+(assert (<= (str.len y) (str.len z)))
+(check-sat)