Support for witnessing choice in models (#3781)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Wed, 26 Feb 2020 15:02:15 +0000 (09:02 -0600)
committerGitHub <noreply@github.com>
Wed, 26 Feb 2020 15:02:15 +0000 (09:02 -0600)
Fixes #3300.

This adds an option --model-witness-choice that ensures that choices in models are of the form (choice ((x Int)) (or (= x c) P)), which is helpful if the user wants to know a potential value in the range of the choice.

src/options/smt_options.toml
src/theory/arith/nl_model.cpp
src/theory/arith/nl_model.h
src/theory/arith/nonlinear_extension.cpp
src/theory/arith/nonlinear_extension.h
src/theory/theory_model.cpp
src/theory/theory_model.h
test/regress/CMakeLists.txt
test/regress/regress1/nl/issue3300-approx-sqrt-witness.smt2 [new file with mode: 0644]

index 02453022430f4df0e9e4ca8e59cd9c14b5d7f8de..88842faf70cf64b418cc125e56e9876038268c52 100644 (file)
@@ -419,6 +419,15 @@ header = "options/smt_options.h"
   read_only  = true
   help       = "in models, output uninterpreted sorts as datatype enumerations"
 
+[[option]]
+  name       = "modelWitnessChoice"
+  category   = "regular"
+  long       = "model-witness-choice"
+  type       = "bool"
+  default    = "false"
+  read_only  = true
+  help       = "in models, use a witness constant for choice functions"
+
 [[option]]
   name       = "regularChannelName"
   smt_name   = "regular-output-channel"
index 8a4145552551020e34eab8bb54ebdeba9f5b41e5..929eb4acc71c7ba05e012bf518069b9f53cabde2 100644 (file)
@@ -1247,8 +1247,9 @@ void NlModel::printModelValue(const char* c, Node n, unsigned prec) const
   }
 }
 
-void NlModel::getModelValueRepair(std::map<Node, Node>& arithModel,
-                                  std::map<Node, Node>& approximations)
+void NlModel::getModelValueRepair(
+    std::map<Node, Node>& arithModel,
+    std::map<Node, std::pair<Node, Node>>& approximations)
 {
   // Record the approximations we used. This code calls the
   // recordApproximation method of the model, which overrides the model
@@ -1266,8 +1267,17 @@ void NlModel::getModelValueRepair(std::map<Node, Node>& arithModel,
     if (l != u)
     {
       Node pred = nm->mkNode(AND, nm->mkNode(GEQ, v, l), nm->mkNode(GEQ, u, v));
-      approximations[v] = pred;
       Trace("nl-model") << v << " approximated as " << pred << std::endl;
+      Node witness;
+      if (options::modelWitnessChoice())
+      {
+        // witness is the midpoint
+        witness = nm->mkNode(
+            MULT, nm->mkConst(Rational(1, 2)), nm->mkNode(PLUS, l, u));
+        witness = Rewriter::rewrite(witness);
+        Trace("nl-model") << v << " witness is " << witness << std::endl;
+      }
+      approximations[v] = std::pair<Node, Node>(pred, witness);
     }
     else
     {
index 66c5d89c1796ae39c074585867c0887f8fbeb568..354f6f71c7ef35146bcbfbdbc0aa61a4092abe73 100644 (file)
@@ -180,10 +180,12 @@ class NlModel
    * The mapping arithModel is updated by this method to map arithmetic terms v
    * to their (exact) value that was computed during checkModel; the mapping
    * approximations is updated to store approximate values in the form of a
-   * predicate over v.
+   * pair (P, w), where P is a predicate that describes the possible values of
+   * v and w is a witness point that satisfies this predicate.
    */
-  void getModelValueRepair(std::map<Node, Node>& arithModel,
-                           std::map<Node, Node>& approximations);
+  void getModelValueRepair(
+      std::map<Node, Node>& arithModel,
+      std::map<Node, std::pair<Node, Node>>& approximations);
 
  private:
   /** The current model */
index 6c04268dbbe272f7115cd163c69c6b55c1d40adb..acae404baeda3b5aca5ce6317d576fda2464ffa0 100644 (file)
@@ -1304,9 +1304,16 @@ void NonlinearExtension::check(Theory::Effort e) {
     // Otherwise, we will answer SAT. The values that we approximated are
     // recorded as approximations here.
     TheoryModel* tm = d_containing.getValuation().getModel();
-    for (std::pair<const Node, Node>& a : d_approximations)
+    for (std::pair<const Node, std::pair<Node, Node>>& a : d_approximations)
     {
-      tm->recordApproximation(a.first, a.second);
+      if (a.second.second.isNull())
+      {
+        tm->recordApproximation(a.first, a.second.first);
+      }
+      else
+      {
+        tm->recordApproximation(a.first, a.second.first, a.second.second);
+      }
     }
   }
 }
index ddca284a4845f0588467592a2d15c5e176387dc2..338ae6611f8ea981818b3fe85c2ab7b02cae2d5b 100644 (file)
@@ -480,8 +480,11 @@ class NonlinearExtension {
    */
   std::vector<Node> d_cmiLemmas;
   std::vector<Node> d_cmiLemmasPp;
-  /** The approximations computed during collectModelInfo. */
-  std::map<Node, Node> d_approximations;
+  /**
+   * The approximations computed during collectModelInfo. For details, see
+   * NlModel::getModelValueRepair.
+   */
+  std::map<Node, std::pair<Node, Node>> d_approximations;
   /** have we successfully built the model in this SAT context? */
   context::CDO<bool> d_builtModel;
 
index c6055ede9220cf9829b3d0cce8b76b4f16334519..3996951315b5ad7039d12a4632151f5a9e65358d 100644 (file)
@@ -598,6 +598,11 @@ void TheoryModel::recordApproximation(TNode n, TNode pred)
   // model cache is invalid
   d_modelCache.clear();
 }
+void TheoryModel::recordApproximation(TNode n, TNode pred, Node witness)
+{
+  Node predDisj = NodeManager::currentNM()->mkNode(OR, n.eqNode(witness), pred);
+  recordApproximation(n, predDisj);
+}
 void TheoryModel::setUsingModelCore()
 {
   d_using_model_core = true;
index 0914987cc1e8cb9996a10eb3a8c072aac895fe6e..d2ce63ac59e75d0eef3de988c53963e9a2ed801f 100644 (file)
@@ -215,6 +215,13 @@ public:
    * construction process.
    */
   void recordApproximation(TNode n, TNode pred);
+  /**
+   * Same as above, but with a witness constant. This ensures that the
+   * approximation predicate is of the form (or (= n witness) pred). This
+   * is useful if the user wants to know a possible concrete value in
+   * the range of the predicate.
+   */
+  void recordApproximation(TNode n, TNode pred, Node witness);
   /** set unevaluate/semi-evaluated kind
    *
    * This informs this model how it should interpret applications of terms with
index b909817df040e734b2d0d1bcdcd6fe746b4dbd43..6acc6f7c82f9cdf48011d63d14fde6f83f439fed 100644 (file)
@@ -1306,6 +1306,7 @@ set(regress_1_tests
   regress1/nl/exp1-lb.smt2
   regress1/nl/exp_monotone.smt2
   regress1/nl/factor_agg_s.smt2
+  regress1/nl/issue3300-approx-sqrt-witness.smt2
   regress1/nl/issue3441.smt2
   regress1/nl/issue3617.smt2
   regress1/nl/issue3656.smt2
diff --git a/test/regress/regress1/nl/issue3300-approx-sqrt-witness.smt2 b/test/regress/regress1/nl/issue3300-approx-sqrt-witness.smt2
new file mode 100644 (file)
index 0000000..9bc47f9
--- /dev/null
@@ -0,0 +1,11 @@
+; SCRUBBER: sed -e 's/BOUND_VARIABLE_[0-9]*/BOUND_VARIABLE/; s/((x (choice ((BOUND_VARIABLE Real)) (or (= BOUND_VARIABLE.*/SUCCESS/'
+; COMMAND-LINE: --produce-models --model-witness-choice --no-check-models
+; EXPECT: sat
+; EXPECT: SUCCESS
+(set-logic QF_NRA)
+(set-info :status sat)
+(declare-fun x () Real)
+(assert (= (* x x) 2))
+(check-sat)
+(get-value (x))
+