search_sat.cpp

Go to the documentation of this file.
00001 /*****************************************************************************/
00002 /*!
00003  *\file search_sat.cpp
00004  *\brief Implementation of Search engine with generic external sat solver
00005  *
00006  * Author: Clark Barrett
00007  *
00008  * Created: Wed Dec  7 21:00:24 2005
00009  *
00010  * <hr>
00011  *
00012  * License to use, copy, modify, sell and/or distribute this software
00013  * and its documentation for any purpose is hereby granted without
00014  * royalty, subject to the terms and conditions defined in the \ref
00015  * LICENSE file provided with this distribution.
00016  * 
00017  * <hr>
00018  */
00019 /*****************************************************************************/
00020 
00021 
00022 #include "search_sat.h"
00023 #ifdef DPLL_BASIC
00024 #include "dpllt_basic.h"
00025 #endif
00026 #include "dpllt_minisat.h"
00027 #include "theory_core.h"
00028 #include "eval_exception.h"
00029 #include "typecheck_exception.h"
00030 #include "expr_transform.h"
00031 #include "search_rules.h"
00032 #include "command_line_flags.h"
00033 #include "theorem_manager.h"
00034 #include "theory.h"
00035 #include "debug.h"
00036 
00037 
00038 using namespace std;
00039 using namespace CVC3;
00040 using namespace SAT;
00041 
00042 
00043 namespace CVC3 {
00044 
00045 
00046 class SearchSatCoreSatAPI :public TheoryCore::CoreSatAPI {
00047   SearchSat* d_ss;
00048 public:
00049   SearchSatCoreSatAPI(SearchSat* ss) : d_ss(ss) {}
00050   ~SearchSatCoreSatAPI() {}
00051   void addLemma(const Theorem& thm, int priority, bool atBottomScope)
00052     { d_ss->addLemma(thm, priority, atBottomScope); }
00053   Theorem addAssumption(const Expr& assump)
00054   { return d_ss->newUserAssumption(assump); }
00055   void addSplitter(const Expr& e, int priority)
00056   { d_ss->addSplitter(e, priority); }
00057   bool check(const Expr& e);
00058   
00059 };
00060 
00061 
00062 bool SearchSatCoreSatAPI::check(const Expr& e)
00063 {
00064   Theorem thm;
00065   d_ss->push();
00066   QueryResult res = d_ss->check(e, thm);
00067   d_ss->pop();
00068   return res == VALID;
00069 }
00070 
00071 
00072 class SearchSatTheoryAPI :public DPLLT::TheoryAPI {
00073   ContextManager* d_cm;
00074   SearchSat* d_ss;
00075 public:
00076   SearchSatTheoryAPI(SearchSat* ss)
00077     : d_cm(ss->theoryCore()->getCM()), d_ss(ss) {}
00078   ~SearchSatTheoryAPI() {}
00079   void push() { return d_cm->push(); }
00080   void pop() { return d_cm->pop(); }
00081   void assertLit(Lit l) { d_ss->assertLit(l); }
00082   SAT::DPLLT::ConsistentResult checkConsistent(CNF_Formula& cnf, bool fullEffort)
00083     { return d_ss->checkConsistent(cnf, fullEffort); }
00084   bool outOfResources() { return d_ss->theoryCore()->outOfResources(); }
00085   Lit getImplication() { return d_ss->getImplication(); }
00086   void getExplanation(Lit l, CNF_Formula& cnf) { return d_ss->getExplanation(l, cnf); }
00087   bool getNewClauses(CNF_Formula& cnf) { return d_ss->getNewClauses(cnf); }
00088 };
00089 
00090 
00091 class SearchSatDecider :public DPLLT::Decider {
00092   SearchSat* d_ss;
00093 public:
00094   SearchSatDecider(SearchSat* ss) : d_ss(ss) {}
00095   ~SearchSatDecider() {}
00096 
00097   Lit makeDecision() { return d_ss->makeDecision(); }
00098 };
00099 
00100 
00101 class SearchSatCNFCallback :public CNF_Manager::CNFCallback {
00102   SearchSat* d_ss;
00103 public:
00104   SearchSatCNFCallback(SearchSat* ss) : d_ss(ss) {}
00105   ~SearchSatCNFCallback() {}
00106 
00107   void registerAtom(const Expr& e, const Theorem& thm)
00108   { d_ss->theoryCore()->theoryOf(e)->registerAtom(e, thm); }
00109 };
00110 
00111 
00112 }
00113 
00114 
00115 void SearchSat::restorePre()
00116 {
00117   if (d_core->getCM()->scopeLevel() == d_bottomScope) {
00118     DebugAssert(d_prioritySetBottomEntriesSizeStack.size() > 0, "Expected non-empty stack");
00119     d_prioritySetBottomEntriesSize = d_prioritySetBottomEntriesSizeStack.back();
00120     d_prioritySetBottomEntriesSizeStack.pop_back();
00121     while (d_prioritySetBottomEntriesSize < d_prioritySetBottomEntries.size()) {
00122       d_prioritySet.erase(d_prioritySetBottomEntries.back());
00123       d_prioritySetBottomEntries.pop_back();
00124     }
00125   }
00126 }
00127 
00128 
00129 void SearchSat::restore()
00130 {
00131   while (d_prioritySetEntriesSize < d_prioritySetEntries.size()) {
00132     d_prioritySet.erase(d_prioritySetEntries.back());
00133     d_prioritySetEntries.pop_back();
00134   }
00135   while (d_pendingLemmasSize < d_pendingLemmas.size()) {
00136     d_pendingLemmas.pop_back();
00137     d_pendingScopes.pop_back();
00138   }
00139   while (d_varsUndoListSize < d_varsUndoList.size()) {
00140     d_vars[d_varsUndoList.back()] = SAT::Var::UNKNOWN;
00141     d_varsUndoList.pop_back();
00142   }
00143 }
00144 
00145 
00146 bool SearchSat::recordNewRootLit(Lit lit, int priority, bool atBottomScope)
00147 {
00148   DebugAssert(d_prioritySetEntriesSize == d_prioritySetEntries.size() &&
00149               d_prioritySetBottomEntriesSize == d_prioritySetBottomEntries.size(),
00150               "Size mismatch");
00151   pair<set<LitPriorityPair>::iterator,bool> status =
00152     d_prioritySet.insert(LitPriorityPair(lit, priority));
00153   if (!status.second) return false;
00154   if (!atBottomScope || d_bottomScope == d_core->getCM()->scopeLevel()) {
00155     d_prioritySetEntries.push_back(status.first);
00156     d_prioritySetEntriesSize = d_prioritySetEntriesSize + 1;
00157   }
00158   else {
00159     d_prioritySetBottomEntries.push_back(status.first);
00160     ++d_prioritySetBottomEntriesSize;
00161   }
00162   
00163   if (d_prioritySetStart.get() == d_prioritySet.end() ||
00164       ((*status.first) < (*(d_prioritySetStart.get()))))
00165     d_prioritySetStart = status.first;
00166   return true;
00167 }
00168 
00169 
00170 void SearchSat::addLemma(const Theorem& thm, int priority, bool atBottomScope)
00171 {
00172   IF_DEBUG(
00173   string indentStr(theoryCore()->getCM()->scopeLevel(), ' ');
00174   TRACE("addLemma", indentStr, "AddLemma: ", thm.getExpr().toString(PRESENTATION_LANG));
00175   )
00176     //  DebugAssert(!thm.getExpr().isAbsLiteral(), "Expected non-literal");
00177   DebugAssert(d_pendingLemmasSize == d_pendingLemmas.size(), "Size mismatch");
00178   DebugAssert(d_pendingLemmasSize == d_pendingScopes.size(), "Size mismatch");
00179   DebugAssert(d_pendingLemmasNext <= d_pendingLemmas.size(), "Size mismatch");
00180   d_pendingLemmas.push_back(pair<Theorem,int>(thm, priority));
00181   d_pendingScopes.push_back(atBottomScope);
00182   d_pendingLemmasSize = d_pendingLemmasSize + 1;
00183 }
00184 
00185 
00186 void SearchSat::addSplitter(const Expr& e, int priority)
00187 {
00188   DebugAssert(!e.isEq() || e[0] != e[1], "Expected non-trivial splitter");
00189   addLemma(d_commonRules->excludedMiddle(e), priority);
00190 }
00191 
00192 
00193 void SearchSat::assertLit(Lit l)
00194 {
00195   //  DebugAssert(d_inCheckSat, "Should only be used as a call-back");
00196   Expr e = d_cnfManager->concreteLit(l);
00197 
00198   IF_DEBUG(
00199   string indentStr(theoryCore()->getCM()->scopeLevel(), ' ');
00200   string val = " := ";
00201   
00202   std::stringstream ss;
00203   ss<<theoryCore()->getCM()->scopeLevel();
00204   std::string temp;
00205   ss>>temp;
00206 
00207   if (l.isPositive()) val += "1"; else val += "0";
00208   TRACE("assertLit", "", "", "");
00209   TRACE("assertLitScope", indentStr, "Scope level = ", temp);
00210   TRACE("assertLit", indentStr, l.getVar(), val+": "+e.toString());
00211   )
00212 
00213     //=======
00214     //  IF_DEBUG(
00215     //  string indentStr(theoryCore()->getCM()->scopeLevel(), ' ');
00216     //  string val = " := ";
00217     //  if (l.isPositive()) val += "1"; else val += "0";
00218     //  TRACE("assertLit", indentStr, l.getVar(), val);
00219     //  )
00220 
00221   // This can happen if the SAT solver propagates a learned unit clause from a p
00222   bool isSATLemma = false;
00223   if (e.isNull()) {
00224     e = d_cnfManager->concreteLit(l, false);
00225     DebugAssert(!e.isNull(), "Expected known expr");
00226     isSATLemma = true;
00227     TRACE("quant-level", "found null expr ",e, "");
00228   }
00229 
00230   DebugAssert(!e.isNull(), "Expected known expr");
00231   DebugAssert(!e.isIntAssumption() || getValue(l) == SAT::Var::TRUE_VAL,
00232               "internal assumptions should be true");
00233   // This happens if the SAT solver has been restarted--it re-asserts its old assumptions
00234   if (e.isIntAssumption()) return;
00235   if (getValue(l) == SAT::Var::UNKNOWN) {
00236     setValue(l.getVar(), l.isPositive() ? Var::TRUE_VAL : Var::FALSE_VAL);
00237   }
00238   else {
00239     DebugAssert(!e.isAbsLiteral(), "invariant violated");
00240     DebugAssert(getValue(l) == Var::TRUE_VAL, "invariant violated");
00241     return;
00242   }
00243   if (!e.isAbsLiteral()) return;
00244   e.setIntAssumption();
00245 
00246   Theorem thm = d_commonRules->assumpRule(e);
00247   if (isSATLemma) {
00248     CNF_Formula_Impl cnf;
00249     d_cnfManager->addAssumption(thm, cnf);
00250   }
00251 
00252   thm.setQuantLevel(theoryCore()->getQuantLevelForTerm(e.isNot() ? e[0] : e));
00253   d_intAssumptions.push_back(thm);
00254   d_core->addFact(thm);
00255 }
00256 
00257 
00258 SAT::DPLLT::ConsistentResult SearchSat::checkConsistent(SAT::CNF_Formula& cnf, bool fullEffort)
00259 {
00260   DebugAssert(d_inCheckSat, "Should only be used as a call-back");
00261   if (d_core->inconsistent()) {
00262     d_cnfManager->convertLemma(d_core->inconsistentThm(), cnf);
00263     if (d_cnfManager->numVars() > d_vars.size()) {
00264       d_vars.resize(d_cnfManager->numVars(), SAT::Var::UNKNOWN);
00265     }
00266     return DPLLT::INCONSISTENT;
00267   }
00268   if (fullEffort) {
00269     if (d_core->checkSATCore() && d_pendingLemmasNext == d_pendingLemmas.size() && d_lemmasNext == d_lemmas.numClauses()) {
00270       if (d_core->inconsistent()) {
00271         d_cnfManager->convertLemma(d_core->inconsistentThm(), cnf);
00272         if (d_cnfManager->numVars() > d_vars.size()) {
00273           d_vars.resize(d_cnfManager->numVars(), SAT::Var::UNKNOWN);
00274         }
00275         return DPLLT::INCONSISTENT;
00276       }
00277       else return DPLLT::CONSISTENT;
00278     }
00279   }
00280   return DPLLT::MAYBE_CONSISTENT;
00281 }
00282 
00283 
00284 Lit SearchSat::getImplication()
00285 {
00286   //  DebugAssert(d_inCheckSat, "Should only be used as a call-back");
00287   Lit l;
00288   Theorem imp = d_core->getImpliedLiteral();
00289   while (!imp.isNull()) {
00290     l = d_cnfManager->getCNFLit(imp.getExpr());
00291     DebugAssert(!l.isNull() || imp.getExpr().unnegate().isUserRegisteredAtom(),
00292                 "implied literals should be registered by cnf or by user");
00293     if (!l.isNull() && getValue(l) != Var::TRUE_VAL) {
00294       d_theorems.insert(imp.getExpr(), imp);
00295       break;
00296     }
00297     l.reset();
00298     imp = d_core->getImpliedLiteral();
00299   }
00300   return l;
00301 }
00302 
00303 
00304 void SearchSat::getExplanation(Lit l, SAT::CNF_Formula& cnf)
00305 {
00306   //  DebugAssert(d_inCheckSat, "Should only be used as a call-back");
00307   DebugAssert(cnf.empty(), "Expected empty cnf");
00308   Expr e = d_cnfManager->concreteLit(l);
00309   CDMap<Expr, Theorem>::iterator i = d_theorems.find(e);
00310   DebugAssert(i != d_theorems.end(), "getExplanation: no explanation found");
00311   d_cnfManager->convertLemma((*i).second, cnf);  
00312   if (d_cnfManager->numVars() > d_vars.size()) {
00313     d_vars.resize(d_cnfManager->numVars(), SAT::Var::UNKNOWN);
00314   }
00315 }
00316 
00317 
00318 bool SearchSat::getNewClauses(CNF_Formula& cnf)
00319 {
00320   unsigned i;
00321 
00322   Lit l;
00323   for (i = d_pendingLemmasNext; i < d_pendingLemmas.size(); ++i) {
00324     l = d_cnfManager->addLemma(d_pendingLemmas[i].first, d_lemmas);
00325     if (!recordNewRootLit(l, d_pendingLemmas[i].second, d_pendingScopes[i])) {
00326       // Already have this lemma: delete it
00327       d_lemmas.deleteLast();
00328     }
00329   }
00330   d_pendingLemmasNext = d_pendingLemmas.size();
00331 
00332   if (d_cnfManager->numVars() > d_vars.size()) {
00333     d_vars.resize(d_cnfManager->numVars(), SAT::Var::UNKNOWN);
00334   }
00335 
00336   DebugAssert(d_lemmasNext <= d_lemmas.numClauses(), "");
00337   if (d_lemmasNext == d_lemmas.numClauses()) return false;
00338   do {
00339     cnf += d_lemmas[d_lemmasNext];
00340     d_lemmasNext = d_lemmasNext + 1;
00341   } while (d_lemmasNext < d_lemmas.numClauses());
00342   return true;
00343 }
00344 
00345 
00346 Lit SearchSat::makeDecision()
00347 {
00348   DebugAssert(d_inCheckSat, "Should only be used as a call-back");
00349   Lit litDecision;
00350 
00351   set<LitPriorityPair>::const_iterator i, iend;
00352   Lit lit;
00353   for (i = d_prioritySetStart, iend = d_prioritySet.end(); i != iend; ++i) {
00354     lit = (*i).getLit();
00355     if (findSplitterRec(lit, getValue(lit), &litDecision)) {
00356       break;
00357     }
00358   }
00359   d_prioritySetStart = i;
00360   return litDecision;
00361 }
00362 
00363 
00364 bool SearchSat::findSplitterRec(Lit lit, Var::Val value, Lit* litDecision)
00365 {
00366   if (lit.isFalse() || lit.isTrue()) return false;
00367 
00368   unsigned i, n;
00369   Lit litTmp;
00370   Var varTmp;
00371   bool ret;
00372   Var v = lit.getVar();
00373 
00374   DebugAssert(value != Var::UNKNOWN, "expected known value");
00375   DebugAssert(getValue(lit) == value || getValue(lit) == Var::UNKNOWN,
00376               "invariant violated");
00377 
00378   if (checkJustified(v)) return false;
00379 
00380   if (lit.isInverted()) {
00381     value = Var::invertValue(value);
00382   }
00383 
00384   if (d_cnfManager->numFanins(v) == 0) {
00385     if (getValue(v) != Var::UNKNOWN) {
00386       setJustified(v);
00387       return false;
00388     }
00389     else {
00390       *litDecision = Lit(v, value == Var::TRUE_VAL);
00391       return true;
00392     }
00393   }
00394   else if (d_cnfManager->concreteVar(v).isAbsAtomicFormula()) {
00395     // This node represents a predicate with embedded ITE's
00396     // We handle this case specially in order to catch the
00397     // corner case when a variable is in its own fanin.
00398     n = d_cnfManager->numFanins(v);
00399     for (i=0; i < n; ++i) {
00400       litTmp = d_cnfManager->getFanin(v, i);
00401       varTmp = litTmp.getVar();
00402       DebugAssert(!litTmp.isInverted(),"Expected positive fanin");
00403       if (checkJustified(varTmp)) continue;
00404       DebugAssert(d_cnfManager->concreteVar(varTmp).getKind() == ITE,
00405                   "Expected ITE");
00406       DebugAssert(getValue(varTmp) == Var::TRUE_VAL,"Expected TRUE");
00407       Lit cIf = d_cnfManager->getFanin(varTmp,0);
00408       Lit cThen = d_cnfManager->getFanin(varTmp,1);
00409       Lit cElse = d_cnfManager->getFanin(varTmp,2);
00410       if (getValue(cIf) == Var::UNKNOWN) {
00411   if (getValue(cElse) == Var::TRUE_VAL ||
00412             getValue(cThen) == Var::FALSE_VAL) {
00413     ret = findSplitterRec(cIf, Var::FALSE_VAL, litDecision);
00414   }
00415   else {
00416     ret = findSplitterRec(cIf, Var::TRUE_VAL, litDecision);
00417   }
00418   if (!ret) {
00419     cout << d_cnfManager->concreteVar(cIf.getVar()) << endl;
00420     DebugAssert(false,"No controlling input found (1)");
00421   }   
00422   return true;
00423       }
00424       else if (getValue(cIf) == Var::TRUE_VAL) {
00425   if (findSplitterRec(cIf, Var::TRUE_VAL, litDecision)) {
00426       return true;
00427   }
00428   if (cThen.getVar() != v &&
00429             (getValue(cThen) == Var::UNKNOWN ||
00430              getValue(cThen) == Var::TRUE_VAL) &&
00431       findSplitterRec(cThen, Var::TRUE_VAL, litDecision)) {
00432     return true;
00433   }
00434       }
00435       else {
00436   if (findSplitterRec(cIf, Var::FALSE_VAL, litDecision)) {
00437     return true;
00438   }
00439   if (cElse.getVar() != v &&
00440             (getValue(cElse) == Var::UNKNOWN ||
00441              getValue(cElse) == Var::TRUE_VAL) &&
00442       findSplitterRec(cElse, Var::TRUE_VAL, litDecision)) {
00443     return true;
00444   }
00445       }
00446       setJustified(varTmp);
00447     }
00448     if (getValue(v) != Var::UNKNOWN) {
00449       setJustified(v);
00450       return false;
00451     }
00452     else {
00453       *litDecision = Lit(v, value == Var::TRUE_VAL);
00454       return true;
00455     }
00456   }
00457 
00458   int kind = d_cnfManager->concreteVar(v).getKind();
00459   Var::Val valHard = Var::FALSE_VAL;
00460   switch (kind) {
00461     case AND:
00462       valHard = Var::TRUE_VAL;
00463     case OR:
00464       if (value == valHard) {
00465         n = d_cnfManager->numFanins(v);
00466   for (i=0; i < n; ++i) {
00467           litTmp = d_cnfManager->getFanin(v, i);
00468     if (findSplitterRec(litTmp, valHard, litDecision)) {
00469       return true;
00470     }
00471   }
00472   DebugAssert(getValue(v) == valHard, "Output should be justified");
00473   setJustified(v);
00474   return false;
00475       }
00476       else {
00477         Var::Val valEasy = Var::invertValue(valHard);
00478         n = d_cnfManager->numFanins(v);
00479   for (i=0; i < n; ++i) {
00480           litTmp = d_cnfManager->getFanin(v, i);
00481     if (getValue(litTmp) != valHard) {
00482       if (findSplitterRec(litTmp, valEasy, litDecision)) {
00483         return true;
00484       }
00485       DebugAssert(getValue(v) == valEasy, "Output should be justified");
00486             setJustified(v);
00487       return false;
00488     }
00489   }
00490   DebugAssert(false, "No controlling input found (2)");
00491       }
00492       break;
00493     case IMPLIES:
00494       DebugAssert(d_cnfManager->numFanins(v) == 2, "Expected 2 fanins");
00495       if (value == Var::FALSE_VAL) {
00496         litTmp = d_cnfManager->getFanin(v, 0);
00497         if (findSplitterRec(litTmp, Var::TRUE_VAL, litDecision)) {
00498           return true;
00499         }
00500         litTmp = d_cnfManager->getFanin(v, 1);
00501         if (findSplitterRec(litTmp, Var::FALSE_VAL, litDecision)) {
00502           return true;
00503         }
00504   DebugAssert(getValue(v) == Var::FALSE_VAL, "Output should be justified");
00505   setJustified(v);
00506   return false;
00507       }
00508       else {
00509         litTmp = d_cnfManager->getFanin(v, 0);
00510         if (getValue(litTmp) != Var::TRUE_VAL) {
00511           if (findSplitterRec(litTmp, Var::FALSE_VAL, litDecision)) {
00512             return true;
00513           }
00514           DebugAssert(getValue(v) == Var::TRUE_VAL, "Output should be justified");
00515           setJustified(v);
00516           return false;
00517   }
00518         litTmp = d_cnfManager->getFanin(v, 1);
00519         if (getValue(litTmp) != Var::FALSE_VAL) {
00520           if (findSplitterRec(litTmp, Var::TRUE_VAL, litDecision)) {
00521             return true;
00522           }
00523           DebugAssert(getValue(v) == Var::TRUE_VAL, "Output should be justified");
00524           setJustified(v);
00525           return false;
00526   }
00527   DebugAssert(false, "No controlling input found (3)");
00528       }
00529       break;
00530     case IFF: {
00531       litTmp = d_cnfManager->getFanin(v, 0);
00532       Var::Val val = getValue(litTmp);
00533       if (val != Var::UNKNOWN) {
00534   if (findSplitterRec(litTmp, val, litDecision)) {
00535     return true;
00536   }
00537   if (value == Var::FALSE_VAL) val = Var::invertValue(val);
00538         litTmp = d_cnfManager->getFanin(v, 1);
00539 
00540   if (findSplitterRec(litTmp, val, litDecision)) {
00541     return true;
00542   }
00543   DebugAssert(getValue(v) == value, "Output should be justified");
00544   setJustified(v);
00545   return false;
00546       }
00547       else {
00548         val = getValue(d_cnfManager->getFanin(v, 1));
00549         if (val == Var::UNKNOWN) val = Var::FALSE_VAL;
00550   if (value == Var::FALSE_VAL) val = Var::invertValue(val);
00551   if (findSplitterRec(litTmp, val, litDecision)) {
00552     return true;
00553   }
00554   DebugAssert(false, "Unable to find controlling input (4)");
00555       }
00556       break;
00557     }
00558     case XOR: {
00559       litTmp = d_cnfManager->getFanin(v, 0);
00560       Var::Val val = getValue(litTmp);
00561       if (val != Var::UNKNOWN) {
00562   if (findSplitterRec(litTmp, val, litDecision)) {
00563     return true;
00564   }
00565   if (value == Var::TRUE_VAL) val = Var::invertValue(val);
00566         litTmp = d_cnfManager->getFanin(v, 1);
00567   if (findSplitterRec(litTmp, val, litDecision)) {
00568     return true;
00569   }
00570   DebugAssert(getValue(v) == value, "Output should be justified");
00571   setJustified(v);
00572   return false;
00573       }
00574       else {
00575         val = getValue(d_cnfManager->getFanin(v, 1));
00576         if (val == Var::UNKNOWN) val = Var::FALSE_VAL;
00577   if (value == Var::TRUE_VAL) val = Var::invertValue(val);
00578   if (findSplitterRec(litTmp, val, litDecision)) {
00579     return true;
00580   }
00581   DebugAssert(false, "Unable to find controlling input (5)");
00582       }
00583       break;
00584     }
00585     case ITE: {
00586       Lit cIf = d_cnfManager->getFanin(v, 0);
00587       Lit cThen = d_cnfManager->getFanin(v, 1);
00588       Lit cElse = d_cnfManager->getFanin(v, 2);
00589       if (getValue(cIf) == Var::UNKNOWN) {
00590   if (getValue(cElse) == value ||
00591             getValue(cThen) == Var::invertValue(value)) {
00592     ret = findSplitterRec(cIf, Var::FALSE_VAL, litDecision);
00593   }
00594   else {
00595     ret = findSplitterRec(cIf, Var::TRUE_VAL, litDecision);
00596   }
00597   if (!ret) {
00598     cout << d_cnfManager->concreteVar(cIf.getVar()) << endl;
00599     DebugAssert(false,"No controlling input found (6)");
00600   }   
00601   return true;
00602       }
00603       else if (getValue(cIf) == Var::TRUE_VAL) {
00604   if (findSplitterRec(cIf, Var::TRUE_VAL, litDecision)) {
00605       return true;
00606   }
00607   if (cThen.isVar() && cThen.getVar() != v &&
00608             (getValue(cThen) == Var::UNKNOWN ||
00609              getValue(cThen) == value) &&
00610       findSplitterRec(cThen, value, litDecision)) {
00611     return true;
00612   }
00613       }
00614       else {
00615   if (findSplitterRec(cIf, Var::FALSE_VAL, litDecision)) {
00616     return true;
00617   }
00618   if (cElse.isVar() && cElse.getVar() != v &&
00619             (getValue(cElse) == Var::UNKNOWN ||
00620              getValue(cElse) == value) &&
00621       findSplitterRec(cElse, value, litDecision)) {
00622     return true;
00623   }
00624       }
00625       DebugAssert(getValue(v) == value, "Output should be justified");
00626       setJustified(v);
00627       return false;
00628     }
00629     default:
00630       DebugAssert(false, "Unexpected Boolean operator");
00631       break;
00632   }
00633   FatalAssert(false, "Should be unreachable");
00634   return false;
00635 }
00636 
00637 
00638 QueryResult SearchSat::check(const Expr& e, Theorem& result, bool isRestart)
00639 {
00640   DebugAssert(d_dplltReady, "SAT solver is not ready");
00641   if (isRestart && d_lastCheck.get().isNull()) {
00642     throw Exception
00643       ("restart called without former call to checkValid");
00644   }
00645 
00646   DebugAssert(!d_inCheckSat, "checkValid should not be called recursively");
00647   TRACE("searchsat", "checkValid: ", e, "");
00648 
00649   if (!e.getType().isBool())
00650     throw TypecheckException
00651       ("checking validity of a non-Boolean expression:\n\n  "
00652        + e.toString()
00653        + "\n\nwhich has the following type:\n\n  "
00654        + e.getType().toString());
00655 
00656   Expr e2 = e;
00657 
00658   // Set up and quick exits
00659   if (isRestart) {
00660     while (e2.isNot() && e2[0].isNot()) e2 = e2[0][0];
00661     if (e2.isTrue() || (e2.isNot() && e2[0].isFalse())) {
00662       result = d_lastValid;
00663       return INVALID;
00664     }
00665     if (e2.isFalse() || (e2.isNot() && e2[0].isTrue())) {
00666       pop();
00667       //TODO: real theorem
00668       d_lastValid = d_commonRules->assumpRule(d_lastCheck);
00669       result = d_lastValid;
00670       return VALID;
00671     }
00672   }
00673   else {
00674     if (e.isTrue()) {
00675       d_lastValid = d_commonRules->trueTheorem();
00676       result = d_lastValid;
00677       return VALID;
00678     }
00679     push();
00680     d_bottomScope = d_core->getCM()->scopeLevel();
00681     d_prioritySetBottomEntriesSizeStack.push_back(d_prioritySetBottomEntriesSize);
00682     d_lastCheck = e;
00683     e2 = !e;
00684   }
00685 
00686   Theorem thm;
00687   CNF_Formula_Impl cnf;
00688   QueryResult qres;
00689   d_cnfManager->setBottomScope(d_bottomScope);
00690   d_dplltReady = false;
00691 
00692   newUserAssumptionInt(e2, cnf, true);
00693 
00694   d_inCheckSat = true;
00695   
00696   getNewClauses(cnf);
00697 
00698   if (!isRestart && d_core->inconsistent()) {
00699     qres = UNSATISFIABLE;
00700     thm = d_rules->proofByContradiction(e, d_core->inconsistentThm());
00701     pop();
00702     d_lastValid = thm;
00703     d_cnfManager->setBottomScope(-1);
00704     d_inCheckSat = false;
00705     result = d_lastValid;
00706     return qres;
00707   }
00708   else {
00709     // Run DPLLT engine
00710     qres = isRestart ? d_dpllt->continueCheck(cnf) : d_dpllt->checkSat(cnf);
00711   }
00712 
00713   if (qres == UNSATISFIABLE) {
00714     DebugAssert(d_core->getCM()->scopeLevel() == d_bottomScope,
00715                 "Expected unchanged context after unsat");
00716     e2 = d_lastCheck;
00717     pop();
00718     if (d_core->getTM()->withProof()) {
00719       Proof pf = d_dpllt->getSatProof(d_cnfManager, d_core);
00720       d_lastValid = d_rules->satProof(e2, pf);
00721     }
00722     else {
00723       d_lastValid = d_commonRules->assumpRule(d_lastCheck);
00724     }
00725   }
00726   else {
00727     DebugAssert(d_lemmasNext == d_lemmas.numClauses(),
00728                 "Expected no lemmas after satisfiable check");
00729     d_dplltReady = true;
00730     d_lastValid = Theorem();
00731     if (qres == SATISFIABLE && d_core->incomplete()) qres = UNKNOWN;
00732 
00733 #ifdef _CVC3_DEBUG_MODE
00734 
00735     if( CVC3::debugger.trace("quant debug")  ){
00736       d_core->theoryOf(FORALL)->debug(1);
00737     }
00738 
00739 
00740     if( CVC3::debugger.trace("sat model unknown")  ){
00741       std::vector<SAT::Lit> cur_assigns = d_dpllt->getCurAssignments();
00742       cout<<"Current assignments"<<endl;
00743       {
00744   for(size_t i = 0 ; i < cur_assigns.size(); i++){
00745     Lit l = cur_assigns[i];
00746     Expr e = d_cnfManager->concreteLit(l);
00747     
00748     string val = " := ";
00749     
00750     if (l.isPositive()) val += "1"; else val += "0";
00751     cout<<l.getVar()<<val<<endl;
00752     //    cout<<e<<endl;
00753     
00754   }
00755       }
00756       
00757       
00758       std::vector< std::vector<SAT::Lit> > cur_clauses = d_dpllt->getCurClauses();
00759       cout<<"Current Clauses"<<endl;
00760       {
00761   for(size_t i = 0 ; i < cur_clauses.size(); i++){
00762     //  cout<<"clause "<<i<<"================="<<endl;
00763     for(size_t j = 0; j < cur_clauses[i].size(); j++){
00764       
00765       Lit l = cur_clauses[i][j];
00766       string val ;
00767       if (l.isPositive()) val += "+"; else val += "-";
00768       cout<<val<<l.getVar()<<" ";
00769     }
00770     cout<<endl;
00771   }
00772       }
00773     }
00774     
00775     if( CVC3::debugger.trace("model unknown")  ){
00776       const CDList<Expr>& allterms = d_core->getTerms();
00777       cout<<"===========terms begin=========="<<endl;
00778       
00779       for (size_t i=0; i<allterms.size(); i++){
00780   //  cout<<"i="<<i<<" :"<<allterms[i].getFindLevel()<<":"<<d_core->simplifyExpr(allterms[i])<<"|"<<allterms[i]<<endl;
00781   cout<<"i="<<i<<" :"<<allterms[i].getFindLevel()<<":"<<d_core->simplifyExpr(allterms[i])<<"|"<<allterms[i]<<endl;
00782 
00783     //<<" and type is "<<allterms[i].getType() 
00784     //      << " and kind is" << allterms[i].getEM()->getKindName(allterms[i].getKind())<<endl;
00785       }
00786       cout<<"-----------term end ---------"<<endl;
00787       const CDList<Expr>& allpreds = d_core->getPredicates();
00788       cout<<"===========pred begin=========="<<endl;
00789       
00790       for (size_t i=0; i<allpreds.size(); i++){
00791   if(allpreds[i].hasFind()){
00792     if( (d_core->findExpr(allpreds[i])).isTrue()){
00793       cout<<"ASSERT "<< allpreds[i] <<";" <<endl;
00794     }
00795     else{
00796       cout<<"ASSERT NOT("<< allpreds[i] <<");" <<endl;
00797     }
00798     //    cout<<"i="<<i<<" :";
00799     //    cout<<allpreds[i].getFindLevel();
00800     //    cout<<":"<<d_core->findExpr(allpreds[i])<<"|"<<allpreds[i]<<endl;
00801   }
00802   //  else cout<<"U "<<endl;;
00803 
00804 
00805   //" and type is "<<allpreds[i].getType() 
00806   //      << " and kind is" << allpreds[i].getEM()->getKindName(allpreds[i].getKind())<<endl;
00807       }
00808       cout<<"-----------end----------pred"<<endl;
00809     }
00810 
00811     if( CVC3::debugger.trace("model unknown quant")  ){
00812       cout<<"=========== quant pred begin=========="<<endl;
00813       const CDList<Expr>& allpreds = d_core->getPredicates();
00814       for (size_t i=0; i<allpreds.size(); i++){
00815 
00816   Expr cur = allpreds[i];
00817   if(cur.isForall() || cur.isExists() || (cur.isNot() && (cur[0].isForall()||cur[0].isExists()))){
00818     if(allpreds[i].hasFind()) {
00819       cout<<"i="<<i<<" :";
00820       cout<<allpreds[i].getFindLevel();
00821       cout<<":"<<d_core->findExpr(allpreds[i])<<"|"<<allpreds[i]<<endl;
00822     }
00823   }
00824       }
00825       cout<<"-----------end----------pred"<<endl;
00826     }
00827 
00828     if( CVC3::debugger.trace("model unknown nonquant")  ){
00829       cout<<"=========== quant pred begin=========="<<endl;
00830       const CDList<Expr>& allpreds = d_core->getPredicates();
00831       for (size_t i=0; i<allpreds.size(); i++){
00832 
00833   Expr cur = allpreds[i];
00834   if(cur.isForall() || cur.isExists() || 
00835      (cur.isNot() && (cur[0].isForall()||cur[0].isExists())) ||
00836      cur.isEq() || 
00837      (cur.isNot() && cur[0].isEq())){
00838   }
00839   else{
00840     if(allpreds[i].hasFind()) {
00841       cout<<"i="<<i<<" :";
00842       cout<<allpreds[i].getFindLevel();
00843       cout<<":"<<d_core->findExpr(allpreds[i])<<"|"<<allpreds[i]<<endl;
00844     }
00845   }
00846       }
00847       cout<<"-----------end----------pred"<<endl;
00848     }
00849 
00850     if( CVC3::debugger.trace("unknown state")  ){
00851       const CDList<Expr>& allpreds = d_core->getPredicates();
00852       cout<<"===========pred begin=========="<<endl;
00853       
00854       for (size_t i=0; i<allpreds.size(); i++){
00855   if(allpreds[i].hasFind()){
00856     //    Expr cur(allpreds[i]);
00857 //    if(cur.isForall() || cur.isExists() || 
00858 //       (cur.isNot() && (cur[0].isForall()||cur[0].isExists()))
00859 //       ){
00860 //      continue;
00861 //    }
00862     
00863     if( (d_core->findExpr(allpreds[i])).isTrue()){
00864       cout<<":assumption "<< allpreds[i] <<"" <<endl;
00865     }
00866     else{
00867       cout<<":assumption(not "<< allpreds[i] <<")" <<endl;
00868     }
00869     //    cout<<"i="<<i<<" :";
00870     //    cout<<allpreds[i].getFindLevel();
00871     //    cout<<":"<<d_core->findExpr(allpreds[i])<<"|"<<allpreds[i]<<endl;
00872   }
00873   //  else cout<<"U "<<endl;;
00874   
00875 
00876   //" and type is "<<allpreds[i].getType() 
00877   //      << " and kind is" << allpreds[i].getEM()->getKindName(allpreds[i].getKind())<<endl;
00878       }
00879       cout<<"-----------end----------pred"<<endl;
00880     }
00881 
00882     if( CVC3::debugger.trace("unknown state noforall")  ){
00883       const CDList<Expr>& allpreds = d_core->getPredicates();
00884       cout<<"===========pred begin=========="<<endl;
00885       
00886       for (size_t i=0; i<allpreds.size(); i++){
00887   if(allpreds[i].hasFind()){
00888       Expr cur(allpreds[i]);
00889 //        if(cur.isForall() || cur.isExists() || 
00890 //           (cur.isNot() && (cur[0].isForall()||cur[0].isExists()))
00891 //           ){
00892 //          continue;
00893 //      }
00894 
00895     if( (d_core->findExpr(allpreds[i])).isTrue()){
00896 //      if(cur.isExists()){
00897 //          continue;
00898 //        }
00899       cout<<"ASSERT "<< allpreds[i] <<";" <<endl;
00900 //      cout<<":assumption "<< allpreds[i] <<"" <<endl;
00901     }
00902     else if ( (d_core->findExpr(allpreds[i])).isFalse()){
00903 //        if (cur.isForall()){
00904 //          continue;
00905 //        }
00906       cout<<"ASSERT (NOT "<< allpreds[i] <<");" <<endl;
00907 //      cout<<":assumption(not "<< allpreds[i] <<")" <<endl;
00908     }
00909     else{
00910       cout<<"--ERROR"<<endl;
00911     }
00912     //    cout<<"i="<<i<<" :";
00913     //    cout<<allpreds[i].getFindLevel();
00914     //    cout<<":"<<d_core->findExpr(allpreds[i])<<"|"<<allpreds[i]<<endl;
00915   }
00916   //  else cout<<"U "<<endl;;
00917   
00918 
00919   //" and type is "<<allpreds[i].getType() 
00920   //      << " and kind is" << allpreds[i].getEM()->getKindName(allpreds[i].getKind())<<endl;
00921       }
00922       cout<<"-----------end----------pred"<<endl;
00923     }
00924 
00925 
00926 #endif
00927   }
00928   d_cnfManager->setBottomScope(-1);
00929   d_inCheckSat = false;
00930   result = d_lastValid;
00931   return qres;
00932 }
00933 
00934 
00935 SearchSat::SearchSat(TheoryCore* core, const string& name)
00936   : SearchEngine(core),
00937     d_name(name),
00938     d_bottomScope(core->getCM()->getCurrentContext(), -1),
00939     d_lastCheck(core->getCM()->getCurrentContext()),
00940     d_lastValid(core->getCM()->getCurrentContext(),
00941                 d_commonRules->trueTheorem()),
00942     d_userAssumptions(core->getCM()->getCurrentContext()),
00943     d_intAssumptions(core->getCM()->getCurrentContext()),
00944     d_idxUserAssump(core->getCM()->getCurrentContext(), 0),
00945     d_decider(NULL),
00946     d_theorems(core->getCM()->getCurrentContext()),
00947     d_inCheckSat(false),
00948     d_lemmas(core->getCM()->getCurrentContext()),
00949     d_pendingLemmasSize(core->getCM()->getCurrentContext(), 0),
00950     d_pendingLemmasNext(core->getCM()->getCurrentContext(), 0),
00951     d_lemmasNext(core->getCM()->getCurrentContext(), 0),
00952     d_varsUndoListSize(core->getCM()->getCurrentContext(), 0),
00953     d_prioritySetStart(core->getCM()->getCurrentContext()),
00954     d_prioritySetEntriesSize(core->getCM()->getCurrentContext(), 0),
00955     d_prioritySetBottomEntriesSize(0),
00956     d_lastRegisteredVar(core->getCM()->getCurrentContext(), 0),
00957     d_dplltReady(core->getCM()->getCurrentContext(), true),
00958     d_nextImpliedLiteral(core->getCM()->getCurrentContext(), 0),
00959     d_restorer(core->getCM()->getCurrentContext(), this)
00960 {
00961   d_cnfManager = new CNF_Manager(core->getTM(), core->getStatistics(),
00962                                  core->getFlags());
00963 
00964   d_cnfCallback = new SearchSatCNFCallback(this);
00965   d_cnfManager->registerCNFCallback(d_cnfCallback);
00966   d_coreSatAPI = new SearchSatCoreSatAPI(this);
00967   core->registerCoreSatAPI(d_coreSatAPI);
00968   d_theoryAPI = new SearchSatTheoryAPI(this);
00969   if (core->getFlags()["de"].getString() == "dfs") d_decider = new SearchSatDecider(this);
00970 
00971   if (core->getFlags()["sat"].getString() == "sat") {
00972 #ifdef DPLL_BASIC
00973     d_dpllt = new DPLLTBasic(d_theoryAPI, d_decider, core->getCM(),
00974                              core->getFlags()["stats"].getBool());
00975 #else
00976     throw CLException("SAT solver 'sat' not supported in this build");
00977 #endif
00978   } else if (core->getFlags()["sat"].getString() == "minisat") {
00979     d_dpllt = new DPLLTMiniSat(d_theoryAPI, d_decider,
00980                                core->getFlags()["stats"].getBool(),
00981                                core->getTM()->withProof());
00982   } else {
00983     throw CLException("Unrecognized SAT solver name: " + (core->getFlags()["sat"].getString()));
00984   }
00985 
00986   d_prioritySetStart = d_prioritySet.end();
00987 }
00988 
00989 
00990 SearchSat::~SearchSat()
00991 {
00992   delete d_dpllt;
00993   delete d_decider;
00994   delete d_theoryAPI;
00995   delete d_coreSatAPI;
00996   delete d_cnfCallback;
00997   delete d_cnfManager;
00998 }
00999 
01000 
01001 void SearchSat::registerAtom(const Expr& e)
01002 {
01003   e.setUserRegisteredAtom();
01004   if (!e.isRegisteredAtom())
01005     d_core->registerAtom(e, Theorem());
01006 }
01007 
01008 
01009 Theorem SearchSat::getImpliedLiteral(void)
01010 {
01011   Theorem imp;
01012   while (d_nextImpliedLiteral < d_core->numImpliedLiterals()) {
01013     imp = d_core->getImpliedLiteralByIndex(d_nextImpliedLiteral);
01014     d_nextImpliedLiteral = d_nextImpliedLiteral + 1;
01015     if (imp.getExpr().unnegate().isUserRegisteredAtom()) return imp;
01016   }
01017   return Theorem();
01018 }
01019 
01020 
01021 void SearchSat::returnFromCheck()
01022 {
01023   if (d_bottomScope < 0) {
01024     throw Exception
01025       ("returnFromCheck called with no previous invalid call to checkValid");
01026   }
01027   pop();
01028 }
01029 
01030 
01031 static void setRecursiveInUserAssumption(const Expr& e, int scope)
01032 {
01033   if (e.inUserAssumption()) return;
01034   for (int i = 0; i < e.arity(); ++i) {
01035     setRecursiveInUserAssumption(e[i], scope);
01036   }
01037   e.setInUserAssumption(scope);
01038 }
01039 
01040 
01041 void SearchSat::newUserAssumptionIntHelper(const Theorem& thm, CNF_Formula_Impl& cnf, bool atBottomScope)
01042 {
01043   Expr e = thm.getExpr();
01044   if (e.isAnd()) {
01045     for (int i = 0; i < e.arity(); ++i) {
01046       newUserAssumptionIntHelper(d_commonRules->andElim(thm, i), cnf, atBottomScope);
01047     }
01048   }
01049   else {
01050     if ( ! d_core->getFlags()["cnf-formula"].getBool()) {
01051       if (!recordNewRootLit(d_cnfManager->addAssumption(thm, cnf), 0, atBottomScope)) {
01052   cnf.deleteLast();
01053       }
01054     }
01055     else{
01056       d_cnfManager->addAssumption(thm, cnf);
01057     }
01058     // if cnf-formula is enabled,  d_cnfManager->addAssumption returns a random literal, not a RootLit.  A random lit can make recordNewRootLit return false, which in turn makes cnf.deleteLast() to delete the last clause, which is not correct. 
01059   }
01060 }
01061 
01062 
01063 Theorem SearchSat::newUserAssumptionInt(const Expr& e, CNF_Formula_Impl& cnf, bool atBottomScope)
01064 {
01065   DebugAssert(!d_inCheckSat,
01066               "User assumptions should be added before calling checkSat");
01067   Theorem thm;
01068   int scope;
01069   if (atBottomScope) scope = d_bottomScope;
01070   else scope = -1;
01071   setRecursiveInUserAssumption(e, scope);
01072   if (!isAssumption(e)) {
01073     e.setUserAssumption(scope);
01074     thm = d_commonRules->assumpRule(e, scope);
01075     d_userAssumptions.push_back(thm, scope);
01076 
01077     if (atBottomScope && d_bottomScope != d_core->getCM()->scopeLevel()) {
01078       //TODO: run preprocessor without using context-dependent information
01079       //TODO: this will break if we have stuff like the BVDIV rewrite that needs to get enqueued during preprocessing
01080       newUserAssumptionIntHelper(thm, cnf, true);
01081     }
01082     else {
01083       Theorem thm2 = d_core->getExprTrans()->preprocess(thm);
01084       Expr e2 = thm2.getExpr(); 
01085       if (e2.isFalse()) {
01086         d_core->addFact(thm2);
01087         return thm;
01088       }
01089       else if (!e2.isTrue()) {
01090         newUserAssumptionIntHelper(thm2, cnf, false);
01091       }
01092     }
01093     if (d_cnfManager->numVars() > d_vars.size()) {
01094       d_vars.resize(d_cnfManager->numVars(), SAT::Var::UNKNOWN);
01095     }
01096   }
01097   return thm;
01098 }
01099 
01100 
01101 Theorem SearchSat::newUserAssumption(const Expr& e)
01102 {
01103   CNF_Formula_Impl cnf;
01104   Theorem thm = newUserAssumptionInt(e, cnf, false);
01105   d_dpllt->addAssertion(cnf);
01106   return thm;
01107 }
01108 
01109 
01110 void SearchSat::getUserAssumptions(vector<Expr>& assumptions)
01111 {
01112   for(CDList<Theorem>::const_iterator i=d_userAssumptions.begin(),
01113         iend=d_userAssumptions.end(); i!=iend; ++i)
01114     assumptions.push_back((*i).getExpr());
01115 }
01116 
01117 
01118 void SearchSat::getInternalAssumptions(vector<Expr>& assumptions)
01119 {
01120   for(CDList<Theorem>::const_iterator i=d_intAssumptions.begin(),
01121         iend=d_intAssumptions.end(); i!=iend; ++i)
01122     assumptions.push_back((*i).getExpr());
01123 }
01124 
01125 
01126 
01127 void SearchSat::getAssumptions(vector<Expr>& assumptions)
01128 {
01129   CDList<Theorem>::const_iterator iU=d_userAssumptions.begin(),
01130     iUend=d_userAssumptions.end(), iI = d_intAssumptions.begin(),
01131     iIend=d_intAssumptions.end();
01132   while (true) {
01133     if (iI == iIend) {
01134       if (iU == iUend) break;
01135       assumptions.push_back((*iU).getExpr());
01136       ++iU;
01137     }
01138     else if (iU == iUend) {
01139       Expr intAssump = (*iI).getExpr();
01140       if (!intAssump.isUserAssumption()) {
01141         assumptions.push_back(intAssump);
01142       }
01143       ++iI;
01144     }
01145     else {
01146       if ((*iI).getScope() <= (*iU).getScope()) {
01147         Expr intAssump = (*iI).getExpr();
01148         if (!intAssump.isUserAssumption()) {
01149           assumptions.push_back(intAssump);
01150         }
01151         ++iI;
01152       }
01153       else {
01154         assumptions.push_back((*iU).getExpr());
01155         ++iU;
01156       }
01157     }
01158   }
01159 }
01160 
01161 
01162 bool SearchSat::isAssumption(const Expr& e)
01163 {
01164   return e.isUserAssumption() || e.isIntAssumption();
01165 }
01166 
01167 
01168 void SearchSat::getCounterExample(vector<Expr>& assumptions, bool inOrder)
01169 {
01170   if (!d_lastValid.get().isNull()) {
01171     throw Exception("Expected last query to be invalid");
01172   }
01173   getInternalAssumptions(assumptions);
01174 }
01175 
01176 
01177 Proof SearchSat::getProof()
01178 {
01179   if(!d_core->getTM()->withProof())
01180     throw EvalException
01181       ("getProof cannot be called without proofs activated");
01182   if(d_lastValid.get().isNull())
01183     throw EvalException
01184       ("getProof must be called only after a successful check");
01185   if(d_lastValid.get().isNull()) return Proof();
01186   else  return d_lastValid.get().getProof();
01187 }

Generated on Wed Nov 18 16:13:31 2009 for CVC3 by  doxygen 1.5.2