Model generation for arith, boolean, and uf theories via
authorMorgan Deters <mdeters@gmail.com>
Sat, 9 Oct 2010 04:24:15 +0000 (04:24 +0000)
committerMorgan Deters <mdeters@gmail.com>
Sat, 9 Oct 2010 04:24:15 +0000 (04:24 +0000)
(get-value ...) SMT-LIBv2 command.  As per SMT-LIBv2 spec,
you must pass --interactive --produce-models on the command
line (although they don't currently make us do any extra
work).  Closes bug #213.

31 files changed:
src/expr/node_value.cpp
src/expr/type.h
src/parser/smt2/Smt2.g
src/prop/cnf_stream.h
src/prop/prop_engine.cpp
src/prop/prop_engine.h
src/prop/sat.h
src/smt/smt_engine.cpp
src/smt/smt_engine.h
src/theory/arith/delta_rational.cpp
src/theory/arith/delta_rational.h
src/theory/arith/tableau.cpp
src/theory/arith/tableau.h
src/theory/arith/theory_arith.cpp
src/theory/arith/theory_arith.h
src/theory/arrays/theory_arrays.cpp
src/theory/arrays/theory_arrays.h
src/theory/booleans/theory_bool.cpp
src/theory/booleans/theory_bool.h
src/theory/builtin/theory_builtin.cpp
src/theory/builtin/theory_builtin.h
src/theory/bv/theory_bv.cpp
src/theory/bv/theory_bv.h
src/theory/theory.h
src/theory/theory_engine.cpp
src/theory/theory_engine.h
src/theory/uf/morgan/theory_uf_morgan.cpp
src/theory/uf/morgan/theory_uf_morgan.h
src/theory/uf/tim/theory_uf_tim.h
src/util/congruence_closure.h
src/util/result.h

index 004f0c9a928d2421b1789485b6730d7dc85a9c7d..70f1047f513a9ea73abab8de623f605bff00917b 100644 (file)
@@ -71,7 +71,7 @@ void NodeValue::toStream(std::ostream& out, int toDepth, bool types,
                           VarNameAttr(), s)) {
         out << s;
       } else {
-        out << "var_" << d_id << "[" << this << "]";
+        out << "var_" << d_id;
       }
       if(types) {
         // print the whole type, but not *its* type
index 435d640d0242c6e336edb1bdd23d48683ac0b119..453eaf5c4aa049604d2cffd6092ba8e56879f1d9 100644 (file)
@@ -216,7 +216,7 @@ public:
 
   /**
    * Is this a predicate type, i.e. if it's a function type mapping to Boolean.
-   * Aall predicate types are also function types.
+   * All predicate types are also function types.
    * @return true if the type is a predicate type
    */
   bool isPredicate() const;
index 2c460c831aea677cff87e5f6c4f1dfcc540be9de..4c447f9c10fdd0a9ce7a370f3ad78e864da11fbb 100644 (file)
@@ -257,6 +257,35 @@ command returns [CVC4::Command* cmd]
   | /* get-assertions */
     GET_ASSERTIONS_TOK
     { cmd = new GetAssertionsCommand; }
+  | /* push */
+    PUSH_TOK k=INTEGER_LITERAL
+    { unsigned n = AntlrInput::tokenToUnsigned(k);
+      if(n == 0) {
+        cmd = new EmptyCommand;
+      } else if(n == 1) {
+        cmd = new PushCommand;
+      } else {
+        CommandSequence* seq = new CommandSequence;
+        do {
+          seq->addCommand(new PushCommand);
+        } while(--n > 0);
+        cmd = seq;
+      }
+    }
+  | POP_TOK k=INTEGER_LITERAL
+    { unsigned n = AntlrInput::tokenToUnsigned(k);
+      if(n == 0) {
+        cmd = new EmptyCommand;
+      } else if(n == 1) {
+        cmd = new PopCommand;
+      } else {
+        CommandSequence* seq = new CommandSequence;
+        do {
+          seq->addCommand(new PopCommand);
+        } while(--n > 0);
+        cmd = seq;
+      }
+    }
   | EXIT_TOK
     { cmd = NULL; }
   ;
@@ -536,6 +565,8 @@ SET_INFO_TOK : 'set-info';
 GET_INFO_TOK : 'get-info';
 SET_OPTION_TOK : 'set-option';
 GET_OPTION_TOK : 'get-option';
+PUSH_TOK : 'push';
+POP_TOK : 'pop';
 
 // operators (NOTE: theory symbols go here)
 AMPERSAND_TOK     : '&';
index 2ff1070682a806eae8e94ca82a54c9004ff2bd69..919214f9b8171d05a4cc1e3ac6f3b674390831ad 100644 (file)
@@ -184,7 +184,7 @@ public:
   const NodeCache& getNodeCache() const {
     return d_nodeCache;
   }
-}; /* class CnfStream */
+};/* class CnfStream */
 
 /**
  * TseitinCnfStream is based on the following recursive algorithm
index c49d5f38ad43df5ee4ff044cf145f60f5489e485..961a18bdb6427db084b7b897e4927892d0cd0673 100644 (file)
@@ -130,6 +130,20 @@ Result PropEngine::checkSat() {
   return Result(result ? Result::SAT : Result::UNSAT);
 }
 
+Node PropEngine::getValue(TNode node) {
+  Assert(node.getKind() == kind::VARIABLE &&
+         node.getType().isBoolean());
+  SatLiteralValue v = d_satSolver->value(d_cnfStream->getLiteral(node));
+  if(v == l_True) {
+    return NodeManager::currentNM()->mkConst(true);
+  } else if(v == l_False) {
+    return NodeManager::currentNM()->mkConst(false);
+  } else {
+    Assert(v == l_Undef);
+    return Node::null();
+  }
+}
+
 void PropEngine::push() {
   Assert(!d_inCheckSat, "Sat solver in solve()!");
   Debug("prop") << "push()" << endl;
index c33982ddca2ce9aa3080c9dee327554eb6bf6632..7eb9031804dfac2e85cc3d4d0caec20f26fdef89 100644 (file)
@@ -113,6 +113,14 @@ public:
    */
   Result checkSat();
 
+  /**
+   * Get the value of a boolean variable.
+   *
+   * @return mkConst<true>, mkConst<false>, or Node::null() if
+   * unassigned.
+   */
+  Node getValue(TNode node);
+
   /**
    * Push the context level.
    */
index d780230a8c4013c84bd117bff22858a36a343c09..776895b4c5bd689c1e762914e400a8642a7d7d9a 100644 (file)
@@ -89,9 +89,9 @@ inline std::string stringOfLiteralValue(SatLiteralValue val) {
 }
 #endif /* __CVC4_USE_MINISAT */
 
-/** Interface encapsulating the "input" to the solver, e.g., from the 
- * CNF converter. 
- * 
+/** Interface encapsulating the "input" to the solver, e.g., from the
+ * CNF converter.
+ *
  * TODO: Is it possible to push the typedefs of SatClause and SatVariable
  * into here, somehow?
  */
@@ -226,7 +226,7 @@ public:
   void setCnfStream(CnfStream* cnfStream);
 
   SatLiteralValue value(SatLiteral l);
-};
+};/* class SatSolver */
 
 /* Functions that delegate to the concrete SAT solver. */
 
index de2fa4ebc09f2af7718278209b4af68aebce9d15..3a4fd90e998502b837a598637dc864571d1cd24c 100644 (file)
@@ -119,7 +119,9 @@ SmtEngine::SmtEngine(ExprManager* em, const Options* opts) throw() :
   d_decisionEngine(NULL),
   d_theoryEngine(NULL),
   d_propEngine(NULL),
-  d_assertionList(NULL) {
+  d_assertionList(NULL),
+  d_haveAdditions(false),
+  d_lastResult() {
 
   NodeManagerScope nms(d_nodeManager);
 
@@ -162,7 +164,9 @@ SmtEngine::~SmtEngine() {
 
 void SmtEngine::setInfo(const string& key, const SExpr& value)
   throw(BadOptionException) {
-  if(key == ":status" ||
+  Debug("smt") << "SMT setInfo(" << key << ", " << value << ")" << endl;
+  if(key == ":name" ||
+     key == ":status" ||
      key == ":source" ||
      key == ":category" ||
      key == ":difficulty" ||
@@ -176,18 +180,21 @@ void SmtEngine::setInfo(const string& key, const SExpr& value)
 
 const SExpr& SmtEngine::getInfo(const string& key) const
   throw(BadOptionException) {
+  Debug("smt") << "SMT getInfo(" << key << ")" << endl;
   // FIXME implement me
   throw BadOptionException();
 }
 
 void SmtEngine::setOption(const string& key, const SExpr& value)
   throw(BadOptionException) {
+  Debug("smt") << "SMT setOption(" << key << ", " << value << ")" << endl;
   // FIXME implement me
   throw BadOptionException();
 }
 
 const SExpr& SmtEngine::getOption(const string& key) const
   throw(BadOptionException) {
+  Debug("smt") << "SMT getOption(" << key << ")" << endl;
   // FIXME implement me
   throw BadOptionException();
 }
@@ -195,6 +202,7 @@ const SExpr& SmtEngine::getOption(const string& key) const
 void SmtEngine::defineFunction(Expr func,
                                const vector<Expr>& formals,
                                Expr formula) {
+  Debug("smt") << "SMT defineFunction(" << func << ")" << endl;
   NodeManagerScope nms(d_nodeManager);
   Type formulaType = formula.getType(true);// type check body
   if(formulaType != FunctionType(func.getType()).getRangeType()) {
@@ -217,6 +225,7 @@ void SmtEngine::defineFunction(Expr func,
   }
   TNode formulaNode = formula.getTNode();
   DefinedFunction def(funcNode, formalsNodes, formulaNode);
+  d_haveAdditions = true;
   d_definedFunctions->insert(funcNode, def);
 }
 
@@ -303,6 +312,7 @@ Result SmtEngine::quickCheck() {
 void SmtEnginePrivate::addFormula(SmtEngine& smt, TNode n)
   throw(NoSuchFunctionException, AssertionException) {
   Debug("smt") << "push_back assertion " << n << endl;
+  smt.d_haveAdditions = true;
   smt.d_propEngine->assertFormula(SmtEnginePrivate::preprocess(smt, n));
 }
 
@@ -319,42 +329,56 @@ void SmtEngine::ensureBoolean(const BoolExpr& e) {
 }
 
 Result SmtEngine::checkSat(const BoolExpr& e) {
+  Assert(e.getExprManager() == d_exprManager);
   NodeManagerScope nms(d_nodeManager);
   Debug("smt") << "SMT checkSat(" << e << ")" << endl;
   ensureBoolean(e);// ensure expr is type-checked at this point
   SmtEnginePrivate::addFormula(*this, e.getNode());
   Result r = check().asSatisfiabilityResult();
+  d_lastResult = r;
+  d_haveAdditions = false;
   Debug("smt") << "SMT checkSat(" << e << ") ==> " << r << endl;
   return r;
 }
 
 Result SmtEngine::query(const BoolExpr& e) {
+  Assert(e.getExprManager() == d_exprManager);
   NodeManagerScope nms(d_nodeManager);
   Debug("smt") << "SMT query(" << e << ")" << endl;
   ensureBoolean(e);// ensure expr is type-checked at this point
   SmtEnginePrivate::addFormula(*this, e.getNode().notNode());
   Result r = check().asValidityResult();
+  d_lastResult = r;
+  d_haveAdditions = false;
   Debug("smt") << "SMT query(" << e << ") ==> " << r << endl;
   return r;
 }
 
 Result SmtEngine::assertFormula(const BoolExpr& e) {
+  Assert(e.getExprManager() == d_exprManager);
   NodeManagerScope nms(d_nodeManager);
   Debug("smt") << "SMT assertFormula(" << e << ")" << endl;
   ensureBoolean(e);// type check node
+  if(d_assertionList != NULL) {
+    d_assertionList->push_back(e);
+  }
   SmtEnginePrivate::addFormula(*this, e.getNode());
   return quickCheck().asValidityResult();
 }
 
 Expr SmtEngine::simplify(const Expr& e) {
+  Assert(e.getExprManager() == d_exprManager);
   NodeManagerScope nms(d_nodeManager);
   e.getType(true);// ensure expr is type-checked at this point
   Debug("smt") << "SMT simplify(" << e << ")" << endl;
   Unimplemented();
 }
 
-Expr SmtEngine::getValue(Expr term)
+Expr SmtEngine::getValue(Expr e)
   throw(ModalException, AssertionException) {
+  Assert(e.getExprManager() == d_exprManager);
+  Type type = e.getType(true);// ensure expr is type-checked at this point
+  Debug("smt") << "SMT getValue(" << e << ")" << endl;
   if(!d_options->interactive) {
     const char* msg =
       "Cannot get value when not in interactive mode.";
@@ -365,17 +389,33 @@ Expr SmtEngine::getValue(Expr term)
       "Cannot get value when produce-models options is off.";
     throw ModalException(msg);
   }
-  // TODO also check that the last query was sat/unknown, without intervening
-  // assertions
+  if(d_lastResult.asSatisfiabilityResult() != Result::SAT ||
+     d_haveAdditions) {
+    const char* msg =
+      "Cannot get value unless immediately proceded by SAT/INVALID response.";
+    throw ModalException(msg);
+  }
+  if(type.isFunction() || type.isPredicate() ||
+     type.isKind() || type.isSortConstructor()) {
+    const char* msg =
+      "Cannot get value of a function, predicate, or sort.";
+    throw ModalException(msg);
+  }
 
   NodeManagerScope nms(d_nodeManager);
-  Type type = term.getType(true);// ensure expr is type-checked at this point
-  SmtEnginePrivate::preprocess(*this, term.getNode());
+  Node eNode = e.getNode();
+  Node n = SmtEnginePrivate::preprocess(*this, eNode);
 
-  Unimplemented();
+  Debug("smt") << "--- getting value of " << n << endl;
+  Node resultNode = d_theoryEngine->getValue(n);
+
+  // type-check the result we got
+  Assert(resultNode.getType(true) == eNode.getType());
+  return Expr(d_exprManager, new Node(resultNode));
 }
 
 SExpr SmtEngine::getAssignment() throw(ModalException, AssertionException) {
+  Debug("smt") << "SMT getAssignment()" << endl;
   if(!d_options->produceAssignments) {
     const char* msg =
       "Cannot get the current assignment when produce-assignments option is off.";
@@ -389,7 +429,9 @@ SExpr SmtEngine::getAssignment() throw(ModalException, AssertionException) {
   Unimplemented();
 }
 
-vector<Expr> SmtEngine::getAssertions() throw(ModalException, AssertionException) {
+vector<Expr> SmtEngine::getAssertions()
+  throw(ModalException, AssertionException) {
+  Debug("smt") << "SMT getAssertions()" << endl;
   if(!d_options->interactive) {
     const char* msg =
       "Cannot query the current assertion list when not in interactive mode.";
@@ -402,13 +444,22 @@ vector<Expr> SmtEngine::getAssertions() throw(ModalException, AssertionException
 void SmtEngine::push() {
   NodeManagerScope nms(d_nodeManager);
   Debug("smt") << "SMT push()" << endl;
-  Unimplemented();
+  d_context->push();
+  d_propEngine->push();
+  Debug("userpushpop") << "SmtEngine: pushed to level "
+                       << d_context->getLevel() << endl;
 }
 
 void SmtEngine::pop() {
   NodeManagerScope nms(d_nodeManager);
   Debug("smt") << "SMT pop()" << endl;
-  Unimplemented();
+  d_propEngine->pop();
+  d_context->pop();
+  Debug("userpushpop") << "SmtEngine: popped to level "
+                       << d_context->getLevel() << endl;
+  // clear out last result: get-value/get-assignment no longer
+  // available here
+  d_lastResult = Result();
 }
 
 }/* CVC4 namespace */
index 1fd68d2a54095e2efa6115af1925eaf6e43647ab..efa48e517e8d65e0b28b2094f3548521864179eb 100644 (file)
@@ -111,6 +111,18 @@ class CVC4_PUBLIC SmtEngine {
    */
   AssertionList* d_assertionList;
 
+  /**
+   * Whether or not we have added any
+   * assertions/declarations/definitions since the last checkSat/query
+   * (and therefore we're not responsible for an assignment).
+   */
+  bool d_haveAdditions;
+
+  /**
+   * Result of last checkSat/query.
+   */
+  Result d_lastResult;
+
   /**
    * This is called by the destructor, just before destroying the
    * PropEngine, TheoryEngine, and DecisionEngine (in that order).  It
@@ -213,14 +225,14 @@ public:
   Expr simplify(const Expr& e);
 
   /**
-   * Get the assigned value of a term (only if preceded by a SAT or
-   * INVALID query).  Only permitted if the SmtEngine is set to
-   * operate interactively and produce-models is on.
+   * Get the assigned value of an expr (only if immediately preceded
+   * by a SAT or INVALID query).  Only permitted if the SmtEngine is
+   * set to operate interactively and produce-models is on.
    */
-  Expr getValue(Expr term) throw(ModalException, AssertionException);
+  Expr getValue(Expr e) throw(ModalException, AssertionException);
 
   /**
-   * Get the assigned value of a term (only if preceded by a SAT or
+   * Get the assignment (only if immediately preceded by a SAT or
    * INVALID query).  Only permitted if the SmtEngine is set to
    * operate interactively and produce-assignments is on.
    */
index f6a460fee3b7192ba9369d20dbf9f8525ec55a6d..d0e4ed1f4c030c3d02efe887404225b2c761e170 100644 (file)
@@ -24,12 +24,12 @@ using namespace std;
 using namespace CVC4;
 
 std::ostream& CVC4::operator<<(std::ostream& os, const DeltaRational& dq){
-  return os << "(" << dq.getNoninfintestimalPart()
-            << "," << dq.getInfintestimalPart() << ")";
+  return os << "(" << dq.getNoninfinitesimalPart()
+            << "," << dq.getInfinitesimalPart() << ")";
 }
 
 
 std::string DeltaRational::toString() const {
-  return "(" + getNoninfintestimalPart().toString() + "," +
-    getInfintestimalPart().toString() + ")";
+  return "(" + getNoninfinitesimalPart().toString() + "," +
+    getInfinitesimalPart().toString() + ")";
 }
index 26212ec66f564ef7f3ea6529696f7abd9e094c41..b75f5334c66e463d2a560447c7628cb974344bd9 100644 (file)
@@ -46,11 +46,11 @@ public:
   DeltaRational(const CVC4::Rational& base, const CVC4::Rational& coeff) :
     c(base), k(coeff) {}
 
-  const CVC4::Rational& getInfintestimalPart() const {
+  const CVC4::Rational& getInfinitesimalPart() const {
     return k;
   }
 
-  const CVC4::Rational& getNoninfintestimalPart() const {
+  const CVC4::Rational& getNoninfinitesimalPart() const {
     return c;
   }
 
index aaeadb629f909714f34766d9dcbb783db7aec945..d6a30ac91edde85f906916e322deec9197966e30 100644 (file)
@@ -48,7 +48,7 @@ Row::Row(ArithVar basic,
     Assert(d_coeffs[var_i] != Rational(0,1));
   }
 }
-void Row::subsitute(Row& row_s){
+void Row::substitute(Row& row_s){
   ArithVar x_s = row_s.basicVar();
 
   Assert(has(x_s));
@@ -133,7 +133,7 @@ void Tableau::addRow(ArithVar basicVar,
       Assert(isActiveBasicVariable(var));
 
       Row* row_var = lookup(var);
-      row_current->subsitute(*row_var);
+      row_current->substitute(*row_var);
     }
   }
 }
@@ -163,7 +163,7 @@ void Tableau::pivot(ArithVar x_r, ArithVar x_s){
     Row* row_k = lookup(basic);
     if(row_k->has(x_s)){
       d_activityMonitor.increaseActivity(basic, 30);
-      row_k->subsitute(*row_s);
+      row_k->substitute(*row_s);
     }
   }
 }
@@ -189,7 +189,7 @@ void Tableau::updateRow(Row* row){
       Row* row_var = isActiveBasicVariable(var) ? lookup(var) : lookupEjected(var);
 
       Assert(row_var != row);
-      row->subsitute(*row_var);
+      row->substitute(*row_var);
 
       i = row->begin();
       endIter = row->end();
index a69493ee04e2ee18b3187fd93d2b19b9341e8e59..88a5c23174cad62b08c0a601deeac1c0dcb59ae1 100644 (file)
@@ -83,7 +83,7 @@ public:
 
   void pivot(ArithVar x_j);
 
-  void subsitute(Row& row_s);
+  void substitute(Row& row_s);
 
   void printRow();
 };
index 26bb58e904b9f519fa5e77e74e276ac594de3c7a..ac3dc3d401b21c11de4de526b422ab9364344b44 100644 (file)
@@ -22,6 +22,8 @@
 #include "expr/metakind.h"
 #include "expr/node_builder.h"
 
+#include "theory/theory_engine.h"
+
 #include "util/rational.h"
 #include "util/integer.h"
 
@@ -927,3 +929,77 @@ void TheoryArith::propagate(Effort e) {
     }
   }
 }
+
+Node TheoryArith::getValue(TNode n, TheoryEngine* engine) {
+  NodeManager* nodeManager = NodeManager::currentNM();
+
+  switch(n.getKind()) {
+  case kind::VARIABLE: {
+    DeltaRational drat = d_partialModel.getAssignment(asArithVar(n));
+    // FIXME our infinitesimal is fixed here at 1e-06
+    return nodeManager->
+      mkConst( drat.getNoninfinitesimalPart() +
+               drat.getInfinitesimalPart() * Rational(1, 1000000) );
+  }
+
+  case kind::EQUAL: // 2 args
+    return nodeManager->
+      mkConst( engine->getValue(n[0]) == engine->getValue(n[1]) );
+
+  case kind::PLUS: { // 2+ args
+    Rational value = d_constants.d_ZERO;
+    for(TNode::iterator i = n.begin(),
+            iend = n.end();
+          i != iend;
+          ++i) {
+      value += engine->getValue(*i).getConst<Rational>();
+    }
+    return nodeManager->mkConst(value);
+  }
+
+  case kind::MULT: { // 2+ args
+    Rational value = d_constants.d_ONE;
+    for(TNode::iterator i = n.begin(),
+            iend = n.end();
+          i != iend;
+          ++i) {
+      value *= engine->getValue(*i).getConst<Rational>();
+    }
+    return nodeManager->mkConst(value);
+  }
+
+  case kind::MINUS: // 2 args
+    // should have been rewritten
+    Unreachable();
+
+  case kind::UMINUS: // 1 arg
+    // should have been rewritten
+    Unreachable();
+
+  case kind::DIVISION: // 2 args
+    return nodeManager->mkConst( engine->getValue(n[0]).getConst<Rational>() /
+                                 engine->getValue(n[1]).getConst<Rational>() );
+
+  case kind::IDENTITY: // 1 arg
+    return engine->getValue(n[0]);
+
+  case kind::LT: // 2 args
+    return nodeManager->mkConst( engine->getValue(n[0]).getConst<Rational>() <
+                                 engine->getValue(n[1]).getConst<Rational>() );
+
+  case kind::LEQ: // 2 args
+    return nodeManager->mkConst( engine->getValue(n[0]).getConst<Rational>() <=
+                                 engine->getValue(n[1]).getConst<Rational>() );
+
+  case kind::GT: // 2 args
+    return nodeManager->mkConst( engine->getValue(n[0]).getConst<Rational>() >
+                                 engine->getValue(n[1]).getConst<Rational>() );
+
+  case kind::GEQ: // 2 args
+    return nodeManager->mkConst( engine->getValue(n[0]).getConst<Rational>() >=
+                                 engine->getValue(n[1]).getConst<Rational>() );
+
+  default:
+    Unhandled(n.getKind());
+  }
+}
index cbfdf27f3ed4538f454bdf931cbe8c5a538b43a6..26dcc98253b4d8a3a6686bb1e87750f0e9ef7180 100644 (file)
@@ -128,6 +128,8 @@ public:
   void propagate(Effort e);
   void explain(TNode n, Effort e);
 
+  Node getValue(TNode n, TheoryEngine* engine);
+
   void shutdown(){ }
 
   std::string identify() const { return std::string("TheoryArith"); }
index 8997138cb9172680b7bfd35a80570f923c97793a..55a539f4431260897ba8e6afd82ca083f7e4f7bc 100644 (file)
@@ -18,6 +18,7 @@
 
 
 #include "theory/arrays/theory_arrays.h"
+#include "theory/theory_engine.h"
 #include "expr/kind.h"
 
 
@@ -58,3 +59,20 @@ void TheoryArrays::check(Effort e) {
   }
   Debug("arrays") << "TheoryArrays::check(): done" << endl;
 }
+
+Node TheoryArrays::getValue(TNode n, TheoryEngine* engine) {
+  NodeManager* nodeManager = NodeManager::currentNM();
+
+  switch(n.getKind()) {
+
+  case kind::VARIABLE:
+    Unhandled(kind::VARIABLE);
+
+  case kind::EQUAL: // 2 args
+    return nodeManager->
+      mkConst( engine->getValue(n[0]) == engine->getValue(n[1]) );
+
+  default:
+    Unhandled(n.getKind());
+  }
+}
index cb738d085570d8faf78f04e04ccd0dae86f74f49..89631d59fe4ff00a584a277354012b053748b9d7 100644 (file)
@@ -53,6 +53,7 @@ public:
   void check(Effort e);
   void propagate(Effort e) { }
   void explain(TNode n, Effort e) { }
+  Node getValue(TNode n, TheoryEngine* engine);
   void shutdown() { }
   std::string identify() const { return std::string("TheoryArrays"); }
 };/* class TheoryArrays */
index a7e343fdc18a90faf62e7f3b81cedd8917eefcbd..b1ff472acb353ddff0a3258e840972fd39104613 100644 (file)
 
 #include "theory/theory.h"
 #include "theory/booleans/theory_bool.h"
+#include "theory/theory_engine.h"
 
 namespace CVC4 {
 namespace theory {
 namespace booleans {
 
-
 RewriteResponse TheoryBool::preRewrite(TNode n, bool topLevel) {
   if(n.getKind() == kind::IFF) {
     NodeManager* nodeManager = NodeManager::currentNM();
@@ -140,6 +140,70 @@ RewriteResponse TheoryBool::postRewrite(TNode n, bool topLevel) {
   return RewriteComplete(n);
 }
 
+Node TheoryBool::getValue(TNode n, TheoryEngine* engine) {
+  NodeManager* nodeManager = NodeManager::currentNM();
+
+  switch(n.getKind()) {
+  case kind::VARIABLE:
+    // case for Boolean vars is implemented in TheoryEngine (since it
+    // appeals to the PropEngine to get the value)
+    Unreachable();
+
+  case kind::EQUAL: // 2 args
+    // should be handled by IFF
+    Unreachable();
+
+  case kind::NOT: // 1 arg
+    return nodeManager->mkConst(! engine->getValue(n[0]).getConst<bool>());
+
+  case kind::AND: { // 2+ args
+    for(TNode::iterator i = n.begin(),
+            iend = n.end();
+          i != iend;
+          ++i) {
+      if(! engine->getValue(*i).getConst<bool>()) {
+        return nodeManager->mkConst(false);
+      }
+    }
+    return nodeManager->mkConst(true);
+  }
+
+  case kind::IFF: // 2 args
+    return nodeManager->mkConst( engine->getValue(n[0]).getConst<bool>() ==
+                                 engine->getValue(n[1]).getConst<bool>() );
+
+  case kind::IMPLIES: // 2 args
+    return nodeManager->mkConst( (! engine->getValue(n[0]).getConst<bool>()) ||
+                                 engine->getValue(n[1]).getConst<bool>() );
+
+  case kind::OR: { // 2+ args
+    for(TNode::iterator i = n.begin(),
+            iend = n.end();
+          i != iend;
+          ++i) {
+      if(engine->getValue(*i).getConst<bool>()) {
+        return nodeManager->mkConst(true);
+      }
+    }
+    return nodeManager->mkConst(false);
+  }
+
+  case kind::XOR: // 2 args
+    return nodeManager->mkConst( engine->getValue(n[0]).getConst<bool>() !=
+                                 engine->getValue(n[1]).getConst<bool>() );
+
+  case kind::ITE: // 3 args
+    // all ITEs should be gone except (bool,bool,bool) ones
+    Assert( n[1].getType() == nodeManager->booleanType() &&
+            n[2].getType() == nodeManager->booleanType() );
+    return nodeManager->mkConst( engine->getValue(n[0]).getConst<bool>() ?
+                                 engine->getValue(n[1]).getConst<bool>() :
+                                 engine->getValue(n[2]).getConst<bool>() );
+
+  default:
+    Unhandled(n.getKind());
+  }
+}
 
 }/* CVC4::theory::booleans namespace */
 }/* CVC4::theory namespace */
index d87aa95b5b06a3cc40c6d58ef9c4b8985740c720..2c77c09b3208338daf0dd64afc2d2e712b4e83a8 100644 (file)
@@ -46,6 +46,8 @@ public:
   void propagate(Effort e) { Unimplemented(); }
   void explain(TNode n, Effort e) { Unimplemented(); }
 
+  Node getValue(TNode n, TheoryEngine* engine);
+
   RewriteResponse preRewrite(TNode n, bool topLevel);
   RewriteResponse postRewrite(TNode n, bool topLevel);
 
index ba258aafdcd7acb369d7d3010f510afe4f98abc8..810cd1d390f74d261fc0b6fc4642f90acf0be1ef 100644 (file)
@@ -17,6 +17,7 @@
  **/
 
 #include "theory/builtin/theory_builtin.h"
+#include "theory/theory_engine.h"
 #include "expr/kind.h"
 
 using namespace std;
@@ -26,7 +27,8 @@ namespace theory {
 namespace builtin {
 
 Node TheoryBuiltin::blastDistinct(TNode in) {
-  Debug("theory-rewrite") << "TheoryBuiltin::blastDistinct: " << in << std::endl;
+  Debug("theory-rewrite") << "TheoryBuiltin::blastDistinct: "
+                          << in << std::endl;
   Assert(in.getKind() == kind::DISTINCT);
   if(in.getNumChildren() == 2) {
     // if this is the case exactly 1 != pair will be generated so the
@@ -67,6 +69,40 @@ RewriteResponse TheoryBuiltin::preRewrite(TNode in, bool topLevel) {
   }
 }
 
+Node TheoryBuiltin::getValue(TNode n, TheoryEngine* engine) {
+  switch(n.getKind()) {
+
+  case kind::VARIABLE:
+    // no variables that the builtin theory is responsible for
+    Unreachable();
+
+  case kind::EQUAL: { // 2 args
+    // has to be an EQUAL over tuples, since all others should be
+    // handled elsewhere
+    Assert(n[0].getKind() == kind::TUPLE &&
+           n[1].getKind() == kind::TUPLE);
+    return NodeManager::currentNM()->
+      mkConst( getValue(n[0], engine) == getValue(n[1], engine) );
+  }
+
+  case kind::TUPLE: { // 2+ args
+    NodeBuilder<> nb(kind::TUPLE);
+    for(TNode::iterator i = n.begin(),
+          iend = n.end();
+        i != iend;
+        ++i) {
+      nb << engine->getValue(*i);
+    }
+    return Node(nb);
+  }
+
+  default:
+    // all other "builtins" should have been rewritten away or handled
+    // by theory engine, or handled elsewhere.
+    Unhandled(n.getKind());
+  }
+}
+
 }/* CVC4::theory::builtin namespace */
 }/* CVC4::theory */
 }/* CVC4 namespace */
index a08ed9b780165b347cad939165b09f0d77906399..57c5d1558973df615629091b22b7e19f817df31b 100644 (file)
@@ -39,6 +39,7 @@ public:
   void check(Effort e) { Unreachable(); }
   void propagate(Effort e) { Unreachable(); }
   void explain(TNode n, Effort e) { Unreachable(); }
+  Node getValue(TNode n, TheoryEngine* engine);
   void shutdown() { }
   RewriteResponse preRewrite(TNode n, bool topLevel);
   std::string identify() const { return std::string("TheoryBuiltin"); }
index bd0d738657c9e3e3c77c2064b908bd6297dbb738..69ff487215b96304a4a3c58ebcedb80ff4d70d0b 100644 (file)
  ** \todo document this file
  **/
 
-#include "theory_bv.h"
-#include "theory_bv_utils.h"
-#include "theory_bv_rewrite_rules.h"
+#include "theory/bv/theory_bv.h"
+#include "theory/bv/theory_bv_utils.h"
+#include "theory/bv/theory_bv_rewrite_rules.h"
+
+#include "theory/theory_engine.h"
 
 using namespace CVC4;
 using namespace CVC4::theory;
@@ -160,3 +162,19 @@ bool TheoryBV::triggerEquality(size_t triggerId) {
   return true;
 }
 
+Node TheoryBV::getValue(TNode n, TheoryEngine* engine) {
+  NodeManager* nodeManager = NodeManager::currentNM();
+
+  switch(n.getKind()) {
+
+  case kind::VARIABLE:
+    Unhandled(kind::VARIABLE);
+
+  case kind::EQUAL: // 2 args
+    return nodeManager->
+      mkConst( engine->getValue(n[0]) == engine->getValue(n[1]) );
+
+  default:
+    Unhandled(n.getKind());
+  }
+}
index ff5d4c2a25aa1e339dfa1444970e0cf02395a18d..c673a56b41b8e4f953424d4344f4ec39ca76ff21 100644 (file)
@@ -75,6 +75,8 @@ public:
 
   void explain(TNode n, Effort e) { }
 
+  Node getValue(TNode n, TheoryEngine* engine);
+
   RewriteResponse postRewrite(TNode n, bool topLevel);
 
   std::string identify() const { return std::string("TheoryBV"); }
index 2711a0b95c825558eb0fdd4a0699442eb8adc022..df9dcafef328ffdaa9abae396866998d75f6397a 100644 (file)
@@ -262,9 +262,11 @@ public:
   // TODO add compiler annotation "constant function" here
   static bool minEffortOnly(Effort e)        { return e == MIN_EFFORT; }
   static bool quickCheckOrMore(Effort e)     { return e >= QUICK_CHECK; }
-  static bool quickCheckOnly(Effort e)       { return e >= QUICK_CHECK && e < STANDARD; }
+  static bool quickCheckOnly(Effort e)       { return e >= QUICK_CHECK &&
+                                                      e <  STANDARD; }
   static bool standardEffortOrMore(Effort e) { return e >= STANDARD; }
-  static bool standardEffortOnly(Effort e)   { return e >= STANDARD && e < FULL_EFFORT; }
+  static bool standardEffortOnly(Effort e)   { return e >= STANDARD &&
+                                                      e <  FULL_EFFORT; }
   static bool fullEffort(Effort e)           { return e >= FULL_EFFORT; }
 
   /**
@@ -328,7 +330,8 @@ public:
    * (ITE true x y).
    */
   virtual RewriteResponse preRewrite(TNode n, bool topLevel) {
-    Debug("theory-rewrite") << "no pre-rewriting to perform for " << n << std::endl;
+    Debug("theory-rewrite") << "no pre-rewriting to perform for "
+                            << n << std::endl;
     return RewriteComplete(n);
   }
 
@@ -340,7 +343,8 @@ public:
    * memory leakage).
    */
   virtual RewriteResponse postRewrite(TNode n, bool topLevel) {
-    Debug("theory-rewrite") << "no post-rewriting to perform for " << n << std::endl;
+    Debug("theory-rewrite") << "no post-rewriting to perform for "
+                            << n << std::endl;
     return RewriteComplete(n);
   }
 
@@ -366,18 +370,21 @@ public:
   }
 
   /**
-   * This method is called to notify a theory that the node n should be considered a "shared term" by this theory
+   * This method is called to notify a theory that the node n should
+   * be considered a "shared term" by this theory
    */
   virtual void addSharedTerm(TNode n) { }
 
   /**
-   * This method is called by the shared term manager when a shared term lhs
-   * which this theory cares about (either because it received a previous
-   * addSharedTerm call with lhs or because it received a previous notifyEq call
-   * with lhs as the second argument) becomes equal to another shared term rhs.
-   * This call also serves as notice to the theory that the shared term manager
-   * now considers rhs the representative for this equivalence class of shared
-   * terms, so future notifications for this class will be based on rhs not lhs.
+   * This method is called by the shared term manager when a shared
+   * term lhs which this theory cares about (either because it
+   * received a previous addSharedTerm call with lhs or because it
+   * received a previous notifyEq call with lhs as the second
+   * argument) becomes equal to another shared term rhs.  This call
+   * also serves as notice to the theory that the shared term manager
+   * now considers rhs the representative for this equivalence class
+   * of shared terms, so future notifications for this class will be
+   * based on rhs not lhs.
    */
   virtual void notifyEq(TNode lhs, TNode rhs) { }
 
@@ -404,6 +411,38 @@ public:
    */
   virtual void explain(TNode n, Effort level = FULL_EFFORT) = 0;
 
+  /**
+   * Return the value of a node (typically used after a ).  If the
+   * theory supports model generation but has no value for this node,
+   * it should return Node::null().  If the theory doesn't support
+   * model generation at all, or usually would but doesn't in its
+   * current state, it should throw an exception saying so.
+   *
+   * The TheoryEngine is passed in so that you can recursively request
+   * values for the Node's children.  This is important because the
+   * TheoryEngine takes care of simple cases (metakind CONSTANT,
+   * Boolean-valued VARIABLES, ...) and can dispatch to other theories
+   * if that's necessary.  Only call your own getValue() recursively
+   * if you *know* that you are responsible handle the Node you're
+   * asking for; other theories can use your types, so be careful
+   * here!  To be safe, it's best to delegate back to the
+   * TheoryEngine.
+   *
+   * Usually, you need to handle at least the two cases of EQUAL and
+   * VARIABLE---EQUAL in case a value of yours is on the LHS of an
+   * EQUAL, and VARIABLE for variables of your types.  You also need
+   * to support any operators that can survive your rewriter.  You
+   * don't need to handle constants, as they are handled by the
+   * TheoryEngine.
+   *
+   * There are some gotchas here.  The user may be requesting the
+   * value of an expression that wasn't part of the satisfiable
+   * assertion, or has been declared since.  If you don't have a value
+   * and suspect this situation is the case, return Node::null()
+   * rather than throwing an exception.
+   */
+  virtual Node getValue(TNode n, TheoryEngine* engine) = 0;
+
   /**
    * Identify this theory (for debugging, dynamic configuration,
    * etc..)
@@ -550,7 +589,8 @@ std::ostream& operator<<(std::ostream& os, Theory::Effort level);
 
 }/* CVC4::theory namespace */
 
-inline std::ostream& operator<<(std::ostream& out, const CVC4::theory::Theory& theory) {
+inline std::ostream& operator<<(std::ostream& out,
+                                const CVC4::theory::Theory& theory) {
   return out << theory.identify();
 }
 
index 041b6852bc659038725075c45ac7b9d0e695b642..388167e0024c7b2dd8dce373299d128a20048bfc 100644 (file)
@@ -537,4 +537,20 @@ Node TheoryEngine::rewrite(TNode in, bool topLevel) {
   return out;
 }/* TheoryEngine::rewrite(TNode in) */
 
+
+Node TheoryEngine::getValue(TNode node) {
+  kind::MetaKind metakind = node.getMetaKind();
+  // special case: prop engine handles boolean vars
+  if(metakind == kind::metakind::VARIABLE && node.getType().isBoolean()) {
+    return d_propEngine->getValue(node);
+  }
+  // special case: value of a constant == itself
+  if(metakind == kind::metakind::CONSTANT) {
+    return node;
+  }
+
+  // otherwise ask the theory-in-charge
+  return theoryOf(node)->getValue(node, this);
+}/* TheoryEngine::getValue(TNode node) */
+
 }/* CVC4 namespace */
index 5227d32f02ba95f0cccbfd83c2468379490de844..0eaf6105570487750fb8da06ec4462b0e9410318 100644 (file)
@@ -380,7 +380,7 @@ public:
     return d_theoryOut.d_explanationNode;
   }
 
-  inline Node getExplanation(TNode node){
+  inline Node getExplanation(TNode node) {
     d_theoryOut.d_explanationNode = Node::null();
     theory::Theory* theory =
               node.getKind() == kind::NOT ? theoryOf(node[0]) : theoryOf(node);
@@ -388,6 +388,8 @@ public:
     return d_theoryOut.d_explanationNode;
   }
 
+  Node getValue(TNode node);
+
 private:
   class Statistics {
   public:
index eb6c430ba6845c90a5491326f32d63093a9e6b9f..a1eec9d4c37564b0bc62096a58e4a7439814ea24 100644 (file)
@@ -17,6 +17,7 @@
  **/
 
 #include "theory/uf/morgan/theory_uf_morgan.h"
+#include "theory/theory_engine.h"
 #include "expr/kind.h"
 #include "util/congruence_closure.h"
 
@@ -451,6 +452,32 @@ void TheoryUFMorgan::propagate(Effort level) {
   Debug("uf") << "uf: end propagate(" << level << ")" << std::endl;
 }
 
+Node TheoryUFMorgan::getValue(TNode n, TheoryEngine* engine) {
+  NodeManager* nodeManager = NodeManager::currentNM();
+
+  switch(n.getKind()) {
+
+  case kind::VARIABLE:
+  case kind::APPLY_UF:
+    if(n.getType().isBoolean()) {
+      if(d_cc.areCongruent(d_trueNode, n)) {
+        return nodeManager->mkConst(true);
+      } else if(d_cc.areCongruent(d_trueNode, n)) {
+        return nodeManager->mkConst(false);
+      }
+      return Node::null();
+    }
+    return d_cc.normalize(n);
+
+  case kind::EQUAL: // 2 args
+    return nodeManager->
+      mkConst( engine->getValue(n[0]) == engine->getValue(n[1]) );
+
+  default:
+    Unhandled(n.getKind());
+  }
+}
+
 void TheoryUFMorgan::dump() {
   if(!Debug.isOn("uf")) {
     return;
index d17eb87f59cb6a1cb774fa7e23310b6a091d5d04..7a12f216b597d70856e7fae95dbab67cdb2cc593 100644 (file)
@@ -151,13 +151,21 @@ public:
   void propagate(Effort level);
 
   /**
-   * Explains a previously reported conflict. Currently does nothing.
+   * Explains a previously theory-propagated literal.
    *
    * Overloads void explain(TNode n, Effort level); from theory.h.
    * See theory/theory.h for more information about this method.
    */
   void explain(TNode n, Effort level) {}
 
+  /**
+   * Gets a theory value.
+   *
+   * Overloads Node getValue(TNode n); from theory.h.
+   * See theory/theory.h for more information about this method.
+   */
+  Node getValue(TNode n, TheoryEngine* engine);
+
   std::string identify() const { return std::string("TheoryUFMorgan"); }
 
 private:
index 9a2d252c098e83802436709d882d9511849cc702..4c8a1a71a7c2dd17667e17d83355d9e7eb9bc5e7 100644 (file)
@@ -156,6 +156,16 @@ public:
    */
   void explain(TNode n, Effort level) {}
 
+  /**
+   * Get a theory value.
+   *
+   * Overloads Node getValue(TNode n); from theory.h.
+   * See theory/theory.h for more information about this method.
+   */
+  Node getValue(TNode n, TheoryEngine* engine) {
+    Unimplemented("TheoryUFTim doesn't support model generation");
+  }
+
   std::string identify() const { return std::string("TheoryUFTim"); }
 
 private:
index 88c17a19349a8fb9817f21459019b4563303bb41..cc18a33052469b964a4316234c829b5aae553de7 100644 (file)
@@ -286,6 +286,11 @@ public:
     return explain(eq[0], eq[1]);
   }
 
+  /**
+   * Normalization.
+   */
+  Node normalize(TNode t) const throw(AssertionException);
+
 private:
 
   friend std::ostream& operator<< <>(std::ostream& out,
@@ -360,11 +365,6 @@ private:
   void merge(TNode ec1, TNode ec2);
   void mergeProof(TNode a, TNode b, TNode e);
 
-  /**
-   * Internal normalization.
-   */
-  Node normalize(TNode t) const throw(AssertionException);
-
 };/* class CongruenceClosure */
 
 
index ccc36973d4b68b512a5d0bef2d9b93d6b1b2485b..f1f6ae1c2c35acfa5e99fe57f2aed20ada9bfcfa 100644 (file)
@@ -86,6 +86,21 @@ public:
   }
   enum UnknownExplanation whyUnknown();
 
+  inline bool operator==(Result r) {
+    if(d_which != r.d_which) {
+      return false;
+    }
+    if(d_which == TYPE_SAT) {
+      return d_sat == r.d_sat;
+    }
+    if(d_which == TYPE_VALIDITY) {
+      return d_validity == r.d_validity;
+    }
+    return false;
+  }
+  inline bool operator!=(Result r) {
+    return !(*this == r);
+  }
   inline Result asSatisfiabilityResult() const;
   inline Result asValidityResult() const;