Ensure variables are constrained in model when equal to transcendental function apps...
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Thu, 24 Feb 2022 23:56:33 +0000 (17:56 -0600)
committerGitHub <noreply@github.com>
Thu, 24 Feb 2022 23:56:33 +0000 (23:56 +0000)
Fixes #8147.

src/smt/check_models.cpp
src/theory/arith/nl/transcendental/transcendental_solver.cpp
src/theory/arith/nl/transcendental/transcendental_solver.h
test/regress/CMakeLists.txt
test/regress/regress0/nl/nta/issue8147-unc-model.smt2 [new file with mode: 0644]

index f3393caae3afc406d9115c3af3a2fceac4708828..5a410ba2dfbfe1a9d9a82f6fd62c8648e74fe24e 100644 (file)
@@ -41,13 +41,6 @@ void CheckModels::checkModel(TheoryModel* m,
   // If this function is running, the user gave --check-model (or equivalent),
   // and if verbose(1) is on, the user gave --verbose (or equivalent).
 
-  // check-model is not guaranteed to succeed if approximate values were used.
-  // Thus, we intentionally abort here.
-  if (m->hasApproximations())
-  {
-    throw RecoverableModalException(
-        "Cannot run check-model on a model with approximate values.");
-  }
   Node sepHeap, sepNeq;
   if (m->getHeapModel(sepHeap, sepNeq))
   {
index 6a254fc54731f4bffe298770b93d7b58da485162..c394512d1632c92e231972a4b6385b34f7fa3d73 100644 (file)
@@ -455,13 +455,15 @@ void TranscendentalSolver::postProcessModel(std::map<Node, Node>& arithModel,
                                             const std::set<Node>& termSet)
 {
   Trace("nl-ext") << "TranscendentalSolver::postProcessModel" << std::endl;
-  std::unordered_set<Node> trReps;
+  // map from equivalence classes to a transcendental function application,
+  // if it exists.
+  std::unordered_map<Node, Node> trReps;
   for (const Node& n : termSet)
   {
     if (isTranscendentalKind(n.getKind()))
     {
       Node r = d_astate.getRepresentative(n);
-      trReps.insert(r);
+      trReps[r] = n;
     }
   }
   if (trReps.empty())
@@ -469,25 +471,29 @@ void TranscendentalSolver::postProcessModel(std::map<Node, Node>& arithModel,
     Trace("nl-ext") << "...no transcendental functions" << std::endl;
     return;
   }
-  std::vector<Node> rmFromModel;
+  std::unordered_map<Node, Node>::iterator it;
   for (auto& am : arithModel)
   {
+    // skip integer variables
+    if (am.first.getType().isInteger())
+    {
+      continue;
+    }
     Node r = d_astate.getRepresentative(am.first);
-    if (trReps.find(r) != trReps.end())
+    it = trReps.find(r);
+    // if it is in the same equivalence class as a trancendental function
+    // application, we replace its value in the model with that application
+    if (it != trReps.end())
     {
-      Trace("nl-ext") << "...erase value for " << am.first
-                      << ", since approximate" << std::endl;
-      rmFromModel.push_back(am.first);
+      Trace("nl-ext") << "...abstract value for " << am.first << " to "
+                      << it->second << std::endl;
+      am.second = it->second;
     }
     else
     {
       Trace("nl-ext") << "...keep model value for " << am.first << std::endl;
     }
   }
-  for (const Node& n : rmFromModel)
-  {
-    arithModel.erase(n);
-  }
 }
 
 }  // namespace transcendental
index f8c7b8c9b54d02da2d8dab4251245428a74561fb..413d4535fd2a51528318f2292592c5695cf73dae 100644 (file)
@@ -153,9 +153,16 @@ class TranscendentalSolver : protected EnvObj
   void checkTranscendentalTangentPlanes();
 
   /**
-   * Post-process model. This ensures that the domain of arithModel does not
-   * contain terms that are equal to any transcendental function applications,
+   * Post-process model. This ensures that for all terms t in the domain of
+   * arithModel, if t is in the same equivalence class as a transcendental
+   * function application, then arithModel[t] maps to one such application.
+   *
+   * This is to ensure that the linear model is ignored for such terms,
    * as their values cannot be properly represented in the model.
+   *
+   * It is important to map such terms t to a transcendental function
+   * application, or otherwise they would be unconstrained, leading to
+   * spurious models.
    */
   void postProcessModel(std::map<Node, Node>& arithModel,
                         const std::set<Node>& termSet);
index b28651d95314dee6ef0e6ef3bc4dbe63deb9ecc0..71219634bb7b92b031489e0840ae69ca93055115 100644 (file)
@@ -789,6 +789,7 @@ set(regress_0_tests
   regress0/nl/nta/exp-n0.5-ub.smt2
   regress0/nl/nta/exp-neg2-unsat-unsound.smt2
   regress0/nl/nta/exp1-ub.smt2
+  regress0/nl/nta/issue8147-unc-model.smt2
   regress0/nl/nta/real-pi.smt2
   regress0/nl/nta/sin-sym.smt2
   regress0/nl/nta/sqrt-simple.smt2
diff --git a/test/regress/regress0/nl/nta/issue8147-unc-model.smt2 b/test/regress/regress0/nl/nta/issue8147-unc-model.smt2
new file mode 100644 (file)
index 0000000..1acdab1
--- /dev/null
@@ -0,0 +1,8 @@
+(set-logic ALL)
+(set-info :status sat)
+(declare-const x Bool)
+(declare-fun a () Real)
+(declare-fun r () Real)
+(declare-fun v () Real)
+(assert (and (xor (= a 1.0) x) (= 1 (- v (exp (* r v))))))
+(check-sat)