Add notions of evaluated kinds in TheoryModel (#1947)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Wed, 23 May 2018 20:44:50 +0000 (15:44 -0500)
committerGitHub <noreply@github.com>
Wed, 23 May 2018 20:44:50 +0000 (15:44 -0500)
15 files changed:
src/options/theory_options.toml
src/options/uf_options.toml
src/smt/smt_engine.cpp
src/theory/bv/theory_bv.cpp
src/theory/bv/theory_bv.h
src/theory/quantifiers/fmf/ambqi_builder.cpp
src/theory/quantifiers/fmf/full_model_check.cpp
src/theory/quantifiers/fmf/model_builder.cpp
src/theory/quantifiers/theory_quantifiers.cpp
src/theory/quantifiers/theory_quantifiers.h
src/theory/theory_engine.cpp
src/theory/theory_model.cpp
src/theory/theory_model.h
src/theory/theory_model_builder.cpp
src/theory/uf/theory_uf.cpp

index 2b1f7507283d9f941e0fb7445f2dd8eee1fd2f75..3509f408ddfeb0dc5dcd15d38a36fc7633ca4f10 100644 (file)
@@ -23,3 +23,20 @@ header = "options/theory_options.h"
   notifies   = ["notifyUseTheoryList"]
   read_only  = true
   help       = "use alternate theory implementation NAME (--use-theory=help for a list). This option may be repeated or a comma separated list."
+
+[[option]]
+  name       = "assignFunctionValues"
+  category   = "regular"
+  long       = "assign-function-values"
+  type       = "bool"
+  default    = "true"
+  help       = "assign values for uninterpreted functions in models"
+
+[[option]]
+  name       = "condenseFunctionValues"
+  category   = "regular"
+  long       = "condense-function-values"
+  type       = "bool"
+  default    = "true"
+  read_only  = true
+  help       = "condense values for functions in models rather than explicitly representing them"
index 791b6e0bb7de1505e5baf5cf6f16652cda3cd2c4..73a2be9ff7121452c8d6bcf3b7321343740232ff 100644 (file)
@@ -11,15 +11,6 @@ header = "options/uf_options.h"
   default    = "true"
   help       = "use UF symmetry breaker (Deharbe et al., CADE 2011)"
 
-[[option]]
-  name       = "condenseFunctionValues"
-  category   = "regular"
-  long       = "condense-function-values"
-  type       = "bool"
-  default    = "true"
-  read_only  = true
-  help       = "condense models for functions rather than explicitly representing them"
-
 [[option]]
   name       = "ufssRegions"
   category   = "regular"
index 1145273348d273a8430e8beee04f872a0584ae08..9d4462210b21a92dddbe52087ef6d3388b247a33 100644 (file)
@@ -1313,6 +1313,11 @@ void SmtEngine::setDefaults() {
     {
       options::cbqiMidpoint.set(true);
     }
+    // do not assign function values (optimization)
+    if (!options::assignFunctionValues.wasSetByUser())
+    {
+      options::assignFunctionValues.set(false);
+    }
   }
   else
   {
@@ -5189,6 +5194,13 @@ Model* SmtEngine::getModel() {
     Dump("benchmark") << GetModelCommand();
   }
 
+  if (!options::assignFunctionValues())
+  {
+    const char* msg =
+        "Cannot get the model when --assign-function-values is false.";
+    throw RecoverableModalException(msg);
+  }
+
   if(d_status.isNull() ||
      d_status.asSatisfiabilityResult() == Result::UNSAT ||
      d_problemExtended) {
index 2041b0805c8af1bbed05456fae7689d84ef9853d..107cb96724943bc5a3966cdfc2db594d948e08c7 100644 (file)
@@ -187,6 +187,16 @@ Node TheoryBV::getBVDivByZero(Kind k, unsigned width) {
   Unreachable();
 }
 
+void TheoryBV::finishInit()
+{
+  // these kinds are semi-evaluated in getModelValue (applications of this
+  // kind are treated as variables)
+  TheoryModel* tm = d_valuation.getModel();
+  Assert(tm != nullptr);
+  tm->setSemiEvaluatedKind(kind::BITVECTOR_ACKERMANNIZE_UDIV);
+  tm->setSemiEvaluatedKind(kind::BITVECTOR_ACKERMANNIZE_UREM);
+}
+
 Node TheoryBV::expandDefinition(LogicRequest &logicRequest, Node node) {
   Debug("bitvector-expandDefinition") << "TheoryBV::expandDefinition(" << node << ")" << std::endl;
 
index 13469d5621820ef9b1127a43dc67747aad88ef16..603823ff0095f1ae47ead4b9643ab88313901ca9 100644 (file)
@@ -66,6 +66,8 @@ public:
 
   void setMasterEqualityEngine(eq::EqualityEngine* eq) override;
 
+  void finishInit() override;
+
   Node expandDefinition(LogicRequest& logicRequest, Node node) override;
 
   void preRegisterTerm(TNode n) override;
index cedd2a2ed2a0c6da5cdfdd9f365bf7365fc2beae..52ce4407aa592ef0a358226ef76b59c389683fa2 100644 (file)
@@ -744,6 +744,11 @@ QModelBuilder( c, qe ){
 //------------------------model construction----------------------------
 
 bool AbsMbqiBuilder::processBuildModel(TheoryModel* m) {
+  if (!m->areFunctionValuesEnabled())
+  {
+    // nothing to do if no functions
+    return true;
+  }
   Trace("ambqi-debug") << "process build model " << std::endl;
   FirstOrderModel* f = (FirstOrderModel*)m;
   FirstOrderModelAbs* fm = f->asFirstOrderModelAbs();
index 0ec8b00b22f8b075745ce38ddf6f8133befe45ba..e97f716cb71fe37556cf50c421d11926ed0e8e37 100644 (file)
@@ -14,6 +14,7 @@
 
 #include "theory/quantifiers/fmf/full_model_check.h"
 #include "options/quantifiers_options.h"
+#include "options/theory_options.h"
 #include "options/uf_options.h"
 #include "theory/quantifiers/first_order_model.h"
 #include "theory/quantifiers/instantiate.h"
@@ -370,6 +371,11 @@ bool FullModelChecker::preProcessBuildModel(TheoryModel* m) {
 }
 
 bool FullModelChecker::processBuildModel(TheoryModel* m){
+  if (!m->areFunctionValuesEnabled())
+  {
+    // nothing to do if no functions
+    return true;
+  }
   FirstOrderModelFmc * fm = ((FirstOrderModelFmc*)m)->asFirstOrderModelFmc();
   Trace("fmc") << "---Full Model Check reset() " << std::endl;
   d_quant_models.clear();
index 9e7961172fbcd39dafa4dae21bc6a2b1d4a2360b..055bee23175e860f9bd3e2e678d2a37971928a78 100644 (file)
@@ -183,6 +183,11 @@ Node QModelBuilderIG::getCurrentUfModelValue( FirstOrderModel* fm, Node n, std::
 */
 
 bool QModelBuilderIG::processBuildModel( TheoryModel* m ) {
+  if (!m->areFunctionValuesEnabled())
+  {
+    // nothing to do if no functions
+    return true;
+  }
   FirstOrderModel* f = (FirstOrderModel*)m;
   FirstOrderModelIG* fm = f->asFirstOrderModelIG();
   Trace("model-engine-debug") << "Process build model " << optUseModel() << std::endl;
index 74d8269f93aefdb606925f0a15e6b2238a4a8c7e..5016bd87fc8af57a95d219d6242fe513b6d1fb9b 100644 (file)
@@ -73,6 +73,15 @@ void TheoryQuantifiers::notifyEq(TNode lhs, TNode rhs) {
 
 }
 
+void TheoryQuantifiers::finishInit()
+{
+  // quantifiers are not evaluated in getModelValue
+  TheoryModel* tm = d_valuation.getModel();
+  Assert(tm != nullptr);
+  tm->setUnevaluatedKind(EXISTS);
+  tm->setUnevaluatedKind(FORALL);
+}
+
 void TheoryQuantifiers::preRegisterTerm(TNode n) {
   Debug("quantifiers-prereg") << "TheoryQuantifiers::preRegisterTerm() " << n << endl;
   if( n.getKind()==FORALL ){
index 4bb28fe79a0244c6223d18feae6bc575ed820787..4f0302bf3c96d22d3087eef68e95f46a2cc882bd 100644 (file)
@@ -42,6 +42,8 @@ class TheoryQuantifiers : public Theory {
   void setMasterEqualityEngine(eq::EqualityEngine* eq) override;
   void addSharedTerm(TNode t) override;
   void notifyEq(TNode lhs, TNode rhs);
+  /** finish initialization */
+  void finishInit() override;
   void preRegisterTerm(TNode n) override;
   void presolve() override;
   void ppNotifyAssertions(const std::vector<Node>& assertions) override;
index 08d33fe6cdde6aa95715a5fad6d3957a38a2e658..4d78482c5ded13a502a109a617cad78854744c42 100644 (file)
@@ -232,7 +232,8 @@ void TheoryEngine::finishInit() {
     d_curr_model_builder = d_quantEngine->getModelBuilder();
     d_curr_model = d_quantEngine->getModel();
   } else {
-    d_curr_model = new theory::TheoryModel(d_userContext, "DefaultModel", true);
+    d_curr_model = new theory::TheoryModel(
+        d_userContext, "DefaultModel", options::assignFunctionValues());
     d_aloc_curr_model = true;
   }
   //make the default builder, e.g. in the case that the quantifiers engine does not have a model builder
index 35ada798cf54042cacd875589db7cde03ef36f83..0258e65cc511c2cfc175426c1194a7e0a884c63e 100644 (file)
@@ -48,6 +48,12 @@ TheoryModel::TheoryModel(context::Context* c,
   d_equalityEngine->addFunctionKind(kind::APPLY_SELECTOR_TOTAL);
   d_equalityEngine->addFunctionKind(kind::APPLY_TESTER);
   d_eeContext->push();
+  // do not interpret APPLY_UF if we are not assigning function values
+  if (!enableFuncModels)
+  {
+    setSemiEvaluatedKind(kind::APPLY_UF);
+  }
+  setUnevaluatedKind(kind::BOUND_VARIABLE);
 }
 
 TheoryModel::~TheoryModel()
@@ -160,148 +166,157 @@ Node TheoryModel::getModelValue(TNode n, bool hasBoundVars, bool useDontCares) c
   }
   Debug("model-getvalue-debug") << "Get model value " << n << " ... ";
   Debug("model-getvalue-debug") << d_equalityEngine->hasTerm(n) << std::endl;
+  if (n.isConst())
+  {
+    d_modelCache[n] = n;
+    return n;
+  }
+
   Node ret = n;
-  if(n.getKind() == kind::EXISTS || n.getKind() == kind::FORALL || n.getKind() == kind::COMBINED_CARDINALITY_CONSTRAINT ) {
-    // We should have terms, thanks to TheoryQuantifiers::collectModelInfo().
-    // However, if the Decision Engine stops us early, there might be a
-    // quantifier that isn't assigned.  In conjunction with miniscoping, this
-    // might lead to a perfectly good model.  Think of
-    //     ASSERT FORALL(x) : p OR x=5
-    // The p is pulled out by miniscoping, and set to TRUE by the decision
-    // engine, then the quantifier's value in the model doesn't matter, so the
-    // Decision Engine stops.  So even though the top-level quantifier was
-    // asserted, it can't be checked directly: first, it doesn't "exist" in
-    // non-miniscoped form, and second, no quantifiers have been asserted, so
-    // none is in the model.  We used to fail an assertion here, but that's
-    // no good.  Instead, return the quantifier itself.  If we're in
-    // checkModel(), and the quantifier actually matters, we'll get an
-    // assert-fail since the quantifier isn't a constant.
-    Node nr = Rewriter::rewrite(n);
-    if(!d_equalityEngine->hasTerm(nr)) {
-      d_modelCache[n] = ret;
-      return ret;
-    } else {
-      ret = nr;
+  Kind nk = n.getKind();
+  NodeManager* nm = NodeManager::currentNM();
+
+  // if it is an evaluated kind, compute model values for children and evaluate
+  if (n.getNumChildren() > 0
+      && d_not_evaluated_kinds.find(nk) == d_not_evaluated_kinds.end())
+  {
+    Debug("model-getvalue-debug")
+        << "Get model value children " << n << std::endl;
+    std::vector<Node> children;
+    if (n.getKind() == APPLY_UF)
+    {
+      Node op = getModelValue(n.getOperator(), hasBoundVars);
+      Debug("model-getvalue-debug") << "  operator : " << op << std::endl;
+      children.push_back(op);
     }
-  } else {
-    // FIXME : special case not necessary? (also address BV_ACKERMANNIZE
-    // functions below), github issue #1116
-    if(n.getKind() == kind::LAMBDA) {
-      NodeManager* nm = NodeManager::currentNM();
-      Node body = getModelValue(n[1], true);
-      body = Rewriter::rewrite(body);
-      ret = nm->mkNode(kind::LAMBDA, n[0], body);
-      ret = Rewriter::rewrite( ret );
-      d_modelCache[n] = ret;
-      return ret;
+    else if (n.getMetaKind() == kind::metakind::PARAMETERIZED)
+    {
+      children.push_back(n.getOperator());
+    }
+    // evaluate the children
+    for (unsigned i = 0, nchild = n.getNumChildren(); i < nchild; ++i)
+    {
+      ret = getModelValue(n[i], hasBoundVars);
+      Debug("model-getvalue-debug")
+          << "  " << n << "[" << i << "] is " << ret << std::endl;
+      children.push_back(ret);
     }
-    if(n.isConst() || (hasBoundVars && n.getKind() == kind::BOUND_VARIABLE)) {
+    ret = nm->mkNode(n.getKind(), children);
+    Debug("model-getvalue-debug") << "ret (pre-rewrite): " << ret << std::endl;
+    ret = Rewriter::rewrite(ret);
+    Debug("model-getvalue-debug") << "ret (post-rewrite): " << ret << std::endl;
+    // special cases
+    if (ret.getKind() == kind::CARDINALITY_CONSTRAINT)
+    {
+      Debug("model-getvalue-debug")
+          << "get cardinality constraint " << ret[0].getType() << std::endl;
+      ret = nm->mkConst(
+          getCardinality(ret[0].getType().toType()).getFiniteCardinality()
+          <= ret[1].getConst<Rational>().getNumerator());
+    }
+    else if (ret.getKind() == kind::CARDINALITY_VALUE)
+    {
+      Debug("model-getvalue-debug")
+          << "get cardinality value " << ret[0].getType() << std::endl;
+      ret = nm->mkConst(Rational(
+          getCardinality(ret[0].getType().toType()).getFiniteCardinality()));
+    }
+    d_modelCache[n] = ret;
+    return ret;
+  }
+  // must rewrite the term at this point
+  ret = Rewriter::rewrite(n);
+  // return the representative of the term in the equality engine, if it exists
+  TypeNode t = ret.getType();
+  bool eeHasTerm;
+  if (!options::ufHo() && (t.isFunction() || t.isPredicate()))
+  {
+    // functions are in the equality engine, but *not* as first-class members
+    // when higher-order is disabled. In this case, we cannot query
+    // representatives for functions since they are "internal" nodes according
+    // to the equality engine despite hasTerm returning true. However, they are
+    // first class members when higher-order is enabled. Hence, the special
+    // case here.
+    eeHasTerm = false;
+  }
+  else
+  {
+    eeHasTerm = d_equalityEngine->hasTerm(ret);
+  }
+  if (eeHasTerm)
+  {
+    Debug("model-getvalue-debug")
+        << "get value from representative " << ret << "..." << std::endl;
+    ret = d_equalityEngine->getRepresentative(ret);
+    Assert(d_reps.find(ret) != d_reps.end());
+    std::map<Node, Node>::const_iterator it2 = d_reps.find(ret);
+    if (it2 != d_reps.end())
+    {
+      ret = it2->second;
       d_modelCache[n] = ret;
       return ret;
     }
+  }
 
-    if (n.getNumChildren() > 0
-        && n.getKind() != kind::BITVECTOR_ACKERMANNIZE_UDIV
-        && n.getKind() != kind::BITVECTOR_ACKERMANNIZE_UREM)
+  // if we are a evaluated or semi-evaluated kind, return an arbitrary value
+  // if we are not in the d_not_evaluated_kinds map, we are evaluated
+  // if we are in the d_semi_evaluated_kinds, we are semi-evaluated
+  if (d_not_evaluated_kinds.find(nk) == d_not_evaluated_kinds.end()
+      || d_semi_evaluated_kinds.find(nk) != d_semi_evaluated_kinds.end())
+  {
+    if (t.isFunction() || t.isPredicate())
     {
-      Debug("model-getvalue-debug") << "Get model value children " << n << std::endl;
-      std::vector<Node> children;
-      if (n.getKind() == APPLY_UF) {
-        Node op = getModelValue(n.getOperator(), hasBoundVars);
-        Debug("model-getvalue-debug") << "  operator : " << op << std::endl;
-        children.push_back(op);
-      }
-      else if (n.getMetaKind() == kind::metakind::PARAMETERIZED) {
-        children.push_back(n.getOperator());
-      }
-      //evaluate the children
-      for (unsigned i = 0; i < n.getNumChildren(); ++i) {
-        ret = getModelValue(n[i], hasBoundVars);
-        Debug("model-getvalue-debug") << "  " << n << "[" << i << "] is " << ret << std::endl;
-        children.push_back(ret);
+      if (d_enableFuncModels)
+      {
+        std::map<Node, Node>::const_iterator it = d_uf_models.find(n);
+        if (it != d_uf_models.end())
+        {
+          // Existing function
+          ret = it->second;
+          d_modelCache[n] = ret;
+          return ret;
+        }
+        // Unknown function symbol: return LAMBDA x. c, where c is the first
+        // constant in the enumeration of the range type
+        vector<TypeNode> argTypes = t.getArgTypes();
+        vector<Node> args;
+        NodeManager* nm = NodeManager::currentNM();
+        for (unsigned i = 0, size = argTypes.size(); i < size; ++i)
+        {
+          args.push_back(nm->mkBoundVar(argTypes[i]));
+        }
+        Node boundVarList = nm->mkNode(kind::BOUND_VAR_LIST, args);
+        TypeEnumerator te(t.getRangeType());
+        ret = nm->mkNode(kind::LAMBDA, boundVarList, *te);
       }
-      ret = NodeManager::currentNM()->mkNode(n.getKind(), children);
-      Debug("model-getvalue-debug") << "ret (pre-rewrite): " << ret << std::endl;
-      ret = Rewriter::rewrite(ret);
-      Debug("model-getvalue-debug") << "ret (post-rewrite): " << ret << std::endl;
-      if(ret.getKind() == kind::CARDINALITY_CONSTRAINT) {
-        Debug("model-getvalue-debug") << "get cardinality constraint " << ret[0].getType() << std::endl;
-        ret = NodeManager::currentNM()->mkConst(getCardinality(ret[0].getType().toType()).getFiniteCardinality() <= ret[1].getConst<Rational>().getNumerator());
-      }else if(ret.getKind() == kind::CARDINALITY_VALUE) {
-        Debug("model-getvalue-debug") << "get cardinality value " << ret[0].getType() << std::endl;
-        ret = NodeManager::currentNM()->mkConst(Rational(getCardinality(ret[0].getType().toType()).getFiniteCardinality()));
+      else
+      {
+        // if func models not enabled, throw an error
+        Unreachable();
       }
-      d_modelCache[n] = ret;
-      return ret;
     }
-  
-    Debug("model-getvalue-debug") << "Handling special cases for types..." << std::endl;
-    TypeNode t = n.getType();
-    bool eeHasTerm;
-    if( !options::ufHo() && (t.isFunction() || t.isPredicate()) ){
-      // functions are in the equality engine, but *not* as first-class members
-      // when higher-order is disabled. In this case, we cannot query representatives for functions
-      // since they are "internal" nodes according to the equality engine despite hasTerm returning true. 
-      // However, they are first class members when higher-order is enabled. Hence, the special
-      // case here.
-      eeHasTerm = false;
-    }else{
-      eeHasTerm = d_equalityEngine->hasTerm(n);
+    else if (!t.isFirstClass())
+    {
+      // this is the class for regular expressions
+      // we simply invoke the rewriter on them
+      ret = Rewriter::rewrite(ret);
     }
-    // if the term does not exist in the equality engine, return an arbitrary value
-    if (!eeHasTerm) {
-      if (t.isFunction() || t.isPredicate()) {
-        if (d_enableFuncModels) {
-          std::map< Node, Node >::const_iterator it = d_uf_models.find(n);
-          if (it != d_uf_models.end()) {
-            // Existing function
-            ret = it->second;
-            d_modelCache[n] = ret;
-            return ret;
-          }
-          // Unknown function symbol: return LAMBDA x. c, where c is the first constant in the enumeration of the range type
-          vector<TypeNode> argTypes = t.getArgTypes();
-          vector<Node> args;
-          NodeManager* nm = NodeManager::currentNM();
-          for (unsigned i = 0; i < argTypes.size(); ++i) {
-            args.push_back(nm->mkBoundVar(argTypes[i]));
-          }
-          Node boundVarList = nm->mkNode(kind::BOUND_VAR_LIST, args);
-          TypeEnumerator te(t.getRangeType());
-          ret = nm->mkNode(kind::LAMBDA, boundVarList, *te);
-        }else{
-          // TODO: if func models not enabled, throw an error?
-          Unreachable();
-        }
-      }
-      else if (!t.isFirstClass())
+    else
+    {
+      if (options::omitDontCares() && useDontCares)
       {
-        // this is the class for regular expressions
-        // we simply invoke the rewriter on them
-        ret = Rewriter::rewrite(ret);
-      } else {
-        if (options::omitDontCares() && useDontCares) {
-          return Node();
-        }
-        // Unknown term - return first enumerated value for this type
-        TypeEnumerator te(n.getType());
-        ret = *te;
+        return Node();
       }
-      d_modelCache[n] = ret;
-      return ret;
+      // Unknown term - return first enumerated value for this type
+      TypeEnumerator te(n.getType());
+      ret = *te;
     }
+    d_modelCache[n] = ret;
+    return ret;
   }
-  Debug("model-getvalue-debug") << "get value from representative " << ret << "..." << std::endl;
-  ret = d_equalityEngine->getRepresentative(ret);
-  Assert(d_reps.find(ret) != d_reps.end());
-  std::map< Node, Node >::const_iterator it2 = d_reps.find( ret );
-  if (it2 != d_reps.end()) {
-    ret = it2->second;
-  } else {
-    ret = Node::null();
-  }
-  d_modelCache[n] = ret;
-  return ret;
+
+  d_modelCache[n] = n;
+  return n;
 }
 
 /** add substitution */
@@ -477,6 +492,17 @@ void TheoryModel::recordApproximation(TNode n, TNode pred)
   d_approx_list.push_back(std::pair<Node, Node>(n, pred));
 }
 
+void TheoryModel::setUnevaluatedKind(Kind k)
+{
+  d_not_evaluated_kinds.insert(k);
+}
+
+void TheoryModel::setSemiEvaluatedKind(Kind k)
+{
+  d_not_evaluated_kinds.insert(k);
+  d_semi_evaluated_kinds.insert(k);
+}
+
 bool TheoryModel::hasTerm(TNode a)
 {
   return d_equalityEngine->hasTerm( a );
@@ -546,6 +572,11 @@ void TheoryModel::printRepresentative( std::ostream& out, Node r ){
   }
 }
 
+bool TheoryModel::areFunctionValuesEnabled() const
+{
+  return d_enableFuncModels;
+}
+
 void TheoryModel::assignFunctionDefinition( Node f, Node f_def ) {
   Assert( d_uf_models.find( f )==d_uf_models.end() );
   Trace("model-builder") << "  Assigning function (" << f << ") to (" << f_def << ")" << endl;
index b6d1288aea388d16bffc66fc9fd0d44a3e198437..cee39b54295e6627c3efc0e86a8bf29a9e844745 100644 (file)
@@ -161,6 +161,50 @@ public:
    * construction process.
    */
   void recordApproximation(TNode n, TNode pred);
+  /** set unevaluate/semi-evaluated kind
+   *
+   * This informs this model how it should interpret applications of terms with
+   * kind k in getModelValue. We distinguish four categories of kinds:
+   *
+   * [1] "Evaluated"
+   * This includes (standard) interpreted symbols like NOT, PLUS, UNION, etc.
+   * These operators can be characterized by the invariant that they are
+   * "evaluatable". That is, if they are applied to only constants, the rewriter
+   * is guaranteed to rewrite the application to a constant. When getting
+   * the model value of <k>( t1...tn ) where k is a kind of this category, we
+   * compute the (constant) value of t1...tn, say this returns c1...cn, we
+   * return the (constant) result of rewriting <k>( c1...cn ).
+   *
+   * [2] "Unevaluated"
+   * This includes interpreted symbols like FORALL, EXISTS,
+   * CARDINALITY_CONSTRAINT, that are not evaluatable. When getting a model
+   * value for a term <k>( t1...tn ) where k is a kind of this category, we
+   * check whether <k>( t1...tn ) exists in the equality engine of this model.
+   * If it does, we return its representative, otherwise we return the term
+   * itself.
+   *
+   * [3] "Semi-evaluated"
+   * This includes kinds like BITVECTOR_ACKERMANNIZE_UDIV and others, typically
+   * those that correspond to abstractions. Like unevaluated kinds, these
+   * kinds do not have an evaluator. In contrast to unevaluated kinds, we
+   * interpret a term <k>( t1...tn ) not appearing in the equality engine as an
+   * arbitrary value instead of the term itself.
+   *
+   * [4] APPLY_UF, where getting the model value depends on an internally
+   * constructed representation of a lambda model value (d_uf_models).
+   * It is optional whether this kind is "evaluated" or "semi-evaluated".
+   * In the case that it is "evaluated", get model rewrites the application
+   * of the lambda model value of its operator to its evaluated arguments.
+   *
+   * By default, all kinds are considered "evaluated". The following methods
+   * change the interpretation of various (non-APPLY_UF) kinds to one of the
+   * above categories and should be called by the theories that own the kind
+   * during Theory::finishInit. We set APPLY_UF to be semi-interpreted when
+   * this model does not enabled function values (this is the case for the model
+   * of TheoryEngine when the option assignFunctionValues is set to false).
+   */
+  void setUnevaluatedKind(Kind k);
+  void setSemiEvaluatedKind(Kind k);
   //---------------------------- end building the model
 
   // ------------------- general equality queries
@@ -217,6 +261,8 @@ public:
   std::map< Node, std::vector< Node > > d_uf_terms;
   /** a map from functions f to a list of all HO_APPLY terms with first argument f */
   std::map< Node, std::vector< Node > > d_ho_uf_terms;
+  /** are function values enabled? */
+  bool areFunctionValuesEnabled() const;
   /** assign function value f to definition f_def */
   void assignFunctionDefinition( Node f, Node f_def );
   /** have we assigned function f? */
@@ -245,6 +291,10 @@ public:
   std::map<Node, Node> d_approximations;
   /** list of all approximations */
   std::vector<std::pair<Node, Node> > d_approx_list;
+  /** a set of kinds that are not evaluated */
+  std::unordered_set<Kind, kind::KindHashFunction> d_not_evaluated_kinds;
+  /** a set of kinds that are semi-evaluated */
+  std::unordered_set<Kind, kind::KindHashFunction> d_semi_evaluated_kinds;
   /** map of representatives of equality engine to used representatives in
    * representative set */
   std::map<Node, Node> d_reps;
index e88d1e3be11ad408ffb1a653d6f069f86a0b3927..f91a413e3e8a744ddd88da912973b09ea424f895 100644 (file)
@@ -930,7 +930,10 @@ bool TheoryEngineModelBuilder::preProcessBuildModel(TheoryModel* m)
 
 bool TheoryEngineModelBuilder::processBuildModel(TheoryModel* m)
 {
-  assignFunctions(m);
+  if (m->areFunctionValuesEnabled())
+  {
+    assignFunctions(m);
+  }
   return true;
 }
 
@@ -1098,6 +1101,10 @@ struct sortTypeSize
 
 void TheoryEngineModelBuilder::assignFunctions(TheoryModel* m)
 {
+  if (!options::assignFunctionValues())
+  {
+    return;
+  }
   Trace("model-builder") << "Assigning function values..." << std::endl;
   std::vector<Node> funcs_to_assign = m->getFunctionsToAssign();
 
index f5748549ee67af5a2198e9dc0df08f6450e898a4..851f43582cbe6fbb06a4e080f3885457afaaac94 100644 (file)
@@ -75,6 +75,10 @@ void TheoryUF::setMasterEqualityEngine(eq::EqualityEngine* eq) {
 }
 
 void TheoryUF::finishInit() {
+  // combined cardinality constraints are not evaluated in getModelValue
+  TheoryModel* tm = d_valuation.getModel();
+  Assert(tm != nullptr);
+  tm->setUnevaluatedKind(kind::COMBINED_CARDINALITY_CONSTRAINT);
   // initialize the strong solver
   if (options::finiteModelFind() && options::ufssMode()!=UF_SS_NONE) {
     d_thss = new StrongSolverTheoryUF(getSatContext(), getUserContext(), *d_out, this);