Relax what is generated in the model from NL (#8113)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Tue, 22 Feb 2022 17:46:44 +0000 (11:46 -0600)
committerGitHub <noreply@github.com>
Tue, 22 Feb 2022 17:46:44 +0000 (17:46 +0000)
This makes it so that we don't assign concrete values to equivalence classes with transcendental functions.

It also removes some unused infrastructure.

src/theory/arith/nl/cad_solver.cpp
src/theory/arith/nl/nl_model.cpp
src/theory/arith/nl/nl_model.h
src/theory/arith/nl/nonlinear_extension.cpp
src/theory/arith/nl/nonlinear_extension.h
src/theory/arith/nl/transcendental/transcendental_solver.cpp
src/theory/arith/nl/transcendental/transcendental_solver.h
src/theory/arith/theory_arith.cpp
src/theory/arith/theory_arith.h
test/regress/regress0/arith/exp-in-model.smt2
test/regress/regress0/arith/issue7984-quant-trans.smt2

index 18071f3b216ed77886c47cf9c84d4cfad3d61222..57ea8c8bab353b934663006bf642df8f1659f46c 100644 (file)
@@ -245,14 +245,8 @@ bool CadSolver::constructModelIfAvailable(std::vector<Node>& assertions)
 void CadSolver::addToModel(TNode var, TNode value) const
 {
   Trace("nl-cad") << "-> " << var << " = " << value << std::endl;
-  if (value.getType().isRealOrInt())
-  {
-    d_model.addSubstitution(var, value);
-  }
-  else
-  {
-    d_model.addWitness(var, value);
-  }
+  Assert(value.getType().isRealOrInt());
+  d_model.addSubstitution(var, value);
 }
 
 }  // namespace nl
index a28117d87bfb7935cc657c67c4ccf7a690ea4861..e236e3375afaaf22f181eb404396ebd82ed98a72 100644 (file)
@@ -56,7 +56,6 @@ void NlModel::resetCheck()
   d_used_approx = false;
   d_check_model_solved.clear();
   d_check_model_bounds.clear();
-  d_check_model_witnesses.clear();
   d_substitutions.clear();
 }
 
@@ -301,9 +300,6 @@ bool NlModel::addSubstitution(TNode v, TNode s)
       return false;
     }
   }
-  Assert(d_check_model_witnesses.find(v) == d_check_model_witnesses.end())
-      << "We tried to add a substitution where we already had a witness term."
-      << std::endl;
   Subs tmp;
   tmp.add(v, s);
   for (auto& sub : d_substitutions.d_subs)
@@ -351,23 +347,6 @@ bool NlModel::addBound(TNode v, TNode l, TNode u)
   return true;
 }
 
-bool NlModel::addWitness(TNode v, TNode w)
-{
-  Trace("nl-ext-model") << "* check model witness : " << v << " -> " << w
-                        << std::endl;
-  // should not set a witness for a value that is already set
-  if (d_substitutions.contains(v))
-  {
-    Trace("nl-ext-model") << "...ERROR: setting witness for variable that "
-                             "already has a constant value."
-                          << std::endl;
-    Assert(false);
-    return false;
-  }
-  d_check_model_witnesses.emplace(v, w);
-  return true;
-}
-
 void NlModel::setUsedApproximate() { d_used_approx = true; }
 
 bool NlModel::usedApproximate() const { return d_used_approx; }
@@ -886,11 +865,6 @@ bool NlModel::simpleCheckModelMsum(const std::map<Node, Node>& msum, bool pol)
         }
         else
         {
-          Assert(d_check_model_witnesses.find(vc)
-                 == d_check_model_witnesses.end())
-              << "No variable should be assigned a witness term if we get "
-                 "here. "
-              << vc << " is, though." << std::endl;
           Trace("nl-ext-cms-debug") << std::endl;
           Trace("nl-ext-cms")
               << "  failed due to unknown bound for " << vc << std::endl;
@@ -1044,7 +1018,6 @@ void NlModel::printModelValue(const char* c, Node n, unsigned prec) const
 void NlModel::getModelValueRepair(
     std::map<Node, Node>& arithModel,
     std::map<Node, std::pair<Node, Node>>& approximations,
-    std::map<Node, Node>& witnesses,
     bool witnessToValue)
 {
   Trace("nl-model") << "NlModel::getModelValueRepair:" << std::endl;
@@ -1089,11 +1062,6 @@ void NlModel::getModelValueRepair(
       Trace("nl-model") << v << " exact approximation is " << l << std::endl;
     }
   }
-  for (const auto& vw : d_check_model_witnesses)
-  {
-    Trace("nl-model") << vw.first << " witness is " << vw.second << std::endl;
-    witnesses.emplace(vw.first, vw.second);
-  }
   // Also record the exact values we used. An exact value can be seen as a
   // special kind approximation of the form (witness x. x = exact_value).
   // Notice that the above term gets rewritten such that the choice function
@@ -1147,10 +1115,6 @@ bool NlModel::hasAssignment(Node v) const
   {
     return true;
   }
-  if (d_check_model_witnesses.find(v) != d_check_model_witnesses.end())
-  {
-    return true;
-  }
   return (d_substitutions.contains(v));
 }
 
index e195aa9b208202e1e23eb480a023b0189fd7eec6..27cb0f81d19c407d4db8ab202027cf4248b615d0 100644 (file)
@@ -133,13 +133,6 @@ class NlModel : protected EnvObj
    * bounds.
    */
   bool addBound(TNode v, TNode l, TNode u);
-  /**
-   * Adds a model witness v -> w to the underlying theory model.
-   * The witness should only contain a single variable v and evaluate to true
-   * for exactly one value of v. The variable v is then (implicitly,
-   * declaratively) assigned to this single value that satisfies the witness w.
-   */
-  bool addWitness(TNode v, TNode w);
   /**
    * Checks the current model based on solving for equalities, and using error
    * bounds on the Taylor approximation.
@@ -182,14 +175,11 @@ class NlModel : protected EnvObj
    * to their (exact) value that was computed during checkModel; the mapping
    * approximations is updated to store approximate values in the form of a
    * 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; the mapping
-   * witnesses is filled with witness terms that are satisfied by a single
-   * value.
+   * v and w is a witness point that satisfies this predicate.
    */
   void getModelValueRepair(
       std::map<Node, Node>& arithModel,
       std::map<Node, std::pair<Node, Node>>& approximations,
-      std::map<Node, Node>& witnesses,
       bool witnessToValue);
 
  private:
@@ -298,14 +288,6 @@ class NlModel : protected EnvObj
    * involves approximations of square roots.
    */
   std::map<Node, std::pair<Node, Node>> d_check_model_bounds;
-  /**
-   * witnesses for check model
-   *
-   * Stores witnesses for vatiables that define implicit variable assignments.
-   * For some variable v, we map to a formulas that is true for exactly one
-   * value of v.
-   */
-  std::map<Node, Node> d_check_model_witnesses;
   /**
    * The map from literals that our model construction solved, to the variable
    * that was solved for. Examples of such literals are:
index b57c0d1dbb574a09e094ce1307a000cee29256cf..bb360a5428bd1086b32fa98bb463688df15cddb7 100644 (file)
@@ -49,7 +49,7 @@ NonlinearExtension::NonlinearExtension(Env& env,
       d_extTheoryCb(state.getEqualityEngine()),
       d_extTheory(env, d_extTheoryCb, d_im),
       d_model(env),
-      d_trSlv(d_env, d_im, d_model),
+      d_trSlv(d_env, d_astate, d_im, d_model),
       d_extState(d_im, d_model, d_env),
       d_factoringSlv(d_env, &d_extState),
       d_monomialBoundsSlv(d_env, &d_extState),
@@ -277,11 +277,9 @@ void NonlinearExtension::checkFullEffort(std::map<Node, Node>& arithModel,
   {
     Trace("nl-ext") << "interceptModel: do model repair" << std::endl;
     d_approximations.clear();
-    d_witnesses.clear();
     // modify the model values
     d_model.getModelValueRepair(arithModel,
                                 d_approximations,
-                                d_witnesses,
                                 options().smt.modelWitnessValue);
     for (auto& am : arithModel)
     {
@@ -292,6 +290,10 @@ void NonlinearExtension::checkFullEffort(std::map<Node, Node>& arithModel,
       }
     }
   }
+  // must post-process model with transcendental solver, to ensure we don't
+  // assign values for equivalence classes with transcendental function
+  // applications
+  d_trSlv.postProcessModel(arithModel, termSet);
 }
 
 Node NonlinearExtension::getModelValue(TNode var) const
@@ -304,10 +306,6 @@ Node NonlinearExtension::getModelValue(TNode var) const
     }
     return Node::null();
   }
-  if (auto it = d_witnesses.find(var); it != d_witnesses.end())
-  {
-    return it->second;
-  }
   return Node::null();
 }
 
@@ -326,11 +324,6 @@ bool NonlinearExtension::assertModel(TheoryModel* tm, TNode var) const
     }
     return true;
   }
-  if (auto it = d_witnesses.find(var); it != d_witnesses.end())
-  {
-    tm->recordApproximation(var, it->second);
-    return true;
-  }
   return false;
 }
 
index 390dd72a36591464e851292c80241793b78e1343..dc7682f68643fad99e410d5f614cb68f3e8a4cd8 100644 (file)
@@ -278,11 +278,6 @@ class NonlinearExtension : EnvObj
    * NlModel::getModelValueRepair.
    */
   std::map<Node, std::pair<Node, Node>> d_approximations;
-  /**
-   * The witnesses computed during collectModelInfo. For details, see
-   * NlModel::getModelValueRepair.
-   */
-  std::map<Node, Node> d_witnesses;
 }; /* class NonlinearExtension */
 
 }  // namespace nl
index 9f7e474bbbd721bc8a9e8af3c5bdee24b093c2bd..1448bdbd25aa1cc38efa157fc35227d90ce3040e 100644 (file)
@@ -23,6 +23,7 @@
 #include "expr/skolem_manager.h"
 #include "options/arith_options.h"
 #include "theory/arith/arith_msum.h"
+#include "theory/arith/arith_state.h"
 #include "theory/arith/arith_utilities.h"
 #include "theory/arith/inference_manager.h"
 #include "theory/arith/nl/nl_model.h"
@@ -38,9 +39,11 @@ namespace nl {
 namespace transcendental {
 
 TranscendentalSolver::TranscendentalSolver(Env& env,
+                                           ArithState& state,
                                            InferenceManager& im,
                                            NlModel& m)
     : EnvObj(env),
+      d_astate(state),
       d_tstate(env, im, m),
       d_expSlv(env, &d_tstate),
       d_sineSlv(env, &d_tstate)
@@ -443,6 +446,45 @@ int TranscendentalSolver::regionToConcavity(Kind k, int region)
   return 0;
 }
 
+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;
+  for (const Node& n : termSet)
+  {
+    if (isTranscendentalKind(n.getKind()))
+    {
+      Node r = d_astate.getRepresentative(n);
+      trReps.insert(r);
+    }
+  }
+  if (trReps.empty())
+  {
+    Trace("nl-ext") << "...no transcendental functions" << std::endl;
+    return;
+  }
+  std::vector<Node> rmFromModel;
+  for (auto& am : arithModel)
+  {
+    Node r = d_astate.getRepresentative(am.first);
+    if (trReps.find(r) != trReps.end())
+    {
+      Trace("nl-ext") << "...erase value for " << am.first
+                      << ", since approximate" << std::endl;
+      rmFromModel.push_back(am.first);
+    }
+    else
+    {
+      Trace("nl-ext") << "...keep model value for " << am.first << std::endl;
+    }
+  }
+  for (const Node& n : rmFromModel)
+  {
+    arithModel.erase(n);
+  }
+}
+
 }  // namespace transcendental
 }  // namespace nl
 }  // namespace arith
index c1a617b3803d3f6ff7599d8d78283a82cc5cb156..f8c7b8c9b54d02da2d8dab4251245428a74561fb 100644 (file)
@@ -28,6 +28,7 @@ namespace cvc5 {
 namespace theory {
 namespace arith {
 
+class ArithState;
 class InferenceManager;
 
 namespace nl {
@@ -51,7 +52,10 @@ namespace transcendental {
 class TranscendentalSolver : protected EnvObj
 {
  public:
-  TranscendentalSolver(Env& env, InferenceManager& im, NlModel& m);
+  TranscendentalSolver(Env& env,
+                       ArithState& state,
+                       InferenceManager& im,
+                       NlModel& m);
   ~TranscendentalSolver();
 
   /** init last call
@@ -148,6 +152,14 @@ 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,
+   * as their values cannot be properly represented in the model.
+   */
+  void postProcessModel(std::map<Node, Node>& arithModel,
+                        const std::set<Node>& termSet);
+
  private:
   /** check transcendental function refinement for tf
    *
@@ -178,6 +190,8 @@ class TranscendentalSolver : protected EnvObj
    */
   int regionToConcavity(Kind k, int region);
 
+  /** A reference to the arithmetic state object */
+  ArithState& d_astate;
   /** taylor degree
    *
    * Indicates that the degree of the polynomials in the Taylor approximation of
index 60f925e75db4304df70e070737e63f66f0177f99..5218baf9bd58fde1c842e5109d4405114c477145 100644 (file)
@@ -49,7 +49,8 @@ TheoryArith::TheoryArith(Env& env, OutputChannel& out, Valuation valuation)
       d_nonlinearExtension(nullptr),
       d_opElim(d_env),
       d_arithPreproc(env, d_astate, d_im, d_pnm, d_opElim),
-      d_rewriter(d_opElim)
+      d_rewriter(d_opElim),
+      d_arithModelCacheSet(false)
 {
   // currently a cyclic dependency to TheoryArithPrivate
   d_astate.setParent(d_internal);
@@ -193,6 +194,7 @@ void TheoryArith::postCheck(Effort level)
   if (Theory::fullEffort(level))
   {
     d_arithModelCache.clear();
+    d_arithModelCacheSet = false;
     std::set<Node> termSet;
     if (d_nonlinearExtension != nullptr)
     {
@@ -370,16 +372,18 @@ eq::ProofEqEngine* TheoryArith::getProofEqEngine()
 
 void TheoryArith::updateModelCache(std::set<Node>& termSet)
 {
-  if (d_arithModelCache.empty())
+  if (!d_arithModelCacheSet)
   {
+    d_arithModelCacheSet = true;
     collectAssertedTerms(termSet);
     d_internal->collectModelValues(termSet, d_arithModelCache);
   }
 }
 void TheoryArith::updateModelCache(const std::set<Node>& termSet)
 {
-  if (d_arithModelCache.empty())
+  if (!d_arithModelCacheSet)
   {
+    d_arithModelCacheSet = true;
     d_internal->collectModelValues(termSet, d_arithModelCache);
   }
 }
index 8f48194059c0a4ab5e379cd5d94f128d20016c6c..13f4f8ad79f0393cd0600e79a8e3723449ff2551 100644 (file)
@@ -180,6 +180,8 @@ class TheoryArith : public Theory {
    * used to augment the TheoryModel.
    */
   std::map<Node, Node> d_arithModelCache;
+  /** Is the above map computed? */
+  bool d_arithModelCacheSet;
 
 };/* class TheoryArith */
 
index e6185da095856edd9aeb1163b1ae8fb0661d3d87..e63b24e2606bae57b7909963af3469a0f2ad16cb 100644 (file)
@@ -1,6 +1,5 @@
-; COMMAND-LINE: --check-model
-; EXPECT: (error "Cannot run check-model on a model with approximate values.")
-; EXIT: 1
+; COMMAND-LINE: -q
+; EXPECT: sat
 (set-logic QF_UFNRAT)
 (set-option :produce-models true)
 (declare-fun b (Real) Real)
index a6f31ac996d860773c649bbc9a2d881d6780b918..7252e11cf7064965edfb5d5e258f59fa0c249d26 100644 (file)
@@ -1,8 +1,7 @@
-; COMMAND-LINE: --check-models
-; EXPECT: (error "Cannot run check-model on a model with approximate values.")
-; EXIT: 1
+; COMMAND-LINE: -q
+; EXPECT: sat
 (set-logic QF_NRAT)
 (set-option :re-elim-agg true)
 (declare-fun r6 () Real)
 (assert (= 0.0 (cos r6)))
-(check-sat)
\ No newline at end of file
+(check-sat)