From: lianah Date: Tue, 10 Jun 2014 17:48:45 +0000 (-0400) Subject: Merging CAV14 paper bit-vector work. X-Git-Tag: cvc5-1.0.0~6844 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=5f072a19d299191dbedc4b69c8e0eda694cfa957;p=cvc5.git Merging CAV14 paper bit-vector work. --- diff --git a/configure.ac b/configure.ac index 03fbd690c..cfd65aef4 100644 --- a/configure.ac +++ b/configure.ac @@ -254,6 +254,9 @@ AC_ARG_WITH( ] ) +# [chris 8/24/2010] --with-gmp has no practical effect, since GMP is +# the default. Could be useful if other options are added later. + AC_ARG_WITH( [gmp], AS_HELP_STRING( @@ -745,6 +748,27 @@ fi AM_CONDITIONAL([CVC4_USE_GLPK], [test $have_libglpk -eq 1]) AC_SUBST([GLPK_LIBS]) +AC_ARG_WITH( + [abc], + AS_HELP_STRING( + [--with-abc], + [use the ABC AIG library] + ), + [case "$withval" in + y|ye|yes|Y|YE|YES) cvc4_use_abc=1 ;; + n|no|N|NO) cvc4_use_abc=0 ;; + esac + ] +) + +if test $cvc4_use_abc -eq 1; then + # must add dl and pthread separately and before abc + AC_CHECK_LIB(dl, dlopen, , [AC_MSG_ERROR([dl not found])], []) + AC_CHECK_LIB(pthread, pthread_create, , [AC_MSG_ERROR([pthread not found])], []) + AC_CHECK_LIB(abc, Abc_Start, , [AC_MSG_ERROR([abc not found])], [-lm -ldl -rdynamic -lreadline -ltermcap -lpthread -lrt -ldl]) + AC_DEFINE_UNQUOTED(CVC4_USE_ABC, [], [Defined if linked against the ABC AIG library.]) +fi + # Check to see if this version/architecture of GNU C++ explicitly # instantiates __gnu_cxx::hash or not. Some do, some don't. # See src/util/hash.h. @@ -1214,7 +1238,7 @@ AC_DEFINE_UNQUOTED(CVC4_RELEASE_STRING, ["${CVC4_RELEASE_STRING}"], [Full releas CPPFLAGS="${CPPFLAGS:+$CPPFLAGS }${BOOST_CPPFLAGS:+$BOOST_CPPFLAGS }$CVC4CPPFLAGS" CXXFLAGS="${CXXFLAGS:+$CXXFLAGS }$CVC4CXXFLAGS -Wno-deprecated" CFLAGS="${CFLAGS:+$CFLAGS }$CVC4CFLAGS -Wno-deprecated -fexceptions" -LDFLAGS="${LDFLAGS:+$LDFLAGS }$CVC4LDFLAGS" +LDFLAGS="${LDFLAGS:+$LDFLAGS }$CVC4LDFLAGS -ldl" # visibility flag not supported for Windows builds # also increase default stack size for Windows binaries diff --git a/contrib/run-script-smtcomp2014 b/contrib/run-script-smtcomp2014 index e723df9c7..af41aab8b 100755 --- a/contrib/run-script-smtcomp2014 +++ b/contrib/run-script-smtcomp2014 @@ -66,7 +66,7 @@ QF_AUFBV) finishwith --decision=justification-stoponly ;; QF_BV) - trywith 10 --bv-core --decision=justification + trywith 10 --bv-eq-slicer=auto --decision=justification trywith 60 --decision=justification trywith 600 --decision=internal --bitblast-eager finishwith --decision=justification --decision-use-weight --decision-weight-internal=usr1 diff --git a/src/Makefile.am b/src/Makefile.am index ae7cb619a..f3bd85825 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -173,9 +173,8 @@ libcvc4_la_SOURCES = \ theory/uf/theory_uf_model.cpp \ theory/uf/options_handlers.h \ theory/bv/theory_bv_utils.h \ + theory/bv/theory_bv_utils.cpp \ theory/bv/type_enumerator.h \ - theory/bv/bitblaster.h \ - theory/bv/bitblaster.cpp \ theory/bv/bv_to_bool.h \ theory/bv/bv_to_bool.cpp \ theory/bv/bv_subtheory.h \ @@ -187,8 +186,13 @@ libcvc4_la_SOURCES = \ theory/bv/bv_subtheory_inequality.cpp \ theory/bv/bv_inequality_graph.h \ theory/bv/bv_inequality_graph.cpp \ - theory/bv/bitblast_strategies.h \ - theory/bv/bitblast_strategies.cpp \ + theory/bv/bitblast_strategies_template.h \ + theory/bv/bitblaster_template.h \ + theory/bv/lazy_bitblaster.h \ + theory/bv/eager_bitblaster.h \ + theory/bv/aig_bitblaster.h \ + theory/bv/bv_eager_solver.h \ + theory/bv/bv_eager_solver.cpp \ theory/bv/slicer.h \ theory/bv/slicer.cpp \ theory/bv/theory_bv.h \ @@ -203,6 +207,15 @@ libcvc4_la_SOURCES = \ theory/bv/theory_bv_rewriter.h \ theory/bv/theory_bv_rewriter.cpp \ theory/bv/cd_set_collection.h \ + theory/bv/abstraction.h \ + theory/bv/abstraction.cpp \ + theory/bv/bv_quick_check.h \ + theory/bv/bv_quick_check.cpp \ + theory/bv/bv_subtheory_algebraic.h \ + theory/bv/bv_subtheory_algebraic.cpp \ + theory/bv/options_handlers.h \ + theory/bv/bitblast_mode.h \ + theory/bv/bitblast_mode.cpp \ theory/idl/idl_model.h \ theory/idl/idl_model.cpp \ theory/idl/idl_assertion.h \ diff --git a/src/main/command_executor_portfolio.cpp b/src/main/command_executor_portfolio.cpp index 9de3b2134..1cc024117 100644 --- a/src/main/command_executor_portfolio.cpp +++ b/src/main/command_executor_portfolio.cpp @@ -330,6 +330,12 @@ bool CommandExecutorPortfolio::doCommandSingleton(Command* cmd) *d_options[options::out] << d_ostringstreams[portfolioReturn.first]->str(); + // FIXME! MASSIVE HACK!! + if(d_options[options::statistics]) { + pExecutor->flushStatistics(*d_options[options::err]); + } + + exit(0); } /* cleanup this check sat specific stuff */ diff --git a/src/main/portfolio.cpp b/src/main/portfolio.cpp index ebf36b0cd..a5fe46d27 100644 --- a/src/main/portfolio.cpp +++ b/src/main/portfolio.cpp @@ -95,7 +95,7 @@ std::pair runPortfolio(int numThreads, for(int t = 0; t < numThreads; ++t) { if(optionWaitToJoin) { threads[t].join(); - } + } } std::pair retval(global_winner, threads_returnValue[global_winner]); diff --git a/src/prop/bvminisat/bvminisat.cpp b/src/prop/bvminisat/bvminisat.cpp index fa5f53113..46b521e6b 100644 --- a/src/prop/bvminisat/bvminisat.cpp +++ b/src/prop/bvminisat/bvminisat.cpp @@ -22,14 +22,15 @@ using namespace CVC4; using namespace prop; -BVMinisatSatSolver::BVMinisatSatSolver(context::Context* mainSatContext) +BVMinisatSatSolver::BVMinisatSatSolver(context::Context* mainSatContext, const std::string& name) : context::ContextNotifyObj(mainSatContext, false), d_minisat(new BVMinisat::SimpSolver(mainSatContext)), d_minisatNotify(0), d_solveCount(0), d_assertionsCount(0), d_assertionsRealCount(mainSatContext, 0), - d_lastPropagation(mainSatContext, 0) + d_lastPropagation(mainSatContext, 0), + d_statistics(name) { d_statistics.init(d_minisat); } @@ -61,6 +62,7 @@ SatValue BVMinisatSatSolver::propagate() { void BVMinisatSatSolver::addMarkerLiteral(SatLiteral lit) { d_minisat->addMarkerLiteral(BVMinisat::var(toMinisatLit(lit))); + markUnremovable(lit); } void BVMinisatSatSolver::explain(SatLiteral lit, std::vector& explanation) { @@ -113,9 +115,9 @@ SatValue BVMinisatSatSolver::solve(long unsigned int& resource){ } else { d_minisat->setConfBudget(resource); } - BVMinisat::vec empty; + // BVMinisat::vec empty; unsigned long conflictsBefore = d_minisat->conflicts; - SatValue result = toSatLiteralValue(d_minisat->solveLimited(empty)); + SatValue result = toSatLiteralValue(d_minisat->solveLimited()); d_minisat->clearInterrupt(); resource = d_minisat->conflicts - conflictsBefore; Trace("limit") << "& clause, // Satistics for BVMinisatSatSolver -BVMinisatSatSolver::Statistics::Statistics() : - d_statStarts("theory::bv::bvminisat::starts"), - d_statDecisions("theory::bv::bvminisat::decisions"), - d_statRndDecisions("theory::bv::bvminisat::rnd_decisions"), - d_statPropagations("theory::bv::bvminisat::propagations"), - d_statConflicts("theory::bv::bvminisat::conflicts"), - d_statClausesLiterals("theory::bv::bvminisat::clauses_literals"), - d_statLearntsLiterals("theory::bv::bvminisat::learnts_literals"), - d_statMaxLiterals("theory::bv::bvminisat::max_literals"), - d_statTotLiterals("theory::bv::bvminisat::tot_literals"), - d_statEliminatedVars("theory::bv::bvminisat::eliminated_vars"), - d_statCallsToSolve("theory::bv::bvminisat::calls_to_solve", 0), - d_statSolveTime("theory::bv::bvminisat::solve_time", 0) +BVMinisatSatSolver::Statistics::Statistics(const std::string& prefix) : + d_statStarts("theory::bv::"+prefix+"bvminisat::starts"), + d_statDecisions("theory::bv::"+prefix+"bvminisat::decisions"), + d_statRndDecisions("theory::bv::"+prefix+"bvminisat::rnd_decisions"), + d_statPropagations("theory::bv::"+prefix+"bvminisat::propagations"), + d_statConflicts("theory::bv::"+prefix+"bvminisat::conflicts"), + d_statClausesLiterals("theory::bv::"+prefix+"bvminisat::clauses_literals"), + d_statLearntsLiterals("theory::bv::"+prefix+"bvminisat::learnts_literals"), + d_statMaxLiterals("theory::bv::"+prefix+"bvminisat::max_literals"), + d_statTotLiterals("theory::bv::"+prefix+"bvminisat::tot_literals"), + d_statEliminatedVars("theory::bv::"+prefix+"bvminisat::eliminated_vars"), + d_statCallsToSolve("theory::bv::"+prefix+"bvminisat::calls_to_solve", 0), + d_statSolveTime("theory::bv::"+prefix+"bvminisat::solve_time", 0), + d_registerStats(!prefix.empty()) { + if (!d_registerStats) + return; + StatisticsRegistry::registerStat(&d_statStarts); StatisticsRegistry::registerStat(&d_statDecisions); StatisticsRegistry::registerStat(&d_statRndDecisions); @@ -240,6 +246,8 @@ BVMinisatSatSolver::Statistics::Statistics() : } BVMinisatSatSolver::Statistics::~Statistics() { + if (!d_registerStats) + return; StatisticsRegistry::unregisterStat(&d_statStarts); StatisticsRegistry::unregisterStat(&d_statDecisions); StatisticsRegistry::unregisterStat(&d_statRndDecisions); @@ -255,6 +263,9 @@ BVMinisatSatSolver::Statistics::~Statistics() { } void BVMinisatSatSolver::Statistics::init(BVMinisat::SimpSolver* minisat){ + if (!d_registerStats) + return; + d_statStarts.setData(minisat->starts); d_statDecisions.setData(minisat->decisions); d_statRndDecisions.setData(minisat->rnd_decisions); diff --git a/src/prop/bvminisat/bvminisat.h b/src/prop/bvminisat/bvminisat.h index ebf4a44b4..568d89f7f 100644 --- a/src/prop/bvminisat/bvminisat.h +++ b/src/prop/bvminisat/bvminisat.h @@ -69,9 +69,10 @@ public: BVMinisatSatSolver() : ContextNotifyObj(NULL, false), d_assertionsRealCount(NULL, (unsigned)0), - d_lastPropagation(NULL, (unsigned)0) + d_lastPropagation(NULL, (unsigned)0), + d_statistics("") { Unreachable(); } - BVMinisatSatSolver(context::Context* mainSatContext); + BVMinisatSatSolver(context::Context* mainSatContext, const std::string& name = ""); ~BVMinisatSatSolver() throw(AssertionException); void setNotify(Notify* notify); @@ -91,7 +92,6 @@ public: SatValue solve(); SatValue solve(long unsigned int&); - SatValue solve(bool quick_solve); void getUnsatCore(SatClause& unsatCore); SatValue value(SatLiteral l); @@ -129,8 +129,9 @@ public: ReferenceStat d_statTotLiterals; ReferenceStat d_statEliminatedVars; IntStat d_statCallsToSolve; - BackedStat d_statSolveTime; - Statistics(); + BackedStat d_statSolveTime; + bool d_registerStats; + Statistics(const std::string& prefix); ~Statistics(); void init(BVMinisat::SimpSolver* minisat); }; diff --git a/src/prop/bvminisat/core/Solver.cc b/src/prop/bvminisat/core/Solver.cc index 68969c78b..8833eec78 100644 --- a/src/prop/bvminisat/core/Solver.cc +++ b/src/prop/bvminisat/core/Solver.cc @@ -59,19 +59,31 @@ std::ostream& operator << (std::ostream& out, const BVMinisat::Clause& c) { static const char* _cat = "CORE"; +// static DoubleOption opt_var_decay (_cat, "var-decay", "The variable activity decay factor", 0.95, DoubleRange(0, false, 1, false)); +// static DoubleOption opt_clause_decay (_cat, "cla-decay", "The clause activity decay factor", 0.999, DoubleRange(0, false, 1, false)); +// static DoubleOption opt_random_var_freq (_cat, "rnd-freq", "The frequency with which the decision heuristic tries to choose a random variable", 0.0, DoubleRange(0, true, 1, true)); +// static DoubleOption opt_random_seed (_cat, "rnd-seed", "Used by the random variable selection", 91648253, DoubleRange(0, false, HUGE_VAL, false)); +// static IntOption opt_ccmin_mode (_cat, "ccmin-mode", "Controls conflict clause minimization (0=none, 1=basic, 2=deep)", 0, IntRange(0, 2)); +// static IntOption opt_phase_saving (_cat, "phase-saving", "Controls the level of phase saving (0=none, 1=limited, 2=full)", 2, IntRange(0, 2)); +// static BoolOption opt_rnd_init_act (_cat, "rnd-init", "Randomize the initial activity", false); +// static BoolOption opt_luby_restart (_cat, "luby", "Use the Luby restart sequence", true); +// static IntOption opt_restart_first (_cat, "rfirst", "The base restart interval", 100, IntRange(1, INT32_MAX)); +// static DoubleOption opt_restart_inc (_cat, "rinc", "Restart interval increase factor", 1.5, DoubleRange(1, false, HUGE_VAL, false)); +// static DoubleOption opt_garbage_frac (_cat, "gc-frac", "The fraction of wasted memory allowed before a garbage collection is triggered", 0.20, DoubleRange(0, false, HUGE_VAL, false)); + + static DoubleOption opt_var_decay (_cat, "var-decay", "The variable activity decay factor", 0.95, DoubleRange(0, false, 1, false)); static DoubleOption opt_clause_decay (_cat, "cla-decay", "The clause activity decay factor", 0.999, DoubleRange(0, false, 1, false)); -static DoubleOption opt_random_var_freq (_cat, "rnd-freq", "The frequency with which the decision heuristic tries to choose a random variable", 0.0, DoubleRange(0, true, 1, true)); +static DoubleOption opt_random_var_freq (_cat, "rnd-freq", "The frequency with which the decision heuristic tries to choose a random variable", 0, DoubleRange(0, true, 1, true)); static DoubleOption opt_random_seed (_cat, "rnd-seed", "Used by the random variable selection", 91648253, DoubleRange(0, false, HUGE_VAL, false)); -static IntOption opt_ccmin_mode (_cat, "ccmin-mode", "Controls conflict clause minimization (0=none, 1=basic, 2=deep)", 0, IntRange(0, 2)); +static IntOption opt_ccmin_mode (_cat, "ccmin-mode", "Controls conflict clause minimization (0=none, 1=basic, 2=deep)", 2, IntRange(0, 2)); static IntOption opt_phase_saving (_cat, "phase-saving", "Controls the level of phase saving (0=none, 1=limited, 2=full)", 2, IntRange(0, 2)); static BoolOption opt_rnd_init_act (_cat, "rnd-init", "Randomize the initial activity", false); static BoolOption opt_luby_restart (_cat, "luby", "Use the Luby restart sequence", true); -static IntOption opt_restart_first (_cat, "rfirst", "The base restart interval", 100, IntRange(1, INT32_MAX)); -static DoubleOption opt_restart_inc (_cat, "rinc", "Restart interval increase factor", 1.5, DoubleRange(1, false, HUGE_VAL, false)); +static IntOption opt_restart_first (_cat, "rfirst", "The base restart interval", 25, IntRange(1, INT32_MAX)); +static DoubleOption opt_restart_inc (_cat, "rinc", "Restart interval increase factor", 3, DoubleRange(1, false, HUGE_VAL, false)); static DoubleOption opt_garbage_frac (_cat, "gc-frac", "The fraction of wasted memory allowed before a garbage collection is triggered", 0.20, DoubleRange(0, false, HUGE_VAL, false)); - //================================================================================================= // Constructor/Destructor: @@ -395,10 +407,8 @@ void Solver::analyze(CRef confl, vec& out_learnt, int& out_btlevel, UIP uip out_learnt.shrink(i - j); tot_literals += out_learnt.size(); - bool clause_all_marker = true; for (int i = 0; i < out_learnt.size(); ++ i) { if (marker[var(out_learnt[i])] == 0) { - clause_all_marker = false; break; } } @@ -410,9 +420,6 @@ void Solver::analyze(CRef confl, vec& out_learnt, int& out_btlevel, UIP uip } else{ int max_i = 1; - if (marker[var(out_learnt[0])] == 0) { - clause_all_marker = false; - } // Find the first literal assigned at the next-highest level: for (int i = 2; i < out_learnt.size(); i++) if (level(var(out_learnt[i])) > level(var(out_learnt[max_i]))) @@ -424,10 +431,6 @@ void Solver::analyze(CRef confl, vec& out_learnt, int& out_btlevel, UIP uip out_btlevel = level(var(p)); } - if (out_learnt.size() > 0 && clause_all_marker && CVC4::options::bitvectorShareLemmas()) { - notify->notify(out_learnt); - } - for (int j = 0; j < analyze_toclear.size(); j++) seen[var(analyze_toclear[j])] = 0; // ('seen[]' is now cleared) } @@ -462,6 +465,48 @@ bool Solver::litRedundant(Lit p, uint32_t abstract_levels) return true; } +/** + * Specialized analyzeFinal procedure where we test the consistency + * of the assumptions before backtracking bellow the assumption level. + * + * @param p the original uip (may be unit) + * @param confl_clause the conflict clause + * @param out_conflict the conflict in terms of assumptions we are building + */ +void Solver::analyzeFinal2(Lit p, CRef confl_clause, vec& out_conflict) { + assert (confl_clause != CRef_Undef); + assert (decisionLevel() == assumptions.size()); + assert (level(var(p)) == assumptions.size()); + + out_conflict.clear(); + + Clause& cl = ca[confl_clause]; + for (int i = 0; i < cl.size(); ++i) { + seen[var(cl[i])] = 1; + } + + for (int i = trail.size() - 1; i >= trail_lim[0]; i--) { + Var x = var(trail[i]); + if (seen[x]) { + if (reason(x) == CRef_Undef) { + // this is the case when p was a unit + if (x == var(p)) + continue; + + assert (marker[x] == 2); + assert (level(x) > 0); + out_conflict.push(~trail[i]); + } else { + Clause& c = ca[reason(x)]; + for (int j = 1; j < c.size(); j++) + if (level(var(c[j])) > 0) + seen[var(c[j])] = 1; + } + seen[x] = 0; + } + } + assert (out_conflict.size()); +} /*_________________________________________________________________________________________________ | @@ -475,7 +520,9 @@ bool Solver::litRedundant(Lit p, uint32_t abstract_levels) void Solver::analyzeFinal(Lit p, vec& out_conflict) { out_conflict.clear(); - out_conflict.push(p); + if (marker[var(p)] == 2) { + out_conflict.push(p); + } if (decisionLevel() == 0) return; @@ -500,6 +547,7 @@ void Solver::analyzeFinal(Lit p, vec& out_conflict) } seen[var(p)] = 0; + assert (out_conflict.size()); } @@ -755,28 +803,46 @@ lbool Solver::search(int nof_conflicts, UIP uip) analyze(confl, learnt_clause, backtrack_level, uip); Lit p = learnt_clause[0]; - bool assumption = marker[var(p)] == 2; - - cancelUntil(backtrack_level); - - if (learnt_clause.size() == 1){ - uncheckedEnqueue(p); - }else{ - CRef cr = ca.alloc(learnt_clause, true); - learnts.push(cr); - attachClause(cr); - claBumpActivity(ca[cr]); - uncheckedEnqueue(p, cr); + //bool assumption = marker[var(p)] == 2; + + CRef cr = CRef_Undef; + if (learnt_clause.size() > 1) { + cr = ca.alloc(learnt_clause, true); + learnts.push(cr); + attachClause(cr); + claBumpActivity(ca[cr]); } - // if an assumption, we're done - if (assumption) { - assert(decisionLevel() < assumptions.size()); + // if the uip was an assumption we are unsat + if (level(var(p)) <= assumptions.size()) { + for (int i = 0; i < learnt_clause.size(); ++i) { + assert (level(var(learnt_clause[i])) <= decisionLevel()); + seen[var(learnt_clause[i])] = 1; + } analyzeFinal(p, conflict); Debug("bvminisat::search") << OUTPUT_TAG << " conflict on assumptions " << std::endl; return l_False; } - + + if (!CVC4::options::bvEagerExplanations()) { + // check if uip leads to a conflict + if (backtrack_level < assumptions.size()) { + cancelUntil(assumptions.size()); + uncheckedEnqueue(p, cr); + + CRef new_confl = propagate(); + if (new_confl != CRef_Undef) { + // we have a conflict we now need to explain it + analyzeFinal2(p, new_confl, conflict); + return l_False; + } + } + } + + cancelUntil(backtrack_level); + uncheckedEnqueue(p, cr); + + varDecayActivity(); claDecayActivity(); @@ -835,6 +901,7 @@ lbool Solver::search(int nof_conflicts, UIP uip) // Dummy decision level: newDecisionLevel(); }else if (value(p) == l_False){ + marker[var(p)] = 2; analyzeFinal(~p, conflict); Debug("bvminisat::search") << OUTPUT_TAG << " assumption false, we're unsat" << std::endl; return l_False; diff --git a/src/prop/bvminisat/core/Solver.h b/src/prop/bvminisat/core/Solver.h index 53d92ac39..882f23ef7 100644 --- a/src/prop/bvminisat/core/Solver.h +++ b/src/prop/bvminisat/core/Solver.h @@ -290,6 +290,7 @@ protected: void analyze (CRef confl, vec& out_learnt, int& out_btlevel, UIP uip = UIP_FIRST); // (bt = backtrack) void analyzeFinal (Lit p, vec& out_conflict); // COULD THIS BE IMPLEMENTED BY THE ORDINARIY "analyze" BY SOME REASONABLE GENERALIZATION? + void analyzeFinal2(Lit p, CRef confl_clause, vec& out_conflict); bool litRedundant (Lit p, uint32_t abstract_levels); // (helper method for 'analyze()') lbool search (int nof_conflicts, UIP uip = UIP_FIRST); // Search for a given number of conflicts. lbool solve_ (); // Main solve method (assumptions given in 'assumptions'). diff --git a/src/prop/bvminisat/simp/SimpSolver.cc b/src/prop/bvminisat/simp/SimpSolver.cc index 59820e9e3..c65189985 100644 --- a/src/prop/bvminisat/simp/SimpSolver.cc +++ b/src/prop/bvminisat/simp/SimpSolver.cc @@ -21,7 +21,8 @@ OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWA #include "mtl/Sort.h" #include "simp/SimpSolver.h" #include "utils/System.h" - +#include "theory/bv/options.h" +#include "smt/options.h" using namespace BVMinisat; //================================================================================================= @@ -32,7 +33,7 @@ static const char* _cat = "SIMP"; static BoolOption opt_use_asymm (_cat, "asymm", "Shrink clauses by asymmetric branching.", false); static BoolOption opt_use_rcheck (_cat, "rcheck", "Check if a clause is already implied. (costly)", false); -static BoolOption opt_use_elim (_cat, "elim", "Perform variable elimination.", false); +static BoolOption opt_use_elim (_cat, "elim", "Perform variable elimination.", true); static IntOption opt_grow (_cat, "grow", "Allow a variable elimination step to grow by a number of clauses.", 0); static IntOption opt_clause_lim (_cat, "cl-lim", "Variables are not eliminated if it produces a resolvent with a length above this limit. -1 means no limit", 20, IntRange(-1, INT32_MAX)); static IntOption opt_subsumption_lim (_cat, "sub-lim", "Do not check if subsumption against a clause larger than this. -1 means no limit.", 1000, IntRange(-1, INT32_MAX)); @@ -51,11 +52,12 @@ SimpSolver::SimpSolver(CVC4::context::Context* c) : , simp_garbage_frac (opt_simp_garbage_frac) , use_asymm (opt_use_asymm) , use_rcheck (opt_use_rcheck) - , use_elim (opt_use_elim) + , use_elim (opt_use_elim && + CVC4::options::bitblastMode() == CVC4::theory::bv::BITBLAST_MODE_EAGER && + !CVC4::options::produceModels()) , merges (0) , asymm_lits (0) , eliminated_vars (0) - , total_eliminate_time("theory::bv::bvminisat::TotalVariableEliminationTime") , elimorder (1) , use_simplification (true) , occurs (ClauseDeleted(ca)) @@ -63,7 +65,7 @@ SimpSolver::SimpSolver(CVC4::context::Context* c) : , bwdsub_assigns (0) , n_touched (0) { - CVC4::StatisticsRegistry::registerStat(&total_eliminate_time); + vec dummy(1,lit_Undef); ca.extra_clause_field = true; // NOTE: must happen before allocating the dummy clause below. bwdsub_tmpunit = ca.alloc(dummy); @@ -87,7 +89,7 @@ SimpSolver::SimpSolver(CVC4::context::Context* c) : SimpSolver::~SimpSolver() { - CVC4::StatisticsRegistry::unregisterStat(&total_eliminate_time); + // CVC4::StatisticsRegistry::unregisterStat(&total_eliminate_time); } @@ -606,7 +608,7 @@ void SimpSolver::extendModel() bool SimpSolver::eliminate(bool turn_off_elim) { - CVC4::TimerStat::CodeTimer codeTimer(total_eliminate_time); + // CVC4::TimerStat::CodeTimer codeTimer(total_eliminate_time); if (!simplify()) return false; diff --git a/src/prop/bvminisat/simp/SimpSolver.h b/src/prop/bvminisat/simp/SimpSolver.h index 4af82b02d..d808daa22 100644 --- a/src/prop/bvminisat/simp/SimpSolver.h +++ b/src/prop/bvminisat/simp/SimpSolver.h @@ -58,6 +58,7 @@ class SimpSolver : public Solver { // bool solve (const vec& assumps, bool do_simp = true, bool turn_off_simp = false); lbool solveLimited(const vec& assumps, bool do_simp = true, bool turn_off_simp = false); + lbool solveLimited(bool do_simp = true, bool turn_off_simp = false); bool solve ( bool do_simp = true, bool turn_off_simp = false); bool solve (Lit p , bool do_simp = true, bool turn_off_simp = false); bool solve (Lit p, Lit q, bool do_simp = true, bool turn_off_simp = false); @@ -96,7 +97,7 @@ class SimpSolver : public Solver { int merges; int asymm_lits; int eliminated_vars; - CVC4::TimerStat total_eliminate_time; + // CVC4::TimerStat total_eliminate_time; protected: @@ -195,6 +196,9 @@ inline bool SimpSolver::solve (const vec& assumps, bool do_simp, boo inline lbool SimpSolver::solveLimited (const vec& assumps, bool do_simp, bool turn_off_simp){ assumps.copyTo(assumptions); return solve_(do_simp, turn_off_simp); } +inline lbool SimpSolver::solveLimited (bool do_simp, bool turn_off_simp){ + return solve_(do_simp, turn_off_simp); } + //================================================================================================= } diff --git a/src/prop/cnf_stream.cpp b/src/prop/cnf_stream.cpp index 47d352949..3d2c29798 100644 --- a/src/prop/cnf_stream.cpp +++ b/src/prop/cnf_stream.cpp @@ -228,12 +228,6 @@ SatLiteral CnfStream::convertAtom(TNode node) { theoryLiteral = true; canEliminate = false; preRegister = true; - - if (options::bitvectorEagerBitblast() && theory::Theory::theoryOf(node) == theory::THEORY_BV) { - // All BV atoms are treated as boolean except for equality - theoryLiteral = false; - canEliminate = true; - } } // Make a new literal (variables are not considered theory literals) diff --git a/src/prop/sat_solver_factory.cpp b/src/prop/sat_solver_factory.cpp index 6cda02c00..e937c718c 100644 --- a/src/prop/sat_solver_factory.cpp +++ b/src/prop/sat_solver_factory.cpp @@ -25,8 +25,8 @@ namespace prop { template class SatSolverConstructor; template class SatSolverConstructor; -BVSatSolverInterface* SatSolverFactory::createMinisat(context::Context* mainSatContext) { - return new BVMinisatSatSolver(mainSatContext); +BVSatSolverInterface* SatSolverFactory::createMinisat(context::Context* mainSatContext, const std::string& name) { + return new BVMinisatSatSolver(mainSatContext, name); } DPLLSatSolverInterface* SatSolverFactory::createDPLLMinisat() { diff --git a/src/prop/sat_solver_factory.h b/src/prop/sat_solver_factory.h index 7d3a45c59..291609de7 100644 --- a/src/prop/sat_solver_factory.h +++ b/src/prop/sat_solver_factory.h @@ -28,7 +28,7 @@ namespace prop { class SatSolverFactory { public: - static BVSatSolverInterface* createMinisat(context::Context* mainSatContext); + static BVSatSolverInterface* createMinisat(context::Context* mainSatContext, const std::string& name = ""); static DPLLSatSolverInterface* createDPLLMinisat(); static SatSolver* create(const char* id); diff --git a/src/smt/options_handlers.h b/src/smt/options_handlers.h index 058a00f32..af8e8663c 100644 --- a/src/smt/options_handlers.h +++ b/src/smt/options_handlers.h @@ -121,7 +121,13 @@ t-explanations [non-stateful]\n\ bv-rewrites [non-stateful]\n\ + Output correctness queries for all bitvector rewrites\n\ \n\ -theory::fullcheck [non-stateful]\n\ +bv-abstraction [non-stateful]\n\ ++ Output correctness queries for all bv abstraction \n\ +\n\ +bv-algebraic [non-stateful]\n\ ++ Output correctness queries for bv algebraic solver. \n\ +\n\ +theory::fullcheck [non-stateful]\n \ + Output completeness queries for all full-check effort-level theory checks\n\ \n\ Dump modes can be combined with multiple uses of --dump. Generally you want\n\ @@ -237,6 +243,10 @@ inline void dumpMode(std::string option, std::string optarg, SmtEngine* smt) { } else if(!strcmp(optargPtr, "help")) { puts(dumpHelp.c_str()); exit(1); + } else if(!strcmp(optargPtr, "bv-abstraction")) { + Dump.on("bv-abstraction"); + } else if(!strcmp(optargPtr, "bv-algebraic")) { + Dump.on("bv-algebraic"); } else { throw OptionException(std::string("unknown option for --dump: `") + optargPtr + "'. Try --dump help."); diff --git a/src/smt/smt_engine.cpp b/src/smt/smt_engine.cpp index 33496ac3b..6dbef4fe3 100644 --- a/src/smt/smt_engine.cpp +++ b/src/smt/smt_engine.cpp @@ -361,11 +361,15 @@ private: // Lift bit-vectors of size 1 to booleans void bvToBool(); + // Abstract common structure over small domains to UF + // return true if changes were made. + void bvAbstraction(); + // Simplify ITE structure bool simpITE(); // Simplify based on unconstrained values - void unconstrainedSimp(); + void unconstrainedSimp(std::vector& assertions); // Ensures the assertions asserted after before now // effectively come before d_realAssertionsEnd @@ -896,11 +900,6 @@ void SmtEngine::setDefaults() { */ } - if(options::bitvectorEagerBitblast()) { - // Eager solver should use the internal decision strategy - options::decisionMode.set(DECISION_STRATEGY_INTERNAL); - } - if(options::checkModels()) { if(! options::interactive()) { Notice() << "SmtEngine: turning on interactive-mode to support check-models" << endl; @@ -1069,6 +1068,27 @@ void SmtEngine::setDefaults() { setOption("check-models", SExpr("false")); } } + + + if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER && + options::incrementalSolving()) { + if (options::incrementalSolving.wasSetByUser()) { + throw OptionException(std::string("Eager bit-blasting does not currently support incremental mode. \n\ + Try --bitblast=lazy")); + } + Notice() << "SmtEngine: turning off incremental to support eager bit-blasting" << endl; + setOption("incremental", SExpr("false")); + } + + if (! options::bvEagerExplanations.wasSetByUser() && + d_logic.isTheoryEnabled(THEORY_ARRAY) && + d_logic.isTheoryEnabled(THEORY_BV)) { + Trace("smt") << "enabling eager bit-vector explanations " << endl; + options::bvEagerExplanations.set(true); + } + + + // Turn on arith rewrite equalities only for pure arithmetic if(! options::arithRewriteEq.wasSetByUser()) { bool arithRewriteEq = d_logic.isPure(THEORY_ARITH) && !d_logic.isQuantified(); @@ -1971,13 +1991,29 @@ bool SmtEnginePrivate::nonClausalSimplify() { return true; } +void SmtEnginePrivate::bvAbstraction() { + Trace("bv-abstraction") << "SmtEnginePrivate::bvAbstraction()" << endl; + std::vector new_assertions; + bool changed = d_smt.d_theoryEngine->ppBvAbstraction(d_assertionsToPreprocess, new_assertions); + for (unsigned i = 0; i < d_assertionsToPreprocess.size(); ++ i) { + d_assertionsToPreprocess[i] = Rewriter::rewrite(new_assertions[i]); + } + // if we are using the lazy solver and the abstraction + // applies, then UF symbols were introduced + if (options::bitblastMode() == theory::bv::BITBLAST_MODE_LAZY && + changed) { + LogicRequest req(d_smt); + req.widenLogic(THEORY_UF); + } +} + void SmtEnginePrivate::bvToBool() { Trace("bv-to-bool") << "SmtEnginePrivate::bvToBool()" << endl; std::vector new_assertions; - d_smt.d_theoryEngine->ppBvToBool(d_assertionsToCheck, new_assertions); - for (unsigned i = 0; i < d_assertionsToCheck.size(); ++ i) { - d_assertionsToCheck[i] = Rewriter::rewrite(new_assertions[i]); + d_smt.d_theoryEngine->ppBvToBool(d_assertionsToPreprocess, new_assertions); + for (unsigned i = 0; i < d_assertionsToPreprocess.size(); ++ i) { + d_assertionsToPreprocess[i] = Rewriter::rewrite(new_assertions[i]); } } @@ -2032,11 +2068,10 @@ void SmtEnginePrivate::compressBeforeRealAssertions(size_t before){ Assert(d_assertionsToCheck.size() == before); } -void SmtEnginePrivate::unconstrainedSimp() { +void SmtEnginePrivate::unconstrainedSimp(std::vector& assertions) { TimerStat::CodeTimer unconstrainedSimpTimer(d_smt.d_stats->d_unconstrainedSimpTime); - Trace("simplify") << "SmtEnginePrivate::unconstrainedSimp()" << endl; - d_smt.d_theoryEngine->ppUnconstrainedSimp(d_assertionsToCheck); + d_smt.d_theoryEngine->ppUnconstrainedSimp(assertions); } @@ -2533,6 +2568,9 @@ bool SmtEnginePrivate::simplifyAssertions() Debug("smt") << " d_assertionsToPreprocess: " << d_assertionsToPreprocess.size() << endl; Debug("smt") << " d_assertionsToCheck : " << d_assertionsToCheck.size() << endl; + // before ppRewrite check if only core theory for BV theory + d_smt.d_theoryEngine->staticInitializeBVOptions(d_assertionsToCheck); + // Theory preprocessing if (d_smt.d_earlyTheoryPP) { Chat() << "...doing early theory preprocessing..." << endl; @@ -2570,7 +2608,7 @@ bool SmtEnginePrivate::simplifyAssertions() // Unconstrained simplification if(options::unconstrainedSimp()) { Chat() << "...doing unconstrained simplification..." << endl; - unconstrainedSimp(); + unconstrainedSimp(d_assertionsToCheck); } dumpAssertions("post-unconstrained", d_assertionsToCheck); @@ -2825,7 +2863,26 @@ void SmtEnginePrivate::processAssertions() { Debug("smt") << " d_assertionsToPreprocess: " << d_assertionsToPreprocess.size() << endl; Debug("smt") << " d_assertionsToCheck : " << d_assertionsToCheck.size() << endl; + + if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER && + !d_smt.d_logic.isPure(THEORY_BV)) { + throw ModalException("Eager bit-blasting does not currently support theory combination. " + "Note that in a QF_BV problem UF symbols can be introduced for division. " + "Try --bv-div-zero-const to interpret division by zero as a constant."); + } + if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { + d_smt.d_theoryEngine->mkAckermanizationAsssertions(d_assertionsToPreprocess); + } + + + if ( options::bvAbstraction() && + !options::incrementalSolving()) { + dumpAssertions("pre-bv-abstraction", d_assertionsToPreprocess); + bvAbstraction(); + dumpAssertions("post-bv-abstraction", d_assertionsToPreprocess); + } + dumpAssertions("pre-boolean-terms", d_assertionsToPreprocess); { Chat() << "rewriting Boolean terms..." << endl; @@ -2855,6 +2912,15 @@ void SmtEnginePrivate::processAssertions() { Debug("smt") << " d_assertionsToPreprocess: " << d_assertionsToPreprocess.size() << endl; Debug("smt") << " d_assertionsToCheck : " << d_assertionsToCheck.size() << endl; + + // Unconstrained simplification + if(options::unconstrainedSimp()) { + dumpAssertions("pre-unconstrained-simp", d_assertionsToPreprocess); + Chat() << "...doing unconstrained simplification..." << endl; + unconstrainedSimp(d_assertionsToPreprocess); + dumpAssertions("post-unconstrained-simp", d_assertionsToPreprocess); + } + dumpAssertions("pre-substitution", d_assertionsToPreprocess); // Apply the substitutions we already have, and normalize @@ -2871,6 +2937,15 @@ void SmtEnginePrivate::processAssertions() { // Assertions ARE guaranteed to be rewritten by this point + + // Lift bit-vectors of size 1 to bool + if(options::bitvectorToBool()) { + dumpAssertions("pre-bv-to-bool", d_assertionsToPreprocess); + Chat() << "...doing bvToBool..." << endl; + bvToBool(); + dumpAssertions("post-bv-to-bool", d_assertionsToPreprocess); + } + if( d_smt.d_logic.isTheoryEnabled(THEORY_STRINGS) ) { dumpAssertions("pre-strings-pp", d_assertionsToPreprocess); CVC4::theory::strings::StringsPreprocess sp; @@ -2957,12 +3032,6 @@ void SmtEnginePrivate::processAssertions() { } dumpAssertions("post-static-learning", d_assertionsToCheck); - // Lift bit-vectors of size 1 to bool - if(options::bvToBool()) { - Chat() << "...doing bvToBool..." << endl; - bvToBool(); - } - Trace("smt") << "POST bvToBool" << endl; Debug("smt") << " d_assertionsToPreprocess: " << d_assertionsToPreprocess.size() << endl; Debug("smt") << " d_assertionsToCheck : " << d_assertionsToCheck.size() << endl; @@ -3086,6 +3155,15 @@ void SmtEnginePrivate::processAssertions() { } dumpAssertions("post-theory-preprocessing", d_assertionsToCheck); + // If we are using eager bit-blasting wrap assertions in fake atom so that + // everything gets bit-blasted to internal SAT solver + if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { + for (unsigned i = 0; i < d_assertionsToCheck.size(); ++i) { + Node eager_atom = NodeManager::currentNM()->mkNode(kind::BITVECTOR_EAGER_ATOM, d_assertionsToCheck[i]); + d_assertionsToCheck[i] = eager_atom; + } + } + // Push the formula to decision engine if(noConflict) { Chat() << "pushing to decision engine..." << endl; @@ -3099,7 +3177,6 @@ void SmtEnginePrivate::processAssertions() { dumpAssertions("post-everything", d_assertionsToCheck); - // Push the formula to SAT { Chat() << "converting to CNF..." << endl; TimerStat::CodeTimer codeTimer(d_smt.d_stats->d_cnfConversionTime); @@ -3107,6 +3184,7 @@ void SmtEnginePrivate::processAssertions() { d_smt.d_propEngine->assertFormula(d_assertionsToCheck[i]); } } + d_assertionsProcessed = true; @@ -3380,6 +3458,7 @@ Expr SmtEngine::expandDefinitions(const Expr& ex) throw(TypeCheckingException, L hash_map cache; Node n = d_private->expandDefinitions(Node::fromExpr(e), cache); n = postprocess(n, TypeNode::fromType(e.getType())); + return n.toExpr(); } diff --git a/src/theory/bv/abstraction.cpp b/src/theory/bv/abstraction.cpp new file mode 100644 index 000000000..3bff9fc95 --- /dev/null +++ b/src/theory/bv/abstraction.cpp @@ -0,0 +1,1060 @@ +/********************* */ +/*! \file abstraction.cpp +** \verbatim +** Original author: Liana Hadarean +** Major contributors: none. +** Minor contributors (to current version): none. +** This file is part of the CVC4 project. +** Copyright (c) 2009-2013 New York University and The University of Iowa +** See the file COPYING in the top-level source directory for licensing +** information.\endverbatim +** +** \brief [[ Add one-line brief description here ]] +** +** [[ Add lengthier description here ]] +** \todo document this file +**/ + +#include "theory/bv/abstraction.h" +#include "theory/bv/theory_bv_utils.h" +#include "theory/rewriter.h" +#include "theory/bv/options.h" +#include "util/dump.h" + +using namespace CVC4; +using namespace CVC4::theory; +using namespace CVC4::theory::bv; +using namespace CVC4::context; + +using namespace std; +using namespace CVC4::theory::bv::utils; + +bool AbstractionModule::applyAbstraction(const std::vector& assertions, std::vector& new_assertions) { + Debug("bv-abstraction") << "AbstractionModule::applyAbstraction\n"; + + TimerStat::CodeTimer abstractionTimer(d_statistics.d_abstractionTime); + + for (unsigned i = 0; i < assertions.size(); ++i) { + if (assertions[i].getKind() == kind::OR) { + for (unsigned j = 0; j < assertions[i].getNumChildren(); ++j) { + if (!isConjunctionOfAtoms(assertions[i][j])) { + continue; + } + Node signature = computeSignature(assertions[i][j]); + storeSignature(signature, assertions[i][j]); + Debug("bv-abstraction") << " assertion: " << assertions[i][j] <<"\n"; + Debug("bv-abstraction") << " signature: " << signature <<"\n"; + } + } + } + finalizeSignatures(); + + for (unsigned i = 0; i < assertions.size(); ++i) { + if (assertions[i].getKind() == kind::OR && + assertions[i][0].getKind() == kind::AND) { + std::vector new_children; + for (unsigned j = 0; j < assertions[i].getNumChildren(); ++j) { + if (hasSignature(assertions[i][j])) { + new_children.push_back(abstractSignatures(assertions[i][j])); + } else { + new_children.push_back(assertions[i][j]); + } + } + new_assertions.push_back(utils::mkNode(kind::OR, new_children)); + } else { + // assertions that are not changed + new_assertions.push_back(assertions[i]); + } + } + + if (options::skolemizeArguments()) { + skolemizeArguments(new_assertions); + } + + + // if we are using the eager solver reverse the abstraction + if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { + if (d_funcToSignature.size() == 0) { + // we did not change anything + return false; + } + NodeNodeMap seen; + for (unsigned i = 0; i < new_assertions.size(); ++i) { + new_assertions[i] = reverseAbstraction(new_assertions[i], seen); + } + // we undo the abstraction functions so the logic is QF_BV still + return true; + } + + // return true if we have created new function symbols for the problem + return d_funcToSignature.size() != 0; +} + + +bool AbstractionModule::isConjunctionOfAtoms(TNode node) { + TNodeSet seen; + return isConjunctionOfAtomsRec(node, seen); +} + +bool AbstractionModule::isConjunctionOfAtomsRec(TNode node, TNodeSet& seen) { + if (seen.find(node)!= seen.end()) + return true; + + if (!node.getType().isBitVector()) { + return (node.getKind() == kind::AND || utils::isBVPredicate(node)); + } + + if (node.getNumChildren() == 0) + return true; + + for (unsigned i = 0; i < node.getNumChildren(); ++i) { + if (!isConjunctionOfAtomsRec(node[i], seen)) + return false; + } + seen.insert(node); + return true; +} + + +Node AbstractionModule::reverseAbstraction(Node assertion, NodeNodeMap& seen) { + + if (seen.find(assertion) != seen.end()) + return seen[assertion]; + + if (isAbstraction(assertion)) { + Node interp = getInterpretation(assertion); + seen[assertion] = interp; + Assert (interp.getType() == assertion.getType()); + return interp; + } + + if (assertion.getNumChildren() == 0) { + seen[assertion] = assertion; + return assertion; + } + + NodeBuilder<> result(assertion.getKind()); + if (assertion.getMetaKind() == kind::metakind::PARAMETERIZED) { + result << assertion.getOperator(); + } + + for (unsigned i = 0; i < assertion.getNumChildren(); ++i) { + result << reverseAbstraction(assertion[i], seen); + } + Node res = result; + seen[assertion] = res; + return res; +} + +void AbstractionModule::skolemizeArguments(std::vector& assertions) { + for (unsigned i = 0; i < assertions.size(); ++i) { + TNode assertion = assertions[i]; + if (assertion.getKind() != kind::OR) + continue; + + bool is_skolemizable = true; + for (unsigned k = 0; k < assertion.getNumChildren(); ++k) { + if (assertion[k].getKind() != kind::EQUAL || + assertion[k][0].getKind() != kind::APPLY_UF || + assertion[k][1].getKind() != kind::CONST_BITVECTOR || + assertion[k][1].getConst() != BitVector(1, 1u)) { + is_skolemizable = false; + break; + } + } + + if (!is_skolemizable) + continue; + + ArgsTable assertion_table; + + // collect function symbols and their arguments + for (unsigned j = 0; j < assertion.getNumChildren(); ++j) { + TNode current = assertion[j]; + Assert (current.getKind() == kind::EQUAL && + current[0].getKind() == kind::APPLY_UF); + TNode func = current[0]; + ArgsVec args; + for (unsigned k = 0; k < func.getNumChildren(); ++k) { + args.push_back(func[k]); + } + assertion_table.addEntry(func.getOperator(), args); + } + + NodeBuilder<> assertion_builder (kind::OR); + // construct skolemized assertion + for (ArgsTable::iterator it = assertion_table.begin(); it != assertion_table.end(); ++it) { + // for each function symbol + ++(d_statistics.d_numArgsSkolemized); + TNode func = it->first; + ArgsTableEntry& args = it->second; + NodeBuilder<> skolem_func (kind::APPLY_UF); + skolem_func << func; + std::vector skolem_args; + + for (unsigned j = 0; j < args.getArity(); ++j) { + bool all_same = true; + for (unsigned k = 1; k < args.getNumEntries(); ++k) { + if ( args.getEntry(k)[j] != args.getEntry(0)[j]) + all_same = false; + } + Node new_arg = all_same ? (Node)args.getEntry(0)[j] : utils::mkVar(utils::getSize(args.getEntry(0)[j])); + skolem_args.push_back(new_arg); + skolem_func << new_arg; + } + + + Node skolem_func_eq1 = utils::mkNode(kind::EQUAL, (Node)skolem_func, utils::mkConst(1, 1u)); + + // enumerate arguments assignments + std::vector or_assignments; + for (ArgsTableEntry::iterator it = args.begin(); it != args.end(); ++it) { + NodeBuilder<> arg_assignment(kind::AND); + ArgsVec& args = *it; + for (unsigned k = 0; k < args.size(); ++k) { + Node eq = utils::mkNode(kind::EQUAL, args[k], skolem_args[k]); + arg_assignment << eq; + } + or_assignments.push_back(arg_assignment); + } + + Node new_func_def = utils::mkNode(kind::AND, skolem_func_eq1, utils::mkNode(kind::OR, or_assignments)); + assertion_builder << new_func_def; + } + Node new_assertion = assertion_builder; + Debug("bv-abstraction-dbg") << "AbstractionModule::skolemizeArguments " << assertions[i] << " => \n"; + Debug("bv-abstraction-dbg") << " " << new_assertion; + assertions[i] = new_assertion; + } +} + +void AbstractionModule::storeSignature(Node signature, TNode assertion) { + if(d_signatures.find(signature) == d_signatures.end()) { + d_signatures[signature] = 0; + } + d_signatures[signature] = d_signatures[signature] + 1; + d_assertionToSignature[assertion] = signature; +} + +Node AbstractionModule::computeSignature(TNode node) { + resetSignatureIndex(); + NodeNodeMap cache; + Node sig = computeSignatureRec(node, cache); + return sig; +} + +Node AbstractionModule::getSignatureSkolem(TNode node) { + Assert (node.getKind() == kind::VARIABLE); + unsigned bitwidth = utils::getSize(node); + if (d_signatureSkolems.find(bitwidth) == d_signatureSkolems.end()) { + d_signatureSkolems[bitwidth] = vector(); + } + + vector& skolems = d_signatureSkolems[bitwidth]; + // get the index of bv variables of this size + unsigned index = getBitwidthIndex(bitwidth); + Assert (skolems.size() + 1 >= index ); + if (skolems.size() == index) { + ostringstream os; + os << "sig_" <mkSkolem(os.str(), nm->mkBitVectorType(bitwidth), "skolem for computing signatures")); + } + ++(d_signatureIndices[bitwidth]); + return skolems[index]; +} + +unsigned AbstractionModule::getBitwidthIndex(unsigned bitwidth) { + if (d_signatureIndices.find(bitwidth) == d_signatureIndices.end()) { + d_signatureIndices[bitwidth] = 0; + } + return d_signatureIndices[bitwidth]; +} + +void AbstractionModule::resetSignatureIndex() { + for (IndexMap::iterator it = d_signatureIndices.begin(); it != d_signatureIndices.end(); ++it) { + it->second = 0; + } +} + +bool AbstractionModule::hasSignature(Node node) { + return d_assertionToSignature.find(node) != d_assertionToSignature.end(); +} + +Node AbstractionModule::getGeneralizedSignature(Node node) { + NodeNodeMap::const_iterator it = d_assertionToSignature.find(node); + Assert (it != d_assertionToSignature.end()); + Node generalized_signature = getGeneralization(it->second); + return generalized_signature; +} + +Node AbstractionModule::computeSignatureRec(TNode node, NodeNodeMap& cache) { + if (cache.find(node) != cache.end()) { + return cache.find(node)->second; + } + + if (node.getNumChildren() == 0) { + if (node.getKind() == kind::CONST_BITVECTOR) + return node; + + Node sig = getSignatureSkolem(node); + cache[node] = sig; + return sig; + } + + NodeBuilder<> builder(node.getKind()); + if (node.getMetaKind() == kind::metakind::PARAMETERIZED) { + builder << node.getOperator(); + } + for (unsigned i = 0; i < node.getNumChildren(); ++i) { + Node converted = computeSignatureRec(node[i], cache); + builder << converted; + } + Node result = builder; + cache[node] = result; + return result; +} + +/** + * Returns 0, if the two are equal, + * 1 if s is a generalization of t + * 2 if t is a generalization of s + * -1 if the two cannot be unified + * + * @param s + * @param t + * + * @return + */ +int AbstractionModule::comparePatterns(TNode s, TNode t) { + if (s.getKind() == kind::SKOLEM && + t.getKind() == kind::SKOLEM) { + return 0; + } + + if (s.getKind() == kind::CONST_BITVECTOR && + t.getKind() == kind::CONST_BITVECTOR) { + if (s == t) { + return 0; + } else { + return -1; + } + } + + if (s.getKind() == kind::SKOLEM && + t.getKind() == kind::CONST_BITVECTOR) { + return 1; + } + + if (s.getKind() == kind::CONST_BITVECTOR && + t.getKind() == kind::SKOLEM) { + return 2; + } + + if (s.getNumChildren() != t.getNumChildren() || + s.getKind() != t.getKind()) + return -1; + + int unify = 0; + for (unsigned i = 0; i < s.getNumChildren(); ++i) { + int unify_i = comparePatterns(s[i], t[i]); + if (unify_i < 0) + return -1; + if (unify == 0) { + unify = unify_i; + } else if (unify != unify_i && unify_i != 0) { + return -1; + } + } + return unify; +} + +TNode AbstractionModule::getGeneralization(TNode term) { + NodeNodeMap::iterator it = d_sigToGeneralization.find(term); + // if not in the map we add it + if (it == d_sigToGeneralization.end()) { + d_sigToGeneralization[term] = term; + return term; + } + // doesn't have a generalization + if (it->second == term) + return term; + + TNode generalization = getGeneralization(it->second); + Assert (generalization != term); + d_sigToGeneralization[term] = generalization; + return generalization; +} + +void AbstractionModule::storeGeneralization(TNode s, TNode t) { + Assert (s == getGeneralization(s)); + Assert (t == getGeneralization(t)); + d_sigToGeneralization[s] = t; +} + +void AbstractionModule::finalizeSignatures() { + NodeManager* nm = NodeManager::currentNM(); + Debug("bv-abstraction") << "AbstractionModule::finalizeSignatures num signatures = " << d_signatures.size() <<"\n"; + TNodeSet new_signatures; + + // "unify" signatures + for (SignatureMap::const_iterator ss = d_signatures.begin(); ss != d_signatures.end(); ++ss) { + for (SignatureMap::const_iterator tt = ss; tt != d_signatures.end(); ++tt) { + TNode t = getGeneralization(tt->first); + TNode s = getGeneralization(ss->first); + + if (t != s) { + int status = comparePatterns(s, t); + Assert (status); + if (status < 0) + continue; + if (status == 1) { + storeGeneralization(t, s); + } else { + storeGeneralization(s, t); + } + } + } + } + // keep only most general signatures + for (SignatureMap::iterator it = d_signatures.begin(); it != d_signatures.end(); ) { + TNode sig = it->first; + TNode gen = getGeneralization(sig); + if (sig != gen) { + Assert (d_signatures.find(gen) != d_signatures.end()); + // update the count + d_signatures[gen]+= d_signatures[sig]; + d_signatures.erase(it++); + } else { + ++it; + } + } + + + // remove signatures that are not frequent enough + for (SignatureMap::iterator it = d_signatures.begin(); it != d_signatures.end(); ) { + if (it->second <= 7) { + d_signatures.erase(it++); + } else { + ++it; + } + } + + for (SignatureMap::const_iterator it = d_signatures.begin(); it != d_signatures.end(); ++it) { + TNode signature = it->first; + // we already processed this signature + Assert (d_signatureToFunc.find(signature) == d_signatureToFunc.end()); + + Debug("bv-abstraction") << "Processing signature " << signature << " count " << it->second << "\n"; + std::vector arg_types; + TNodeSet seen; + collectArgumentTypes(signature, arg_types, seen); + Assert (signature.getType().isBoolean()); + // make function return a bitvector of size 1 + //Node bv_function = utils::mkNode(kind::ITE, signature, utils::mkConst(1, 1u), utils::mkConst(1, 0u)); + TypeNode range = NodeManager::currentNM()->mkBitVectorType(1); + + TypeNode abs_type = nm->mkFunctionType(arg_types, range); + Node abs_func = nm->mkSkolem("abs_$$", abs_type, "abstraction function for bv theory"); + Debug("bv-abstraction") << " abstracted by function " << abs_func << "\n"; + + // NOTE: signature expression type is BOOLEAN + d_signatureToFunc[signature] = abs_func; + d_funcToSignature[abs_func] = signature; + } + + d_statistics.d_numFunctionsAbstracted.setData(d_signatureToFunc.size()); + + Debug("bv-abstraction") << "AbstractionModule::finalizeSignatures abstracted " << d_signatureToFunc.size() << " signatures. \n"; +} + +void AbstractionModule::collectArgumentTypes(TNode sig, std::vector& types, TNodeSet& seen) { + if (seen.find(sig) != seen.end()) + return; + + if (sig.getKind() == kind::SKOLEM) { + types.push_back(sig.getType()); + seen.insert(sig); + return; + } + + for (unsigned i = 0; i < sig.getNumChildren(); ++i) { + collectArgumentTypes(sig[i], types, seen); + seen.insert(sig); + } +} + +void AbstractionModule::collectArguments(TNode node, TNode signature, std::vector& args, TNodeSet& seen) { + if (seen.find(node)!= seen.end()) + return; + + if (node.getKind() == kind::VARIABLE || + node.getKind() == kind::CONST_BITVECTOR) { + // a constant in the node can either map to an argument of the abstraction + // or can be hard-coded and part of the abstraction + if (signature.getKind() == kind::SKOLEM) { + args.push_back(node); + seen.insert(node); + } else { + Assert (signature.getKind() == kind::CONST_BITVECTOR); + } + // + return; + } + Assert (node.getKind() == signature.getKind() && + node.getNumChildren() == signature.getNumChildren()); + + for (unsigned i = 0; i < node.getNumChildren(); ++i) { + collectArguments(node[i], signature[i], args, seen); + seen.insert(node); + } +} + + +Node AbstractionModule::abstractSignatures(TNode assertion) { + Debug("bv-abstraction") << "AbstractionModule::abstractSignatures "<< assertion <<"\n"; + // assume the assertion has been fully abstracted + Node signature = getGeneralizedSignature(assertion); + + Debug("bv-abstraction") << " with sig "<< signature <<"\n"; + NodeNodeMap::iterator it = d_signatureToFunc.find(signature); + if (it!= d_signatureToFunc.end()) { + std::vector args; + TNode func = it->second; + // pushing the function symbol + args.push_back(func); + TNodeSet seen; + collectArguments(assertion, signature, args, seen); + std::vector real_args; + for (unsigned i = 1; i < args.size(); ++i) { + real_args.push_back(args[i]); + } + d_argsTable.addEntry(func, real_args); + Node result = utils::mkNode(kind::EQUAL, utils::mkNode(kind::APPLY_UF, args), + utils::mkConst(1, 1u)); + Debug("bv-abstraction") << "=> "<< result << "\n"; + Assert (result.getType() == assertion.getType()); + return result; + } + return assertion; +} + +bool AbstractionModule::isAbstraction(TNode node) { + if (node.getKind() != kind::EQUAL) + return false; + if ((node[0].getKind() != kind::CONST_BITVECTOR || + node[1].getKind() != kind::APPLY_UF) && + (node[1].getKind() != kind::CONST_BITVECTOR || + node[0].getKind() != kind::APPLY_UF)) + return false; + + TNode constant = node[0].getKind() == kind::CONST_BITVECTOR ? node[0] : node[1]; + TNode func = node[0].getKind() == kind::APPLY_UF ? node[0] : node[1]; + Assert (constant.getKind() == kind::CONST_BITVECTOR && + func.getKind() == kind::APPLY_UF); + if (utils::getSize(constant) != 1) + return false; + if (constant != utils::mkConst(1, 1u)) + return false; + + TNode func_symbol = func.getOperator(); + if (d_funcToSignature.find(func_symbol) == d_funcToSignature.end()) + return false; + + return true; +} + +Node AbstractionModule::getInterpretation(TNode node) { + Assert (isAbstraction(node)); + TNode constant = node[0].getKind() == kind::CONST_BITVECTOR ? node[0] : node[1]; + TNode apply = node[0].getKind() == kind::APPLY_UF ? node[0] : node[1]; + Assert (constant.getKind() == kind::CONST_BITVECTOR && + apply.getKind() == kind::APPLY_UF); + + Node func = apply.getOperator(); + Assert (d_funcToSignature.find(func) != d_funcToSignature.end()); + + Node sig = d_funcToSignature[func]; + + // substitute arguments in signature + TNodeTNodeMap seen; + unsigned index = 0; + Node result = substituteArguments(sig, apply, index, seen); + Assert (result.getType().isBoolean()); + Assert (index == apply.getNumChildren()); + // Debug("bv-abstraction") << "AbstractionModule::getInterpretation " << node << "\n"; + // Debug("bv-abstraction") << " => " << result << "\n"; + return result; +} + +Node AbstractionModule::substituteArguments(TNode signature, TNode apply, unsigned& index, TNodeTNodeMap& seen) { + if (seen.find(signature) != seen.end()) { + return seen[signature]; + } + + if (signature.getKind() == kind::SKOLEM) { + // return corresponding argument and increment counter + seen[signature] = apply[index]; + return apply[index++]; + } + + if (signature.getNumChildren() == 0) { + Assert (signature.getKind() != kind::VARIABLE && + signature.getKind() != kind::SKOLEM); + seen[signature] = signature; + return signature; + } + + NodeBuilder<> builder(signature.getKind()); + if (signature.getMetaKind() == kind::metakind::PARAMETERIZED) { + builder << signature.getOperator(); + } + + for (unsigned i = 0; i < signature.getNumChildren(); ++i) { + Node child = substituteArguments(signature[i], apply, index, seen); + builder << child; + } + + Node result = builder; + seen[signature]= result; + + return result; +} + +Node AbstractionModule::simplifyConflict(TNode conflict) { + if (Dump.isOn("bv-abstraction")) { + NodeNodeMap seen; + Node c = reverseAbstraction(conflict, seen); + Dump("bv-abstraction") << PushCommand(); + Dump("bv-abstraction") << AssertCommand(c.toExpr()); + Dump("bv-abstraction") << CheckSatCommand(); + Dump("bv-abstraction") << PopCommand(); + } + + Debug("bv-abstraction-dbg") << "AbstractionModule::simplifyConflict " << conflict << "\n"; + if (conflict.getKind() != kind::AND) + return conflict; + + std::vector conjuncts; + for (unsigned i = 0; i < conflict.getNumChildren(); ++i) + conjuncts.push_back(conflict[i]); + + theory::SubstitutionMap subst(new context::Context()); + for (unsigned i = 0; i < conjuncts.size(); ++i) { + TNode conjunct = conjuncts[i]; + // substitute s -> t + Node s, t; + + if (conjunct.getKind() == kind::EQUAL) { + if (conjunct[0].getMetaKind() == kind::metakind::VARIABLE && + conjunct[1].getKind() == kind::CONST_BITVECTOR) { + s = conjunct[0]; + t = conjunct[1]; + } + else if (conjunct[1].getMetaKind() == kind::metakind::VARIABLE && + conjunct[0].getKind() == kind::CONST_BITVECTOR) { + s = conjunct[1]; + t = conjunct[0]; + } else { + continue; + } + + Assert (!subst.hasSubstitution(s)); + Assert (!t.isNull() && + !s.isNull() && + s!= t); + subst.addSubstitution(s, t); + + for (unsigned k = 0; k < conjuncts.size(); k++) { + conjuncts[k] = subst.apply(conjuncts[k]); + } + } + } + Node new_conflict = Rewriter::rewrite(utils::mkAnd(conjuncts)); + + Debug("bv-abstraction") << "AbstractionModule::simplifyConflict conflict " << conflict <<"\n"; + Debug("bv-abstraction") << " => " << new_conflict <<"\n"; + + if (Dump.isOn("bv-abstraction")) { + + NodeNodeMap seen; + Node nc = reverseAbstraction(new_conflict, seen); + Dump("bv-abstraction") << PushCommand(); + Dump("bv-abstraction") << AssertCommand(nc.toExpr()); + Dump("bv-abstraction") << CheckSatCommand(); + Dump("bv-abstraction") << PopCommand(); + } + + return new_conflict; +} + + +void DebugPrintInstantiations(const std::vector< std::vector >& instantiations, + const std::vector functions) { + // print header + Debug("bv-abstraction-dbg") <<"[ "; + for (unsigned i = 0; i < functions.size(); ++i) { + for (unsigned j = 1; j < functions[i].getNumChildren(); ++j) { + Debug("bv-abstraction-dgb") << functions[i][j] <<" "; + } + Debug("bv-abstraction-dgb") << " || "; + } + Debug("bv-abstraction-dbg") <<"]\n"; + + for (unsigned i = 0; i < instantiations.size(); ++i) { + Debug("bv-abstraction-dbg") <<"["; + const std::vector& inst = instantiations[i]; + for (unsigned j = 0; j < inst.size(); ++j) { + for (unsigned k = 0; k < inst[j].size(); ++k) { + Debug("bv-abstraction-dbg") << inst[j][k] << " "; + } + Debug("bv-abstraction-dbg") << " || "; + } + Debug("bv-abstraction-dbg") <<"]\n"; + } +} + +void AbstractionModule::generalizeConflict(TNode conflict, std::vector& lemmas) { + Debug("bv-abstraction") << "AbstractionModule::generalizeConflict " << conflict << "\n"; + std::vector functions; + + // collect abstract functions + if (conflict.getKind() != kind::AND) { + if (isAbstraction(conflict)) { + Assert (conflict[0].getKind() == kind::APPLY_UF); + functions.push_back(conflict[0]); + } + } else { + for (unsigned i = 0; i < conflict.getNumChildren(); ++i) { + TNode conjunct = conflict[i]; + if (isAbstraction(conjunct)) { + Assert (conjunct[0].getKind() == kind::APPLY_UF); + functions.push_back(conjunct[0]); + } + } + } + + // if (functions.size() >= 3) { + // // dump conflict + // NodeNodeMap seen; + // Node reversed = reverseAbstraction(conflict, seen); + // std::cout << "CONFLICT " << reversed << "\n"; + // } + + + if (functions.size() == 0 || functions.size() > options::bvNumFunc()) { + return; + } + + + // Skolemize function arguments to avoid confusion in pattern matching + SubstitutionMap skolem_subst(new context::Context()); + SubstitutionMap reverse_skolem(new context::Context()); + makeFreshSkolems(conflict, skolem_subst, reverse_skolem); + + Node skolemized_conflict = skolem_subst.apply(conflict); + for (unsigned i = 0; i < functions.size(); ++i) { + functions[i] = skolem_subst.apply(functions[i]); + } + + conflict = skolem_subst.apply(conflict); + + LemmaInstantiatior inst(functions, d_argsTable, conflict); + std::vector new_lemmas; + inst.generateInstantiations(new_lemmas); + for (unsigned i = 0; i < new_lemmas.size(); ++i) { + TNode lemma = reverse_skolem.apply(new_lemmas[i]); + if (d_addedLemmas.find(lemma) == d_addedLemmas.end()) { + lemmas.push_back(lemma); + Debug("bv-abstraction-gen") << "adding lemma " << lemma << "\n"; + storeLemma(lemma); + + if (Dump.isOn("bv-abstraction")) { + NodeNodeMap seen; + Node l = reverseAbstraction(lemma, seen); + Dump("bv-abstraction") << PushCommand(); + Dump("bv-abstraction") << AssertCommand(l.toExpr()); + Dump("bv-abstraction") << CheckSatCommand(); + Dump("bv-abstraction") << PopCommand(); + } + } + } +} + +int AbstractionModule::LemmaInstantiatior::next(int val, int index) { + if (val < d_maxMatch[index] - 1) + return val + 1; + return -1; +} + +/** + * Assumes the stack without top is consistent, and checks that the + * full stack is consistent + * + * @param stack + * + * @return + */ +bool AbstractionModule::LemmaInstantiatior::isConsistent(const vector& stack) { + if (stack.empty()) + return true; + + unsigned current = stack.size() - 1; + TNode func = d_functions[current]; + ArgsTableEntry& matches = d_argsTable.getEntry(func.getOperator()); + ArgsVec& args = matches.getEntry(stack[current]); + Assert (args.size() == func.getNumChildren()); + for (unsigned k = 0; k < args.size(); ++k) { + TNode s = func[k]; + TNode t = args[k]; + + TNode s0 = s; + while (d_subst.hasSubstitution(s0)) { + s0 = d_subst.getSubstitution(s0); + } + + TNode t0 = t; + while (d_subst.hasSubstitution(t0)) { + t0 = d_subst.getSubstitution(t0); + } + + if (s0.isConst() && t0.isConst()) { + if (s0 != t0) + return false; // fail + else + continue; + } + + if(s0.getMetaKind() == kind::metakind::VARIABLE && + t0.isConst()) { + d_subst.addSubstitution(s0, t0); + continue; + } + + if (s0.isConst() && + t0.getMetaKind() == kind::metakind::VARIABLE) { + d_subst.addSubstitution(t0, s0); + continue; + } + + Assert (s0.getMetaKind() == kind::metakind::VARIABLE && + t0.getMetaKind() == kind::metakind::VARIABLE); + + if (s0 != t0) { + d_subst.addSubstitution(s0, t0); + } + } + return true; +} + +bool AbstractionModule::LemmaInstantiatior::accept(const vector& stack) { + return stack.size() == d_functions.size(); +} + +void AbstractionModule::LemmaInstantiatior::mkLemma() { + Node lemma = d_subst.apply(d_conflict); + // Debug("bv-abstraction-gen") << "AbstractionModule::LemmaInstantiatior::mkLemma " << lemma <<"\n"; + d_lemmas.push_back(lemma); +} + +void AbstractionModule::LemmaInstantiatior::backtrack(vector& stack) { + if (!isConsistent(stack)) + return; + + if (accept(stack)) { + mkLemma(); + return; + } + + int x = 0; + while (x != -1) { + d_ctx->push(); + stack.push_back(x); + backtrack(stack); + + d_ctx->pop(); + stack.pop_back(); + x = next(x, stack.size()); + } +} + + +void AbstractionModule::LemmaInstantiatior::generateInstantiations(std::vector& lemmas) { + Debug("bv-abstraction-gen") << "AbstractionModule::LemmaInstantiatior::generateInstantiations "; + + std::vector stack; + backtrack(stack); + Assert (d_ctx->getLevel() == 0); + Debug("bv-abstraction-gen") << "numLemmas=" << d_lemmas.size() <<"\n"; + lemmas.swap(d_lemmas); +} + +void AbstractionModule::makeFreshSkolems(TNode node, SubstitutionMap& map, SubstitutionMap& reverse_map) { + if (map.hasSubstitution(node)) { + return; + } + if (node.getMetaKind() == kind::metakind::VARIABLE) { + Node skolem = utils::mkVar(utils::getSize(node)); + map.addSubstitution(node, skolem); + reverse_map.addSubstitution(skolem, node); + return; + } + if (node.isConst()) + return; + + for (unsigned i = 0; i < node.getNumChildren(); ++i) { + makeFreshSkolems(node[i], map, reverse_map); + } +} + +void AbstractionModule::makeFreshArgs(TNode func, std::vector& fresh_args) { + Assert (fresh_args.size() == 0); + Assert (func.getKind() == kind::APPLY_UF); + TNodeNodeMap d_map; + for (unsigned i = 0; i < func.getNumChildren(); ++i) { + TNode arg = func[i]; + if (arg.isConst()) { + fresh_args.push_back(arg); + continue; + } + Assert (arg.getMetaKind() == kind::metakind::VARIABLE); + TNodeNodeMap::iterator it = d_map.find(arg); + if (it != d_map.end()) { + fresh_args.push_back(it->second); + } else { + Node skolem = utils::mkVar(utils::getSize(arg)); + d_map[arg] = skolem; + fresh_args.push_back(skolem); + } + } + Assert (fresh_args.size() == func.getNumChildren()); +} + +Node AbstractionModule::tryMatching(const std::vector& ss, const std::vector& tt, TNode conflict) { + Assert (ss.size() == tt.size()); + + Debug("bv-abstraction-dbg") << "AbstractionModule::tryMatching conflict = " << conflict << "\n"; + if (Debug.isOn("bv-abstraction-dbg")) { + Debug("bv-abstraction-dbg") << " Match: "; + for (unsigned i = 0; i < ss.size(); ++i) { + Debug("bv-abstraction-dbg") << ss[i] <<" "; + + } + Debug("bv-abstraction-dbg") << "\n To: "; + for (unsigned i = 0; i < tt.size(); ++i) { + Debug("bv-abstraction-dbg") << tt[i] <<" "; + } + Debug("bv-abstraction-dbg") <<"\n"; + } + + + SubstitutionMap subst(new context::Context()); + + for (unsigned i = 0; i < ss.size(); ++i) { + TNode s = ss[i]; + TNode t = tt[i]; + + TNode s0 = subst.hasSubstitution(s) ? subst.getSubstitution(s) : s; + TNode t0 = subst.hasSubstitution(t) ? subst.getSubstitution(t) : t; + + if (s0.isConst() && t0.isConst()) { + if (s0 != t0) + return Node(); // fail + else + continue; + } + + if(s0.getMetaKind() == kind::metakind::VARIABLE && + t0.isConst()) { + subst.addSubstitution(s0, t0); + continue; + } + + if (s0.isConst() && + t0.getMetaKind() == kind::metakind::VARIABLE) { + subst.addSubstitution(t0, s0); + continue; + } + + Assert (s0.getMetaKind() == kind::metakind::VARIABLE && + t0.getMetaKind() == kind::metakind::VARIABLE); + + Assert (s0 != t0); + subst.addSubstitution(s0, t0); + } + + Node res = subst.apply(conflict); + Debug("bv-abstraction-dbg") << " Lemma: " << res <<"\n"; + return res; +} + +void AbstractionModule::storeLemma(TNode lemma) { + d_addedLemmas.insert(lemma); + if (lemma.getKind() == kind::AND) { + for (unsigned i = 0; i < lemma.getNumChildren(); i++) { + TNode atom = lemma[i]; + atom = atom.getKind() == kind::NOT ? atom[0] : atom; + Assert (atom.getKind() != kind::NOT); + Assert (utils::isBVPredicate(atom)); + d_lemmaAtoms.insert(atom); + } + } else { + lemma = lemma.getKind() == kind::NOT? lemma[0] : lemma; + Assert (utils::isBVPredicate(lemma)); + d_lemmaAtoms.insert(lemma); + } +} + + +bool AbstractionModule::isLemmaAtom(TNode node) const { + Assert (node.getType().isBoolean()); + node = node.getKind() == kind::NOT? node[0] : node; + + return d_inputAtoms.find(node) == d_inputAtoms.end() && + d_lemmaAtoms.find(node) != d_lemmaAtoms.end(); +} + +void AbstractionModule::addInputAtom(TNode atom) { + if (options::bitblastMode() == theory::bv::BITBLAST_MODE_LAZY) { + d_inputAtoms.insert(atom); + } +} + +void AbstractionModule::ArgsTableEntry::addArguments(const ArgsVec& args) { + Assert (args.size() == d_arity); + d_data.push_back(args); +} + +void AbstractionModule::ArgsTable::addEntry(TNode signature, const ArgsVec& args) { + if (d_data.find(signature) == d_data.end()) { + d_data[signature] = ArgsTableEntry(args.size()); + } + ArgsTableEntry& entry = d_data[signature]; + entry.addArguments(args); +} + + +bool AbstractionModule::ArgsTable::hasEntry(TNode signature) const { + return d_data.find(signature) != d_data.end(); +} + +AbstractionModule::ArgsTableEntry& AbstractionModule::ArgsTable::getEntry(TNode signature) { + Assert (hasEntry(signature)); + return d_data.find(signature)->second; +} + +AbstractionModule::Statistics::Statistics() + : d_numFunctionsAbstracted("theory::bv::AbstractioModule::NumFunctionsAbstracted", 0) + , d_numArgsSkolemized("theory::bv::AbstractioModule::NumArgsSkolemized", 0) + , d_abstractionTime("theory::bv::AbstractioModule::AbstractionTime") +{ + StatisticsRegistry::registerStat(&d_numFunctionsAbstracted); + StatisticsRegistry::registerStat(&d_numArgsSkolemized); + StatisticsRegistry::registerStat(&d_abstractionTime); +} + +AbstractionModule::Statistics::~Statistics() { + StatisticsRegistry::unregisterStat(&d_numFunctionsAbstracted); + StatisticsRegistry::unregisterStat(&d_numArgsSkolemized); + StatisticsRegistry::unregisterStat(&d_abstractionTime); +} diff --git a/src/theory/bv/abstraction.h b/src/theory/bv/abstraction.h new file mode 100644 index 000000000..cd4c443a7 --- /dev/null +++ b/src/theory/bv/abstraction.h @@ -0,0 +1,247 @@ + /********************* */ +/*! \file abstraction.h + ** \verbatim + ** Original author: Liana Hadarean + ** Major contributors: none. + ** Minor contributors (to current version): none. + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2013 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Bitvector theory. + ** + ** Bitvector theory. + **/ + + +#ifndef __CVC4__THEORY__BV__ABSTRACTION_H +#define __CVC4__THEORY__BV__ABSTRACTION_H + +#include "cvc4_private.h" +#include +#include +#include "expr/node.h" +#include "theory/substitutions.h" +#include "util/statistics_registry.h" + +namespace CVC4 { +namespace theory { +namespace bv { + + +typedef std::vector ArgsVec; + +class AbstractionModule { + + struct Statistics { + IntStat d_numFunctionsAbstracted; + IntStat d_numArgsSkolemized; + TimerStat d_abstractionTime; + Statistics(); + ~Statistics(); + }; + + + class ArgsTableEntry { + std::vector d_data; + unsigned d_arity; + public: + ArgsTableEntry(unsigned n) + : d_arity(n) + {} + ArgsTableEntry() + : d_arity(0) + {} + void addArguments(const ArgsVec& args); + typedef std::vector::iterator iterator; + + iterator begin() { return d_data.begin(); } + iterator end() { return d_data.end(); } + unsigned getArity() { return d_arity; } + unsigned getNumEntries() { return d_data.size(); } + ArgsVec& getEntry(unsigned i ) { Assert (i < d_data.size()); return d_data[i]; } + }; + + class ArgsTable { + __gnu_cxx::hash_map d_data; + bool hasEntry(TNode signature) const; + public: + typedef __gnu_cxx::hash_map::iterator iterator; + ArgsTable() {} + void addEntry(TNode signature, const ArgsVec& args); + ArgsTableEntry& getEntry(TNode signature); + iterator begin() { return d_data.begin(); } + iterator end() { return d_data.end(); } + }; + + /** + * Checks if one pattern is a generalization of the other + * + * @param s + * @param t + * + * @return 1 if s :> t, 2 if s <: t, 0 if they equivalent and -1 if they are incomparable + */ + static int comparePatterns(TNode s, TNode t); + + class LemmaInstantiatior { + std::vector d_functions; + std::vector d_maxMatch; + ArgsTable& d_argsTable; + context::Context* d_ctx; + theory::SubstitutionMap d_subst; + TNode d_conflict; + std::vector d_lemmas; + + void backtrack(std::vector& stack); + int next(int val, int index); + bool isConsistent(const std::vector& stack); + bool accept(const std::vector& stack); + void mkLemma(); + public: + LemmaInstantiatior(const std::vector& functions, ArgsTable& table, TNode conflict) + : d_functions(functions) + , d_argsTable(table) + , d_ctx(new context::Context()) + , d_subst(d_ctx) + , d_conflict(conflict) + , d_lemmas() + { + Debug("bv-abstraction-gen") << "LemmaInstantiator conflict:" << conflict << "\n"; + // initializing the search space + for (unsigned i = 0; i < functions.size(); ++i) { + TNode func_op = functions[i].getOperator(); + // number of matches for this function + unsigned maxCount = table.getEntry(func_op).getNumEntries(); + d_maxMatch.push_back(maxCount); + } + } + + void generateInstantiations(std::vector& lemmas); + + }; + + typedef __gnu_cxx::hash_map, NodeHashFunction> NodeVecMap; + typedef __gnu_cxx::hash_map NodeTNodeMap; + typedef __gnu_cxx::hash_map TNodeTNodeMap; + typedef __gnu_cxx::hash_map NodeNodeMap; + typedef __gnu_cxx::hash_map TNodeNodeMap; + typedef __gnu_cxx::hash_set TNodeSet; + typedef __gnu_cxx::hash_map IntNodeMap; + typedef __gnu_cxx::hash_map IndexMap; + typedef __gnu_cxx::hash_map > SkolemMap; + typedef __gnu_cxx::hash_map SignatureMap; + + ArgsTable d_argsTable; + + // mapping between signature and uninterpreted function symbol used to + // abstract the signature + NodeNodeMap d_signatureToFunc; + NodeNodeMap d_funcToSignature; + + NodeNodeMap d_assertionToSignature; + SignatureMap d_signatures; + NodeNodeMap d_sigToGeneralization; + TNodeSet d_skolems; + + // skolems maps + IndexMap d_signatureIndices; + SkolemMap d_signatureSkolems; + + void collectArgumentTypes(TNode sig, std::vector& types, TNodeSet& seen); + void collectArguments(TNode node, TNode sig, std::vector& args, TNodeSet& seen); + void finalizeSignatures(); + Node abstractSignatures(TNode assertion); + Node computeSignature(TNode node); + + bool isConjunctionOfAtoms(TNode node); + bool isConjunctionOfAtomsRec(TNode node, TNodeSet& seen); + + TNode getGeneralization(TNode term); + void storeGeneralization(TNode s, TNode t); + + // signature skolem stuff + Node getGeneralizedSignature(Node node); + Node getSignatureSkolem(TNode node); + + unsigned getBitwidthIndex(unsigned bitwidth); + void resetSignatureIndex(); + Node computeSignatureRec(TNode, NodeNodeMap&); + void storeSignature(Node signature, TNode assertion); + bool hasSignature(Node node); + + Node substituteArguments(TNode signature, TNode apply, unsigned& i, TNodeTNodeMap& seen); + + // crazy instantiation methods + void generateInstantiations(unsigned current, + std::vector& matches, + std::vector >& instantiations, + std::vector >& new_instantiations); + + Node tryMatching(const std::vector& ss, const std::vector& tt, TNode conflict); + void makeFreshArgs(TNode func, std::vector& fresh_args); + void makeFreshSkolems(TNode node, SubstitutionMap& map, SubstitutionMap& reverse_map); + + void skolemizeArguments(std::vector& assertions); + Node reverseAbstraction(Node assertion, NodeNodeMap& seen); + + TNodeSet d_addedLemmas; + TNodeSet d_lemmaAtoms; + TNodeSet d_inputAtoms; + void storeLemma(TNode lemma); + + Statistics d_statistics; + +public: + AbstractionModule() + : d_argsTable() + , d_signatureToFunc() + , d_funcToSignature() + , d_assertionToSignature() + , d_signatures() + , d_sigToGeneralization() + , d_skolems() + , d_signatureIndices() + , d_signatureSkolems() + , d_addedLemmas() + , d_lemmaAtoms() + , d_inputAtoms() + , d_statistics() + {} + /** + * returns true if there are new uninterepreted functions symbols in the output + * + * @param assertions + * @param new_assertions + * + * @return + */ + bool applyAbstraction(const std::vector& assertions, std::vector& new_assertions); + /** + * Returns true if the node represents an abstraction predicate. + * + * @param node + * + * @return + */ + bool isAbstraction(TNode node); + /** + * Returns the interpretation of the abstraction predicate. + * + * @param node + * + * @return + */ + Node getInterpretation(TNode node); + Node simplifyConflict(TNode conflict); + void generalizeConflict(TNode conflict, std::vector& lemmas); + void addInputAtom(TNode atom); + bool isLemmaAtom(TNode node) const; +}; + +} +} +} + +#endif diff --git a/src/theory/bv/aig_bitblaster.h b/src/theory/bv/aig_bitblaster.h new file mode 100644 index 000000000..d1635f950 --- /dev/null +++ b/src/theory/bv/aig_bitblaster.h @@ -0,0 +1,658 @@ +/********************* */ +/*! \file aig_bitblaster.h + ** \verbatim + ** Original author: Liana Hadarean + ** Major contributors: none + ** Minor contributors (to current version): lianah + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2013 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief + ** + ** Bitblaster for the lazy bv solver. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__AIG__BITBLASTER_H +#define __CVC4__AIG__BITBLASTER_H + + +#include "bitblaster_template.h" +#include "prop/cnf_stream.h" +#include "prop/sat_solver_factory.h" +#include "theory/bv/options.h" + + +#ifdef CVC4_USE_ABC +// Function is defined as static in ABC. Not sure how else to do this. +static inline int Cnf_Lit2Var( int Lit ) { return (Lit & 1)? -(Lit >> 1)-1 : (Lit >> 1)+1; } + +extern "C" { +extern Aig_Man_t * Abc_NtkToDar( Abc_Ntk_t * pNtk, int fExors, int fRegisters ); +} + + +namespace CVC4 { +namespace theory { +namespace bv { + +template <> inline +std::string toString (const std::vector& bits) { + Unreachable("Don't know how to print AIG"); +} + + +template <> inline +Abc_Obj_t* mkTrue() { + return Abc_AigConst1(AigBitblaster::currentAigNtk()); +} + +template <> inline +Abc_Obj_t* mkFalse() { + return Abc_ObjNot(mkTrue()); +} + +template <> inline +Abc_Obj_t* mkNot(Abc_Obj_t* a) { + return Abc_ObjNot(a); +} + +template <> inline +Abc_Obj_t* mkOr(Abc_Obj_t* a, Abc_Obj_t* b) { + return Abc_AigOr(AigBitblaster::currentAigM(), a, b); +} + +template <> inline +Abc_Obj_t* mkOr(const std::vector& children) { + Assert (children.size()); + if (children.size() == 1) + return children[0]; + + Abc_Obj_t* result = children[0]; + for (unsigned i = 1; i < children.size(); ++i) { + result = Abc_AigOr(AigBitblaster::currentAigM(), result, children[i]); + } + return result; +} + + +template <> inline +Abc_Obj_t* mkAnd(Abc_Obj_t* a, Abc_Obj_t* b) { + return Abc_AigAnd(AigBitblaster::currentAigM(), a, b); +} + +template <> inline +Abc_Obj_t* mkAnd(const std::vector& children) { + Assert (children.size()); + if (children.size() == 1) + return children[0]; + + Abc_Obj_t* result = children[0]; + for (unsigned i = 1; i < children.size(); ++i) { + result = Abc_AigAnd(AigBitblaster::currentAigM(), result, children[i]); + } + return result; +} + +template <> inline +Abc_Obj_t* mkXor(Abc_Obj_t* a, Abc_Obj_t* b) { + return Abc_AigXor(AigBitblaster::currentAigM(), a, b); +} + +template <> inline +Abc_Obj_t* mkIff(Abc_Obj_t* a, Abc_Obj_t* b) { + return mkNot(mkXor(a, b)); +} + +template <> inline +Abc_Obj_t* mkIte(Abc_Obj_t* cond, Abc_Obj_t* a, Abc_Obj_t* b) { + return Abc_AigMux(AigBitblaster::currentAigM(), cond, a, b); +} + + +Abc_Ntk_t* AigBitblaster::abcAigNetwork = NULL; + +Abc_Ntk_t* AigBitblaster::currentAigNtk() { + if (!AigBitblaster::abcAigNetwork) { + Abc_Start(); + abcAigNetwork = Abc_NtkAlloc( ABC_NTK_STRASH, ABC_FUNC_AIG, 1); + char pName[] = "CVC4::theory::bv::AigNetwork"; + abcAigNetwork->pName = Extra_UtilStrsav(pName); + } + + return abcAigNetwork; +} + + +Abc_Aig_t* AigBitblaster::currentAigM() { + return (Abc_Aig_t*)(currentAigNtk()->pManFunc); +} + +AigBitblaster::AigBitblaster() + : TBitblaster() + , d_aigCache() + , d_bbAtoms() + , d_aigOutputNode(NULL) +{ + d_nullContext = new context::Context(); + d_satSolver = prop::SatSolverFactory::createMinisat(d_nullContext, "AigBitblaster"); + MinisatEmptyNotify* notify = new MinisatEmptyNotify(); + d_satSolver->setNotify(notify); +} + +AigBitblaster::~AigBitblaster() { + Assert (abcAigNetwork == NULL); + delete d_nullContext; +} + +Abc_Obj_t* AigBitblaster::bbFormula(TNode node) { + Assert (node.getType().isBoolean()); + Debug("bitvector-bitblast") << "AigBitblaster::bbFormula "<< node << "\n"; + + if (hasAig(node)) + return getAig(node); + + Abc_Obj_t* result = NULL; + + Debug("bitvector-aig") << "AigBitblaster::convertToAig " << node <<"\n"; + switch (node.getKind()) { + case kind::AND: + { + result = bbFormula(node[0]); + for (unsigned i = 1; i < node.getNumChildren(); ++i) { + Abc_Obj_t* child = bbFormula(node[i]); + result = mkAnd(result, child); + } + break; + } + case kind::OR: + { + result = bbFormula(node[0]); + for (unsigned i = 1; i < node.getNumChildren(); ++i) { + Abc_Obj_t* child = bbFormula(node[i]); + result = mkOr(result, child); + } + break; + } + case kind::IFF: + { + Assert (node.getNumChildren() == 2); + Abc_Obj_t* child1 = bbFormula(node[0]); + Abc_Obj_t* child2 = bbFormula(node[1]); + + result = mkIff(child1, child2); + break; + } + case kind::XOR: + { + result = bbFormula(node[0]); + for (unsigned i = 1; i < node.getNumChildren(); ++i) { + Abc_Obj_t* child = bbFormula(node[i]); + result = mkXor(result, child); + } + break; + } + case kind::IMPLIES: + { + Assert (node.getNumChildren() == 2); + Abc_Obj_t* child1 = bbFormula(node[0]); + Abc_Obj_t* child2 = bbFormula(node[1]); + + result = mkOr(mkNot(child1), child2); + break; + } + case kind::ITE: + { + Assert (node.getNumChildren() == 3); + Abc_Obj_t* a = bbFormula(node[0]); + Abc_Obj_t* b = bbFormula(node[1]); + Abc_Obj_t* c = bbFormula(node[2]); + result = mkIte(a, b, c); + break; + } + case kind::NOT: + { + Abc_Obj_t* child1 = bbFormula(node[0]); + result = mkNot(child1); + break; + } + case kind::CONST_BOOLEAN: + { + result = node.getConst() ? mkTrue() : mkFalse(); + break; + } + case kind::VARIABLE: + case kind::SKOLEM: + { + result = mkInput(node); + break; + } + default: + bbAtom(node); + result = getBBAtom(node); + } + + cacheAig(node, result); + Debug("bitvector-aig") << "AigBitblaster::bbFormula done " << node << " => " << result <<"\n"; + return result; +} + +void AigBitblaster::bbAtom(TNode node) { + if (hasBBAtom(node)) { + return; + } + + Debug("bitvector-bitblast") << "Bitblasting atom " << node <<"\n"; + + // the bitblasted definition of the atom + Node normalized = Rewriter::rewrite(node); + Abc_Obj_t* atom_bb = (d_atomBBStrategies[normalized.getKind()])(normalized, this); + storeBBAtom(node, atom_bb); + Debug("bitvector-bitblast") << "Done bitblasting atom " << node <<"\n"; +} + +void AigBitblaster::bbTerm(TNode node, Bits& bits) { + if (hasBBTerm(node)) { + getBBTerm(node, bits); + return; + } + + Debug("bitvector-bitblast") << "Bitblasting term " << node <<"\n"; + d_termBBStrategies[node.getKind()] (node, bits, this); + + Assert (bits.size() == utils::getSize(node)); + storeBBTerm(node, bits); +} + + +void AigBitblaster::cacheAig(TNode node, Abc_Obj_t* aig) { + Assert (!hasAig(node)); + d_aigCache.insert(std::make_pair(node, aig)); +} +bool AigBitblaster::hasAig(TNode node) { + return d_aigCache.find(node) != d_aigCache.end(); +} +Abc_Obj_t* AigBitblaster::getAig(TNode node) { + Assert(hasAig(node)); + Debug("bitvector-aig") << "AigSimplifer::getAig " << node << " => " << d_aigCache.find(node)->second <<"\n"; + return d_aigCache.find(node)->second; +} + +void AigBitblaster::makeVariable(TNode node, Bits& bits) { + + for (unsigned i = 0; i < utils::getSize(node); ++i) { + Node bit = utils::mkBitOf(node, i); + Abc_Obj_t* input = mkInput(bit); + cacheAig(bit, input); + bits.push_back(input); + } +} + +Abc_Obj_t* AigBitblaster::mkInput(TNode input) { + Assert (!hasInput(input)); + Assert(input.getKind() == kind::BITVECTOR_BITOF || + (input.getType().isBoolean() && + (input.getKind() == kind::VARIABLE || + input.getKind() == kind::SKOLEM))); + Abc_Obj_t* aig_input = Abc_NtkCreatePi(currentAigNtk()); + // d_aigCache.insert(std::make_pair(input, aig_input)); + d_nodeToAigInput.insert(std::make_pair(input, aig_input)); + Debug("bitvector-aig") << "AigSimplifer::mkInput " << input << " " << aig_input <<"\n"; + return aig_input; +} + +bool AigBitblaster::hasInput(TNode input) { + return d_nodeToAigInput.find(input) != d_nodeToAigInput.end(); +} + +bool AigBitblaster::solve(TNode node) { + // setting output of network to be the query + Assert (d_aigOutputNode == NULL); + Abc_Obj_t* query = bbFormula(node); + d_aigOutputNode = Abc_NtkCreatePo(currentAigNtk()); + Abc_ObjAddFanin(d_aigOutputNode, query); + + simplifyAig(); + convertToCnfAndAssert(); + // no need to use abc anymore + + TimerStat::CodeTimer solveTimer(d_statistics.d_solveTime); + prop::SatValue result = d_satSolver->solve(); + + Assert (result != prop::SAT_VALUE_UNKNOWN); + return result == prop::SAT_VALUE_TRUE; +} + + +void addAliases(Abc_Frame_t* pAbc); + +void AigBitblaster::simplifyAig() { + TimerStat::CodeTimer simpTimer(d_statistics.d_simplificationTime); + + Abc_AigCleanup(currentAigM()); + Assert (Abc_NtkCheck(currentAigNtk())); + + const char* command = options::bitvectorAigSimplifications().c_str(); + Abc_Frame_t* pAbc = Abc_FrameGetGlobalFrame(); + Abc_FrameSetCurrentNetwork(pAbc, currentAigNtk()); + + addAliases(pAbc); + if ( Cmd_CommandExecute( pAbc, command ) ) { + fprintf( stdout, "Cannot execute command \"%s\".\n", command ); + exit(-1); + } + abcAigNetwork = Abc_FrameReadNtk(pAbc); +} + + +void AigBitblaster::convertToCnfAndAssert() { + TimerStat::CodeTimer cnfConversionTimer(d_statistics.d_cnfConversionTime); + + Aig_Man_t * pMan = NULL; + Cnf_Dat_t * pCnf = NULL; + assert( Abc_NtkIsStrash(currentAigNtk()) ); + + // convert to the AIG manager + pMan = Abc_NtkToDar(currentAigNtk(), 0, 0 ); + Abc_Stop(); + + // // free old network + // Abc_NtkDelete(currentAigNtk()); + // abcAigNetwork = NULL; + + Assert (pMan != NULL); + Assert (Aig_ManCheck(pMan)); + pCnf = Cnf_DeriveFast( pMan, 0 ); + + assertToSatSolver(pCnf); + + Cnf_DataFree( pCnf ); + Cnf_ManFree(); + Aig_ManStop(pMan); +} + +void AigBitblaster::assertToSatSolver(Cnf_Dat_t* pCnf) { + unsigned numVariables = pCnf->nVars; + unsigned numClauses = pCnf->nClauses; + + d_statistics.d_numVariables += numVariables; + d_statistics.d_numClauses += numClauses; + + // create variables in the sat solver + std::vector sat_variables; + for (unsigned i = 0; i < numVariables; ++i) { + sat_variables.push_back(d_satSolver->newVar(false, false, false)); + } + + // construct clauses and add to sat solver + int * pLit, * pStop; + for (unsigned i = 0; i < numClauses; i++ ) { + prop::SatClause clause; + for (pLit = pCnf->pClauses[i], pStop = pCnf->pClauses[i+1]; pLit < pStop; pLit++ ) { + int int_lit = Cnf_Lit2Var(*pLit); + Assert (int_lit != 0); + unsigned index = int_lit < 0? -int_lit : int_lit; + Assert (index - 1 < sat_variables.size()); + prop::SatLiteral lit(sat_variables[index-1], int_lit < 0); + clause.push_back(lit); + } + d_satSolver->addClause(clause, false); + } +} + +void addAliases(Abc_Frame_t* pAbc) { + std::vector aliases; + aliases.push_back("alias b balance"); + aliases.push_back("alias rw rewrite"); + aliases.push_back("alias rwz rewrite -z"); + aliases.push_back("alias rf refactor"); + aliases.push_back("alias rfz refactor -z"); + aliases.push_back("alias re restructure"); + aliases.push_back("alias rez restructure -z"); + aliases.push_back("alias rs resub"); + aliases.push_back("alias rsz resub -z"); + aliases.push_back("alias rsk6 rs -K 6"); + aliases.push_back("alias rszk5 rsz -K 5"); + aliases.push_back("alias bl b -l"); + aliases.push_back("alias rwl rw -l"); + aliases.push_back("alias rwzl rwz -l"); + aliases.push_back("alias rwzl rwz -l"); + aliases.push_back("alias rfl rf -l"); + aliases.push_back("alias rfzl rfz -l"); + aliases.push_back("alias brw \"b; rw\""); + + for (unsigned i = 0; i < aliases.size(); ++i) { + if ( Cmd_CommandExecute( pAbc, aliases[i].c_str() ) ) { + fprintf( stdout, "Cannot execute command \"%s\".\n", aliases[i].c_str() ); + exit(-1); + } + } +} + +bool AigBitblaster::hasBBAtom(TNode atom) const { + return d_bbAtoms.find(atom) != d_bbAtoms.end(); +} + +void AigBitblaster::storeBBAtom(TNode atom, Abc_Obj_t* atom_bb) { + d_bbAtoms.insert(std::make_pair(atom, atom_bb)); +} + +Abc_Obj_t* AigBitblaster::getBBAtom(TNode atom) const { + Assert (hasBBAtom(atom)); + return d_bbAtoms.find(atom)->second; +} + +AigBitblaster::Statistics::Statistics() + : d_numClauses("theory::bv::AigBitblaster::numClauses", 0) + , d_numVariables("theory::bv::AigBitblaster::numVariables", 0) + , d_simplificationTime("theory::bv::AigBitblaster::simplificationTime") + , d_cnfConversionTime("theory::bv::AigBitblaster::cnfConversionTime") + , d_solveTime("theory::bv::AigBitblaster::solveTime") +{ + StatisticsRegistry::registerStat(&d_numClauses); + StatisticsRegistry::registerStat(&d_numVariables); + StatisticsRegistry::registerStat(&d_simplificationTime); + StatisticsRegistry::registerStat(&d_cnfConversionTime); + StatisticsRegistry::registerStat(&d_solveTime); +} + +AigBitblaster::Statistics::~Statistics() { + StatisticsRegistry::unregisterStat(&d_numClauses); + StatisticsRegistry::unregisterStat(&d_numVariables); + StatisticsRegistry::unregisterStat(&d_simplificationTime); + StatisticsRegistry::unregisterStat(&d_cnfConversionTime); + StatisticsRegistry::unregisterStat(&d_solveTime); +} + + + +} /*bv namespace */ +} /* theory namespace */ +} /* CVC4 namespace*/ + + +#else // CVC4_USE_ABC + +namespace CVC4 { +namespace theory { +namespace bv { + +template <> inline +std::string toString (const std::vector& bits) { + Unreachable("Don't know how to print AIG"); +} + + +template <> inline +Abc_Obj_t* mkTrue() { + Unreachable(); + return NULL; +} + +template <> inline +Abc_Obj_t* mkFalse() { + Unreachable(); + return NULL; +} + +template <> inline +Abc_Obj_t* mkNot(Abc_Obj_t* a) { + Unreachable(); + return NULL; +} + +template <> inline +Abc_Obj_t* mkOr(Abc_Obj_t* a, Abc_Obj_t* b) { + Unreachable(); + return NULL; +} + +template <> inline +Abc_Obj_t* mkOr(const std::vector& children) { + Unreachable(); + return NULL; +} + + +template <> inline +Abc_Obj_t* mkAnd(Abc_Obj_t* a, Abc_Obj_t* b) { + Unreachable(); + return NULL; +} + +template <> inline +Abc_Obj_t* mkAnd(const std::vector& children) { + Unreachable(); + return NULL; +} + +template <> inline +Abc_Obj_t* mkXor(Abc_Obj_t* a, Abc_Obj_t* b) { + Unreachable(); + return NULL; +} + +template <> inline +Abc_Obj_t* mkIff(Abc_Obj_t* a, Abc_Obj_t* b) { + Unreachable(); + return NULL; +} + +template <> inline +Abc_Obj_t* mkIte(Abc_Obj_t* cond, Abc_Obj_t* a, Abc_Obj_t* b) { + Unreachable(); + return NULL; +} + + +Abc_Ntk_t* AigBitblaster::abcAigNetwork = NULL; + +Abc_Ntk_t* AigBitblaster::currentAigNtk() { + Unreachable(); + return NULL; +} + + +Abc_Aig_t* AigBitblaster::currentAigM() { + Unreachable(); + return NULL; +} + +AigBitblaster::~AigBitblaster() { + Unreachable(); +} + +Abc_Obj_t* AigBitblaster::bbFormula(TNode node) { + Unreachable(); + return NULL; +} + +void AigBitblaster::bbAtom(TNode node) { + Unreachable(); +} + +void AigBitblaster::bbTerm(TNode node, Bits& bits) { + Unreachable(); +} + + +void AigBitblaster::cacheAig(TNode node, Abc_Obj_t* aig) { + Unreachable(); +} +bool AigBitblaster::hasAig(TNode node) { + Unreachable(); + return false; +} +Abc_Obj_t* AigBitblaster::getAig(TNode node) { + Unreachable(); + return NULL; +} + +void AigBitblaster::makeVariable(TNode node, Bits& bits) { + Unreachable(); +} + +Abc_Obj_t* AigBitblaster::mkInput(TNode input) { + Unreachable(); + return NULL; +} + +bool AigBitblaster::hasInput(TNode input) { + Unreachable(); + return false; +} + +bool AigBitblaster::solve(TNode node) { + Unreachable(); + return false; +} +void AigBitblaster::simplifyAig() { + Unreachable(); +} + +void AigBitblaster::convertToCnfAndAssert() { + Unreachable(); +} + +void AigBitblaster::assertToSatSolver(Cnf_Dat_t* pCnf) { + Unreachable(); +} +bool AigBitblaster::hasBBAtom(TNode atom) const { + Unreachable(); + return false; +} + +void AigBitblaster::storeBBAtom(TNode atom, Abc_Obj_t* atom_bb) { + Unreachable(); +} + +Abc_Obj_t* AigBitblaster::getBBAtom(TNode atom) const { + Unreachable(); + return NULL; +} + +AigBitblaster::Statistics::Statistics() + : d_numClauses("theory::bv::AigBitblaster::numClauses", 0) + , d_numVariables("theory::bv::AigBitblaster::numVariables", 0) + , d_simplificationTime("theory::bv::AigBitblaster::simplificationTime") + , d_cnfConversionTime("theory::bv::AigBitblaster::cnfConversionTime") + , d_solveTime("theory::bv::AigBitblaster::solveTime") +{} + +AigBitblaster::Statistics::~Statistics() {} + +AigBitblaster::AigBitblaster() { + Unreachable(); +} + +} // namespace bv +} // namespace theory +} // namespace CVC4 + + +#endif // CVC4_USE_ABC + +#endif // __CVC4__AIG__BITBLASTER_H diff --git a/src/theory/bv/bitblast_mode.cpp b/src/theory/bv/bitblast_mode.cpp new file mode 100644 index 000000000..91a140539 --- /dev/null +++ b/src/theory/bv/bitblast_mode.cpp @@ -0,0 +1,55 @@ +/********************* */ +/*! \file bitblast_mode.cpp + ** \verbatim + ** Original author: Liana Hadarean + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2013 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Bitblast modes for bit-vector solver. + ** + ** Bitblast modes for bit-vector solver. + **/ + +#include "theory/bv/bitblast_mode.h" + +namespace CVC4 { + +std::ostream& operator<<(std::ostream& out, theory::bv::BitblastMode mode) { + switch(mode) { + case theory::bv::BITBLAST_MODE_LAZY: + out << "BITBLAST_MODE_LAZY"; + break; + case theory::bv::BITBLAST_MODE_EAGER: + out << "BITBLAST_MODE_EAGER"; + break; + default: + out << "BitblastMode:UNKNOWN![" << unsigned(mode) << "]"; + } + + return out; +} + +std::ostream& operator<<(std::ostream& out, theory::bv::BvSlicerMode mode) { + switch(mode) { + case theory::bv::BITVECTOR_SLICER_ON: + out << "BITVECTOR_SLICER_ON"; + break; + case theory::bv::BITVECTOR_SLICER_OFF: + out << "BITVECTOR_SLICER_OFF"; + break; + case theory::bv::BITVECTOR_SLICER_AUTO: + out << "BITVECTOR_SLICER_AUTO"; + break; + default: + out << "BvSlicerMode:UNKNOWN![" << unsigned(mode) << "]"; + } + + return out; +} + + +}/* CVC4 namespace */ diff --git a/src/theory/bv/bitblast_mode.h b/src/theory/bv/bitblast_mode.h new file mode 100644 index 000000000..318e17467 --- /dev/null +++ b/src/theory/bv/bitblast_mode.h @@ -0,0 +1,72 @@ +/********************* */ +/*! \file bitblast_mode.h + ** \verbatim + ** Original author: Liana Hadarean + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2013 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Bitblasting modes for bit-vector solver. + ** + ** Bitblasting modes for bit-vector solver. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__BV__BITBLAST_MODE_H +#define __CVC4__THEORY__BV__BITBLAST_MODE_H + +#include + +namespace CVC4 { +namespace theory { +namespace bv { + +/** Enumeration of bit-blasting modes */ +enum BitblastMode { + + /** + * Lazy bit-blasting that separates boolean reasoning + * from term reasoning. + */ + BITBLAST_MODE_LAZY, + + /** + * Bit-blast eagerly to the bit-vector SAT solver. + */ + BITBLAST_MODE_EAGER +};/* enum BitblastMode */ + +/** Enumeration of bit-vector equality slicer mode */ +enum BvSlicerMode { + + /** + * Force the slicer on. + */ + BITVECTOR_SLICER_ON, + + /** + * Slicer off. + */ + BITVECTOR_SLICER_OFF, + + /** + * Auto enable slicer if problem has only equalities. + */ + BITVECTOR_SLICER_AUTO + +};/* enum BvSlicerMode */ + + +}/* CVC4::theory::bv namespace */ +}/* CVC4::theory namespace */ + +std::ostream& operator<<(std::ostream& out, theory::bv::BitblastMode mode) CVC4_PUBLIC; +std::ostream& operator<<(std::ostream& out, theory::bv::BvSlicerMode mode) CVC4_PUBLIC; + +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__BV__BITBLAST_MODE_H */ diff --git a/src/theory/bv/bitblast_strategies.cpp b/src/theory/bv/bitblast_strategies.cpp deleted file mode 100644 index 4a0b45d62..000000000 --- a/src/theory/bv/bitblast_strategies.cpp +++ /dev/null @@ -1,936 +0,0 @@ -/********************* */ -/*! \file bitblast_strategies.cpp - ** \verbatim - ** Original author: Liana Hadarean - ** Major contributors: none - ** Minor contributors (to current version): Clark Barrett, Dejan Jovanovic, Morgan Deters, Tim King - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2013 New York University and The University of Iowa - ** See the file COPYING in the top-level source directory for licensing - ** information.\endverbatim - ** - ** \brief Implementation of bitblasting functions for various operators. - ** - ** Implementation of bitblasting functions for various operators. - **/ - -#include "bitblast_strategies.h" -#include "bitblaster.h" -#include "prop/sat_solver.h" -#include "theory/booleans/theory_bool_rewriter.h" -#include - -using namespace CVC4::prop; -using namespace CVC4::theory::bv::utils; -namespace CVC4 { -namespace theory { -namespace bv { - -/* - Purely debugging - */ - -Bits* rewriteBits(const Bits& bits) { - Bits* newbits = new Bits(); - for (unsigned i = 0; i < bits.size(); ++i) { - newbits->push_back(Rewriter::rewrite(bits[i])); - } - return newbits; -} - -Node rewrite(TNode node) { - return Rewriter::rewrite(node); -} - -/* - Various helper functions that get called by the bitblasting procedures - */ - -void inline extractBits(const Bits& b, Bits& dest, unsigned lo, unsigned hi) { - Assert ( lo < b.size() && hi < b.size() && lo <= hi); - for (unsigned i = lo; i <= hi; ++i) { - dest.push_back(b[i]); - } -} - -void inline negateBits(const Bits& bits, Bits& negated_bits) { - for(unsigned i = 0; i < bits.size(); ++i) { - negated_bits.push_back(utils::mkNot(bits[i])); - } -} - -bool inline isZero(const Bits& bits) { - for(unsigned i = 0; i < bits.size(); ++i) { - if(bits[i] != mkFalse()) { - return false; - } - } - return true; -} - -void inline rshift(Bits& bits, unsigned amount) { - for (unsigned i = 0; i < bits.size() - amount; ++i) { - bits[i] = bits[i+amount]; - } - for(unsigned i = bits.size() - amount; i < bits.size(); ++i) { - bits[i] = mkFalse(); - } -} - -void inline lshift(Bits& bits, unsigned amount) { - for (int i = (int)bits.size() - 1; i >= (int)amount ; --i) { - bits[i] = bits[i-amount]; - } - for(unsigned i = 0; i < amount; ++i) { - bits[i] = mkFalse(); - } -} - -void inline makeZero(Bits& bits, unsigned width) { - Assert(bits.size() == 0); - for(unsigned i = 0; i < width; ++i) { - bits.push_back(mkFalse()); - } -} - - -/** - * Constructs a simple ripple carry adder - * - * @param a first term to be added - * @param b second term to be added - * @param res the result - * @param carry the carry-in bit - * - * @return the carry-out - */ -Node inline rippleCarryAdder(const Bits&a, const Bits& b, Bits& res, Node carry) { - Assert(a.size() == b.size() && res.size() == 0); - - for (unsigned i = 0 ; i < a.size(); ++i) { - Node sum = mkXor(mkXor(a[i], b[i]), carry); - carry = mkOr( mkAnd(a[i], b[i]), - mkAnd( mkXor(a[i], b[i]), - carry)); - res.push_back(sum); - } - - return carry; -} - -inline void shiftAddMultiplier(const Bits&a, const Bits&b, Bits& res) { - - for (unsigned i = 0; i < a.size(); ++i) { - res.push_back(mkNode(kind::AND, b[0], a[i])); - } - - for(unsigned k = 1; k < res.size(); ++k) { - Node carry_in = mkFalse(); - Node carry_out; - for(unsigned j = 0; j < res.size() -k; ++j) { - Node aj = mkAnd(a[j], b[k]); - carry_out = mkOr(mkAnd(res[j+k], aj), - mkAnd( mkXor(res[j+k], aj), carry_in)); - res[j+k] = mkXor(mkXor(res[j+k], aj), carry_in); - carry_in = carry_out; - } - } -} - -Node inline uLessThanBB(const Bits&a, const Bits& b, bool orEqual) { - Assert (a.size() && b.size()); - - Node res = mkNode(kind::AND, mkNode(kind::NOT, a[0]), b[0]); - - if(orEqual) { - res = mkNode(kind::OR, res, mkNode(kind::IFF, a[0], b[0])); - } - - for (unsigned i = 1; i < a.size(); ++i) { - // a < b iff ( a[i] <-> b[i] AND a[i-1:0] < b[i-1:0]) OR (~a[i] AND b[i]) - res = mkNode(kind::OR, - mkNode(kind::AND, mkNode(kind::IFF, a[i], b[i]), res), - mkNode(kind::AND, mkNode(kind::NOT, a[i]), b[i])); - } - return res; -} - -Node inline sLessThanBB(const Bits&a, const Bits& b, bool orEqual) { - Assert (a.size() && b.size()); - if (a.size() == 1) { - if(orEqual) { - return mkNode(kind::OR, - mkNode(kind::IFF, a[0], b[0]), - mkNode(kind::AND, a[0], mkNode(kind::NOT, b[0]))); - } - - return mkNode(kind::AND, a[0], mkNode(kind::NOT, b[0])); - } - unsigned n = a.size() - 1; - Bits a1, b1; - extractBits(a, a1, 0, n-1); - extractBits(b, b1, 0, n-1); - - // unsigned comparison of the first n-1 bits - Node ures = uLessThanBB(a1, b1, orEqual); - Node res = mkNode(kind::OR, - // a b have the same sign - mkNode(kind::AND, - mkNode(kind::IFF, a[n], b[n]), - ures), - // a is negative and b positive - mkNode(kind::AND, - a[n], - mkNode(kind::NOT, b[n]))); - return res; -} - - -/* - Atom bitblasting strategies - */ - - -Node UndefinedAtomBBStrategy(TNode node, Bitblaster* bb) { - Debug("bitvector") << "TheoryBV::Bitblaster Undefined bitblasting strategy for kind: " - << node.getKind() << "\n"; - Unreachable(); -} - -Node DefaultEqBB(TNode node, Bitblaster* bb) { - Debug("bitvector-bb") << "Bitblasting node " << node << "\n"; - - Assert(node.getKind() == kind::EQUAL); - Bits lhs, rhs; - bb->bbTerm(node[0], lhs); - bb->bbTerm(node[1], rhs); - - Assert(lhs.size() == rhs.size()); - - NodeManager* nm = NodeManager::currentNM(); - - std::vector bits_eq; - for (unsigned i = 0; i < lhs.size(); i++) { - Node bit_eq = nm->mkNode(kind::IFF, lhs[i], rhs[i]); - bits_eq.push_back(bit_eq); - } - Node bv_eq = utils::mkAnd(bits_eq); - return bv_eq; -} - - -Node AdderUltBB(TNode node, Bitblaster* bb) { - Debug("bitvector-bb") << "Bitblasting node " << node << "\n"; - Assert(node.getKind() == kind::BITVECTOR_ULT); - Bits a, b; - bb->bbTerm(node[0], a); - bb->bbTerm(node[1], b); - Assert(a.size() == b.size()); - - // a < b <=> ~ (add(a, ~b, 1).carry_out) - Bits not_b; - negateBits(b, not_b); - Node carry = mkTrue(); - - for (unsigned i = 0 ; i < a.size(); ++i) { - carry = mkOr( mkAnd(a[i], not_b[i]), - mkAnd( mkXor(a[i], not_b[i]), - carry)); - } - return mkNot(carry); -} - - -Node DefaultUltBB(TNode node, Bitblaster* bb) { - Debug("bitvector-bb") << "Bitblasting node " << node << "\n"; - Assert(node.getKind() == kind::BITVECTOR_ULT); - Bits a, b; - bb->bbTerm(node[0], a); - bb->bbTerm(node[1], b); - Assert(a.size() == b.size()); - - // construct bitwise comparison - Node res = uLessThanBB(a, b, false); - return res; -} - -Node DefaultUleBB(TNode node, Bitblaster* bb){ - Debug("bitvector-bb") << "Bitblasting node " << node << "\n"; - Assert(node.getKind() == kind::BITVECTOR_ULE); - Bits a, b; - - bb->bbTerm(node[0], a); - bb->bbTerm(node[1], b); - Assert(a.size() == b.size()); - // construct bitwise comparison - Node res = uLessThanBB(a, b, true); - return res; -} - -Node DefaultUgtBB(TNode node, Bitblaster* bb){ - Debug("bitvector-bb") << "Bitblasting node " << node << "\n"; - // should be rewritten - Unimplemented(); -} -Node DefaultUgeBB(TNode node, Bitblaster* bb){ - Debug("bitvector-bb") << "Bitblasting node " << node << "\n"; - // should be rewritten - Unimplemented(); -} - -// Node DefaultSltBB(TNode node, Bitblaster* bb){ -// Debug("bitvector-bb") << "Bitblasting node " << node << "\n"; -// // shoudl be rewritten in terms of ult -// Unimplemented(); -// } - -// Node DefaultSleBB(TNode node, Bitblaster* bb){ -// Debug("bitvector-bb") << "Bitblasting node " << node << "\n"; -// // shoudl be rewritten in terms of ule -// Unimplemented(); -// } - - -Node DefaultSltBB(TNode node, Bitblaster* bb){ - Debug("bitvector-bb") << "Bitblasting node " << node << "\n"; - - Bits a, b; - bb->bbTerm(node[0], a); - bb->bbTerm(node[1], b); - Assert(a.size() == b.size()); - - Node res = sLessThanBB(a, b, false); - return res; -} - -Node DefaultSleBB(TNode node, Bitblaster* bb){ - Debug("bitvector-bb") << "Bitblasting node " << node << "\n"; - - Bits a, b; - bb->bbTerm(node[0], a); - bb->bbTerm(node[1], b); - Assert(a.size() == b.size()); - - Node res = sLessThanBB(a, b, true); - return res; -} - -Node DefaultSgtBB(TNode node, Bitblaster* bb){ - Debug("bitvector-bb") << "Bitblasting node " << node << "\n"; - // should be rewritten - Unimplemented(); -} - -Node DefaultSgeBB(TNode node, Bitblaster* bb){ - Debug("bitvector-bb") << "Bitblasting node " << node << "\n"; - // should be rewritten - Unimplemented(); -} - - -/// Term bitblasting strategies - -void UndefinedTermBBStrategy(TNode node, Bits& bits, Bitblaster* bb) { - Debug("bitvector") << "theory::bv:: Undefined bitblasting strategy for kind: " - << node.getKind() << "\n"; - Unreachable(); -} - -void DefaultVarBB (TNode node, Bits& bits, Bitblaster* bb) { - Assert(bits.size() == 0); - for (unsigned i = 0; i < utils::getSize(node); ++i) { - bits.push_back(utils::mkBitOf(node, i)); - } - - if(Debug.isOn("bitvector-bb")) { - Debug("bitvector-bb") << "theory::bv::DefaultVarBB bitblasting " << node << "\n"; - Debug("bitvector-bb") << " with bits " << toString(bits); - } - - bb->storeVariable(node); -} - -void DefaultConstBB (TNode node, Bits& bits, Bitblaster* bb) { - Debug("bitvector-bb") << "theory::bv::DefaultConstBB bitblasting " << node << "\n"; - Assert(node.getKind() == kind::CONST_BITVECTOR); - Assert(bits.size() == 0); - - for (unsigned i = 0; i < utils::getSize(node); ++i) { - Integer bit = node.getConst().extract(i, i).getValue(); - if(bit == Integer(0)){ - bits.push_back(utils::mkFalse()); - } else { - Assert (bit == Integer(1)); - bits.push_back(utils::mkTrue()); - } - } - if(Debug.isOn("bitvector-bb")) { - Debug("bitvector-bb") << "with bits: " << toString(bits) << "\n"; - } -} - - -void DefaultNotBB (TNode node, Bits& bits, Bitblaster* bb) { - Debug("bitvector-bb") << "theory::bv::DefaultNotBB bitblasting " << node << "\n"; - Assert(node.getKind() == kind::BITVECTOR_NOT); - Assert(bits.size() == 0); - Bits bv; - bb->bbTerm(node[0], bv); - negateBits(bv, bits); -} - -void DefaultConcatBB (TNode node, Bits& bits, Bitblaster* bb) { - Debug("bitvector-bb") << "theory::bv::DefaultConcatBB bitblasting " << node << "\n"; - Assert(bits.size() == 0); - - Assert (node.getKind() == kind::BITVECTOR_CONCAT); - for (int i = node.getNumChildren() -1 ; i >= 0; --i ) { - TNode current = node[i]; - Bits current_bits; - bb->bbTerm(current, current_bits); - - for(unsigned j = 0; j < utils::getSize(current); ++j) { - bits.push_back(current_bits[j]); - } - } - Assert (bits.size() == utils::getSize(node)); - if(Debug.isOn("bitvector-bb")) { - Debug("bitvector-bb") << "with bits: " << toString(bits) << "\n"; - } -} - -void DefaultAndBB (TNode node, Bits& bits, Bitblaster* bb) { - Debug("bitvector-bb") << "theory::bv::DefaultAndBB bitblasting " << node << "\n"; - - Assert(node.getKind() == kind::BITVECTOR_AND && - bits.size() == 0); - - for(unsigned j = 0; j < utils::getSize(node); ++j) { - NodeBuilder<> andBuilder(kind::AND); - for (unsigned i = 0; i < node.getNumChildren(); ++i) { - Bits current; - bb->bbTerm(node[i], current); - andBuilder << current[j]; - Assert(utils::getSize(node) == current.size()); - } - bits.push_back(andBuilder); - } -} - -void DefaultOrBB (TNode node, Bits& bits, Bitblaster* bb) { - Debug("bitvector-bb") << "theory::bv::DefaultOrBB bitblasting " << node << "\n"; - - Assert(node.getKind() == kind::BITVECTOR_OR && - bits.size() == 0); - - for(unsigned j = 0; j < utils::getSize(node); ++j) { - NodeBuilder<> orBuilder(kind::OR); - for (unsigned i = 0; i < node.getNumChildren(); ++i) { - Bits current; - bb->bbTerm(node[i], current); - orBuilder << current[j]; - Assert(utils::getSize(node) == current.size()); - } - bits.push_back(orBuilder); - } -} - -void DefaultXorBB (TNode node, Bits& bits, Bitblaster* bb) { - Debug("bitvector-bb") << "theory::bv::DefaultXorBB bitblasting " << node << "\n"; - - Assert(node.getKind() == kind::BITVECTOR_XOR && - bits.size() == 0); - - for(unsigned j = 0; j < utils::getSize(node); ++j) { - Bits first; - bb->bbTerm(node[0], first); - Node bitj = first[j]; - - for (unsigned i = 1; i < node.getNumChildren(); ++i) { - Bits current; - bb->bbTerm(node[i], current); - bitj = utils::mkNode(kind::XOR, bitj, current[j]); - Assert(utils::getSize(node) == current.size()); - } - bits.push_back(bitj); - } -} - -void DefaultXnorBB (TNode node, Bits& bits, Bitblaster* bb) { - Debug("bitvector-bb") << "theory::bv::DefaultXnorBB bitblasting " << node << "\n"; - - Assert(node.getNumChildren() == 2 && - node.getKind() == kind::BITVECTOR_XNOR && - bits.size() == 0); - Bits lhs, rhs; - bb->bbTerm(node[0], lhs); - bb->bbTerm(node[1], rhs); - Assert(lhs.size() == rhs.size()); - - for (unsigned i = 0; i < lhs.size(); ++i) { - bits.push_back(utils::mkNode(kind::IFF, lhs[i], rhs[i])); - } -} - - -void DefaultNandBB (TNode node, Bits& bits, Bitblaster* bb) { - Debug("bitvector") << "theory::bv:: Unimplemented kind " - << node.getKind() << "\n"; - Unimplemented(); -} -void DefaultNorBB (TNode node, Bits& bits, Bitblaster* bb) { - Debug("bitvector") << "theory::bv:: Unimplemented kind " - << node.getKind() << "\n"; - Unimplemented(); -} -void DefaultCompBB (TNode node, Bits& bits, Bitblaster* bb) { - Debug("bitvector") << "theory::bv:: DefaultCompBB bitblasting "<< node << "\n"; - - Assert(getSize(node) == 1 && bits.size() == 0 && node.getKind() == kind::BITVECTOR_COMP); - Bits a, b; - bb->bbTerm(node[0], a); - bb->bbTerm(node[1], b); - - std::vector bit_eqs; - NodeManager* nm = NodeManager::currentNM(); - for (unsigned i = 0; i < a.size(); ++i) { - Node eq = nm->mkNode(kind::IFF, a[i], b[i]); - bit_eqs.push_back(eq); - } - Node a_eq_b = mkAnd(bit_eqs); - bits.push_back(a_eq_b); -} - -void DefaultMultBB (TNode node, Bits& res, Bitblaster* bb) { - Debug("bitvector") << "theory::bv:: DefaultMultBB bitblasting "<< node << "\n"; - Assert(res.size() == 0 && - node.getKind() == kind::BITVECTOR_MULT); - - Bits newres; - bb->bbTerm(node[0], res); - for(unsigned i = 1; i < node.getNumChildren(); ++i) { - Bits current; - bb->bbTerm(node[i], current); - newres.clear(); - // constructs a simple shift and add multiplier building the result - // in res - shiftAddMultiplier(res, current, newres); - res = newres; - } - if(Debug.isOn("bitvector-bb")) { - Debug("bitvector-bb") << "with bits: " << toString(res) << "\n"; - } -} - -void DefaultPlusBB (TNode node, Bits& res, Bitblaster* bb) { - Debug("bitvector-bb") << "theory::bv::DefaultPlusBB bitblasting " << node << "\n"; - Assert(node.getKind() == kind::BITVECTOR_PLUS && - res.size() == 0); - - bb->bbTerm(node[0], res); - - Bits newres; - - for(unsigned i = 1; i < node.getNumChildren(); ++i) { - Bits current; - bb->bbTerm(node[i], current); - newres.clear(); - rippleCarryAdder(res, current, newres, utils::mkFalse()); - res = newres; - } - - Assert(res.size() == utils::getSize(node)); -} - - -void DefaultSubBB (TNode node, Bits& bits, Bitblaster* bb) { - Debug("bitvector-bb") << "theory::bv::DefaultSubBB bitblasting " << node << "\n"; - Assert(node.getKind() == kind::BITVECTOR_SUB && - node.getNumChildren() == 2 && - bits.size() == 0); - - Bits a, b; - bb->bbTerm(node[0], a); - bb->bbTerm(node[1], b); - Assert(a.size() == b.size() && utils::getSize(node) == a.size()); - - // bvsub a b = adder(a, ~b, 1) - Bits not_b; - negateBits(b, not_b); - Node carry = utils::mkTrue(); - rippleCarryAdder(a, not_b, bits, mkTrue()); -} - -void DefaultNegBB (TNode node, Bits& bits, Bitblaster* bb) { - Debug("bitvector-bb") << "theory::bv::DefaultNegBB bitblasting " << node << "\n"; - Assert(node.getKind() == kind::BITVECTOR_NEG); - - Bits a; - bb->bbTerm(node[0], a); - Assert(utils::getSize(node) == a.size()); - - // -a = add(~a, 0, 1). - Bits not_a; - negateBits(a, not_a); - Bits zero; - makeZero(zero, getSize(node)); - - rippleCarryAdder(not_a, zero, bits, mkTrue()); -} - -void uDivModRec(const Bits& a, const Bits& b, Bits& q, Bits& r, unsigned rec_width) { - Assert( q.size() == 0 && r.size() == 0); - - if(rec_width == 0 || isZero(a)) { - makeZero(q, a.size()); - makeZero(r, a.size()); - return; - } - - Bits q1, r1; - Bits a1 = a; - rshift(a1, 1); - - uDivModRec(a1, b, q1, r1, rec_width - 1); - // shift the quotient and remainder (i.e. multiply by two) and add 1 to remainder if a is odd - lshift(q1, 1); - lshift(r1, 1); - - - Node is_odd = mkNode(kind::IFF, a[0], mkTrue()); - Node one_if_odd = mkNode(kind::ITE, is_odd, mkTrue(), mkFalse()); - - Bits zero; - makeZero(zero, b.size()); - - Bits r1_shift_add; - // account for a being odd - rippleCarryAdder(r1, zero, r1_shift_add, one_if_odd); - // now check if the remainder is greater than b - Bits not_b; - negateBits(b, not_b); - Bits r_minus_b; - Node co1; - // use adder because we need r_minus_b anyway - co1 = rippleCarryAdder(r1_shift_add, not_b, r_minus_b, mkTrue()); - // sign is true if r1 < b - Node sign = mkNode(kind::NOT, co1); - - q1[0] = mkNode(kind::ITE, sign, q1[0], mkTrue()); - - // would be nice to have a high level ITE instead of bitwise - for(unsigned i = 0; i < a.size(); ++i) { - r1_shift_add[i] = mkNode(kind::ITE, sign, r1_shift_add[i], r_minus_b[i]); - } - - // check if a < b - - Bits a_minus_b; - Node co2 = rippleCarryAdder(a, not_b, a_minus_b, mkTrue()); - // Node a_lt_b = a_minus_b.back(); - Node a_lt_b = mkNode(kind::NOT, co2); - - for(unsigned i = 0; i < a.size(); ++i) { - Node qval = mkNode(kind::ITE, a_lt_b, mkFalse(), q1[i]); - Node rval = mkNode(kind::ITE, a_lt_b, a[i], r1_shift_add[i]); - q.push_back(qval); - r.push_back(rval); - } - -} - -void DefaultUdivBB (TNode node, Bits& q, Bitblaster* bb) { - Debug("bitvector-bb") << "theory::bv::DefaultUdivBB bitblasting " << node << "\n"; - Assert(node.getKind() == kind::BITVECTOR_UDIV_TOTAL && q.size() == 0); - - Bits a, b; - bb->bbTerm(node[0], a); - bb->bbTerm(node[1], b); - - Bits r; - uDivModRec(a, b, q, r, getSize(node)); - // adding a special case for division by 0 - std::vector iszero; - for (unsigned i = 0; i < b.size(); ++i) { - iszero.push_back(utils::mkNode(kind::IFF, b[i], utils::mkFalse())); - } - Node b_is_0 = utils::mkAnd(iszero); - - for (unsigned i = 0; i < q.size(); ++i) { - q[i] = utils::mkNode(kind::ITE, b_is_0, utils::mkFalse(), q[i]); - r[i] = utils::mkNode(kind::ITE, b_is_0, utils::mkFalse(), r[i]); - } - - // cache the remainder in case we need it later - Node remainder = mkNode(kind::BITVECTOR_UREM_TOTAL, node[0], node[1]); - bb->cacheTermDef(remainder, r); -} - -void DefaultUremBB (TNode node, Bits& rem, Bitblaster* bb) { - Debug("bitvector-bb") << "theory::bv::DefaultUremBB bitblasting " << node << "\n"; - Assert(node.getKind() == kind::BITVECTOR_UREM_TOTAL && rem.size() == 0); - - Bits a, b; - bb->bbTerm(node[0], a); - bb->bbTerm(node[1], b); - - Bits q; - uDivModRec(a, b, q, rem, getSize(node)); - // adding a special case for division by 0 - std::vector iszero; - for (unsigned i = 0; i < b.size(); ++i) { - iszero.push_back(utils::mkNode(kind::IFF, b[i], utils::mkFalse())); - } - Node b_is_0 = utils::mkAnd(iszero); - - for (unsigned i = 0; i < q.size(); ++i) { - q[i] = utils::mkNode(kind::ITE, b_is_0, utils::mkFalse(), q[i]); - rem[i] = utils::mkNode(kind::ITE, b_is_0, utils::mkFalse(), rem[i]); - } - - // cache the quotient in case we need it later - Node quotient = mkNode(kind::BITVECTOR_UDIV_TOTAL, node[0], node[1]); - bb->cacheTermDef(quotient, q); -} - - -void DefaultSdivBB (TNode node, Bits& bits, Bitblaster* bb) { - Debug("bitvector") << "theory::bv:: Unimplemented kind " - << node.getKind() << "\n"; - Unimplemented(); -} -void DefaultSremBB (TNode node, Bits& bits, Bitblaster* bb) { - Debug("bitvector") << "theory::bv:: Unimplemented kind " - << node.getKind() << "\n"; - Unimplemented(); -} -void DefaultSmodBB (TNode node, Bits& bits, Bitblaster* bb) { - Debug("bitvector") << "theory::bv:: Unimplemented kind " - << node.getKind() << "\n"; - Unimplemented(); -} - -void DefaultShlBB (TNode node, Bits& res, Bitblaster* bb) { - Debug("bitvector-bb") << "theory::bv::DefaultShlBB bitblasting " << node << "\n"; - Assert (node.getKind() == kind::BITVECTOR_SHL && - res.size() == 0); - Bits a, b; - bb->bbTerm(node[0], a); - bb->bbTerm(node[1], b); - - // check for b < log2(n) - unsigned size = utils::getSize(node); - unsigned log2_size = std::ceil(log2((double)size)); - Node a_size = utils::mkConst(BitVector(size, size)); - Node b_ult_a_size = utils::mkNode(kind::BITVECTOR_ULT, node[1], a_size); - // ensure that the inequality is bit-blasted - bb->bbAtom(b_ult_a_size); - - Bits prev_res; - res = a; - // we only need to look at the bits bellow log2_a_size - for(unsigned s = 0; s < log2_size; ++s) { - // barrel shift stage: at each stage you can either shift by 2^s bits - // or leave the previous stage untouched - prev_res = res; - unsigned threshold = pow(2, s); - for(unsigned i = 0; i < a.size(); ++i) { - if (i < threshold) { - // if b[s] is true then we must have shifted by at least 2^b bits so - // all bits bellow 2^s will be 0, otherwise just use previous shift value - res[i] = mkNode(kind::ITE, b[s], mkFalse(), prev_res[i]); - } else { - // if b[s]= 0, use previous value, otherwise shift by threshold bits - Assert(i >= threshold); - res[i] = mkNode(kind::ITE, b[s], prev_res[i-threshold], prev_res[i]); - } - } - } - prev_res = res; - for (unsigned i = 0; i < b.size(); ++i) { - // this is fine because b_ult_a_size has been bit-blasted - res[i] = utils::mkNode(kind::ITE, b_ult_a_size, prev_res[i], utils::mkFalse()); - } - - if(Debug.isOn("bitvector-bb")) { - Debug("bitvector-bb") << "with bits: " << toString(res) << "\n"; - } -} - -void DefaultLshrBB (TNode node, Bits& res, Bitblaster* bb) { - Debug("bitvector-bb") << "theory::bv::DefaultLshrBB bitblasting " << node << "\n"; - Assert (node.getKind() == kind::BITVECTOR_LSHR && - res.size() == 0); - Bits a, b; - bb->bbTerm(node[0], a); - bb->bbTerm(node[1], b); - - // check for b < log2(n) - unsigned size = utils::getSize(node); - unsigned log2_size = std::ceil(log2((double)size)); - Node a_size = utils::mkConst(BitVector(size, size)); - Node b_ult_a_size = utils::mkNode(kind::BITVECTOR_ULT, node[1], a_size); - // ensure that the inequality is bit-blasted - bb->bbAtom(b_ult_a_size); - - res = a; - Bits prev_res; - - for(unsigned s = 0; s < log2_size; ++s) { - // barrel shift stage: at each stage you can either shift by 2^s bits - // or leave the previous stage untouched - prev_res = res; - int threshold = pow(2, s); - for(unsigned i = 0; i < a.size(); ++i) { - if (i + threshold >= a.size()) { - // if b[s] is true then we must have shifted by at least 2^b bits so - // all bits above 2^s will be 0, otherwise just use previous shift value - res[i] = mkNode(kind::ITE, b[s], mkFalse(), prev_res[i]); - } else { - // if b[s]= 0, use previous value, otherwise shift by threshold bits - Assert (i+ threshold < a.size()); - res[i] = mkNode(kind::ITE, mkNot(b[s]), prev_res[i], prev_res[i+threshold]); - } - } - } - - prev_res = res; - for (unsigned i = 0; i < b.size(); ++i) { - // this is fine because b_ult_a_size has been bit-blasted - res[i] = utils::mkNode(kind::ITE, b_ult_a_size, prev_res[i], utils::mkFalse()); - } - - if(Debug.isOn("bitvector-bb")) { - Debug("bitvector-bb") << "with bits: " << toString(res) << "\n"; - } -} - -void DefaultAshrBB (TNode node, Bits& res, Bitblaster* bb) { - - Debug("bitvector-bb") << "theory::bv::DefaultAshrBB bitblasting " << node << "\n"; - Assert (node.getKind() == kind::BITVECTOR_ASHR && - res.size() == 0); - Bits a, b; - bb->bbTerm(node[0], a); - bb->bbTerm(node[1], b); - - // check for b < n - unsigned size = utils::getSize(node); - unsigned log2_size = std::ceil(log2((double)size)); - Node a_size = utils::mkConst(BitVector(size, size)); - Node b_ult_a_size = utils::mkNode(kind::BITVECTOR_ULT, node[1], a_size); - // ensure that the inequality is bit-blasted - bb->bbAtom(b_ult_a_size); - - res = a; - TNode sign_bit = a.back(); - Bits prev_res; - - for(unsigned s = 0; s < log2_size; ++s) { - // barrel shift stage: at each stage you can either shift by 2^s bits - // or leave the previous stage untouched - prev_res = res; - int threshold = pow(2, s); - for(unsigned i = 0; i < a.size(); ++i) { - if (i + threshold >= a.size()) { - // if b[s] is true then we must have shifted by at least 2^b bits so - // all bits above 2^s will be the sign bit, otherwise just use previous shift value - res[i] = mkNode(kind::ITE, b[s], sign_bit, prev_res[i]); - } else { - // if b[s]= 0, use previous value, otherwise shift by threshold bits - Assert (i+ threshold < a.size()); - res[i] = mkNode(kind::ITE, mkNot(b[s]), prev_res[i], prev_res[i+threshold]); - } - } - } - - prev_res = res; - for (unsigned i = 0; i < b.size(); ++i) { - // this is fine because b_ult_a_size has been bit-blasted - res[i] = utils::mkNode(kind::ITE, b_ult_a_size, prev_res[i], sign_bit); - } - - if(Debug.isOn("bitvector-bb")) { - Debug("bitvector-bb") << "with bits: " << toString(res) << "\n"; - } -} - -void DefaultExtractBB (TNode node, Bits& bits, Bitblaster* bb) { - Assert (node.getKind() == kind::BITVECTOR_EXTRACT); - Assert(bits.size() == 0); - - Bits base_bits; - bb->bbTerm(node[0], base_bits); - unsigned high = utils::getExtractHigh(node); - unsigned low = utils::getExtractLow(node); - - for (unsigned i = low; i <= high; ++i) { - bits.push_back(base_bits[i]); - } - Assert (bits.size() == high - low + 1); - - if(Debug.isOn("bitvector-bb")) { - Debug("bitvector-bb") << "theory::bv::DefaultExtractBB bitblasting " << node << "\n"; - Debug("bitvector-bb") << " with bits " << toString(bits); - } -} - - -void DefaultRepeatBB (TNode node, Bits& bits, Bitblaster* bb) { - Debug("bitvector") << "theory::bv:: Unimplemented kind " - << node.getKind() << "\n"; - // this should be rewritten - Unimplemented(); -} - -void DefaultZeroExtendBB (TNode node, Bits& res_bits, Bitblaster* bb) { - - Debug("bitvector-bb") << "theory::bv::DefaultZeroExtendBB bitblasting " << node << "\n"; - - // this should be rewritten - Unimplemented(); - -} - -void DefaultSignExtendBB (TNode node, Bits& res_bits, Bitblaster* bb) { - Debug("bitvector-bb") << "theory::bv::DefaultSignExtendBB bitblasting " << node << "\n"; - - Assert (node.getKind() == kind::BITVECTOR_SIGN_EXTEND && - res_bits.size() == 0); - - Bits bits; - bb->bbTerm(node[0], bits); - - TNode sign_bit = bits.back(); - unsigned amount = node.getOperator().getConst().signExtendAmount; - - for (unsigned i = 0; i < bits.size(); ++i ) { - res_bits.push_back(bits[i]); - } - - for (unsigned i = 0 ; i < amount ; ++i ) { - res_bits.push_back(sign_bit); - } - - Assert (res_bits.size() == amount + bits.size()); -} - -void DefaultRotateRightBB (TNode node, Bits& res, Bitblaster* bb) { - Debug("bitvector") << "theory::bv:: Unimplemented kind " - << node.getKind() << "\n"; - - Unimplemented(); -} - -void DefaultRotateLeftBB (TNode node, Bits& bits, Bitblaster* bb) { - Debug("bitvector") << "theory::bv:: Unimplemented kind " - << node.getKind() << "\n"; - Unimplemented(); -} - - -} -} -} - - diff --git a/src/theory/bv/bitblast_strategies.h b/src/theory/bv/bitblast_strategies.h deleted file mode 100644 index cb29e430b..000000000 --- a/src/theory/bv/bitblast_strategies.h +++ /dev/null @@ -1,109 +0,0 @@ -/********************* */ -/*! \file bitblast_strategies.h - ** \verbatim - ** Original author: Liana Hadarean - ** Major contributors: none - ** Minor contributors (to current version): Dejan Jovanovic, Morgan Deters - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2013 New York University and The University of Iowa - ** See the file COPYING in the top-level source directory for licensing - ** information.\endverbatim - ** - ** \brief Implementation of bitblasting functions for various operators. - ** - ** Implementation of bitblasting functions for various operators. - **/ - -#include "cvc4_private.h" - -#ifndef __CVC4__BITBLAST__STRATEGIES_H -#define __CVC4__BITBLAST__STRATEGIES_H - - -#include "expr/node.h" -#include "prop/sat_solver.h" - -namespace CVC4 { - - -namespace theory { -namespace bv { - -class Bitblaster; - - -typedef std::vector Bits; - - -/** - * Default Atom Bitblasting strategies: - * - * @param node the atom to be bitblasted - * @param bb the bitblaster - */ - -Node UndefinedAtomBBStrategy (TNode node, Bitblaster* bb); -Node DefaultEqBB(TNode node, Bitblaster* bb); - -Node DefaultUltBB(TNode node, Bitblaster* bb); -Node DefaultUleBB(TNode node, Bitblaster* bb); -Node DefaultUgtBB(TNode node, Bitblaster* bb); -Node DefaultUgeBB(TNode node, Bitblaster* bb); - -Node DefaultSltBB(TNode node, Bitblaster* bb); -Node DefaultSleBB(TNode node, Bitblaster* bb); -Node DefaultSgtBB(TNode node, Bitblaster* bb); -Node DefaultSgeBB(TNode node, Bitblaster* bb); - -/// other modes -Node AdderUltBB(TNode node, Bitblaster* bb); -Node SltBB(TNode node, Bitblaster* bb); -Node SleBB(TNode node, Bitblaster* bb); - - -/** - * Default Term Bitblasting strategies - * - * @param node the term to be bitblasted - * @param bits [output parameter] bits representing the new term - * @param bb the bitblaster in which the clauses are added - */ - -void UndefinedTermBBStrategy(TNode node, Bits& bits, Bitblaster* bb); - -void DefaultVarBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultConstBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultNotBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultConcatBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultAndBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultOrBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultXorBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultXnorBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultNandBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultNorBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultCompBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultMultBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultPlusBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultSubBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultNegBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultUdivBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultUremBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultSdivBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultSremBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultSmodBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultShlBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultLshrBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultAshrBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultExtractBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultRepeatBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultZeroExtendBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultSignExtendBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultRotateRightBB (TNode node, Bits& bits, Bitblaster* bb); -void DefaultRotateLeftBB (TNode node, Bits& bits, Bitblaster* bb); - - -} -} -} - -#endif diff --git a/src/theory/bv/bitblast_strategies_template.h b/src/theory/bv/bitblast_strategies_template.h new file mode 100644 index 000000000..fba744d69 --- /dev/null +++ b/src/theory/bv/bitblast_strategies_template.h @@ -0,0 +1,829 @@ +/********************* */ +/*! \file bitblast_strategies.h + ** \verbatim + ** Original author: Liana Hadarean + ** Major contributors: Liana Hadarean + ** Minor contributors (to current version): Clark Barrett, Dejan Jovanovic, Morgan Deters, Tim King + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2013 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of bitblasting functions for various operators. + ** + ** Implementation of bitblasting functions for various operators. + **/ + +#ifndef __CVC4__BITBLAST__STRATEGIES_TEMPLATE_H +#define __CVC4__BITBLAST__STRATEGIES_TEMPLATE_H + +#include "cvc4_private.h" +#include "expr/node.h" +#include "theory/bv/bitblast_utils.h" +#include "theory/bv/theory_bv_utils.h" +#include +#include +namespace CVC4 { + +namespace theory { +namespace bv { + +/** + * Default Atom Bitblasting strategies: + * + * @param node the atom to be bitblasted + * @param bb the bitblaster + */ + +template +T UndefinedAtomBBStrategy(TNode node, TBitblaster* bb) { + Debug("bitvector") << "TheoryBV::Bitblaster Undefined bitblasting strategy for kind: " + << node.getKind() << "\n"; + Unreachable(); +} + + +template +T DefaultEqBB(TNode node, TBitblaster* bb) { + Debug("bitvector-bb") << "Bitblasting node " << node << "\n"; + + Assert(node.getKind() == kind::EQUAL); + std::vector lhs, rhs; + bb->bbTerm(node[0], lhs); + bb->bbTerm(node[1], rhs); + + Assert(lhs.size() == rhs.size()); + + std::vector bits_eq; + for (unsigned i = 0; i < lhs.size(); i++) { + T bit_eq = mkIff(lhs[i], rhs[i]); + bits_eq.push_back(bit_eq); + } + T bv_eq = mkAnd(bits_eq); + return bv_eq; +} + +template +T AdderUltBB(TNode node, TBitblaster* bb) { + Debug("bitvector-bb") << "Bitblasting node " << node << "\n"; + Assert(node.getKind() == kind::BITVECTOR_ULT); + std::vector a, b; + bb->bbTerm(node[0], a); + bb->bbTerm(node[1], b); + Assert(a.size() == b.size()); + + // a < b <=> ~ (add(a, ~b, 1).carry_out) + std::vector not_b; + negateBits(b, not_b); + T carry = mkTrue(); + + for (unsigned i = 0 ; i < a.size(); ++i) { + carry = mkOr( mkAnd(a[i], not_b[i]), + mkAnd( mkXor(a[i], not_b[i]), + carry)); + } + return mkNot(carry); +} + + +template +T DefaultUltBB(TNode node, TBitblaster* bb) { + Debug("bitvector-bb") << "Bitblasting node " << node << "\n"; + Assert(node.getKind() == kind::BITVECTOR_ULT); + std::vector a, b; + bb->bbTerm(node[0], a); + bb->bbTerm(node[1], b); + Assert(a.size() == b.size()); + + // construct bitwise comparison + T res = uLessThanBB(a, b, false); + return res; +} + +template +T DefaultUleBB(TNode node, TBitblaster* bb){ + Debug("bitvector-bb") << "Bitblasting node " << node << "\n"; + Assert(node.getKind() == kind::BITVECTOR_ULE); + std::vector a, b; + + bb->bbTerm(node[0], a); + bb->bbTerm(node[1], b); + Assert(a.size() == b.size()); + // construct bitwise comparison + T res = uLessThanBB(a, b, true); + return res; +} + +template +T DefaultUgtBB(TNode node, TBitblaster* bb){ + Debug("bitvector-bb") << "Bitblasting node " << node << "\n"; + // should be rewritten + Unimplemented(); +} +template +T DefaultUgeBB(TNode node, TBitblaster* bb){ + Debug("bitvector-bb") << "Bitblasting node " << node << "\n"; + // should be rewritten + Unimplemented(); +} + +template +T DefaultSltBB(TNode node, TBitblaster* bb){ + Debug("bitvector-bb") << "Bitblasting node " << node << "\n"; + + std::vector a, b; + bb->bbTerm(node[0], a); + bb->bbTerm(node[1], b); + Assert(a.size() == b.size()); + + T res = sLessThanBB(a, b, false); + return res; +} + +template +T DefaultSleBB(TNode node, TBitblaster* bb){ + Debug("bitvector-bb") << "Bitblasting node " << node << "\n"; + + std::vector a, b; + bb->bbTerm(node[0], a); + bb->bbTerm(node[1], b); + Assert(a.size() == b.size()); + + T res = sLessThanBB(a, b, true); + return res; +} + +template +T DefaultSgtBB(TNode node, TBitblaster* bb){ + Debug("bitvector-bb") << "Bitblasting node " << node << "\n"; + // should be rewritten + Unimplemented(); +} + +template +T DefaultSgeBB(TNode node, TBitblaster* bb){ + Debug("bitvector-bb") << "Bitblasting node " << node << "\n"; + // should be rewritten + Unimplemented(); +} + + +/// Term bitblasting strategies + +/** + * Default Term Bitblasting strategies + * + * @param node the term to be bitblasted + * @param bits [output parameter] bits representing the new term + * @param bb the bitblaster in which the clauses are added + */ +template +void UndefinedTermBBStrategy(TNode node, std::vector& bits, TBitblaster* bb) { + Debug("bitvector") << "theory::bv:: Undefined bitblasting strategy for kind: " + << node.getKind() << "\n"; + Unreachable(); +} + +template +void DefaultVarBB (TNode node, std::vector& bits, TBitblaster* bb) { + Assert(bits.size() == 0); + bb->makeVariable(node, bits); + + if(Debug.isOn("bitvector-bb")) { + Debug("bitvector-bb") << "theory::bv::DefaultVarBB bitblasting " << node << "\n"; + Debug("bitvector-bb") << " with bits " << toString(bits); + } +} + +template +void DefaultConstBB (TNode node, std::vector& bits, TBitblaster* bb) { + Debug("bitvector-bb") << "theory::bv::DefaultConstBB bitblasting " << node << "\n"; + Assert(node.getKind() == kind::CONST_BITVECTOR); + Assert(bits.size() == 0); + + for (unsigned i = 0; i < utils::getSize(node); ++i) { + Integer bit = node.getConst().extract(i, i).getValue(); + if(bit == Integer(0)){ + bits.push_back(mkFalse()); + } else { + Assert (bit == Integer(1)); + bits.push_back(mkTrue()); + } + } + if(Debug.isOn("bitvector-bb")) { + Debug("bitvector-bb") << "with bits: " << toString(bits) << "\n"; + } +} + + +template +void DefaultNotBB (TNode node, std::vector& bits, TBitblaster* bb) { + Debug("bitvector-bb") << "theory::bv::DefaultNotBB bitblasting " << node << "\n"; + Assert(node.getKind() == kind::BITVECTOR_NOT); + Assert(bits.size() == 0); + std::vector bv; + bb->bbTerm(node[0], bv); + negateBits(bv, bits); +} + +template +void DefaultConcatBB (TNode node, std::vector& bits, TBitblaster* bb) { + Debug("bitvector-bb") << "theory::bv::DefaultConcatBB bitblasting " << node << "\n"; + Assert(bits.size() == 0); + + Assert (node.getKind() == kind::BITVECTOR_CONCAT); + for (int i = node.getNumChildren() -1 ; i >= 0; --i ) { + TNode current = node[i]; + std::vector current_bits; + bb->bbTerm(current, current_bits); + + for(unsigned j = 0; j < utils::getSize(current); ++j) { + bits.push_back(current_bits[j]); + } + } + Assert (bits.size() == utils::getSize(node)); + if(Debug.isOn("bitvector-bb")) { + Debug("bitvector-bb") << "with bits: " << toString(bits) << "\n"; + } +} + +template +void DefaultAndBB (TNode node, std::vector& bits, TBitblaster* bb) { + Debug("bitvector-bb") << "theory::bv::DefaultAndBB bitblasting " << node << "\n"; + + Assert(node.getKind() == kind::BITVECTOR_AND && + bits.size() == 0); + + for(unsigned j = 0; j < utils::getSize(node); ++j) { + std::vector and_j; + for (unsigned i = 0; i < node.getNumChildren(); ++i) { + std::vector current; + bb->bbTerm(node[i], current); + and_j.push_back(current[j]); + Assert(utils::getSize(node) == current.size()); + } + bits.push_back(mkAnd(and_j)); + } +} + +template +void DefaultOrBB (TNode node, std::vector& bits, TBitblaster* bb) { + Debug("bitvector-bb") << "theory::bv::DefaultOrBB bitblasting " << node << "\n"; + + Assert(node.getKind() == kind::BITVECTOR_OR && + bits.size() == 0); + + for(unsigned j = 0; j < utils::getSize(node); ++j) { + std::vector or_j; + for (unsigned i = 0; i < node.getNumChildren(); ++i) { + std::vector current; + bb->bbTerm(node[i], current); + or_j.push_back(current[j]); + Assert(utils::getSize(node) == current.size()); + } + bits.push_back(mkOr(or_j)); + } +} + +template +void DefaultXorBB (TNode node, std::vector& bits, TBitblaster* bb) { + Debug("bitvector-bb") << "theory::bv::DefaultXorBB bitblasting " << node << "\n"; + + Assert(node.getKind() == kind::BITVECTOR_XOR && + bits.size() == 0); + + for(unsigned j = 0; j < utils::getSize(node); ++j) { + std::vector first; + bb->bbTerm(node[0], first); + T bitj = first[j]; + + for (unsigned i = 1; i < node.getNumChildren(); ++i) { + std::vector current; + bb->bbTerm(node[i], current); + bitj = mkXor(bitj, current[j]); + Assert(utils::getSize(node) == current.size()); + } + bits.push_back(bitj); + } +} + +template +void DefaultXnorBB (TNode node, std::vector& bits, TBitblaster* bb) { + Debug("bitvector-bb") << "theory::bv::DefaultXnorBB bitblasting " << node << "\n"; + + Assert(node.getNumChildren() == 2 && + node.getKind() == kind::BITVECTOR_XNOR && + bits.size() == 0); + std::vector lhs, rhs; + bb->bbTerm(node[0], lhs); + bb->bbTerm(node[1], rhs); + Assert(lhs.size() == rhs.size()); + + for (unsigned i = 0; i < lhs.size(); ++i) { + bits.push_back(mkIff(lhs[i], rhs[i])); + } +} + + +template +void DefaultNandBB (TNode node, std::vector& bits, TBitblaster* bb) { + Debug("bitvector") << "theory::bv:: Unimplemented kind " + << node.getKind() << "\n"; + Unimplemented(); +} +template +void DefaultNorBB (TNode node, std::vector& bits, TBitblaster* bb) { + Debug("bitvector") << "theory::bv:: Unimplemented kind " + << node.getKind() << "\n"; + Unimplemented(); +} +template +void DefaultCompBB (TNode node, std::vector& bits, TBitblaster* bb) { + Debug("bitvector") << "theory::bv:: DefaultCompBB bitblasting "<< node << "\n"; + + Assert(utils::getSize(node) == 1 && bits.size() == 0 && node.getKind() == kind::BITVECTOR_COMP); + std::vector a, b; + bb->bbTerm(node[0], a); + bb->bbTerm(node[1], b); + + std::vector bit_eqs; + for (unsigned i = 0; i < a.size(); ++i) { + T eq = mkIff(a[i], b[i]); + bit_eqs.push_back(eq); + } + T a_eq_b = mkAnd(bit_eqs); + bits.push_back(a_eq_b); +} + +template +void DefaultMultBB (TNode node, std::vector& res, TBitblaster* bb) { + Debug("bitvector") << "theory::bv:: DefaultMultBB bitblasting "<< node << "\n"; + Assert(res.size() == 0 && + node.getKind() == kind::BITVECTOR_MULT); + + // if (node.getNumChildren() == 2) { + // std::vector a; + // std::vector b; + // bb->bbTerm(node[0], a); + // bb->bbTerm(node[1], b); + // unsigned bw = utils::getSize(node); + // unsigned thresh = bw % 2 ? bw/2 : bw/2 - 1; + // bool no_overflow = true; + // for (unsigned i = thresh; i < bw; ++i) { + // if (a[i] != mkFalse || b[i] != mkFalse ) { + // no_overflow = false; + // } + // } + // if (no_overflow) { + // shiftAddMultiplier(); + // return; + // } + + // } + + std::vector newres; + bb->bbTerm(node[0], res); + for(unsigned i = 1; i < node.getNumChildren(); ++i) { + std::vector current; + bb->bbTerm(node[i], current); + newres.clear(); + // constructs a simple shift and add multiplier building the result + // in res + shiftAddMultiplier(res, current, newres); + res = newres; + } + if(Debug.isOn("bitvector-bb")) { + Debug("bitvector-bb") << "with bits: " << toString(res) << "\n"; + } +} + +template +void DefaultPlusBB (TNode node, std::vector& res, TBitblaster* bb) { + Debug("bitvector-bb") << "theory::bv::DefaultPlusBB bitblasting " << node << "\n"; + Assert(node.getKind() == kind::BITVECTOR_PLUS && + res.size() == 0); + + bb->bbTerm(node[0], res); + + std::vector newres; + + for(unsigned i = 1; i < node.getNumChildren(); ++i) { + std::vector current; + bb->bbTerm(node[i], current); + newres.clear(); + rippleCarryAdder(res, current, newres, mkFalse()); + res = newres; + } + + Assert(res.size() == utils::getSize(node)); +} + + +template +void DefaultSubBB (TNode node, std::vector& bits, TBitblaster* bb) { + Debug("bitvector-bb") << "theory::bv::DefaultSubBB bitblasting " << node << "\n"; + Assert(node.getKind() == kind::BITVECTOR_SUB && + node.getNumChildren() == 2 && + bits.size() == 0); + + std::vector a, b; + bb->bbTerm(node[0], a); + bb->bbTerm(node[1], b); + Assert(a.size() == b.size() && utils::getSize(node) == a.size()); + + // bvsub a b = adder(a, ~b, 1) + std::vector not_b; + negateBits(b, not_b); + rippleCarryAdder(a, not_b, bits, mkTrue()); +} + +template +void DefaultNegBB (TNode node, std::vector& bits, TBitblaster* bb) { + Debug("bitvector-bb") << "theory::bv::DefaultNegBB bitblasting " << node << "\n"; + Assert(node.getKind() == kind::BITVECTOR_NEG); + + std::vector a; + bb->bbTerm(node[0], a); + Assert(utils::getSize(node) == a.size()); + + // -a = add(~a, 0, 1). + std::vector not_a; + negateBits(a, not_a); + std::vector zero; + makeZero(zero, utils::getSize(node)); + + rippleCarryAdder(not_a, zero, bits, mkTrue()); +} + +template +void uDivModRec(const std::vector& a, const std::vector& b, std::vector& q, std::vector& r, unsigned rec_width) { + Assert( q.size() == 0 && r.size() == 0); + + if(rec_width == 0 || isZero(a)) { + makeZero(q, a.size()); + makeZero(r, a.size()); + return; + } + + std::vector q1, r1; + std::vector a1 = a; + rshift(a1, 1); + + uDivModRec(a1, b, q1, r1, rec_width - 1); + // shift the quotient and remainder (i.e. multiply by two) and add 1 to remainder if a is odd + lshift(q1, 1); + lshift(r1, 1); + + + T is_odd = mkIff(a[0], mkTrue()); + T one_if_odd = mkIte(is_odd, mkTrue(), mkFalse()); + + std::vector zero; + makeZero(zero, b.size()); + + std::vector r1_shift_add; + // account for a being odd + rippleCarryAdder(r1, zero, r1_shift_add, one_if_odd); + // now check if the remainder is greater than b + std::vector not_b; + negateBits(b, not_b); + std::vector r_minus_b; + T co1; + // use adder because we need r_minus_b anyway + co1 = rippleCarryAdder(r1_shift_add, not_b, r_minus_b, mkTrue()); + // sign is true if r1 < b + T sign = mkNot(co1); + + q1[0] = mkIte(sign, q1[0], mkTrue()); + + // would be nice to have a high level ITE instead of bitwise + for(unsigned i = 0; i < a.size(); ++i) { + r1_shift_add[i] = mkIte(sign, r1_shift_add[i], r_minus_b[i]); + } + + // check if a < b + + std::vector a_minus_b; + T co2 = rippleCarryAdder(a, not_b, a_minus_b, mkTrue()); + // Node a_lt_b = a_minus_b.back(); + T a_lt_b = mkNot(co2); + + for(unsigned i = 0; i < a.size(); ++i) { + T qval = mkIte(a_lt_b, mkFalse(), q1[i]); + T rval = mkIte(a_lt_b, a[i], r1_shift_add[i]); + q.push_back(qval); + r.push_back(rval); + } + +} + +template +void DefaultUdivBB (TNode node, std::vector& q, TBitblaster* bb) { + Debug("bitvector-bb") << "theory::bv::DefaultUdivBB bitblasting " << node << "\n"; + Assert(node.getKind() == kind::BITVECTOR_UDIV_TOTAL && q.size() == 0); + + std::vector a, b; + bb->bbTerm(node[0], a); + bb->bbTerm(node[1], b); + + std::vector r; + uDivModRec(a, b, q, r, utils::getSize(node)); + // adding a special case for division by 0 + std::vector iszero; + for (unsigned i = 0; i < b.size(); ++i) { + iszero.push_back(mkIff(b[i], mkFalse())); + } + T b_is_0 = mkAnd(iszero); + + for (unsigned i = 0; i < q.size(); ++i) { + q[i] = mkIte(b_is_0, mkTrue(), q[i]); + r[i] = mkIte(b_is_0, mkTrue(), r[i]); + } + + // cache the remainder in case we need it later + Node remainder = utils::mkNode(kind::BITVECTOR_UREM_TOTAL, node[0], node[1]); + bb->storeBBTerm(remainder, r); +} + +template +void DefaultUremBB (TNode node, std::vector& rem, TBitblaster* bb) { + Debug("bitvector-bb") << "theory::bv::DefaultUremBB bitblasting " << node << "\n"; + Assert(node.getKind() == kind::BITVECTOR_UREM_TOTAL && rem.size() == 0); + + std::vector a, b; + bb->bbTerm(node[0], a); + bb->bbTerm(node[1], b); + + std::vector q; + uDivModRec(a, b, q, rem, utils::getSize(node)); + // adding a special case for division by 0 + std::vector iszero; + for (unsigned i = 0; i < b.size(); ++i) { + iszero.push_back(mkIff(b[i], mkFalse())); + } + T b_is_0 = mkAnd(iszero); + + for (unsigned i = 0; i < q.size(); ++i) { + q[i] = mkIte(b_is_0, a[i], q[i]); + rem[i] = mkIte(b_is_0, a[i], rem[i]); + } + + // cache the quotient in case we need it later + Node quotient = utils::mkNode(kind::BITVECTOR_UDIV_TOTAL, node[0], node[1]); + bb->storeBBTerm(quotient, q); +} + + +template +void DefaultSdivBB (TNode node, std::vector& bits, TBitblaster* bb) { + Debug("bitvector") << "theory::bv:: Unimplemented kind " + << node.getKind() << "\n"; + Unimplemented(); +} +template +void DefaultSremBB (TNode node, std::vector& bits, TBitblaster* bb) { + Debug("bitvector") << "theory::bv:: Unimplemented kind " + << node.getKind() << "\n"; + Unimplemented(); +} +template +void DefaultSmodBB (TNode node, std::vector& bits, TBitblaster* bb) { + Debug("bitvector") << "theory::bv:: Unimplemented kind " + << node.getKind() << "\n"; + Unimplemented(); +} + +template +void DefaultShlBB (TNode node, std::vector& res, TBitblaster* bb) { + Debug("bitvector-bb") << "theory::bv::DefaultShlBB bitblasting " << node << "\n"; + Assert (node.getKind() == kind::BITVECTOR_SHL && + res.size() == 0); + std::vector a, b; + bb->bbTerm(node[0], a); + bb->bbTerm(node[1], b); + + // check for b < log2(n) + unsigned size = utils::getSize(node); + unsigned log2_size = std::ceil(log2((double)size)); + Node a_size = utils::mkConst(BitVector(size, size)); + Node b_ult_a_size_node = utils::mkNode(kind::BITVECTOR_ULT, node[1], a_size); + // ensure that the inequality is bit-blasted + bb->bbAtom(b_ult_a_size_node); + T b_ult_a_size = bb->getBBAtom(b_ult_a_size_node); + std::vector prev_res; + res = a; + // we only need to look at the bits bellow log2_a_size + for(unsigned s = 0; s < log2_size; ++s) { + // barrel shift stage: at each stage you can either shift by 2^s bits + // or leave the previous stage untouched + prev_res = res; + unsigned threshold = pow(2, s); + for(unsigned i = 0; i < a.size(); ++i) { + if (i < threshold) { + // if b[s] is true then we must have shifted by at least 2^b bits so + // all bits bellow 2^s will be 0, otherwise just use previous shift value + res[i] = mkIte(b[s], mkFalse(), prev_res[i]); + } else { + // if b[s]= 0, use previous value, otherwise shift by threshold bits + Assert(i >= threshold); + res[i] = mkIte(b[s], prev_res[i-threshold], prev_res[i]); + } + } + } + prev_res = res; + for (unsigned i = 0; i < b.size(); ++i) { + // this is fine because b_ult_a_size has been bit-blasted + res[i] = mkIte(b_ult_a_size, prev_res[i], mkFalse()); + } + + if(Debug.isOn("bitvector-bb")) { + Debug("bitvector-bb") << "with bits: " << toString(res) << "\n"; + } +} + +template +void DefaultLshrBB (TNode node, std::vector& res, TBitblaster* bb) { + Debug("bitvector-bb") << "theory::bv::DefaultLshrBB bitblasting " << node << "\n"; + Assert (node.getKind() == kind::BITVECTOR_LSHR && + res.size() == 0); + std::vector a, b; + bb->bbTerm(node[0], a); + bb->bbTerm(node[1], b); + + // check for b < log2(n) + unsigned size = utils::getSize(node); + unsigned log2_size = std::ceil(log2((double)size)); + Node a_size = utils::mkConst(BitVector(size, size)); + Node b_ult_a_size_node = utils::mkNode(kind::BITVECTOR_ULT, node[1], a_size); + // ensure that the inequality is bit-blasted + bb->bbAtom(b_ult_a_size_node); + T b_ult_a_size = bb->getBBAtom(b_ult_a_size_node); + res = a; + std::vector prev_res; + + for(unsigned s = 0; s < log2_size; ++s) { + // barrel shift stage: at each stage you can either shift by 2^s bits + // or leave the previous stage untouched + prev_res = res; + int threshold = pow(2, s); + for(unsigned i = 0; i < a.size(); ++i) { + if (i + threshold >= a.size()) { + // if b[s] is true then we must have shifted by at least 2^b bits so + // all bits above 2^s will be 0, otherwise just use previous shift value + res[i] = mkIte(b[s], mkFalse(), prev_res[i]); + } else { + // if b[s]= 0, use previous value, otherwise shift by threshold bits + Assert (i+ threshold < a.size()); + res[i] = mkIte(mkNot(b[s]), prev_res[i], prev_res[i+threshold]); + } + } + } + + prev_res = res; + for (unsigned i = 0; i < b.size(); ++i) { + // this is fine because b_ult_a_size has been bit-blasted + res[i] = mkIte(b_ult_a_size, prev_res[i], mkFalse()); + } + + if(Debug.isOn("bitvector-bb")) { + Debug("bitvector-bb") << "with bits: " << toString(res) << "\n"; + } +} + +template +void DefaultAshrBB (TNode node, std::vector& res, TBitblaster* bb) { + + Debug("bitvector-bb") << "theory::bv::DefaultAshrBB bitblasting " << node << "\n"; + Assert (node.getKind() == kind::BITVECTOR_ASHR && + res.size() == 0); + std::vector a, b; + bb->bbTerm(node[0], a); + bb->bbTerm(node[1], b); + + // check for b < n + unsigned size = utils::getSize(node); + unsigned log2_size = std::ceil(log2((double)size)); + Node a_size = utils::mkConst(BitVector(size, size)); + Node b_ult_a_size_node = utils::mkNode(kind::BITVECTOR_ULT, node[1], a_size); + // ensure that the inequality is bit-blasted + bb->bbAtom(b_ult_a_size_node); + T b_ult_a_size = bb->getBBAtom(b_ult_a_size_node); + + res = a; + T sign_bit = a.back(); + std::vector prev_res; + + for(unsigned s = 0; s < log2_size; ++s) { + // barrel shift stage: at each stage you can either shift by 2^s bits + // or leave the previous stage untouched + prev_res = res; + int threshold = pow(2, s); + for(unsigned i = 0; i < a.size(); ++i) { + if (i + threshold >= a.size()) { + // if b[s] is true then we must have shifted by at least 2^b bits so + // all bits above 2^s will be the sign bit, otherwise just use previous shift value + res[i] = mkIte(b[s], sign_bit, prev_res[i]); + } else { + // if b[s]= 0, use previous value, otherwise shift by threshold bits + Assert (i+ threshold < a.size()); + res[i] = mkIte(mkNot(b[s]), prev_res[i], prev_res[i+threshold]); + } + } + } + + prev_res = res; + for (unsigned i = 0; i < b.size(); ++i) { + // this is fine because b_ult_a_size has been bit-blasted + res[i] = mkIte(b_ult_a_size, prev_res[i], sign_bit); + } + + if(Debug.isOn("bitvector-bb")) { + Debug("bitvector-bb") << "with bits: " << toString(res) << "\n"; + } +} + +template +void DefaultExtractBB (TNode node, std::vector& bits, TBitblaster* bb) { + Assert (node.getKind() == kind::BITVECTOR_EXTRACT); + Assert(bits.size() == 0); + + std::vector base_bits; + bb->bbTerm(node[0], base_bits); + unsigned high = utils::getExtractHigh(node); + unsigned low = utils::getExtractLow(node); + + for (unsigned i = low; i <= high; ++i) { + bits.push_back(base_bits[i]); + } + Assert (bits.size() == high - low + 1); + + if(Debug.isOn("bitvector-bb")) { + Debug("bitvector-bb") << "theory::bv::DefaultExtractBB bitblasting " << node << "\n"; + Debug("bitvector-bb") << " with bits " << toString(bits); + } +} + + +template +void DefaultRepeatBB (TNode node, std::vector& bits, TBitblaster* bb) { + Debug("bitvector") << "theory::bv:: Unimplemented kind " + << node.getKind() << "\n"; + // this should be rewritten + Unimplemented(); +} + +template +void DefaultZeroExtendBB (TNode node, std::vector& res_bits, TBitblaster* bb) { + + Debug("bitvector-bb") << "theory::bv::DefaultZeroExtendBB bitblasting " << node << "\n"; + + // this should be rewritten + Unimplemented(); + +} + +template +void DefaultSignExtendBB (TNode node, std::vector& res_bits, TBitblaster* bb) { + Debug("bitvector-bb") << "theory::bv::DefaultSignExtendBB bitblasting " << node << "\n"; + + Assert (node.getKind() == kind::BITVECTOR_SIGN_EXTEND && + res_bits.size() == 0); + + std::vector bits; + bb->bbTerm(node[0], bits); + + T sign_bit = bits.back(); + unsigned amount = node.getOperator().getConst().signExtendAmount; + + for (unsigned i = 0; i < bits.size(); ++i ) { + res_bits.push_back(bits[i]); + } + + for (unsigned i = 0 ; i < amount ; ++i ) { + res_bits.push_back(sign_bit); + } + + Assert (res_bits.size() == amount + bits.size()); +} + +template +void DefaultRotateRightBB (TNode node, std::vector& res, TBitblaster* bb) { + Debug("bitvector") << "theory::bv:: Unimplemented kind " + << node.getKind() << "\n"; + + Unimplemented(); +} + +template +void DefaultRotateLeftBB (TNode node, std::vector& bits, TBitblaster* bb) { + Debug("bitvector") << "theory::bv:: Unimplemented kind " + << node.getKind() << "\n"; + Unimplemented(); +} + + +} +} +} + +#endif diff --git a/src/theory/bv/bitblast_utils.h b/src/theory/bv/bitblast_utils.h new file mode 100644 index 000000000..91cc2d640 --- /dev/null +++ b/src/theory/bv/bitblast_utils.h @@ -0,0 +1,283 @@ +/********************* */ +/*! \file bitblast_utils.h + ** \verbatim + ** Original author: Liana Hadarean + ** Major contributors: none + ** Minor contributors (to current version): none. + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2013 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Various utility functions for bit-blasting. + ** + ** Various utility functions for bit-blasting. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__BITBLAST__UTILS_H +#define __CVC4__BITBLAST__UTILS_H + + +#include +#include "expr/node.h" + +#ifdef CVC4_USE_ABC +#include "base/main/main.h" +#include "base/abc/abc.h" + +extern "C" { +#include "sat/cnf/cnf.h" +} +#endif + +namespace CVC4 { + +namespace theory { +namespace bv { + +template class TBitblaster; + +template +std::string toString (const std::vector& bits); + +template <> inline +std::string toString (const std::vector& bits) { + std::ostringstream os; + for (int i = bits.size() - 1; i >= 0; --i) { + TNode bit = bits[i]; + if (bit.getKind() == kind::CONST_BOOLEAN) { + os << (bit.getConst() ? "1" : "0"); + } else { + os << bit<< " "; + } + } + os <<"\n"; + return os.str(); +} + +template T mkTrue(); +template T mkFalse(); +template T mkNot(T a); +template T mkOr(T a, T b); +template T mkOr(const std::vector& a); +template T mkAnd(T a, T b); +template T mkAnd(const std::vector& a); +template T mkXor(T a, T b); +template T mkIff(T a, T b); +template T mkIte(T cond, T a, T b); + + +template <> inline +Node mkTrue() { + return NodeManager::currentNM()->mkConst(true); +} + +template <> inline +Node mkFalse() { + return NodeManager::currentNM()->mkConst(false); +} + +template <> inline +Node mkNot(Node a) { + return NodeManager::currentNM()->mkNode(kind::NOT, a); +} + +template <> inline +Node mkOr(Node a, Node b) { + return NodeManager::currentNM()->mkNode(kind::OR, a, b); +} + +template <> inline +Node mkOr(const std::vector& children) { + Assert (children.size()); + if (children.size() == 1) + return children[0]; + return NodeManager::currentNM()->mkNode(kind::OR, children); +} + + +template <> inline +Node mkAnd(Node a, Node b) { + return NodeManager::currentNM()->mkNode(kind::AND, a, b); +} + +template <> inline +Node mkAnd(const std::vector& children) { + Assert (children.size()); + if (children.size() == 1) + return children[0]; + return NodeManager::currentNM()->mkNode(kind::AND, children); +} + + +template <> inline +Node mkXor(Node a, Node b) { + return NodeManager::currentNM()->mkNode(kind::XOR, a, b); +} + +template <> inline +Node mkIff(Node a, Node b) { + return NodeManager::currentNM()->mkNode(kind::IFF, a, b); +} + +template <> inline +Node mkIte(Node cond, Node a, Node b) { + return NodeManager::currentNM()->mkNode(kind::ITE, cond, a, b); +} + +/* + Various helper functions that get called by the bitblasting procedures + */ + +template +void inline extractBits(const std::vector& b, std::vector& dest, unsigned lo, unsigned hi) { + Assert ( lo < b.size() && hi < b.size() && lo <= hi); + for (unsigned i = lo; i <= hi; ++i) { + dest.push_back(b[i]); + } +} + +template +void inline negateBits(const std::vector& bits, std::vector& negated_bits) { + for(unsigned i = 0; i < bits.size(); ++i) { + negated_bits.push_back(mkNot(bits[i])); + } +} + +template +bool inline isZero(const std::vector& bits) { + for(unsigned i = 0; i < bits.size(); ++i) { + if(bits[i] != mkFalse()) { + return false; + } + } + return true; +} + +template +void inline rshift(std::vector& bits, unsigned amount) { + for (unsigned i = 0; i < bits.size() - amount; ++i) { + bits[i] = bits[i+amount]; + } + for(unsigned i = bits.size() - amount; i < bits.size(); ++i) { + bits[i] = mkFalse(); + } +} + +template +void inline lshift(std::vector& bits, unsigned amount) { + for (int i = (int)bits.size() - 1; i >= (int)amount ; --i) { + bits[i] = bits[i-amount]; + } + for(unsigned i = 0; i < amount; ++i) { + bits[i] = mkFalse(); + } +} + +template +void inline makeZero(std::vector& bits, unsigned width) { + Assert(bits.size() == 0); + for(unsigned i = 0; i < width; ++i) { + bits.push_back(mkFalse()); + } +} + + +/** + * Constructs a simple ripple carry adder + * + * @param a first term to be added + * @param b second term to be added + * @param res the result + * @param carry the carry-in bit + * + * @return the carry-out + */ +template +T inline rippleCarryAdder(const std::vector&a, const std::vector& b, std::vector& res, T carry) { + Assert(a.size() == b.size() && res.size() == 0); + + for (unsigned i = 0 ; i < a.size(); ++i) { + T sum = mkXor(mkXor(a[i], b[i]), carry); + carry = mkOr( mkAnd(a[i], b[i]), + mkAnd( mkXor(a[i], b[i]), + carry)); + res.push_back(sum); + } + + return carry; +} + +template +inline void shiftAddMultiplier(const std::vector&a, const std::vector&b, std::vector& res) { + + for (unsigned i = 0; i < a.size(); ++i) { + res.push_back(mkAnd(b[0], a[i])); + } + + for(unsigned k = 1; k < res.size(); ++k) { + T carry_in = mkFalse(); + T carry_out; + for(unsigned j = 0; j < res.size() -k; ++j) { + T aj = mkAnd(a[j], b[k]); + carry_out = mkOr(mkAnd(res[j+k], aj), + mkAnd( mkXor(res[j+k], aj), carry_in)); + res[j+k] = mkXor(mkXor(res[j+k], aj), carry_in); + carry_in = carry_out; + } + } +} + +template +T inline uLessThanBB(const std::vector&a, const std::vector& b, bool orEqual) { + Assert (a.size() && b.size()); + + T res = mkAnd(mkNot(a[0]), b[0]); + + if(orEqual) { + res = mkOr(res, mkIff(a[0], b[0])); + } + + for (unsigned i = 1; i < a.size(); ++i) { + // a < b iff ( a[i] <-> b[i] AND a[i-1:0] < b[i-1:0]) OR (~a[i] AND b[i]) + res = mkOr(mkAnd(mkIff(a[i], b[i]), res), + mkAnd(mkNot(a[i]), b[i])); + } + return res; +} + +template +T inline sLessThanBB(const std::vector&a, const std::vector& b, bool orEqual) { + Assert (a.size() && b.size()); + if (a.size() == 1) { + if(orEqual) { + return mkOr(mkIff(a[0], b[0]), + mkAnd(a[0], mkNot(b[0]))); + } + + return mkAnd(a[0], mkNot(b[0])); + } + unsigned n = a.size() - 1; + std::vector a1, b1; + extractBits(a, a1, 0, n-1); + extractBits(b, b1, 0, n-1); + + // unsigned comparison of the first n-1 bits + T ures = uLessThanBB(a1, b1, orEqual); + T res = mkOr(// a b have the same sign + mkAnd(mkIff(a[n], b[n]), + ures), + // a is negative and b positive + mkAnd(a[n], + mkNot(b[n]))); + return res; +} + + +} +} +} + +#endif diff --git a/src/theory/bv/bitblaster.cpp b/src/theory/bv/bitblaster.cpp deleted file mode 100644 index 552f3b448..000000000 --- a/src/theory/bv/bitblaster.cpp +++ /dev/null @@ -1,505 +0,0 @@ -/********************* */ -/*! \file bitblaster.cpp - ** \verbatim - ** Original author: Liana Hadarean - ** Major contributors: Dejan Jovanovic, Andrew Reynolds - ** Minor contributors (to current version): Clark Barrett, Kshitij Bansal, Morgan Deters - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2013 New York University and The University of Iowa - ** See the file COPYING in the top-level source directory for licensing - ** information.\endverbatim - ** - ** \brief [[ Add one-line brief description here ]] - ** - ** [[ Add lengthier description here ]] - ** - **/ - -#include "bitblaster.h" -#include "theory_bv_utils.h" -#include "theory/rewriter.h" -#include "prop/cnf_stream.h" -#include "prop/sat_solver.h" -#include "prop/sat_solver_factory.h" -#include "theory/bv/theory_bv_rewrite_rules_simplification.h" -#include "theory/bv/theory_bv.h" -#include "theory/bv/options.h" -#include "theory/theory_model.h" - -using namespace std; - -using namespace CVC4::theory::bv::utils; -using namespace CVC4::context; -using namespace CVC4::prop; - -namespace CVC4 { -namespace theory { -namespace bv{ - -std::string toString(Bits& bits) { - ostringstream os; - for (int i = bits.size() - 1; i >= 0; --i) { - TNode bit = bits[i]; - if (bit.getKind() == kind::CONST_BOOLEAN) { - os << (bit.getConst() ? "1" : "0"); - } else { - os << bit<< " "; - } - } - os <<"\n"; - - return os.str(); -} -/////// Bitblaster - -Bitblaster::Bitblaster(context::Context* c, bv::TheoryBV* bv) : - d_bv(bv), - d_bvOutput(bv->d_out), - d_termCache(), - d_bitblastedAtoms(), - d_assertedAtoms(c), - d_statistics() - { - d_satSolver = prop::SatSolverFactory::createMinisat(c); - d_nullRegistrar = new NullRegistrar(); - d_nullContext = new Context(); - d_cnfStream = new TseitinCnfStream(d_satSolver, d_nullRegistrar, d_nullContext); - - d_notify = new MinisatNotify(d_cnfStream, bv); - d_satSolver->setNotify(d_notify); - // initializing the bit-blasting strategies - initAtomBBStrategies(); - initTermBBStrategies(); - } - -Bitblaster::~Bitblaster() { - delete d_cnfStream; - delete d_nullContext; - delete d_nullRegistrar; - delete d_satSolver; - delete d_notify; -} - - -/** - * Bitblasts the atom, assigns it a marker literal, adding it to the SAT solver - * NOTE: duplicate clauses are not detected because of marker literal - * @param node the atom to be bitblasted - * - */ -void Bitblaster::bbAtom(TNode node) { - node = node.getKind() == kind::NOT? node[0] : node; - - if (hasBBAtom(node)) { - return; - } - - // make sure it is marked as an atom - addAtom(node); - - Debug("bitvector-bitblast") << "Bitblasting node " << node <<"\n"; - ++d_statistics.d_numAtoms; - // the bitblasted definition of the atom - Node normalized = Rewriter::rewrite(node); - Node atom_bb = normalized.getKind() != kind::CONST_BOOLEAN ? - Rewriter::rewrite(d_atomBBStrategies[normalized.getKind()](normalized, this)) : - normalized; - // asserting that the atom is true iff the definition holds - Node atom_definition = mkNode(kind::IFF, node, atom_bb); - - if (!options::bitvectorEagerBitblast()) { - d_cnfStream->convertAndAssert(atom_definition, false, false); - d_bitblastedAtoms.insert(node); - } else { - d_bvOutput->lemma(atom_definition, false); - d_bitblastedAtoms.insert(node); - } -} - -uint64_t Bitblaster::computeAtomWeight(TNode node) { - node = node.getKind() == kind::NOT? node[0] : node; - - Node atom_bb = Rewriter::rewrite(d_atomBBStrategies[node.getKind()](node, this)); - uint64_t size = utils::numNodes(atom_bb); - return size; -} - -void Bitblaster::bbTerm(TNode node, Bits& bits) { - - if (hasBBTerm(node)) { - getBBTerm(node, bits); - return; - } - - Debug("bitvector-bitblast") << "Bitblasting node " << node <<"\n"; - ++d_statistics.d_numTerms; - - d_termBBStrategies[node.getKind()] (node, bits,this); - - Assert (bits.size() == utils::getSize(node)); - - cacheTermDef(node, bits); -} - -Node Bitblaster::bbOptimize(TNode node) { - std::vector children; - - if (node.getKind() == kind::BITVECTOR_PLUS) { - if (RewriteRule::applies(node)) { - Node res = RewriteRule::run(node); - return res; - } - // if (RewriteRule::applies(node)) { - // Node res = RewriteRule::run(node); - // return res; - // } - - } else if (node.getKind() == kind::BITVECTOR_MULT) { - if (RewriteRule::applies(node)) { - Node res = RewriteRule::run(node); - return res; - } - } - - return node; -} - -/// Public methods - -void Bitblaster::addAtom(TNode atom) { - if (!options::bitvectorEagerBitblast()) { - d_cnfStream->ensureLiteral(atom); - SatLiteral lit = d_cnfStream->getLiteral(atom); - d_satSolver->addMarkerLiteral(lit); - } -} - -void Bitblaster::explain(TNode atom, std::vector& explanation) { - std::vector literal_explanation; - d_satSolver->explain(d_cnfStream->getLiteral(atom), literal_explanation); - for (unsigned i = 0; i < literal_explanation.size(); ++i) { - explanation.push_back(d_cnfStream->getNode(literal_explanation[i])); - } -} - - -/* - * Asserts the clauses corresponding to the atom to the Sat Solver - * by turning on the marker literal (i.e. setting it to false) - * @param node the atom to be asserted - * - */ - -bool Bitblaster::propagate() { - return d_satSolver->propagate() == prop::SAT_VALUE_TRUE; -} - -bool Bitblaster::assertToSat(TNode lit, bool propagate) { - // strip the not - TNode atom; - if (lit.getKind() == kind::NOT) { - atom = lit[0]; - } else { - atom = lit; - } - - Assert (hasBBAtom(atom)); - - SatLiteral markerLit = d_cnfStream->getLiteral(atom); - - if(lit.getKind() == kind::NOT) { - markerLit = ~markerLit; - } - - Debug("bitvector-bb") << "TheoryBV::Bitblaster::assertToSat asserting node: " << atom <<"\n"; - Debug("bitvector-bb") << "TheoryBV::Bitblaster::assertToSat with literal: " << markerLit << "\n"; - - SatValue ret = d_satSolver->assertAssumption(markerLit, propagate); - - d_assertedAtoms.push_back(markerLit); - - Assert(ret != prop::SAT_VALUE_UNKNOWN); - return ret == prop::SAT_VALUE_TRUE; -} - -/** - * Calls the solve method for the Sat Solver. - * passing it the marker literals to be asserted - * - * @return true for sat, and false for unsat - */ - -bool Bitblaster::solve(bool quick_solve) { - if (Trace.isOn("bitvector")) { - Trace("bitvector") << "Bitblaster::solve() asserted atoms "; - context::CDList::const_iterator it = d_assertedAtoms.begin(); - for (; it != d_assertedAtoms.end(); ++it) { - Trace("bitvector") << " " << d_cnfStream->getNode(*it) << "\n"; - } - } - Debug("bitvector") << "Bitblaster::solve() asserted atoms " << d_assertedAtoms.size() <<"\n"; - return SAT_VALUE_TRUE == d_satSolver->solve(); -} - -void Bitblaster::getConflict(std::vector& conflict) { - SatClause conflictClause; - d_satSolver->getUnsatCore(conflictClause); - - for (unsigned i = 0; i < conflictClause.size(); i++) { - SatLiteral lit = conflictClause[i]; - TNode atom = d_cnfStream->getNode(lit); - Node not_atom; - if (atom.getKind() == kind::NOT) { - not_atom = atom[0]; - } else { - not_atom = NodeManager::currentNM()->mkNode(kind::NOT, atom); - } - conflict.push_back(not_atom); - } -} - - -/// Helper methods - - -void Bitblaster::initAtomBBStrategies() { - for (int i = 0 ; i < kind::LAST_KIND; ++i ) { - d_atomBBStrategies[i] = UndefinedAtomBBStrategy; - } - - /// setting default bb strategies for atoms - d_atomBBStrategies [ kind::EQUAL ] = DefaultEqBB; - d_atomBBStrategies [ kind::BITVECTOR_ULT ] = DefaultUltBB; - d_atomBBStrategies [ kind::BITVECTOR_ULE ] = DefaultUleBB; - d_atomBBStrategies [ kind::BITVECTOR_UGT ] = DefaultUgtBB; - d_atomBBStrategies [ kind::BITVECTOR_UGE ] = DefaultUgeBB; - d_atomBBStrategies [ kind::BITVECTOR_SLT ] = DefaultSltBB; - d_atomBBStrategies [ kind::BITVECTOR_SLE ] = DefaultSleBB; - d_atomBBStrategies [ kind::BITVECTOR_SGT ] = DefaultSgtBB; - d_atomBBStrategies [ kind::BITVECTOR_SGE ] = DefaultSgeBB; - -} - -void Bitblaster::initTermBBStrategies() { - // Changed this to DefaultVarBB because any foreign kind should be treated as a variable - // TODO: check this is OK - for (int i = 0 ; i < kind::LAST_KIND; ++i ) { - d_termBBStrategies[i] = DefaultVarBB; - } - - /// setting default bb strategies for terms: - // d_termBBStrategies [ kind::VARIABLE ] = DefaultVarBB; - d_termBBStrategies [ kind::CONST_BITVECTOR ] = DefaultConstBB; - d_termBBStrategies [ kind::BITVECTOR_NOT ] = DefaultNotBB; - d_termBBStrategies [ kind::BITVECTOR_CONCAT ] = DefaultConcatBB; - d_termBBStrategies [ kind::BITVECTOR_AND ] = DefaultAndBB; - d_termBBStrategies [ kind::BITVECTOR_OR ] = DefaultOrBB; - d_termBBStrategies [ kind::BITVECTOR_XOR ] = DefaultXorBB; - d_termBBStrategies [ kind::BITVECTOR_XNOR ] = DefaultXnorBB; - d_termBBStrategies [ kind::BITVECTOR_NAND ] = DefaultNandBB ; - d_termBBStrategies [ kind::BITVECTOR_NOR ] = DefaultNorBB; - d_termBBStrategies [ kind::BITVECTOR_COMP ] = DefaultCompBB ; - d_termBBStrategies [ kind::BITVECTOR_MULT ] = DefaultMultBB; - d_termBBStrategies [ kind::BITVECTOR_PLUS ] = DefaultPlusBB; - d_termBBStrategies [ kind::BITVECTOR_SUB ] = DefaultSubBB; - d_termBBStrategies [ kind::BITVECTOR_NEG ] = DefaultNegBB; - d_termBBStrategies [ kind::BITVECTOR_UDIV ] = UndefinedTermBBStrategy; - d_termBBStrategies [ kind::BITVECTOR_UREM ] = UndefinedTermBBStrategy; - d_termBBStrategies [ kind::BITVECTOR_UDIV_TOTAL ] = DefaultUdivBB; - d_termBBStrategies [ kind::BITVECTOR_UREM_TOTAL ] = DefaultUremBB; - d_termBBStrategies [ kind::BITVECTOR_SDIV ] = UndefinedTermBBStrategy; - d_termBBStrategies [ kind::BITVECTOR_SREM ] = UndefinedTermBBStrategy; - d_termBBStrategies [ kind::BITVECTOR_SMOD ] = UndefinedTermBBStrategy; - d_termBBStrategies [ kind::BITVECTOR_SHL ] = DefaultShlBB; - d_termBBStrategies [ kind::BITVECTOR_LSHR ] = DefaultLshrBB; - d_termBBStrategies [ kind::BITVECTOR_ASHR ] = DefaultAshrBB; - d_termBBStrategies [ kind::BITVECTOR_EXTRACT ] = DefaultExtractBB; - d_termBBStrategies [ kind::BITVECTOR_REPEAT ] = DefaultRepeatBB; - d_termBBStrategies [ kind::BITVECTOR_ZERO_EXTEND ] = DefaultZeroExtendBB; - d_termBBStrategies [ kind::BITVECTOR_SIGN_EXTEND ] = DefaultSignExtendBB; - d_termBBStrategies [ kind::BITVECTOR_ROTATE_RIGHT ] = DefaultRotateRightBB; - d_termBBStrategies [ kind::BITVECTOR_ROTATE_LEFT ] = DefaultRotateLeftBB; - -} - -bool Bitblaster::hasBBAtom(TNode atom) const { - return d_bitblastedAtoms.find(atom) != d_bitblastedAtoms.end(); -} - -void Bitblaster::cacheTermDef(TNode term, Bits def) { - Assert (d_termCache.find(term) == d_termCache.end()); - d_termCache[term] = def; -} - -bool Bitblaster::hasBBTerm(TNode node) const { - return d_termCache.find(node) != d_termCache.end(); -} - -void Bitblaster::getBBTerm(TNode node, Bits& bits) const { - Assert (hasBBTerm(node)); - // copy? - bits = d_termCache.find(node)->second; -} - -Bitblaster::Statistics::Statistics() : - d_numTermClauses("theory::bv::NumberOfTermSatClauses", 0), - d_numAtomClauses("theory::bv::NumberOfAtomSatClauses", 0), - d_numTerms("theory::bv::NumberOfBitblastedTerms", 0), - d_numAtoms("theory::bv::NumberOfBitblastedAtoms", 0), - d_bitblastTimer("theory::bv::BitblastTimer") -{ - StatisticsRegistry::registerStat(&d_numTermClauses); - StatisticsRegistry::registerStat(&d_numAtomClauses); - StatisticsRegistry::registerStat(&d_numTerms); - StatisticsRegistry::registerStat(&d_numAtoms); - StatisticsRegistry::registerStat(&d_bitblastTimer); -} - - -Bitblaster::Statistics::~Statistics() { - StatisticsRegistry::unregisterStat(&d_numTermClauses); - StatisticsRegistry::unregisterStat(&d_numAtomClauses); - StatisticsRegistry::unregisterStat(&d_numTerms); - StatisticsRegistry::unregisterStat(&d_numAtoms); - StatisticsRegistry::unregisterStat(&d_bitblastTimer); -} - -bool Bitblaster::MinisatNotify::notify(prop::SatLiteral lit) { - return d_bv->storePropagation(d_cnf->getNode(lit), SUB_BITBLAST); -}; - -void Bitblaster::MinisatNotify::notify(prop::SatClause& clause) { - if (clause.size() > 1) { - NodeBuilder<> lemmab(kind::OR); - for (unsigned i = 0; i < clause.size(); ++ i) { - lemmab << d_cnf->getNode(clause[i]); - } - Node lemma = lemmab; - d_bv->d_out->lemma(lemma); - } else { - d_bv->d_out->lemma(d_cnf->getNode(clause[0])); - } -}; - -void Bitblaster::MinisatNotify::safePoint() { - d_bv->d_out->safePoint(); -} - -EqualityStatus Bitblaster::getEqualityStatus(TNode a, TNode b) { - - // We don't want to bit-blast every possibly expensive term for the sake of equality checking - if (hasBBTerm(a) && hasBBTerm(b)) { - - Bits a_bits, b_bits; - getBBTerm(a, a_bits); - getBBTerm(b, b_bits); - EqualityStatus status = EQUALITY_TRUE_IN_MODEL; - for (unsigned i = 0; i < a_bits.size(); ++ i) { - if (d_cnfStream->hasLiteral(a_bits[i]) && d_cnfStream->hasLiteral(b_bits[i])) { - SatLiteral a_lit = d_cnfStream->getLiteral(a_bits[i]); - SatValue a_lit_value = d_satSolver->value(a_lit); - if (a_lit_value != SAT_VALUE_UNKNOWN) { - SatLiteral b_lit = d_cnfStream->getLiteral(b_bits[i]); - SatValue b_lit_value = d_satSolver->value(b_lit); - if (b_lit_value != SAT_VALUE_UNKNOWN) { - if (a_lit_value != b_lit_value) { - return EQUALITY_FALSE_IN_MODEL; - } - } else { - status = EQUALITY_UNKNOWN; - } - } { - status = EQUALITY_UNKNOWN; - } - } else { - status = EQUALITY_UNKNOWN; - } - } - - return status; - - } else { - return EQUALITY_UNKNOWN; - } -} - - -bool Bitblaster::isSharedTerm(TNode node) { - return d_bv->d_sharedTermsSet.find(node) != d_bv->d_sharedTermsSet.end(); -} - -bool Bitblaster::hasValue(TNode a) { - Assert (d_termCache.find(a) != d_termCache.end()); - Bits bits = d_termCache[a]; - for (int i = bits.size() -1; i >= 0; --i) { - SatValue bit_value; - if (d_cnfStream->hasLiteral(bits[i])) { - SatLiteral bit = d_cnfStream->getLiteral(bits[i]); - bit_value = d_satSolver->value(bit); - if (bit_value == SAT_VALUE_UNKNOWN) - return false; - } else { - return false; - } - } - return true; -} -/** - * Returns the value a is currently assigned to in the SAT solver - * or null if the value is completely unassigned. - * - * @param a - * @param fullModel whether to create a "full model," i.e., add - * constants to equivalence classes that don't already have them - * - * @return - */ -Node Bitblaster::getVarValue(TNode a, bool fullModel) { - if (d_termCache.find(a) == d_termCache.end()) { - Assert(isSharedTerm(a)); - return Node(); - } - Bits bits = d_termCache[a]; - Integer value(0); - for (int i = bits.size() -1; i >= 0; --i) { - SatValue bit_value; - if (d_cnfStream->hasLiteral(bits[i])) { - SatLiteral bit = d_cnfStream->getLiteral(bits[i]); - bit_value = d_satSolver->value(bit); - Assert (bit_value != SAT_VALUE_UNKNOWN); - } else { - //TODO: return Node() if fullModel=false? - // the bit is unconstrainted so we can give it an arbitrary value - bit_value = SAT_VALUE_FALSE; - } - Integer bit_int = bit_value == SAT_VALUE_TRUE ? Integer(1) : Integer(0); - value = value * 2 + bit_int; - } - return utils::mkConst(BitVector(bits.size(), value)); -} - -void Bitblaster::collectModelInfo(TheoryModel* m, bool fullModel) { - __gnu_cxx::hash_set::iterator it = d_variables.begin(); - for (; it!= d_variables.end(); ++it) { - TNode var = *it; - if (Theory::theoryOf(var) == theory::THEORY_BV || isSharedTerm(var)) { - Node const_value = getVarValue(var, fullModel); - if(const_value == Node()) { - if( fullModel ){ - // if the value is unassigned just set it to zero - const_value = utils::mkConst(BitVector(utils::getSize(var), 0u)); - } - } - if(const_value != Node()) { - Debug("bitvector-model") << "Bitblaster::collectModelInfo (assert (= " - << var << " " - << const_value << "))\n"; - m->assertEquality(var, const_value, true); - } - } - } -} - -} /*bv namespace */ -} /* theory namespace */ -} /* CVC4 namespace*/ diff --git a/src/theory/bv/bitblaster.h b/src/theory/bv/bitblaster.h deleted file mode 100644 index 34345086b..000000000 --- a/src/theory/bv/bitblaster.h +++ /dev/null @@ -1,197 +0,0 @@ -/********************* */ -/*! \file bitblaster.h - ** \verbatim - ** Original author: Liana Hadarean - ** Major contributors: Andrew Reynolds - ** Minor contributors (to current version): Kshitij Bansal, Morgan Deters, Dejan Jovanovic - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2013 New York University and The University of Iowa - ** See the file COPYING in the top-level source directory for licensing - ** information.\endverbatim - ** - ** \brief Wrapper around the SAT solver used for bitblasting - ** - ** Wrapper around the SAT solver used for bitblasting. - **/ - -#include "cvc4_private.h" - -#ifndef __CVC4__BITBLASTER_H -#define __CVC4__BITBLASTER_H - - -#include "expr/node.h" -#include -#include -#include -#include -#include - -#include "context/cdo.h" -#include "context/cdhashset.h" -#include "context/cdlist.h" - -#include "theory/theory.h" -#include "theory_bv_utils.h" -#include "util/statistics_registry.h" -#include "bitblast_strategies.h" - -#include "prop/sat_solver.h" -#include "prop/registrar.h" - -namespace CVC4 { - -// forward declarations -namespace prop { -class CnfStream; -class BVSatSolverInterface; -} - -namespace theory { - -class OutputChannel; -class TheoryModel; - -namespace bv { - -typedef std::vector Bits; - -std::string toString (Bits& bits); - -class TheoryBV; - -/** - * The Bitblaster that manages the mapping between Nodes - * and their bitwise definition - * - */ -class Bitblaster { - - /** This class gets callbacks from minisat on propagations */ - class MinisatNotify : public prop::BVSatSolverInterface::Notify { - prop::CnfStream* d_cnf; - TheoryBV *d_bv; - public: - MinisatNotify(prop::CnfStream* cnf, TheoryBV *bv) - : d_cnf(cnf) - , d_bv(bv) - {} - bool notify(prop::SatLiteral lit); - void notify(prop::SatClause& clause); - void safePoint(); - }; - - - typedef __gnu_cxx::hash_map TermDefMap; - typedef __gnu_cxx::hash_set AtomSet; - typedef __gnu_cxx::hash_set VarSet; - - typedef void (*TermBBStrategy) (TNode, Bits&, Bitblaster*); - typedef Node (*AtomBBStrategy) (TNode, Bitblaster*); - - TheoryBV *d_bv; - - // sat solver used for bitblasting and associated CnfStream - theory::OutputChannel* d_bvOutput; - MinisatNotify* d_notify; - prop::BVSatSolverInterface* d_satSolver; - prop::CnfStream* d_cnfStream; - prop::NullRegistrar* d_nullRegistrar; - context::Context* d_nullContext; - - // caches and mappings - TermDefMap d_termCache; - AtomSet d_bitblastedAtoms; - VarSet d_variables; - context::CDList d_assertedAtoms; /**< context dependent list storing the atoms - currently asserted by the DPLL SAT solver. */ - - /// helper methods - public: - bool hasBBAtom(TNode node) const; - private: - bool hasBBTerm(TNode node) const; - void getBBTerm(TNode node, Bits& bits) const; - - /// function tables for the various bitblasting strategies indexed by node kind - TermBBStrategy d_termBBStrategies[kind::LAST_KIND]; - AtomBBStrategy d_atomBBStrategies[kind::LAST_KIND]; - - // helper methods to initialize function tables - void initAtomBBStrategies(); - void initTermBBStrategies(); - - // returns a node that might be easier to bitblast - Node bbOptimize(TNode node); - - void addAtom(TNode atom); - // division is bitblasted in terms of constraints - // so it needs to use private bitblaster interface - void bbUdiv(TNode node, Bits& bits); - void bbUrem(TNode node, Bits& bits); - bool hasValue(TNode a); -public: - void cacheTermDef(TNode node, Bits def); // public so we can cache remainder for division - void bbTerm(TNode node, Bits& bits); - void bbAtom(TNode node); - - Bitblaster(context::Context* c, bv::TheoryBV* bv); - ~Bitblaster(); - bool assertToSat(TNode node, bool propagate = true); - bool propagate(); - bool solve(bool quick_solve = false); - void getConflict(std::vector& conflict); - void explain(TNode atom, std::vector& explanation); - - EqualityStatus getEqualityStatus(TNode a, TNode b); - /** - * Return a constant Node representing the value of a variable - * in the current model. - * @param a - * - * @return - */ - Node getVarValue(TNode a, bool fullModel=true); - /** - * Adds a constant value for each bit-blasted variable in the model. - * - * @param m the model - * @param fullModel whether to create a "full model," i.e., add - * constants to equivalence classes that don't already have them - */ - void collectModelInfo(TheoryModel* m, bool fullModel); - /** - * Stores the variable (or non-bv term) and its corresponding bits. - * - * @param var - */ - void storeVariable(TNode var) { - d_variables.insert(var); - } - - bool isSharedTerm(TNode node); - uint64_t computeAtomWeight(TNode node); - -private: - - class Statistics { - public: - IntStat d_numTermClauses, d_numAtomClauses; - IntStat d_numTerms, d_numAtoms; - TimerStat d_bitblastTimer; - Statistics(); - ~Statistics(); - }; - - Statistics d_statistics; -}; - - - -} /* bv namespace */ - -} /* theory namespace */ - -} /* CVC4 namespace */ - -#endif /* __CVC4__BITBLASTER_H */ diff --git a/src/theory/bv/bitblaster_template.h b/src/theory/bv/bitblaster_template.h new file mode 100644 index 000000000..25de81f2c --- /dev/null +++ b/src/theory/bv/bitblaster_template.h @@ -0,0 +1,399 @@ +/********************* */ +/*! \file bitblaster.h + ** \verbatim + ** Original author: Liana Hadarean + ** Major contributors: none + ** Minor contributors (to current version): lianah, Morgan Deters, Dejan Jovanovic + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2013 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Wrapper around the SAT solver used for bitblasting + ** + ** Wrapper around the SAT solver used for bitblasting. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__BITBLASTER_TEMPLATE_H +#define __CVC4__BITBLASTER_TEMPLATE_H + + +#include "expr/node.h" +#include +#include +#include "context/cdhashmap.h" +#include "bitblast_strategies_template.h" +#include "prop/sat_solver.h" +#include "theory/valuation.h" + +class Abc_Obj_t_; +typedef Abc_Obj_t_ Abc_Obj_t; + +class Abc_Ntk_t_; +typedef Abc_Ntk_t_ Abc_Ntk_t; + +class Abc_Aig_t_; +typedef Abc_Aig_t_ Abc_Aig_t; + +class Cnf_Dat_t_; +typedef Cnf_Dat_t_ Cnf_Dat_t; + + +namespace CVC4 { +namespace prop { +class CnfStream; +class BVSatSolverInterface; +class NullRegistrar; +} + +namespace theory { +class OutputChannel; +class TheoryModel; + +namespace bv { + +class BitblastingRegistrar; + +typedef __gnu_cxx::hash_set NodeSet; +class AbstractionModule; + +/** + * The Bitblaster that manages the mapping between Nodes + * and their bitwise definition + * + */ + +template +class TBitblaster { +protected: + typedef std::vector Bits; + typedef __gnu_cxx::hash_map TermDefMap; + typedef __gnu_cxx::hash_set AtomSet; + + typedef void (*TermBBStrategy) (TNode, Bits&, TBitblaster*); + typedef T (*AtomBBStrategy) (TNode, TBitblaster*); + + // caches and mappings + TermDefMap d_termCache; + + void initAtomBBStrategies(); + void initTermBBStrategies(); +protected: + /// function tables for the various bitblasting strategies indexed by node kind + TermBBStrategy d_termBBStrategies[kind::LAST_KIND]; + AtomBBStrategy d_atomBBStrategies[kind::LAST_KIND]; +public: + TBitblaster(); + virtual ~TBitblaster() {} + virtual void bbAtom(TNode node) = 0; + virtual void bbTerm(TNode node, Bits& bits) = 0; + virtual void makeVariable(TNode node, Bits& bits) = 0; + virtual T getBBAtom(TNode atom) const = 0; + virtual bool hasBBAtom(TNode atom) const = 0; + virtual void storeBBAtom(TNode atom, T atom_bb) = 0; + + bool hasBBTerm(TNode node) const; + void getBBTerm(TNode node, Bits& bits) const; + void storeBBTerm(TNode term, const Bits& bits); +}; + + +class TheoryBV; + +class TLazyBitblaster : public TBitblaster { + typedef std::vector Bits; + typedef __gnu_cxx::hash_set VarSet; + typedef __gnu_cxx::hash_set AtomSet; + typedef context::CDList AssertionList; + typedef context::CDHashMap , prop::SatLiteralHashFunction> ExplanationMap; + + /** This class gets callbacks from minisat on propagations */ + class MinisatNotify : public prop::BVSatSolverInterface::Notify { + prop::CnfStream* d_cnf; + TheoryBV *d_bv; + TLazyBitblaster* d_lazyBB; + public: + MinisatNotify(prop::CnfStream* cnf, TheoryBV *bv, TLazyBitblaster* lbv) + : d_cnf(cnf) + , d_bv(bv) + , d_lazyBB(lbv) + {} + bool notify(prop::SatLiteral lit); + void notify(prop::SatClause& clause); + void safePoint(); + }; + + TheoryBV *d_bv; + context::Context* d_ctx; + + prop::NullRegistrar* d_nullRegistrar; + context::Context* d_nullContext; + // sat solver used for bitblasting and associated CnfStream + prop::BVSatSolverInterface* d_satSolver; + prop::CnfStream* d_cnfStream; + + AssertionList d_assertedAtoms; /**< context dependent list storing the atoms + currently asserted by the DPLL SAT solver. */ + ExplanationMap d_explanations; /**< context dependent list of explanations for the propagated literals. + Only used when bvEagerPropagate option enabled. */ + VarSet d_variables; + AtomSet d_bbAtoms; + AbstractionModule* d_abstraction; + bool d_emptyNotify; + + void addAtom(TNode atom); + bool hasValue(TNode a); +public: + void bbTerm(TNode node, Bits& bits); + void bbAtom(TNode node); + Node getBBAtom(TNode atom) const; + void storeBBAtom(TNode atom, Node atom_bb); + bool hasBBAtom(TNode atom) const; + TLazyBitblaster(context::Context* c, bv::TheoryBV* bv, const std::string name="", bool emptyNotify = false); + ~TLazyBitblaster(); + /** + * Pushes the assumption literal associated with node to the SAT + * solver assumption queue. + * + * @param node assumption + * @param propagate run bcp or not + * + * @return false if a conflict detected + */ + bool assertToSat(TNode node, bool propagate = true); + bool propagate(); + bool solve(); + prop::SatValue solveWithBudget(unsigned long conflict_budget); + void getConflict(std::vector& conflict); + void explain(TNode atom, std::vector& explanation); + void setAbstraction(AbstractionModule* abs); + + theory::EqualityStatus getEqualityStatus(TNode a, TNode b); + /** + * Return a constant Node representing the value of a variable + * in the current model. + * @param a + * + * @return + */ + Node getVarValue(TNode a, bool fullModel=true); + /** + * Adds a constant value for each bit-blasted variable in the model. + * + * @param m the model + * @param fullModel whether to create a "full model," i.e., add + * constants to equivalence classes that don't already have them + */ + void collectModelInfo(TheoryModel* m, bool fullModel); + + typedef VarSet::const_iterator vars_iterator; + vars_iterator beginVars() { return d_variables.begin(); } + vars_iterator endVars() { return d_variables.end(); } + + /** + * Creates the bits corresponding to the variable (or non-bv term). + * + * @param var + */ + void makeVariable(TNode var, Bits& bits); + + bool isSharedTerm(TNode node); + uint64_t computeAtomWeight(TNode node, NodeSet& seen); + /** + * Deletes SatSolver and CnfCache, but maintains bit-blasting + * terms cache. + * + */ + void clearSolver(); +private: + + class Statistics { + public: + IntStat d_numTermClauses, d_numAtomClauses; + IntStat d_numTerms, d_numAtoms; + IntStat d_numExplainedPropagations; + IntStat d_numBitblastingPropagations; + TimerStat d_bitblastTimer; + Statistics(const std::string& name); + ~Statistics(); + }; + std::string d_name; + Statistics d_statistics; +}; + +class MinisatEmptyNotify : public prop::BVSatSolverInterface::Notify { +public: + MinisatEmptyNotify() {} + bool notify(prop::SatLiteral lit) { return true; } + void notify(prop::SatClause& clause) { } + void safePoint() {} +}; + + +class EagerBitblaster : public TBitblaster { + typedef __gnu_cxx::hash_set TNodeSet; + // sat solver used for bitblasting and associated CnfStream + prop::BVSatSolverInterface* d_satSolver; + BitblastingRegistrar* d_bitblastingRegistrar; + context::Context* d_nullContext; + prop::CnfStream* d_cnfStream; + TNodeSet d_bbAtoms; +public: + void addAtom(TNode atom); + void makeVariable(TNode node, Bits& bits); + void bbTerm(TNode node, Bits& bits); + void bbAtom(TNode node); + Node getBBAtom(TNode node) const; + bool hasBBAtom(TNode atom) const; + void bbFormula(TNode formula); + void storeBBAtom(TNode atom, Node atom_bb); + EagerBitblaster(); + ~EagerBitblaster(); + bool assertToSat(TNode node, bool propagate = true); + bool solve(); +}; + +class AigBitblaster : public TBitblaster { + typedef std::hash_map TNodeAigMap; + typedef std::hash_map NodeAigMap; + + static Abc_Ntk_t* abcAigNetwork; + context::Context* d_nullContext; + prop::BVSatSolverInterface* d_satSolver; + TNodeAigMap d_aigCache; + NodeAigMap d_bbAtoms; + + NodeAigMap d_nodeToAigInput; + // the thing we are checking for sat + Abc_Obj_t* d_aigOutputNode; + + void addAtom(TNode atom); + void simplifyAig(); + void storeBBAtom(TNode atom, Abc_Obj_t* atom_bb); + Abc_Obj_t* getBBAtom(TNode atom) const; + bool hasBBAtom(TNode atom) const; + void cacheAig(TNode node, Abc_Obj_t* aig); + bool hasAig(TNode node); + Abc_Obj_t* getAig(TNode node); + Abc_Obj_t* mkInput(TNode input); + bool hasInput(TNode input); + void convertToCnfAndAssert(); + void assertToSatSolver(Cnf_Dat_t* pCnf); + +public: + AigBitblaster(); + ~AigBitblaster(); + + void makeVariable(TNode node, Bits& bits); + void bbTerm(TNode node, Bits& bits); + void bbAtom(TNode node); + Abc_Obj_t* bbFormula(TNode formula); + bool solve(TNode query); + static Abc_Aig_t* currentAigM(); + static Abc_Ntk_t* currentAigNtk(); + +private: + class Statistics { + public: + IntStat d_numClauses; + IntStat d_numVariables; + TimerStat d_simplificationTime; + TimerStat d_cnfConversionTime; + TimerStat d_solveTime; + Statistics(); + ~Statistics(); + }; + + Statistics d_statistics; + +}; + + +// Bitblaster implementation + +template void TBitblaster::initAtomBBStrategies() { + for (int i = 0 ; i < kind::LAST_KIND; ++i ) { + d_atomBBStrategies[i] = UndefinedAtomBBStrategy; + } + /// setting default bb strategies for atoms + d_atomBBStrategies [ kind::EQUAL ] = DefaultEqBB; + d_atomBBStrategies [ kind::BITVECTOR_ULT ] = DefaultUltBB; + d_atomBBStrategies [ kind::BITVECTOR_ULE ] = DefaultUleBB; + d_atomBBStrategies [ kind::BITVECTOR_UGT ] = DefaultUgtBB; + d_atomBBStrategies [ kind::BITVECTOR_UGE ] = DefaultUgeBB; + d_atomBBStrategies [ kind::BITVECTOR_SLT ] = DefaultSltBB; + d_atomBBStrategies [ kind::BITVECTOR_SLE ] = DefaultSleBB; + d_atomBBStrategies [ kind::BITVECTOR_SGT ] = DefaultSgtBB; + d_atomBBStrategies [ kind::BITVECTOR_SGE ] = DefaultSgeBB; +} + +template void TBitblaster::initTermBBStrategies() { + for (int i = 0 ; i < kind::LAST_KIND; ++i ) { + d_termBBStrategies[i] = DefaultVarBB; + } + /// setting default bb strategies for terms: + d_termBBStrategies [ kind::CONST_BITVECTOR ] = DefaultConstBB; + d_termBBStrategies [ kind::BITVECTOR_NOT ] = DefaultNotBB; + d_termBBStrategies [ kind::BITVECTOR_CONCAT ] = DefaultConcatBB; + d_termBBStrategies [ kind::BITVECTOR_AND ] = DefaultAndBB; + d_termBBStrategies [ kind::BITVECTOR_OR ] = DefaultOrBB; + d_termBBStrategies [ kind::BITVECTOR_XOR ] = DefaultXorBB; + d_termBBStrategies [ kind::BITVECTOR_XNOR ] = DefaultXnorBB; + d_termBBStrategies [ kind::BITVECTOR_NAND ] = DefaultNandBB; + d_termBBStrategies [ kind::BITVECTOR_NOR ] = DefaultNorBB; + d_termBBStrategies [ kind::BITVECTOR_COMP ] = DefaultCompBB; + d_termBBStrategies [ kind::BITVECTOR_MULT ] = DefaultMultBB; + d_termBBStrategies [ kind::BITVECTOR_PLUS ] = DefaultPlusBB; + d_termBBStrategies [ kind::BITVECTOR_SUB ] = DefaultSubBB; + d_termBBStrategies [ kind::BITVECTOR_NEG ] = DefaultNegBB; + d_termBBStrategies [ kind::BITVECTOR_UDIV ] = UndefinedTermBBStrategy; + d_termBBStrategies [ kind::BITVECTOR_UREM ] = UndefinedTermBBStrategy; + d_termBBStrategies [ kind::BITVECTOR_UDIV_TOTAL ] = DefaultUdivBB; + d_termBBStrategies [ kind::BITVECTOR_UREM_TOTAL ] = DefaultUremBB; + d_termBBStrategies [ kind::BITVECTOR_SDIV ] = UndefinedTermBBStrategy; + d_termBBStrategies [ kind::BITVECTOR_SREM ] = UndefinedTermBBStrategy; + d_termBBStrategies [ kind::BITVECTOR_SMOD ] = UndefinedTermBBStrategy; + d_termBBStrategies [ kind::BITVECTOR_SHL ] = DefaultShlBB; + d_termBBStrategies [ kind::BITVECTOR_LSHR ] = DefaultLshrBB; + d_termBBStrategies [ kind::BITVECTOR_ASHR ] = DefaultAshrBB; + d_termBBStrategies [ kind::BITVECTOR_EXTRACT ] = DefaultExtractBB; + d_termBBStrategies [ kind::BITVECTOR_REPEAT ] = DefaultRepeatBB; + d_termBBStrategies [ kind::BITVECTOR_ZERO_EXTEND ] = DefaultZeroExtendBB; + d_termBBStrategies [ kind::BITVECTOR_SIGN_EXTEND ] = DefaultSignExtendBB; + d_termBBStrategies [ kind::BITVECTOR_ROTATE_RIGHT ] = DefaultRotateRightBB; + d_termBBStrategies [ kind::BITVECTOR_ROTATE_LEFT ] = DefaultRotateLeftBB; +} + +template +TBitblaster::TBitblaster() + : d_termCache() +{ + initAtomBBStrategies(); + initTermBBStrategies(); +} + +template +bool TBitblaster::hasBBTerm(TNode node) const { + return d_termCache.find(node) != d_termCache.end(); +} +template +void TBitblaster::getBBTerm(TNode node, Bits& bits) const { + Assert (hasBBTerm(node)); + bits = d_termCache.find(node)->second; +} + +template +void TBitblaster::storeBBTerm(TNode node, const Bits& bits) { + d_termCache.insert(std::make_pair(node, bits)); +} + + +} /* bv namespace */ + +} /* theory namespace */ + +} /* CVC4 namespace */ + +#endif /* __CVC4__BITBLASTER_H */ diff --git a/src/theory/bv/bv_eager_solver.cpp b/src/theory/bv/bv_eager_solver.cpp new file mode 100644 index 000000000..af35c0499 --- /dev/null +++ b/src/theory/bv/bv_eager_solver.cpp @@ -0,0 +1,108 @@ +/********************* */ +/*! \file bv_eager_solver.h + ** \verbatim + ** Original author: Liana Hadarean + ** Major contributors: none + ** Minor contributors (to current version): + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2013 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Eager bit-blasting solver. + ** + ** Eager bit-blasting solver. + **/ + +#include "theory/bv/bv_eager_solver.h" +#include "theory/bv/bitblaster_template.h" +#include "theory/bv/eager_bitblaster.h" +#include "theory/bv/aig_bitblaster.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::theory; +using namespace CVC4::theory::bv; + +EagerBitblastSolver::EagerBitblastSolver() + : d_assertionSet() + , d_bitblaster(NULL) + , d_aigBitblaster(NULL) + , d_useAig(options::bitvectorAig()) +{} + +EagerBitblastSolver::~EagerBitblastSolver() { + if (d_useAig) { + Assert (d_bitblaster == NULL); + delete d_aigBitblaster; + } + else { + Assert (d_aigBitblaster == NULL); + delete d_bitblaster; + } +} + +void EagerBitblastSolver::turnOffAig() { + Assert (d_aigBitblaster == NULL && + d_bitblaster == NULL); + d_useAig = false; +} + +void EagerBitblastSolver::initialize() { + Assert(!isInitialized()); + if (d_useAig) { + d_aigBitblaster = new AigBitblaster(); + } else { + d_bitblaster = new EagerBitblaster(); + } +} + +bool EagerBitblastSolver::isInitialized() { + bool init = d_aigBitblaster != NULL || d_bitblaster != NULL; + if (init) { + Assert (!d_useAig || d_aigBitblaster); + Assert (d_useAig || d_bitblaster); + } + return init; +} + +void EagerBitblastSolver::assertFormula(TNode formula) { + Assert (isInitialized()); + Debug("bitvector-eager") << "EagerBitblastSolver::assertFormula "<< formula <<"\n"; + d_assertionSet.insert(formula); + //ensures all atoms are bit-blasted and converted to AIG + if (d_useAig) + d_aigBitblaster->bbFormula(formula); + else + d_bitblaster->bbFormula(formula); +} + +bool EagerBitblastSolver::checkSat() { + Assert (isInitialized()); + std::vector assertions; + for (AssertionSet::const_iterator it = d_assertionSet.begin(); it != d_assertionSet.end(); ++it) { + assertions.push_back(*it); + } + if (!assertions.size()) + return true; + + if (d_useAig) { + Node query = utils::mkAnd(assertions); + return d_aigBitblaster->solve(query); + } + + return d_bitblaster->solve(); +} + +bool EagerBitblastSolver::hasAssertions(const std::vector &formulas) { + Assert (isInitialized()); + if (formulas.size() != d_assertionSet.size()) + return false; + for (unsigned i = 0; i < formulas.size(); ++i) { + Assert (formulas[i].getKind() == kind::BITVECTOR_EAGER_ATOM); + TNode formula = formulas[i][0]; + if (d_assertionSet.find(formula) == d_assertionSet.end()) + return false; + } + return true; +} diff --git a/src/theory/bv/bv_eager_solver.h b/src/theory/bv/bv_eager_solver.h new file mode 100644 index 000000000..1fb65c9c8 --- /dev/null +++ b/src/theory/bv/bv_eager_solver.h @@ -0,0 +1,55 @@ +/********************* */ +/*! \file bv_eager_solver.h + ** \verbatim + ** Original author: Liana Hadarean + ** Major contributors: none + ** Minor contributors (to current version): + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2013 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Eager bit-blasting solver. + ** + ** Eager bit-blasting solver. + **/ + +#include "cvc4_private.h" +#include "expr/node.h" +#include +#pragma once + + +namespace CVC4 { +namespace theory { +namespace bv { + +class EagerBitblaster; +class AigBitblaster; + +/** + * BitblastSolver + */ +class EagerBitblastSolver { + typedef __gnu_cxx::hash_set AssertionSet; + AssertionSet d_assertionSet; + /** Bitblasters */ + EagerBitblaster* d_bitblaster; + AigBitblaster* d_aigBitblaster; + bool d_useAig; +public: + EagerBitblastSolver(); + ~EagerBitblastSolver(); + bool checkSat(); + void assertFormula(TNode formula); + // purely for debugging purposes + bool hasAssertions(const std::vector &formulas); + + void turnOffAig(); + bool isInitialized(); + void initialize(); +}; + +} +} +} diff --git a/src/theory/bv/bv_quick_check.cpp b/src/theory/bv/bv_quick_check.cpp new file mode 100644 index 000000000..1adbb83d9 --- /dev/null +++ b/src/theory/bv/bv_quick_check.cpp @@ -0,0 +1,377 @@ +/********************* */ +/*! \file bv_quick_check.cpp + ** \verbatim + ** Original author: Liana Hadaren + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2013 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Wrapper around the SAT solver used for bitblasting. + ** + ** Wrapper around the SAT solver used for bitblasting. + **/ + +#include "theory/bv/bv_quick_check.h" +#include "theory/bv/theory_bv_utils.h" + +#include "theory/bv/bitblaster_template.h" + +using namespace CVC4; +using namespace CVC4::theory; +using namespace CVC4::theory::bv; +using namespace CVC4::prop; + +BVQuickCheck::BVQuickCheck(const std::string& name, theory::bv::TheoryBV* bv) + : d_ctx(new context::Context()) + , d_bitblaster(new TLazyBitblaster(d_ctx, bv, name, true)) + , d_conflict() + , d_inConflict(d_ctx, false) +{} + + +bool BVQuickCheck::inConflict() { return d_inConflict.get(); } + +uint64_t BVQuickCheck::computeAtomWeight(TNode node, NodeSet& seen) { + return d_bitblaster->computeAtomWeight(node, seen); +} + +void BVQuickCheck::setConflict() { + Assert (!inConflict()); + std::vector conflict; + d_bitblaster->getConflict(conflict); + Node confl = utils::mkConjunction(conflict); + d_inConflict = true; + d_conflict = confl; +} + +prop::SatValue BVQuickCheck::checkSat(std::vector& assumptions, unsigned long budget) { + Node conflict; + + for (unsigned i = 0; i < assumptions.size(); ++i) { + TNode a = assumptions[i]; + Assert (a.getType().isBoolean()); + d_bitblaster->bbAtom(a); + bool ok = d_bitblaster->assertToSat(a, false); + if (!ok) { + setConflict(); + return SAT_VALUE_FALSE; + } + } + + if (budget == 0) { + bool ok = d_bitblaster->propagate(); + if (!ok) { + setConflict(); + return SAT_VALUE_FALSE; + } + return SAT_VALUE_UNKNOWN; // could check if assignment is full and return SAT_VALUE_TRUE + } + + prop::SatValue res = d_bitblaster->solveWithBudget(budget); + if (res == SAT_VALUE_FALSE) { + setConflict(); + return res; + } + // could be unknown or could be sat + return res; +} + +prop::SatValue BVQuickCheck::checkSat(unsigned long budget) { + prop::SatValue res = d_bitblaster->solveWithBudget(budget); + if (res == SAT_VALUE_FALSE) { + setConflict(); + } + return res; +} + +bool BVQuickCheck::addAssertion(TNode assertion) { + Assert (assertion.getType().isBoolean()); + d_bitblaster->bbAtom(assertion); + // assert to sat solver and run bcp to detect easy conflicts + bool ok = d_bitblaster->assertToSat(assertion, true); + if (!ok) { + setConflict(); + } + return ok; +} + + +void BVQuickCheck::push() { + d_ctx->push(); +} + +void BVQuickCheck::pop() { + d_ctx->pop(); +} + +BVQuickCheck::vars_iterator BVQuickCheck::beginVars() { + return d_bitblaster->beginVars(); +} +BVQuickCheck::vars_iterator BVQuickCheck::endVars() { + return d_bitblaster->endVars(); +} + +Node BVQuickCheck::getVarValue(TNode var) { + return d_bitblaster->getVarValue(var); +} + + +/** + * Constructs a new sat solver which requires throwing out the atomBBCache + * but keeps all the termBBCache + * + */ +void BVQuickCheck::clearSolver() { + popToZero(); + d_bitblaster->clearSolver(); +} + +void BVQuickCheck::popToZero() { + while (d_ctx->getLevel() > 0) { + d_ctx->pop(); + } +} + +void BVQuickCheck::collectModelInfo(theory::TheoryModel* model, bool fullModel) { + d_bitblaster->collectModelInfo(model, fullModel); +} + +BVQuickCheck::~BVQuickCheck() { + delete d_bitblaster; +} + +QuickXPlain::QuickXPlain(const std::string& name, BVQuickCheck* solver, unsigned long budget) + : d_solver(solver) + , d_budget(budget) + , d_numCalled(0) + , d_minRatioSum(0) + , d_numConflicts(0) + , d_period(20) + , d_thresh(0.7) + , d_hardThresh(0.9) + , d_statistics(name) +{} +QuickXPlain::~QuickXPlain() {} + +unsigned QuickXPlain::selectUnsatCore(unsigned low, unsigned high, + std::vector& conflict) { + + Assert(!d_solver->getConflict().isNull() && + d_solver->inConflict()); + Node query_confl = d_solver->getConflict(); + + // conflict wasn't actually minimized + if (query_confl.getNumChildren() == high - low + 1) { + return high; + } + + TNodeSet nodes; + for (unsigned i = low; i <= high; i++) { + nodes.insert(conflict[i]); + } + + unsigned write = low; + for (unsigned i = 0; i < query_confl.getNumChildren(); ++i) { + TNode current = query_confl[i]; + // the conflict can have nodes in lower decision levels + if (nodes.find(current) != nodes.end()) { + conflict[write++] = current; + nodes.erase(nodes.find(current)); + } + } + // if all of the nodes in the conflict were on a lower level + if (write == low) { + return low; + } + Assert (write != 0); + unsigned new_high = write - 1; + + for (TNodeSet::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { + conflict[write++] = *it; + } + Assert (write -1 == high); + Assert (new_high <= high); + + return new_high; +} + +void QuickXPlain::minimizeConflictInternal(unsigned low, unsigned high, + std::vector& conflict, + std::vector& new_conflict) { + + Assert (low <= high && high < conflict.size()); + + if (low == high) { + new_conflict.push_back(conflict[low]); + return; + } + + // check if top half is unsat + unsigned new_low = (high - low + 1)/ 2 + low; + d_solver->push(); + + for (unsigned i = new_low; i <=high; ++i) { + bool ok = d_solver->addAssertion(conflict[i]); + if (!ok) { + unsigned top = selectUnsatCore(new_low, i, conflict); + d_solver->pop(); + minimizeConflictInternal(new_low, top, conflict, new_conflict); + return; + } + } + + SatValue res = d_solver->checkSat(d_budget); + + if (res == SAT_VALUE_UNKNOWN) { + ++(d_statistics.d_numUnknown); + } else { + ++(d_statistics.d_numSolved); + } + + if (res == SAT_VALUE_FALSE) { + unsigned top = selectUnsatCore(new_low, high, conflict); + d_solver->pop(); + minimizeConflictInternal(new_low, top, conflict, new_conflict); + return; + } + + d_solver->pop(); + unsigned new_high = new_low - 1; + d_solver->push(); + + // check bottom half + for (unsigned i = low; i <= new_high; ++i) { + bool ok = d_solver->addAssertion(conflict[i]); + if (!ok) { + unsigned top = selectUnsatCore(low, i, conflict); + d_solver->pop(); + minimizeConflictInternal(low, top, conflict, new_conflict); + return; + } + } + + res = d_solver->checkSat(d_budget); + + if (res == SAT_VALUE_UNKNOWN) { + ++(d_statistics.d_numUnknown); + } else { + ++(d_statistics.d_numSolved); + } + + if (res == SAT_VALUE_FALSE) { + unsigned top = selectUnsatCore(low, new_high, conflict); + d_solver->pop(); + minimizeConflictInternal(low, top, conflict, new_conflict); + return; + } + + // conflict (probably) contains literals in both halves + // keep bottom half in context (no pop) + minimizeConflictInternal(new_low, high, conflict, new_conflict); + d_solver->pop(); + d_solver->push(); + for (unsigned i = 0; i < new_conflict.size(); ++i) { + bool ok = d_solver->addAssertion(new_conflict[i]); + if (!ok) { + ++(d_statistics.d_numUnknownWasUnsat); + d_solver->pop(); + return; + } + } + minimizeConflictInternal(low, new_high, conflict, new_conflict); + d_solver->pop(); +} + + +bool QuickXPlain::useHeuristic() { + return true; + // d_statistics.d_finalPeriod.setData(d_period); + // // try to minimize conflict periodically + // if (d_numConflicts % d_period == 0) + // return true; + + // if (d_numCalled == 0) { + // return true; + // } + + // if (d_minRatioSum / d_numCalled >= d_thresh && + // d_numCalled <= 20 ) { + // return false; + // } + + // if (d_minRatioSum / d_numCalled >= d_hardThresh) { + // return false; + // } + + // return true; +} + +Node QuickXPlain::minimizeConflict(TNode confl) { + ++d_numConflicts; + + if (!useHeuristic()) { + return confl; + } + + ++d_numCalled; + ++(d_statistics.d_numConflictsMinimized); + TimerStat::CodeTimer xplainTimer(d_statistics.d_xplainTime); + Assert (confl.getNumChildren() > 2); + std::vector conflict; + for (unsigned i = 0; i < confl.getNumChildren(); ++i) { + conflict.push_back(confl[i]); + } + d_solver->popToZero(); + std::vector minimized; + minimizeConflictInternal(0, conflict.size() - 1, conflict, minimized); + + double minimization_ratio = ((double) minimized.size())/confl.getNumChildren(); + d_minRatioSum+= minimization_ratio; + + + // if (minimization_ratio >= d_hardThresh) { + // d_period = d_period * 5; + // } + + // if (minimization_ratio <= d_thresh && d_period >= 40) { + // d_period = d_period *0.5; + // } + + // if (1.5* d_statistics.d_numUnknown.getData() > d_statistics.d_numSolved.getData()) { + // d_period = d_period * 2; + // } + d_statistics.d_avgMinimizationRatio.addEntry(minimization_ratio); + return utils::mkAnd(minimized); +} + +QuickXPlain::Statistics::Statistics(const std::string& name) + : d_xplainTime("theory::bv::"+name+"::QuickXplain::Time") + , d_numSolved("theory::bv::"+name+"::QuickXplain::NumSolved", 0) + , d_numUnknown("theory::bv::"+name+"::QuickXplain::NumUnknown", 0) + , d_numUnknownWasUnsat("theory::bv::"+name+"::QuickXplain::NumUnknownWasUnsat", 0) + , d_numConflictsMinimized("theory::bv::"+name+"::QuickXplain::NumConflictsMinimized", 0) + , d_finalPeriod("theory::bv::"+name+"::QuickXplain::FinalPeriod", 0) + , d_avgMinimizationRatio("theory::bv::"+name+"::QuickXplain::AvgMinRatio") +{ + StatisticsRegistry::registerStat(&d_xplainTime); + StatisticsRegistry::registerStat(&d_numSolved); + StatisticsRegistry::registerStat(&d_numUnknown); + StatisticsRegistry::registerStat(&d_numUnknownWasUnsat); + StatisticsRegistry::registerStat(&d_numConflictsMinimized); + StatisticsRegistry::registerStat(&d_finalPeriod); + StatisticsRegistry::registerStat(&d_avgMinimizationRatio); +} + +QuickXPlain::Statistics::~Statistics() { + StatisticsRegistry::unregisterStat(&d_xplainTime); + StatisticsRegistry::unregisterStat(&d_numSolved); + StatisticsRegistry::unregisterStat(&d_numUnknown); + StatisticsRegistry::unregisterStat(&d_numUnknownWasUnsat); + StatisticsRegistry::unregisterStat(&d_numConflictsMinimized); + StatisticsRegistry::unregisterStat(&d_finalPeriod); + StatisticsRegistry::unregisterStat(&d_avgMinimizationRatio); +} + diff --git a/src/theory/bv/bv_quick_check.h b/src/theory/bv/bv_quick_check.h new file mode 100644 index 000000000..c09994c06 --- /dev/null +++ b/src/theory/bv/bv_quick_check.h @@ -0,0 +1,179 @@ +/********************* */ +/*! \file bv_quick_check.h + ** \verbatim + ** Original author: Liana Hadarean + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2013 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Sandboxed sat solver for bv quickchecks. + ** + ** Sandboxed sat solver for bv quickchecks. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__BV_QUICK_CHECK_H +#define __CVC4__BV_QUICK_CHECK_H + +#include +#include + +#include "expr/node.h" +#include "context/cdo.h" +#include "prop/sat_solver_types.h" +#include "util/statistics_registry.h" + +namespace CVC4 { +namespace theory { + +class TheoryModel; + +namespace bv { + + + +typedef __gnu_cxx::hash_set NodeSet; +typedef __gnu_cxx::hash_set TNodeSet; + +class TLazyBitblaster; +class TheoryBV; + +class BVQuickCheck { + context::Context* d_ctx; + TLazyBitblaster* d_bitblaster; + Node d_conflict; + context::CDO d_inConflict; + void setConflict(); + +public: + BVQuickCheck(const std::string& name, theory::bv::TheoryBV* bv); + ~BVQuickCheck(); + bool inConflict(); + Node getConflict() { return d_conflict; } + /** + * Checks the satisfiability for a given set of assumptions. + * + * @param assumptions literals assumed true + * @param budget max number of conflicts + * + * @return + */ + prop::SatValue checkSat(std::vector& assumptions, unsigned long budget); + /** + * Checks the satisfiability of given assertions. + * + * @param budget max number of conflicts + * + * @return + */ + prop::SatValue checkSat(unsigned long budget); + + /** + * Convert to CNF and assert the given literal. + * + * @param assumption bv literal + * + * @return false if a conflict has been found via bcp. + */ + bool addAssertion(TNode assumption); + + void push(); + void pop(); + void popToZero(); + /** + * Deletes the SAT solver and CNF stream, but maintains the + * bit-blasting term cache. + * + */ + void clearSolver(); + + /** + * Computes the size of the circuit required to bit-blast + * atom, by not recounting the nodes in seen. + * + * @param node + * @param seen + * + * @return + */ + uint64_t computeAtomWeight(TNode atom, NodeSet& seen); + void collectModelInfo(theory::TheoryModel* model, bool fullModel); + + typedef __gnu_cxx::hash_set::const_iterator vars_iterator; + vars_iterator beginVars(); + vars_iterator endVars(); + + Node getVarValue(TNode var); + +}; + + +class QuickXPlain { + struct Statistics { + TimerStat d_xplainTime; + IntStat d_numSolved; + IntStat d_numUnknown; + IntStat d_numUnknownWasUnsat; + IntStat d_numConflictsMinimized; + IntStat d_finalPeriod; + AverageStat d_avgMinimizationRatio; + Statistics(const std::string&); + ~Statistics(); + }; + BVQuickCheck* d_solver; + unsigned long d_budget; + + // crazy heuristic variables + unsigned d_numCalled; // number of times called + double d_minRatioSum; // sum of minimization ratio for computing average min ratio + unsigned d_numConflicts; // number of conflicts (including when minimization not applied) + unsigned d_period; // after how many conflicts to try minimizing again + + double d_thresh; // if minimization ratio is less, increase period + double d_hardThresh; // decrease period if minimization ratio is greater than this + + + Statistics d_statistics; + /** + * Uses solve with assumptions unsat core feature to + * further minimize a conflict. The minimized conflict + * will be between low and the returned value in conflict. + * + * @param low + * @param high + * @param conflict + * + * @return + */ + unsigned selectUnsatCore(unsigned low, unsigned high, + std::vector& conflict); + /** + * Internal conflict minimization, attempts to minimize + * literals in conflict between low and high and adds the + * result in new_conflict. + * + * @param low + * @param high + * @param conflict + * @param new_conflict + */ + void minimizeConflictInternal(unsigned low, unsigned high, + std::vector& conflict, + std::vector& new_conflict); + + bool useHeuristic(); +public: + QuickXPlain(const std::string& name, BVQuickCheck* solver, unsigned long budged = 10000); + ~QuickXPlain(); + Node minimizeConflict(TNode conflict); +}; + +} /* bv namespace */ +} /* theory namespace */ +} /* CVC4 namespace */ + +#endif /* __CVC4__BV_QUICK_CHECK_H */ diff --git a/src/theory/bv/bv_subtheory.h b/src/theory/bv/bv_subtheory.h index 5a46f7a0f..8d21734db 100644 --- a/src/theory/bv/bv_subtheory.h +++ b/src/theory/bv/bv_subtheory.h @@ -9,16 +9,15 @@ ** See the file COPYING in the top-level source directory for licensing ** information.\endverbatim ** - ** \brief Algebraic solver. + ** \brief Interface for bit-vectors sub-solvers. ** - ** Algebraic solver. + ** Interface for bit-vectors sub-solvers. **/ -#include "cvc4_private.h" - #ifndef __CVC4__THEORY__BV__BV_SUBTHEORY_H #define __CVC4__THEORY__BV__BV_SUBTHEORY_H +#include "cvc4_private.h" #include "context/context.h" #include "context/cdqueue.h" #include "theory/uf/equality_engine.h" @@ -34,7 +33,8 @@ namespace bv { enum SubTheory { SUB_CORE = 1, SUB_BITBLAST = 2, - SUB_INEQUALITY = 3 + SUB_INEQUALITY = 3, + SUB_ALGEBRAIC = 4 }; inline std::ostream& operator << (std::ostream& out, SubTheory subtheory) { @@ -47,6 +47,8 @@ inline std::ostream& operator << (std::ostream& out, SubTheory subtheory) { break; case SUB_INEQUALITY: out << "BV_INEQUALITY_SUBTHEORY"; + case SUB_ALGEBRAIC: + out << "BV_ALGEBRAIC_SUBTHEORY"; default: Unreachable(); break; @@ -100,6 +102,8 @@ public: return res; } virtual void assertFact(TNode fact) { d_assertionQueue.push_back(fact); } + AssertionQueue::const_iterator assertionsBegin() { return d_assertionQueue.begin(); } + AssertionQueue::const_iterator assertionsEnd() { return d_assertionQueue.end(); } }; } diff --git a/src/theory/bv/bv_subtheory_algebraic.cpp b/src/theory/bv/bv_subtheory_algebraic.cpp new file mode 100644 index 000000000..0b65ea0db --- /dev/null +++ b/src/theory/bv/bv_subtheory_algebraic.cpp @@ -0,0 +1,948 @@ +/********************* */ +/*! \file bv_subtheory_bitblast.cpp +** \verbatim +** Original author: Liana Hadarean +** Major contributors: none +** Minor contributors (to current version): none +** This file is part of the CVC4 project. +** Copyright (c) 2009-2013 New York University and The University of Iowa +** See the file COPYING in the top-level source directory for licensing +** information.\endverbatim +** +** \brief Algebraic solver. +** +** Algebraic solver. +**/ +#include "util/boolean_simplification.h" +#include "theory/theory_model.h" + +#include "theory/bv/options.h" +#include "theory/bv/theory_bv.h" +#include "theory/bv/bv_subtheory_algebraic.h" +#include "theory/bv/bv_quick_check.h" +#include "theory/bv/theory_bv_utils.h" + + +using namespace std; +using namespace CVC4; +using namespace CVC4::context; +using namespace CVC4::prop; +using namespace CVC4::theory; +using namespace CVC4::theory::bv; +using namespace CVC4::theory::bv::utils; + +bool hasExpensiveBVOperators(TNode fact); + +SubstitutionEx::SubstitutionEx(theory::SubstitutionMap* modelMap) + : d_substitutions() + , d_cache() + , d_cacheInvalid(true) + , d_modelMap(modelMap) +{} + +void SubstitutionEx::addSubstitution(TNode from, TNode to, TNode reason) { + Debug("bv-substitution") << "SubstitutionEx::addSubstitution: "<< from <<" => "<< to <<"\n"; + Debug("bv-substitution") << " reason "<addSubstitution(from, to); + + d_cacheInvalid = true; + Assert (from != to); + Assert (d_substitutions.find(from) == d_substitutions.end()); + d_substitutions[from] = SubstitutionElement(to, reason); +} + +Node SubstitutionEx::apply(TNode node) { + Debug("bv-substitution") << "SubstitutionEx::apply("<< node <<")\n"; + if (d_cacheInvalid) { + d_cache.clear(); + d_cacheInvalid = false; + } + + SubstitutionsCache::iterator it = d_cache.find(node); + + if (it != d_cache.end()) { + Node res = it->second.to; + Debug("bv-substitution") << " =>"<< res <<"\n"; + return res; + } + + Node result = internalApply(node); + Debug("bv-substitution") << " =>"<< result <<"\n"; + return result; +} + +Node SubstitutionEx::internalApply(TNode node) { + if (d_substitutions.empty()) + return node; + + vector stack; + stack.push_back(SubstitutionStackElement(node)); + + while (!stack.empty()) { + SubstitutionStackElement head = stack.back(); + stack.pop_back(); + + TNode current = head.node; + + if (hasCache(current)) { + continue; + } + + // check if it has substitution + Substitutions::const_iterator it = d_substitutions.find(current); + if (it != d_substitutions.end()) { + vector reasons; + TNode to = it->second.to; + reasons.push_back(it->second.reason); + // check if the thing we subsituted to has substitutions + TNode res = internalApply(to); + // update reasons + reasons.push_back(getReason(to)); + Node reason = mergeExplanations(reasons); + storeCache(current, res, reason); + continue; + } + + // if no children then just continue + if(current.getNumChildren() == 0) { + storeCache(current, current, utils::mkTrue()); + continue; + } + + // children already processed + if (head.childrenAdded) { + NodeBuilder<> nb(current.getKind()); + std::vector reasons; + + if (current.getMetaKind() == kind::metakind::PARAMETERIZED) { + TNode op = current.getOperator(); + Assert (hasCache(op)); + nb << getCache(op); + reasons.push_back(getReason(op)); + } + for (unsigned i = 0; i < current.getNumChildren(); ++i) { + Assert (hasCache(current[i])); + nb << getCache(current[i]); + reasons.push_back(getReason(current[i])); + } + Node result = nb; + // if the node is new apply substitutions to it + Node subst_result = result; + if (result != current) { + subst_result = result!= current? internalApply(result) : result; + reasons.push_back(getReason(result)); + } + Node reason = mergeExplanations(reasons); + storeCache(current, subst_result, reason); + continue; + } else { + // add children to stack + stack.push_back(SubstitutionStackElement(current, true)); + if (current.getMetaKind() == kind::metakind::PARAMETERIZED) { + stack.push_back(SubstitutionStackElement(current.getOperator())); + } + for (unsigned i = 0; i < current.getNumChildren(); ++i) { + stack.push_back(SubstitutionStackElement(current[i])); + } + } + } + + Assert(hasCache(node)); + return getCache(node); +} + +Node SubstitutionEx::explain(TNode node) const { + if(!hasCache(node)) + return utils::mkTrue(); + + Debug("bv-substitution") << "SubstitutionEx::explain("<< node <<")\n"; + Node res = getReason(node); + Debug("bv-substitution") << " with "<< res <<"\n"; + return res; +} + +Node SubstitutionEx::getReason(TNode node) const { + Assert(hasCache(node)); + SubstitutionsCache::const_iterator it = d_cache.find(node); + return it->second.reason; +} + +bool SubstitutionEx::hasCache(TNode node) const { + return d_cache.find(node) != d_cache.end(); +} + +Node SubstitutionEx::getCache(TNode node) const { + Assert (hasCache(node)); + return d_cache.find(node)->second.to; +} + +void SubstitutionEx::storeCache(TNode from, TNode to, Node reason) { + // Debug("bv-substitution") << "SubstitutionEx::storeCache(" << from <<", " << to <<", "<< reason<<")\n"; + Assert (!hasCache(from)); + d_cache[from] = SubstitutionElement(to, reason); +} + +AlgebraicSolver::AlgebraicSolver(context::Context* c, TheoryBV* bv) + : SubtheorySolver(c, bv) + , d_modelMap(NULL) + , d_quickSolver(new BVQuickCheck("alg", bv)) + , d_isComplete(c, false) + , d_isDifficult(c, false) + , d_budget(options::bitvectorAlgebraicBudget()) + , d_explanations() + , d_ctx(new context::Context()) + , d_quickXplain(options::bitvectorQuickXplain() ? new QuickXPlain("alg", d_quickSolver) : NULL) + , d_statistics() +{} + +AlgebraicSolver::~AlgebraicSolver() { + delete d_quickXplain; + delete d_quickSolver; + delete d_ctx; +} + + + +bool AlgebraicSolver::check(Theory::Effort e) { + Assert(options::bitblastMode() == theory::bv::BITBLAST_MODE_LAZY); + + if (!Theory::fullEffort(e)) + return true; + + if (!useHeuristic()) + return true; + + ++(d_numCalls); + + TimerStat::CodeTimer algebraicTimer(d_statistics.d_solveTime); + Debug("bv-subtheory-algebraic") << "AlgebraicSolver::check (" << e << ")\n"; + ++(d_statistics.d_numCallstoCheck); + + d_explanations.clear(); + d_ids.clear(); + d_inputAssertions.clear(); + + std::vector worklist; + + uint64_t original_bb_cost = 0; + + NodeSet seen_assertions; + // Processing assertions from scratch + for (AssertionQueue::const_iterator it = assertionsBegin(); it != assertionsEnd(); ++it) { + Debug("bv-subtheory-algebraic") << " " << *it << "\n"; + TNode assertion = *it; + unsigned id = worklist.size(); + d_ids[assertion] = id; + worklist.push_back(WorklistElement(assertion, id)); + d_inputAssertions.insert(assertion); + storeExplanation(assertion); + + uint64_t assertion_size = d_quickSolver->computeAtomWeight(assertion, seen_assertions); + Assert (original_bb_cost <= original_bb_cost + assertion_size); + original_bb_cost+= assertion_size; + } + + for (unsigned i = 0; i < worklist.size(); ++i) { + d_ids[worklist[i].node] = worklist[i].id; + } + + Debug("bv-subtheory-algebraic") << "Assertions " << worklist.size() <<" : \n"; + + Assert (d_explanations.size() == worklist.size()); + + delete d_modelMap; + d_modelMap = new SubstitutionMap(d_context); + SubstitutionEx subst(d_modelMap); + + // first round of substitutions + processAssertions(worklist, subst); + + if (!d_isDifficult.get()) { + // skolemize all possible extracts + ExtractSkolemizer skolemizer(d_modelMap); + skolemizer.skolemize(worklist); + // second round of substitutions + processAssertions(worklist, subst); + } + + NodeSet subst_seen; + uint64_t subst_bb_cost = 0; + + unsigned r = 0; + unsigned w = 0; + + for (; r < worklist.size(); ++r) { + + TNode fact = worklist[r].node; + unsigned id = worklist[r].id; + + if (Dump.isOn("bv-algebraic")) { + Node expl = d_explanations[id]; + Node query = utils::mkNot(utils::mkNode(kind::IMPLIES, expl, fact)); + Dump("bv-algebraic") << EchoCommand("ThoeryBV::AlgebraicSolver::substitution explanation"); + Dump("bv-algebraic") << PushCommand(); + Dump("bv-algebraic") << AssertCommand(query.toExpr()); + Dump("bv-algebraic") << CheckSatCommand(); + Dump("bv-algebraic") << PopCommand(); + } + + if (fact.isConst() && + fact.getConst() == true) { + continue; + } + + if (fact.isConst() && + fact.getConst() == false) { + // we have a conflict + Node conflict = BooleanSimplification::simplify(d_explanations[id]); + d_bv->setConflict(conflict); + d_isComplete.set(true); + Debug("bv-subtheory-algebraic") << " UNSAT: assertion simplfies to false with conflict: "<< conflict << "\n"; + + if (Dump.isOn("bv-algebraic")) { + Dump("bv-algebraic") << EchoCommand("TheoryBV::AlgebraicSolver::conflict"); + Dump("bv-algebraic") << PushCommand(); + Dump("bv-algebraic") << AssertCommand(conflict.toExpr()); + Dump("bv-algebraic") << CheckSatCommand(); + Dump("bv-algebraic") << PopCommand(); + } + + + ++(d_statistics.d_numSimplifiesToFalse); + ++(d_numSolved); + return false; + } + + subst_bb_cost+= d_quickSolver->computeAtomWeight(fact, subst_seen); + worklist[w] = WorklistElement(fact, id); + Node expl = BooleanSimplification::simplify(d_explanations[id]); + storeExplanation(id, expl); + d_ids[fact] = id; + ++w; + } + + worklist.resize(w); + + + if(Debug.isOn("bv-subtheory-algebraic")) { + Debug("bv-subtheory-algebraic") << "Assertions post-substitutions " << worklist.size() << ":\n"; + for (unsigned i = 0; i < worklist.size(); ++i) { + Debug("bv-subtheory-algebraic") << " " << worklist[i].node << "\n"; + } + } + + + // all facts solved to true + if (worklist.empty()) { + Debug("bv-subtheory-algebraic") << " SAT: everything simplifies to true.\n"; + ++(d_statistics.d_numSimplifiesToTrue); + ++(d_numSolved); + return true; + } + + double ratio = ((double)subst_bb_cost)/original_bb_cost; + if (ratio > 0.5 || + !d_isDifficult.get()) { + // give up if problem not reduced enough + d_isComplete.set(false); + return true; + } + + d_quickSolver->clearSolver(); + + d_quickSolver->push(); + std::vector facts; + for (unsigned i = 0; i < worklist.size(); ++i) { + facts.push_back(worklist[i].node); + } + bool ok = quickCheck(facts); + + Debug("bv-subtheory-algebraic") << "AlgebraicSolver::check done " << ok << ".\n"; + return ok; +} + +bool AlgebraicSolver::quickCheck(std::vector& facts) { + SatValue res = d_quickSolver->checkSat(facts, d_budget); + + if (res == SAT_VALUE_UNKNOWN) { + d_isComplete.set(false); + Debug("bv-subtheory-algebraic") << " Unknown.\n"; + ++(d_statistics.d_numUnknown); + return true; + } + + if (res == SAT_VALUE_TRUE) { + Debug("bv-subtheory-algebraic") << " Sat.\n"; + ++(d_statistics.d_numSat); + ++(d_numSolved); + d_isComplete.set(true); + return true; + } + + Assert (res == SAT_VALUE_FALSE); + Assert (d_quickSolver->inConflict()); + d_isComplete.set(true); + + Debug("bv-subtheory-algebraic") << " Unsat.\n"; + ++(d_numSolved); + ++(d_statistics.d_numUnsat); + + + Node conflict = d_quickSolver->getConflict(); + Debug("bv-subtheory-algebraic") << " Conflict: " << conflict << "\n"; + + // singleton conflict + if (conflict.getKind() != kind::AND) { + Assert (d_ids.find(conflict) != d_ids.end()); + unsigned id = d_ids[conflict]; + Assert (id < d_explanations.size()); + Node theory_confl = d_explanations[id]; + d_bv->setConflict(theory_confl); + return false; + } + + Assert (conflict.getKind() == kind::AND); + if (options::bitvectorQuickXplain()) { + d_quickSolver->popToZero(); + Debug("bv-quick-xplain") << "AlgebraicSolver::quickCheck original conflict size " << conflict.getNumChildren() << "\n"; + conflict = d_quickXplain->minimizeConflict(conflict); + Debug("bv-quick-xplain") << "AlgebraicSolver::quickCheck minimized conflict size " << conflict.getNumChildren() << "\n"; + } + + vector theory_confl; + for (unsigned i = 0; i < conflict.getNumChildren(); ++i) { + TNode c = conflict[i]; + + Assert (d_ids.find(c) != d_ids.end()); + unsigned c_id = d_ids[c]; + Assert (c_id < d_explanations.size()); + TNode c_expl = d_explanations[c_id]; + theory_confl.push_back(c_expl); + } + + Node confl = BooleanSimplification::simplify(utils::mkAnd(theory_confl)); + + Debug("bv-subtheory-algebraic") << " Out Conflict: " << confl << "\n"; + setConflict(confl); + return false; +} + +void AlgebraicSolver::setConflict(TNode conflict) { + Node final_conflict = conflict; + if (options::bitvectorQuickXplain() && + conflict.getKind() == kind::AND && + conflict.getNumChildren() > 4) { + final_conflict = d_quickXplain->minimizeConflict(conflict); + } + d_bv->setConflict(final_conflict); +} + +bool AlgebraicSolver::solve(TNode fact, TNode reason, SubstitutionEx& subst) { + if (fact.getKind() != kind::EQUAL) return false; + + TNode left = fact[0]; + TNode right = fact[1]; + + + if (left.isVar() && !right.hasSubterm(left)) { + subst.addSubstitution(left, right, reason); + return true; + } + if (right.isVar() && !left.hasSubterm(right)) { + subst.addSubstitution(right, left, reason); + return true; + } + + // xor simplification + if (right.getKind() == kind::BITVECTOR_XOR && + left.getKind() == kind::BITVECTOR_XOR) { + TNode var = left[0]; + if (!var.getMetaKind() == kind::metakind::VARIABLE) + return false; + + // simplify xor with same variable on both sides + if (right.hasSubterm(var)) { + std::vector right_children; + for (unsigned i = 0; i < right.getNumChildren(); ++i) { + if (right[i] != var) + right_children.push_back(right[i]); + } + Assert (right_children.size()); + Node new_right = right_children.size() > 1 ? utils::mkNode(kind::BITVECTOR_XOR, right_children) + : right_children[0]; + std::vector left_children; + for (unsigned i = 1; i < left.getNumChildren(); ++i) { + left_children.push_back(left[i]); + } + Node new_left = left_children.size() > 1 ? utils::mkNode(kind::BITVECTOR_XOR, left_children) + : left_children[0]; + Node new_fact = utils::mkNode(kind::EQUAL, new_left, new_right); + subst.addSubstitution(fact, new_fact, reason); + return true; + } + + NodeBuilder<> nb(kind::BITVECTOR_XOR); + for (unsigned i = 1; i < left.getNumChildren(); ++i) { + nb << left[i]; + } + Node inverse = left.getNumChildren() == 2? (Node)left[1] : (Node)nb; + Node new_right = utils::mkNode(kind::BITVECTOR_XOR, right, inverse); + subst.addSubstitution(var, new_right, reason); + + if (Dump.isOn("bv-algebraic")) { + Node query = utils::mkNot(utils::mkNode(kind::IFF, fact, utils::mkNode(kind::EQUAL, var, new_right))); + Dump("bv-algebraic") << EchoCommand("ThoeryBV::AlgebraicSolver::substitution explanation"); + Dump("bv-algebraic") << PushCommand(); + Dump("bv-algebraic") << AssertCommand(query.toExpr()); + Dump("bv-algebraic") << CheckSatCommand(); + Dump("bv-algebraic") << PopCommand(); + } + + + return true; + } + + // (a xor t = a) <=> (t = 0) + if (left.getKind() == kind::BITVECTOR_XOR && + right.getMetaKind() == kind::metakind::VARIABLE && + left.hasSubterm(right)) { + TNode var = right; + Node new_left = utils::mkNode(kind::BITVECTOR_XOR, var, left); + Node zero = utils::mkConst(utils::getSize(var), 0u); + Node new_fact = utils::mkNode(kind::EQUAL, zero, new_left); + subst.addSubstitution(fact, new_fact, reason); + return true; + } + + if (right.getKind() == kind::BITVECTOR_XOR && + left.getMetaKind() == kind::metakind::VARIABLE && + right.hasSubterm(left)) { + TNode var = left; + Node new_right = utils::mkNode(kind::BITVECTOR_XOR, var, right); + Node zero = utils::mkConst(utils::getSize(var), 0u); + Node new_fact = utils::mkNode(kind::EQUAL, zero, new_right); + subst.addSubstitution(fact, new_fact, reason); + return true; + } + + // (a xor b = 0) <=> (a = b) + if (left.getKind() == kind::BITVECTOR_XOR && + left.getNumChildren() == 2 && + right.getKind() == kind::CONST_BITVECTOR && + right.getConst() == BitVector(utils::getSize(left), 0u)) { + Node new_fact = utils::mkNode(kind::EQUAL, left[0], left[1]); + subst.addSubstitution(fact, new_fact, reason); + return true; + } + + + return false; +} + +bool AlgebraicSolver::isSubstitutableIn(TNode node, TNode in) { + if (node.getMetaKind() == kind::metakind::VARIABLE && + !in.hasSubterm(node)) + return true; + return false; +} + +void AlgebraicSolver::processAssertions(std::vector& worklist, SubstitutionEx& subst) { + bool changed = true; + while(changed) { + changed = false; + for (unsigned i = 0; i < worklist.size(); ++i) { + // apply current substitutions + Node current = subst.apply(worklist[i].node); + unsigned current_id = worklist[i].id; + Node subst_expl = subst.explain(worklist[i].node); + worklist[i] = WorklistElement(Rewriter::rewrite(current), current_id); + // explanation for this assertion + Node old_expl = d_explanations[current_id]; + Node new_expl = mergeExplanations(subst_expl, old_expl); + storeExplanation(current_id, new_expl); + + // use the new substitution to solve + if(solve(worklist[i].node, new_expl, subst)) { + changed = true; + } + } + + // check for concat slicings + for (unsigned i = 0; i < worklist.size(); ++i) { + TNode fact = worklist[i].node; + unsigned current_id = worklist[i].id; + + if (fact.getKind() != kind::EQUAL) + continue; + + TNode left = fact[0]; + TNode right = fact[1]; + if (left.getKind() != kind::BITVECTOR_CONCAT || + right.getKind() != kind::BITVECTOR_CONCAT || + left.getNumChildren() != right.getNumChildren()) + continue; + + bool can_slice = true; + for (unsigned j = 0; j < left.getNumChildren(); ++j) { + if (utils::getSize(left[j]) != utils::getSize(right[j])) + can_slice = false; + } + + if (!can_slice) + continue; + + for (unsigned j = 0; j < left.getNumChildren(); ++j) { + Node eq_j = utils::mkNode(kind::EQUAL, left[j], right[j]); + unsigned id = d_explanations.size(); + TNode expl = d_explanations[current_id]; + storeExplanation(expl); + worklist.push_back(WorklistElement(eq_j, id)); + d_ids[eq_j] = id; + } + worklist[i] = WorklistElement(utils::mkTrue(), worklist[i].id); + changed = true; + } + Assert (d_explanations.size() == worklist.size()); + } +} + +void AlgebraicSolver::storeExplanation(unsigned id, TNode explanation) { + Assert (checkExplanation(explanation)); + d_explanations[id] = explanation; +} + +void AlgebraicSolver::storeExplanation(TNode explanation) { + Assert (checkExplanation(explanation)); + d_explanations.push_back(explanation); +} + +bool AlgebraicSolver::checkExplanation(TNode explanation) { + Node simplified_explanation = explanation; //BooleanSimplification::simplify(explanation); + if (simplified_explanation.getKind() != kind::AND) { + return d_inputAssertions.find(simplified_explanation) != d_inputAssertions.end(); + } + for (unsigned i = 0; i < simplified_explanation.getNumChildren(); ++i) { + if (d_inputAssertions.find(simplified_explanation[i]) == d_inputAssertions.end()) { + return false; + } + } + return true; +} + + +bool AlgebraicSolver::isComplete() { + return d_isComplete.get(); +} + +bool AlgebraicSolver::useHeuristic() { + if (d_numCalls == 0) + return true; + + double success_rate = d_numSolved/d_numCalls; + d_statistics.d_useHeuristic.setData(success_rate); + return success_rate > 0.8; +} + + +void AlgebraicSolver::assertFact(TNode fact) { + d_assertionQueue.push_back(fact); + if (!d_isDifficult.get()) { + d_isDifficult.set(hasExpensiveBVOperators(fact)); + } +} + +EqualityStatus AlgebraicSolver::getEqualityStatus(TNode a, TNode b) { + return EQUALITY_UNKNOWN; +} +void AlgebraicSolver::collectModelInfo(TheoryModel* model, bool fullModel) { + Debug("bv-subtheory-algebraic-models") << "AlgebraicSolver::collectModelInfo\n"; + AlwaysAssert (!d_quickSolver->inConflict()); + set termSet; + d_bv->computeRelevantTerms(termSet); + + // collect relevant terms that the bv theory abstracts to variables + // (variables and parametric terms such as select apply_uf) + std::vector variables; + std::vector values; + for (set::const_iterator it = termSet.begin(); it != termSet.end(); ++it) { + TNode term = *it; + if (term.getType().isBitVector() && + (term.getMetaKind() == kind::metakind::VARIABLE || + Theory::theoryOf(term) != THEORY_BV)) { + variables.push_back(term); + values.push_back(term); + } + } + + Debug("bv-subtheory-algebraic-models") << "Substitutions:\n"; + for (unsigned i = 0; i < variables.size(); ++i) { + TNode current = variables[i]; + TNode subst = Rewriter::rewrite(d_modelMap->apply(current)); + Debug("bv-subtheory-algebraic-models") << " " << current << " => " << subst << "\n"; + values[i] = subst; + } + + Debug("bv-subtheory-algebraic-models") << "Model:\n"; + for (BVQuickCheck::vars_iterator it = d_quickSolver->beginVars(); it != d_quickSolver->endVars(); ++it) { + TNode var = *it; + Node value = d_quickSolver->getVarValue(var); + Debug("bv-subtheory-algebraic-models") << " " << var << " => " << value << "\n"; + Assert (value.getKind() == kind::CONST_BITVECTOR); + d_modelMap->addSubstitution(var, value); + } + + Debug("bv-subtheory-algebraic-models") << "Final Model:\n"; + for (unsigned i = 0; i < variables.size(); ++i) { + TNode current = values[i]; + TNode subst = Rewriter::rewrite(d_modelMap->apply(current)); + Debug("bv-subtheory-algebraic-models") << " " << variables[i] << " => " << subst << "\n"; + // Doesn't have to be constant as it may be irrelevant + // Assert (subst.getKind() == kind::CONST_BITVECTOR); + model->assertEquality(variables[i], subst, true); + } + + } + +Node AlgebraicSolver::getModelValue(TNode node) { + return Node::null(); +} + +AlgebraicSolver::Statistics::Statistics() + : d_numCallstoCheck("theory::bv::AlgebraicSolver::NumCallsToCheck", 0) + , d_numSimplifiesToTrue("theory::bv::AlgebraicSolver::NumSimplifiesToTrue", 0) + , d_numSimplifiesToFalse("theory::bv::AlgebraicSolver::NumSimplifiesToFalse", 0) + , d_numUnsat("theory::bv::AlgebraicSolver::NumUnsat", 0) + , d_numSat("theory::bv::AlgebraicSolver::NumSat", 0) + , d_numUnknown("theory::bv::AlgebraicSolver::NumUnknown", 0) + , d_solveTime("theory::bv::AlgebraicSolver::SolveTime") + , d_useHeuristic("theory::bv::AlgebraicSolver::UseHeuristic", 0.2) +{ + StatisticsRegistry::registerStat(&d_numCallstoCheck); + StatisticsRegistry::registerStat(&d_numSimplifiesToTrue); + StatisticsRegistry::registerStat(&d_numSimplifiesToFalse); + StatisticsRegistry::registerStat(&d_numUnsat); + StatisticsRegistry::registerStat(&d_numSat); + StatisticsRegistry::registerStat(&d_numUnknown); + StatisticsRegistry::registerStat(&d_solveTime); + StatisticsRegistry::registerStat(&d_useHeuristic); +} + +AlgebraicSolver::Statistics::~Statistics() { + StatisticsRegistry::unregisterStat(&d_numCallstoCheck); + StatisticsRegistry::unregisterStat(&d_numSimplifiesToTrue); + StatisticsRegistry::unregisterStat(&d_numSimplifiesToFalse); + StatisticsRegistry::unregisterStat(&d_numUnsat); + StatisticsRegistry::unregisterStat(&d_numSat); + StatisticsRegistry::unregisterStat(&d_numUnknown); + StatisticsRegistry::unregisterStat(&d_solveTime); + StatisticsRegistry::unregisterStat(&d_useHeuristic); +} + +bool hasExpensiveBVOperatorsRec(TNode fact, TNodeSet& seen) { + if (fact.getKind() == kind::BITVECTOR_MULT || + fact.getKind() == kind::BITVECTOR_UDIV_TOTAL || + fact.getKind() == kind::BITVECTOR_UREM_TOTAL) { + return true; + } + + if (seen.find(fact) != seen.end()) + return false; + + if (fact.getNumChildren() == 0) + return false; + + for (unsigned i = 0; i < fact.getNumChildren(); ++i) { + bool difficult = hasExpensiveBVOperatorsRec(fact[i], seen); + if (difficult) + return true; + } + seen.insert(fact); + return false; +} + +bool hasExpensiveBVOperators(TNode fact) { + TNodeSet seen; + return hasExpensiveBVOperatorsRec(fact, seen); +} + +void ExtractSkolemizer::skolemize(std::vector& facts) { + TNodeSet seen; + for (unsigned i = 0; i < facts.size(); ++i) { + TNode current = facts[i].node; + collectExtracts(current, seen); + } + + for (VarExtractMap::iterator it = d_varToExtract.begin(); it != d_varToExtract.end(); ++it) { + ExtractList& el = it->second; + TNode var = it->first; + Base& base = el.base; + + unsigned bw = utils::getSize(var); + // compute decomposition + std::vector cuts; + for (unsigned i = 1; i <= bw; ++i) { + if (base.isCutPoint(i)) { + cuts.push_back(i); + } + } + unsigned previous = 0; + unsigned current = 0; + std::vector skolems; + for (unsigned i = 0; i < cuts.size(); ++i) { + current = cuts[i]; + Assert (current > 0); + int size = current - previous; + Assert (size > 0); + Node sk = utils::mkVar(size); + skolems.push_back(sk); + previous = current; + } + if (current < bw -1) { + int size = bw - current; + Assert (size > 0); + Node sk = utils::mkVar(size); + skolems.push_back(sk); + } + NodeBuilder<> skolem_nb(kind::BITVECTOR_CONCAT); + + for (int i = skolems.size() - 1; i >= 0; --i) { + skolem_nb << skolems[i]; + } + + Node skolem_concat = skolems.size() == 1 ? (Node)skolems[0] : (Node) skolem_nb; + Assert (utils::getSize(skolem_concat) == utils::getSize(var)); + storeSkolem(var, skolem_concat); + + for (unsigned i = 0; i < el.extracts.size(); ++i) { + unsigned h = el.extracts[i].high; + unsigned l = el.extracts[i].low; + Node extract = utils::mkExtract(var, h, l); + Node skolem_extract = Rewriter::rewrite(utils::mkExtract(skolem_concat, h, l)); + Assert (skolem_extract.getMetaKind() == kind::metakind::VARIABLE || + skolem_extract.getKind() == kind::BITVECTOR_CONCAT); + storeSkolem(extract, skolem_extract); + } + } + + for (unsigned i = 0; i < facts.size(); ++i) { + facts[i] = WorklistElement(skolemize(facts[i].node), facts[i].id); + } +} + +Node ExtractSkolemizer::mkSkolem(Node node) { + Assert (node.getKind() == kind::BITVECTOR_EXTRACT && + node[0].getMetaKind() == kind::metakind::VARIABLE); + Assert (!d_skolemSubst.hasSubstitution(node)); + return utils::mkVar(utils::getSize(node)); +} + +void ExtractSkolemizer::unSkolemize(std::vector& facts) { + for (unsigned i = 0; i < facts.size(); ++i) { + facts[i] = WorklistElement(unSkolemize(facts[i].node), facts[i].id); + } +} + +void ExtractSkolemizer::storeSkolem(TNode node, TNode skolem) { + d_skolemSubst.addSubstitution(node, skolem); + d_modelMap->addSubstitution(node, skolem); + d_skolemSubstRev.addSubstitution(skolem, node); +} + +Node ExtractSkolemizer::unSkolemize(TNode node) { + return d_skolemSubstRev.apply(node); +} + +Node ExtractSkolemizer::skolemize(TNode node) { + return d_skolemSubst.apply(node); +} + +void ExtractSkolemizer::ExtractList::addExtract(Extract& e) { + extracts.push_back(e); + base.sliceAt(e.low); + base.sliceAt(e.high+1); +} + +void ExtractSkolemizer::storeExtract(TNode var, unsigned high, unsigned low) { + Assert (var.getMetaKind() == kind::metakind::VARIABLE); + if (d_varToExtract.find(var) == d_varToExtract.end()) { + d_varToExtract[var] = ExtractList(utils::getSize(var)); + } + // std::cout << "extract " << var <<"["<second; + Extract e(high, low); + el.addExtract(e); +} + +void ExtractSkolemizer::collectExtracts(TNode node, TNodeSet& seen) { + if (seen.find(node) != seen.end()) { + return; + } + + if (node.getKind() == kind::BITVECTOR_EXTRACT && + node[0].getMetaKind() == kind::metakind::VARIABLE) { + unsigned high = utils::getExtractHigh(node); + unsigned low = utils::getExtractLow(node); + TNode var = node[0]; + storeExtract(var, high, low); + seen.insert(node); + return; + } + + if (node.getNumChildren() == 0) + return; + + for (unsigned i = 0; i < node.getNumChildren(); ++i) { + collectExtracts(node[i], seen); + } + seen.insert(node); +} + +ExtractSkolemizer::ExtractSkolemizer(theory::SubstitutionMap* modelMap) + : d_emptyContext() + , d_varToExtract() + , d_modelMap(modelMap) + , d_skolemSubst(&d_emptyContext) + , d_skolemSubstRev(&d_emptyContext) +{} + +ExtractSkolemizer::~ExtractSkolemizer() { +} + +Node CVC4::theory::bv::mergeExplanations(const std::vector& expls) { + TNodeSet literals; + for (unsigned i = 0; i < expls.size(); ++i) { + TNode expl = expls[i]; + Assert (expl.getType().isBoolean()); + if (expl.getKind() == kind::AND) { + for (unsigned i = 0; i < expl.getNumChildren(); ++i) { + TNode child = expl[i]; + if (child == utils::mkTrue()) + continue; + literals.insert(child); + } + } else if (expl != utils::mkTrue()) { + literals.insert(expl); + } + } + if (literals.size() == 0) + return utils::mkTrue(); + + if (literals.size() == 1) + return *literals.begin(); + + NodeBuilder<> nb(kind::AND); + + for (TNodeSet::const_iterator it = literals.begin(); it!= literals.end(); ++it) { + nb << *it; + } + return nb; +} + +Node CVC4::theory::bv::mergeExplanations(TNode expl1, TNode expl2) { + std::vector expls; + expls.push_back(expl1); + expls.push_back(expl2); + return mergeExplanations(expls); +} diff --git a/src/theory/bv/bv_subtheory_algebraic.h b/src/theory/bv/bv_subtheory_algebraic.h new file mode 100644 index 000000000..4acab29b3 --- /dev/null +++ b/src/theory/bv/bv_subtheory_algebraic.h @@ -0,0 +1,226 @@ +/********************* */ +/*! \file bv_subtheory_algebraic.h +** \verbatim +** Original author: Liana Hadarean +** Major contributors: none +** Minor contributors (to current version): none +** This file is part of the CVC4 project. +** Copyright (c) 2009-2013 New York University and The University of Iowa +** See the file COPYING in the top-level source directory for licensing +** information.\endverbatim +** +** \brief Algebraic solver. +** +** Algebraic solver. +**/ + +#pragma once + +#include "cvc4_private.h" +#include "theory/bv/bv_subtheory.h" +#include "theory/substitutions.h" +#include "theory/bv/slicer.h" + +namespace CVC4 { +namespace theory { +namespace bv { + +class AlgebraicSolver; + + +Node mergeExplanations(TNode expl1, TNode expl2); +Node mergeExplanations(const std::vector& expls); + + +/** + * Non-context dependent substitution with explanations. + * + */ +class SubstitutionEx { + struct SubstitutionElement { + Node to; + Node reason; + SubstitutionElement() + : to() + , reason() + {} + + SubstitutionElement(TNode t, TNode r) + : to(t) + , reason(r) + {} + }; + + struct SubstitutionStackElement { + TNode node; + bool childrenAdded; + SubstitutionStackElement(TNode n, bool ca = false) + : node(n) + , childrenAdded(ca) + {} + }; + + typedef __gnu_cxx::hash_map Substitutions; + typedef __gnu_cxx::hash_map SubstitutionsCache; + + Substitutions d_substitutions; + SubstitutionsCache d_cache; + bool d_cacheInvalid; + theory::SubstitutionMap* d_modelMap; + + + Node getReason(TNode node) const; + bool hasCache(TNode node) const; + Node getCache(TNode node) const; + void storeCache(TNode from, TNode to, Node rason); + Node internalApply(TNode node); + +public: + SubstitutionEx(theory::SubstitutionMap* modelMap); + void addSubstitution(TNode from, TNode to, TNode reason); + Node apply(TNode node); + Node explain(TNode node) const; +}; + +/** + * In-processing worklist element, id keeps track of + * original assertion. + * + */ +struct WorklistElement { + Node node; + unsigned id; + WorklistElement(Node n, unsigned i) : node(n), id(i) {} + WorklistElement() : node(), id(-1) {} +}; + + +typedef __gnu_cxx::hash_map NodeNodeMap; +typedef __gnu_cxx::hash_map NodeIdMap; +typedef __gnu_cxx::hash_set TNodeSet; + + +class ExtractSkolemizer { + struct Extract { + unsigned high; + unsigned low; + Extract(unsigned h, unsigned l) : high(h), low(l) {} + }; + + struct ExtractList { + Base base; + std::vector extracts; + ExtractList(unsigned bitwidth) : base(bitwidth), extracts() {} + ExtractList() : base(1), extracts() {} + void addExtract(Extract& e); + }; + typedef __gnu_cxx::hash_map VarExtractMap; + context::Context d_emptyContext; + VarExtractMap d_varToExtract; + theory::SubstitutionMap* d_modelMap; + theory::SubstitutionMap d_skolemSubst; + theory::SubstitutionMap d_skolemSubstRev; + + void storeSkolem(TNode node, TNode skolem); + void storeExtract(TNode var, unsigned high, unsigned low); + void collectExtracts(TNode node, TNodeSet& seen); + Node skolemize(TNode); + Node unSkolemize(TNode); + + Node mkSkolem(Node node); +public: + ExtractSkolemizer(theory::SubstitutionMap* modelMap); + void skolemize(std::vector&); + void unSkolemize(std::vector&); + ~ExtractSkolemizer(); +}; + +class BVQuickCheck; +class QuickXPlain; + +/** + * AlgebraicSolver + */ +class AlgebraicSolver : public SubtheorySolver { + + struct Statistics { + IntStat d_numCallstoCheck; + IntStat d_numSimplifiesToTrue; + IntStat d_numSimplifiesToFalse; + IntStat d_numUnsat; + IntStat d_numSat; + IntStat d_numUnknown; + TimerStat d_solveTime; + BackedStat d_useHeuristic; + Statistics(); + ~Statistics(); + }; + + SubstitutionMap* d_modelMap; + BVQuickCheck* d_quickSolver; + context::CDO d_isComplete; + context::CDO d_isDifficult; /**< flag to indicate whether the current assertions contain expensive BV operators */ + + unsigned long d_budget; + std::vector d_explanations; /**< explanations for assertions indexed by assertion id */ + TNodeSet d_inputAssertions; /**< assertions in current context (for debugging purposes only) */ + NodeIdMap d_ids; /**< map from assertions to ids */ + double d_numSolved; + double d_numCalls; + + context::Context* d_ctx; + QuickXPlain* d_quickXplain; /**< separate quickXplain module as it can reuse the current SAT solver */ + + Statistics d_statistics; + bool useHeuristic(); + void setConflict(TNode conflict); + bool isSubstitutableIn(TNode node, TNode in); + bool checkExplanation(TNode expl); + void storeExplanation(TNode expl); + void storeExplanation(unsigned id, TNode expl); + /** + * Apply substitutions and rewriting to the worklist assertions to a fixpoint. + * Subsitutions learned store in subst. + * + * @param worklist + * @param subst + */ + void processAssertions(std::vector& worklist, SubstitutionEx& subst); + /** + * Attempt to solve the equation in fact, and if successful + * add a substitution to subst. + * + * @param fact equation we are trying to solve + * @param reason the reason in terms of original assertions + * @param subst substitution map + * + * @return true if added a substitution to subst + */ + bool solve(TNode fact, TNode reason, SubstitutionEx& subst); + /** + * Run a SAT solver on the given facts with the given budget. + * Sets the isComplete flag and conflict accordingly. + * + * @param facts + * + * @return true if no conflict was detected. + */ + bool quickCheck(std::vector& facts); + +public: + AlgebraicSolver(context::Context* c, TheoryBV* bv); + ~AlgebraicSolver(); + + void preRegister(TNode node) {} + bool check(Theory::Effort e); + void explain(TNode literal, std::vector& assumptions) {Unreachable("AlgebraicSolver does not propagate.\n");} + EqualityStatus getEqualityStatus(TNode a, TNode b); + void collectModelInfo(TheoryModel* m, bool fullModel); + Node getModelValue(TNode node); + bool isComplete(); + virtual void assertFact(TNode fact); +}; + +} +} +} diff --git a/src/theory/bv/bv_subtheory_bitblast.cpp b/src/theory/bv/bv_subtheory_bitblast.cpp index e4b1a346d..d606ccee8 100644 --- a/src/theory/bv/bv_subtheory_bitblast.cpp +++ b/src/theory/bv/bv_subtheory_bitblast.cpp @@ -17,11 +17,13 @@ #include "theory/bv/bv_subtheory_bitblast.h" #include "theory/bv/theory_bv.h" #include "theory/bv/theory_bv_utils.h" -#include "theory/bv/bitblaster.h" +#include "theory/bv/lazy_bitblaster.h" +#include "theory/bv/bv_quick_check.h" #include "theory/bv/options.h" #include "theory/decision_attributes.h" #include "decision/options.h" + using namespace std; using namespace CVC4; using namespace CVC4::context; @@ -31,24 +33,38 @@ using namespace CVC4::theory::bv::utils; BitblastSolver::BitblastSolver(context::Context* c, TheoryBV* bv) : SubtheorySolver(c, bv), - d_bitblaster(new Bitblaster(c, bv)), + d_bitblaster(new TLazyBitblaster(c, bv, "lazy")), d_bitblastQueue(c), d_statistics(), d_validModelCache(c, true), - d_useSatPropagation(options::bvPropagate()) + d_lemmaAtomsQueue(c), + d_useSatPropagation(options::bitvectorPropagate()), + d_abstractionModule(NULL), + d_quickCheck(options::bitvectorQuickXplain() ? new BVQuickCheck("bb", bv) : NULL), + d_quickXplain(options::bitvectorQuickXplain() ? new QuickXPlain("bb", d_quickCheck) : NULL) {} BitblastSolver::~BitblastSolver() { + delete d_quickXplain; + delete d_quickCheck; delete d_bitblaster; } BitblastSolver::Statistics::Statistics() : d_numCallstoCheck("theory::bv::BitblastSolver::NumCallsToCheck", 0) + , d_numBBLemmas("theory::bv::BitblastSolver::NumTimesLemmasBB", 0) { StatisticsRegistry::registerStat(&d_numCallstoCheck); + StatisticsRegistry::registerStat(&d_numBBLemmas); } BitblastSolver::Statistics::~Statistics() { StatisticsRegistry::unregisterStat(&d_numCallstoCheck); + StatisticsRegistry::unregisterStat(&d_numBBLemmas); +} + +void BitblastSolver::setAbstraction(AbstractionModule* abs) { + d_abstractionModule = abs; + d_bitblaster->setAbstraction(abs); } void BitblastSolver::preRegister(TNode node) { @@ -58,19 +74,20 @@ void BitblastSolver::preRegister(TNode node) { node.getKind() == kind::BITVECTOR_SLT || node.getKind() == kind::BITVECTOR_SLE) && !d_bitblaster->hasBBAtom(node)) { - if (options::bitvectorEagerBitblast()) { - d_bitblaster->bbAtom(node); - } else { - CodeTimer weightComputationTime(d_bv->d_statistics.d_weightComputationTimer); - d_bitblastQueue.push_back(node); - if ((options::decisionUseWeight() || options::decisionThreshold() != 0) && - !node.hasAttribute(theory::DecisionWeightAttr())) { - node.setAttribute(theory::DecisionWeightAttr(), d_bitblaster->computeAtomWeight(node)); - } + CodeTimer weightComputationTime(d_bv->d_statistics.d_weightComputationTimer); + d_bitblastQueue.push_back(node); + if ((options::decisionUseWeight() || options::decisionThreshold() != 0) && + !node.hasAttribute(theory::DecisionWeightAttr())) { + node.setAttribute(theory::DecisionWeightAttr(),computeAtomWeight(node)); } } } +uint64_t BitblastSolver::computeAtomWeight(TNode node) { + NodeSet seen; + return d_bitblaster->computeAtomWeight(node, seen); +} + void BitblastSolver::explain(TNode literal, std::vector& assumptions) { d_bitblaster->explain(literal, assumptions); } @@ -78,14 +95,19 @@ void BitblastSolver::explain(TNode literal, std::vector& assumptions) { void BitblastSolver::bitblastQueue() { while (!d_bitblastQueue.empty()) { TNode atom = d_bitblastQueue.front(); - d_bitblaster->bbAtom(atom); d_bitblastQueue.pop(); + if (options::bvAbstraction() && + d_abstractionModule->isLemmaAtom(atom)) { + // don't bit-blast lemma atoms + continue; + } + d_bitblaster->bbAtom(atom); } } bool BitblastSolver::check(Theory::Effort e) { Debug("bv-bitblast") << "BitblastSolver::check (" << e << ")\n"; - Assert(!options::bitvectorEagerBitblast()); + Assert(options::bitblastMode() == theory::bv::BITBLAST_MODE_LAZY); ++(d_statistics.d_numCallstoCheck); @@ -98,7 +120,17 @@ bool BitblastSolver::check(Theory::Effort e) { TNode fact = get(); d_validModelCache = false; Debug("bv-bitblast") << " fact " << fact << ")\n"; - if (!d_bv->inConflict() && (!d_bv->wasPropagatedBySubtheory(fact) || d_bv->getPropagatingSubtheory(fact) != SUB_BITBLAST)) { + + if (options::bvAbstraction()) { + // skip atoms that are the result of abstraction lemmas + if (d_abstractionModule->isLemmaAtom(fact)) { + d_lemmaAtomsQueue.push_back(fact); + continue; + } + } + + if (!d_bv->inConflict() && + (!d_bv->wasPropagatedBySubtheory(fact) || d_bv->getPropagatingSubtheory(fact) != SUB_BITBLAST)) { // Some atoms have not been bit-blasted yet d_bitblaster->bbAtom(fact); // Assert to sat @@ -106,7 +138,7 @@ bool BitblastSolver::check(Theory::Effort e) { if (!ok) { std::vector conflictAtoms; d_bitblaster->getConflict(conflictAtoms); - d_bv->setConflict(mkConjunction(conflictAtoms)); + setConflict(mkConjunction(conflictAtoms)); return false; } } @@ -118,13 +150,13 @@ bool BitblastSolver::check(Theory::Effort e) { if (!ok) { std::vector conflictAtoms; d_bitblaster->getConflict(conflictAtoms); - d_bv->setConflict(mkConjunction(conflictAtoms)); + setConflict(mkConjunction(conflictAtoms)); return false; } } // Solving - if (e == Theory::EFFORT_FULL || options::bitvectorEagerFullcheck()) { + if (e == Theory::EFFORT_FULL) { Assert(!d_bv->inConflict()); Debug("bitvector::bitblaster") << "BitblastSolver::addAssertions solving. \n"; bool ok = d_bitblaster->solve(); @@ -132,11 +164,45 @@ bool BitblastSolver::check(Theory::Effort e) { std::vector conflictAtoms; d_bitblaster->getConflict(conflictAtoms); Node conflict = mkConjunction(conflictAtoms); - d_bv->setConflict(conflict); + setConflict(conflict); return false; } } + if (options::bvAbstraction() && + e == Theory::EFFORT_FULL && + d_lemmaAtomsQueue.size()) { + + // bit-blast lemma atoms + while(!d_lemmaAtomsQueue.empty()) { + TNode lemma_atom = d_lemmaAtomsQueue.front(); + d_bitblaster->bbAtom(lemma_atom); + d_lemmaAtomsQueue.pop(); + + // Assert to sat and check for conflicts + bool ok = d_bitblaster->assertToSat(lemma_atom, d_useSatPropagation); + if (!ok) { + std::vector conflictAtoms; + d_bitblaster->getConflict(conflictAtoms); + setConflict(mkConjunction(conflictAtoms)); + return false; + } + } + + Assert(!d_bv->inConflict()); + bool ok = d_bitblaster->solve(); + if (!ok) { + std::vector conflictAtoms; + d_bitblaster->getConflict(conflictAtoms); + Node conflict = mkConjunction(conflictAtoms); + setConflict(conflict); + ++(d_statistics.d_numBBLemmas); + return false; + } + + } + + return true; } @@ -191,3 +257,15 @@ Node BitblastSolver::getModelValueRec(TNode node) Debug("bitvector-model") << node << " => " << val <<"\n"; return val; } + + +void BitblastSolver::setConflict(TNode conflict) { + Node final_conflict = conflict; + if (options::bitvectorQuickXplain() && + conflict.getKind() == kind::AND) { + // std::cout << "Original conflict " << conflict.getNumChildren() << "\n"; + final_conflict = d_quickXplain->minimizeConflict(conflict); + //std::cout << "Minimized conflict " << final_conflict.getNumChildren() << "\n"; + } + d_bv->setConflict(final_conflict); +} diff --git a/src/theory/bv/bv_subtheory_bitblast.h b/src/theory/bv/bv_subtheory_bitblast.h index b1c1b3b66..511318521 100644 --- a/src/theory/bv/bv_subtheory_bitblast.h +++ b/src/theory/bv/bv_subtheory_bitblast.h @@ -19,11 +19,16 @@ #pragma once #include "theory/bv/bv_subtheory.h" +#include "theory/bv/bitblaster_template.h" + namespace CVC4 { namespace theory { namespace bv { -class Bitblaster; +class LazyBitblaster; +class AbstractionModule; +class BVQuickCheck; +class QuickXPlain; /** * BitblastSolver @@ -31,11 +36,12 @@ class Bitblaster; class BitblastSolver : public SubtheorySolver { struct Statistics { IntStat d_numCallstoCheck; + IntStat d_numBBLemmas; Statistics(); ~Statistics(); }; /** Bitblaster */ - Bitblaster* d_bitblaster; + TLazyBitblaster* d_bitblaster; /** Nodes that still need to be bit-blasted */ context::CDQueue d_bitblastQueue; @@ -44,9 +50,15 @@ class BitblastSolver : public SubtheorySolver { typedef std::hash_map NodeMap; NodeMap d_modelCache; context::CDO d_validModelCache; - Node getModelValueRec(TNode node); + /** Queue for bit-blasting lemma atoms only in full check if we are sat */ + context::CDQueue d_lemmaAtomsQueue; bool d_useSatPropagation; + AbstractionModule* d_abstractionModule; + BVQuickCheck* d_quickCheck; + QuickXPlain* d_quickXplain; + Node getModelValueRec(TNode node); + void setConflict(TNode conflict); public: BitblastSolver(context::Context* c, TheoryBV* bv); ~BitblastSolver(); @@ -59,6 +71,8 @@ public: Node getModelValue(TNode node); bool isComplete() { return true; } void bitblastQueue(); + void setAbstraction(AbstractionModule* module); + uint64_t computeAtomWeight(TNode atom); }; } diff --git a/src/theory/bv/bv_subtheory_core.cpp b/src/theory/bv/bv_subtheory_core.cpp index 2433dc1ee..ca414a2ff 100644 --- a/src/theory/bv/bv_subtheory_core.cpp +++ b/src/theory/bv/bv_subtheory_core.cpp @@ -34,10 +34,12 @@ CoreSolver::CoreSolver(context::Context* c, TheoryBV* bv) d_notify(*this), d_equalityEngine(d_notify, c, "theory::bv::TheoryBV"), d_slicer(new Slicer()), - d_isCoreTheory(c, true), + d_isComplete(c, true), + d_useSlicer(false), + d_preregisterCalled(false), + d_checkCalled(false), d_reasons(c) { - // The kinds we are treating as function application in congruence d_equalityEngine.addFunctionKind(kind::BITVECTOR_CONCAT, true); // d_equalityEngine.addFunctionKind(kind::BITVECTOR_AND); @@ -78,11 +80,19 @@ void CoreSolver::setMasterEqualityEngine(eq::EqualityEngine* eq) { d_equalityEngine.setMasterEqualityEngine(eq); } +void CoreSolver::enableSlicer() { + AlwaysAssert (!d_preregisterCalled); + d_useSlicer = true; + d_statistics.d_slicerEnabled.setData(true); +} + void CoreSolver::preRegister(TNode node) { + d_preregisterCalled = true; if (node.getKind() == kind::EQUAL) { d_equalityEngine.addTriggerEquality(node); - if (options::bitvectorCoreSolver()) { + if (d_useSlicer) { d_slicer->processEquality(node); + AlwaysAssert(!d_checkCalled); } } else { d_equalityEngine.addTerm(node); @@ -109,11 +119,6 @@ Node CoreSolver::getBaseDecomposition(TNode a) { } bool CoreSolver::decomposeFact(TNode fact) { - Debug("bv-slicer") << "CoreSolver::decomposeFact fact=" << fact << endl; - // assert decompositions since the equality engine does not know the semantics of - // concat: - // a == a_1 concat ... concat a_k - // b == b_1 concat ... concat b_k Debug("bv-slicer") << "CoreSolver::decomposeFact fact=" << fact << endl; // FIXME: are this the right things to assert? // assert decompositions since the equality engine does not know the semantics of @@ -161,21 +166,21 @@ bool CoreSolver::decomposeFact(TNode fact) { bool CoreSolver::check(Theory::Effort e) { Trace("bitvector::core") << "CoreSolver::check \n"; + d_checkCalled = true; Assert (!d_bv->inConflict()); ++(d_statistics.d_numCallstoCheck); bool ok = true; std::vector core_eqs; + TNodeBoolMap seen; while (! done()) { TNode fact = get(); - - // update whether we are in the core fragment - if (d_isCoreTheory && !d_slicer->isCoreTerm(fact)) { - d_isCoreTheory = false; + if (d_isComplete && !isCompleteForTerm(fact, seen)) { + d_isComplete = false; } - + // only reason about equalities if (fact.getKind() == kind::EQUAL || (fact.getKind() == kind::NOT && fact[0].getKind() == kind::EQUAL)) { - if (options::bitvectorCoreSolver()) { + if (d_useSlicer) { ok = decomposeFact(fact); } else { ok = assertFactToEqualityEngine(fact, fact); @@ -195,11 +200,6 @@ bool CoreSolver::check(Theory::Effort e) { } void CoreSolver::buildModel() { - if (options::bitvectorCoreSolver()) { - // FIXME - Unreachable(); - return; - } Debug("bv-core") << "CoreSolver::buildModel() \n"; d_modelValues.clear(); TNodeSet constants; @@ -218,6 +218,7 @@ void CoreSolver::buildModel() { } ++eqcs_i; } + // build repr to value map eqcs_i = eq::EqClassesIterator(&d_equalityEngine); @@ -356,10 +357,16 @@ void CoreSolver::conflict(TNode a, TNode b) { d_bv->setConflict(conflict); } +bool CoreSolver::isCompleteForTerm(TNode term, TNodeBoolMap& seen) { + if (d_useSlicer) + return utils::isCoreTerm(term, seen); + + return utils::isEqualityTerm(term, seen); +} + void CoreSolver::collectModelInfo(TheoryModel* m, bool fullModel) { - if (options::bitvectorCoreSolver()) { - Unreachable(); - return; + if (d_useSlicer) { + Unreachable(); } if (Debug.isOn("bitvector-model")) { context::CDQueue::const_iterator it = d_assertionQueue.begin(); @@ -376,6 +383,8 @@ void CoreSolver::collectModelInfo(TheoryModel* m, bool fullModel) { for (ModelValue::const_iterator it = d_modelValues.begin(); it != d_modelValues.end(); ++it) { Node a = it->first; Node b = it->second; + Debug("bitvector-model") << "CoreSolver::collectModelInfo modelValues " + << a << " => " << b <<")\n"; m->assertEquality(a, b, true); } } @@ -406,9 +415,12 @@ Node CoreSolver::getModelValue(TNode var) { CoreSolver::Statistics::Statistics() : d_numCallstoCheck("theory::bv::CoreSolver::NumCallsToCheck", 0) + , d_slicerEnabled("theory::bv::CoreSolver::SlicerEnabled", false) { StatisticsRegistry::registerStat(&d_numCallstoCheck); + StatisticsRegistry::registerStat(&d_slicerEnabled); } CoreSolver::Statistics::~Statistics() { StatisticsRegistry::unregisterStat(&d_numCallstoCheck); + StatisticsRegistry::unregisterStat(&d_slicerEnabled); } diff --git a/src/theory/bv/bv_subtheory_core.h b/src/theory/bv/bv_subtheory_core.h index e1a73d404..9ab6cfce4 100644 --- a/src/theory/bv/bv_subtheory_core.h +++ b/src/theory/bv/bv_subtheory_core.h @@ -32,10 +32,13 @@ class Base; */ class CoreSolver : public SubtheorySolver { typedef __gnu_cxx::hash_map ModelValue; + typedef __gnu_cxx::hash_map TNodeBoolMap; typedef __gnu_cxx::hash_set TNodeSet; + struct Statistics { IntStat d_numCallstoCheck; + BackedStat d_slicerEnabled; Statistics(); ~Statistics(); }; @@ -70,7 +73,13 @@ class CoreSolver : public SubtheorySolver { void conflict(TNode a, TNode b); Slicer* d_slicer; - context::CDO d_isCoreTheory; + context::CDO d_isComplete; + + /** Used to ensure that the core slicer is used properly*/ + bool d_useSlicer; + bool d_preregisterCalled; + bool d_checkCalled; + /** To make sure we keep the explanations */ context::CDHashSet d_reasons; ModelValue d_modelValues; @@ -78,11 +87,12 @@ class CoreSolver : public SubtheorySolver { bool assertFactToEqualityEngine(TNode fact, TNode reason); bool decomposeFact(TNode fact); Node getBaseDecomposition(TNode a); + bool isCompleteForTerm(TNode term, TNodeBoolMap& seen); Statistics d_statistics; public: CoreSolver(context::Context* c, TheoryBV* bv); ~CoreSolver(); - bool isComplete() { return d_isCoreTheory; } + bool isComplete() { return d_isComplete; } void setMasterEqualityEngine(eq::EqualityEngine* eq); void preRegister(TNode node); bool check(Theory::Effort e); @@ -105,6 +115,7 @@ public: } bool hasTerm(TNode node) const { return d_equalityEngine.hasTerm(node); } void addTermToEqualityEngine(TNode node) { d_equalityEngine.addTerm(node); } + void enableSlicer(); }; diff --git a/src/theory/bv/bv_to_bool.cpp b/src/theory/bv/bv_to_bool.cpp index d137d09a0..72131d6e7 100644 --- a/src/theory/bv/bv_to_bool.cpp +++ b/src/theory/bv/bv_to_bool.cpp @@ -23,171 +23,141 @@ using namespace CVC4; using namespace CVC4::theory; using namespace CVC4::theory::bv; -void BvToBoolVisitor::addToCache(TNode term, Node new_term) { +BvToBoolPreprocessor::BvToBoolPreprocessor() + : d_liftCache() + , d_boolCache() + , d_one(utils::mkConst(BitVector(1, 1u))) + , d_zero(utils::mkConst(BitVector(1, 0u))) + , d_statistics() +{} + +void BvToBoolPreprocessor::addToLiftCache(TNode term, Node new_term) { Assert (new_term != Node()); - Assert (!hasCache(term)); - d_cache[term] = new_term; + Assert (!hasLiftCache(term)); + Assert (term.getType() == new_term.getType()); + d_liftCache[term] = new_term; } -Node BvToBoolVisitor::getCache(TNode term) const { - if (!hasCache(term) || term.getKind() == kind::CONST_BITVECTOR) { - return term; - } - return d_cache.find(term)->second; +Node BvToBoolPreprocessor::getLiftCache(TNode term) const { + Assert(hasLiftCache(term)); + return d_liftCache.find(term)->second; } -bool BvToBoolVisitor::hasCache(TNode term) const { - return d_cache.find(term) != d_cache.end(); +bool BvToBoolPreprocessor::hasLiftCache(TNode term) const { + return d_liftCache.find(term) != d_liftCache.end(); } -void BvToBoolVisitor::start(TNode node) {} - -void BvToBoolVisitor::storeBvToBool(TNode bv_term, TNode bool_term) { - Assert (bv_term.getType().isBitVector() && - bv_term.getType().getBitVectorSize() == 1 && - bool_term.getType().isBoolean() && bv_term != Node() && bool_term != Node()); - if (d_bvToBoolMap.find(bv_term) != d_bvToBoolMap.end()) { - Assert (d_bvToBoolMap[bv_term] == bool_term); - } - d_bvToBoolMap[bv_term] = bool_term; +void BvToBoolPreprocessor::addToBoolCache(TNode term, Node new_term) { + Assert (new_term != Node()); + Assert (!hasBoolCache(term)); + Assert (utils::getSize(term) == 1); + Assert (new_term.getType().isBoolean()); + d_boolCache[term] = new_term; } -Node BvToBoolVisitor::getBoolForBvTerm(TNode node) { - Assert (d_bvToBoolMap.find(node) != d_bvToBoolMap.end()); - return d_bvToBoolMap[node]; +Node BvToBoolPreprocessor::getBoolCache(TNode term) const { + Assert(hasBoolCache(term)); + return d_boolCache.find(term)->second; } -bool BvToBoolVisitor::alreadyVisited(TNode current, TNode parent) { - return d_cache.find(current) != d_cache.end(); +bool BvToBoolPreprocessor::hasBoolCache(TNode term) const { + return d_boolCache.find(term) != d_boolCache.end(); } - -bool BvToBoolVisitor::isConvertibleBvAtom(TNode node) { +bool BvToBoolPreprocessor::isConvertibleBvAtom(TNode node) { Kind kind = node.getKind(); - return (kind == kind::BITVECTOR_ULT || - kind == kind::BITVECTOR_ULE || - kind == kind::BITVECTOR_SLT || - kind == kind::BITVECTOR_SLE || - kind == kind::EQUAL) && - isConvertibleBvTerm(node[0]) && - isConvertibleBvTerm(node[1]); -} - -bool BvToBoolVisitor::isConvertibleBvTerm(TNode node) { - // we have already converted it and the result is cached - if (d_bvToBoolMap.find(node) != d_bvToBoolMap.end()) { - return true; - } - - if (!node.getType().isBitVector() || node.getType().getBitVectorSize() != 1) + return (kind == kind::EQUAL && + node[0].getType().isBitVector() && + node[0].getType().getBitVectorSize() == 1 && + node[1].getType().isBitVector() && + node[1].getType().getBitVectorSize() == 1 && + node[0].getKind() != kind::BITVECTOR_EXTRACT && + node[1].getKind() != kind::BITVECTOR_EXTRACT); +} + +bool BvToBoolPreprocessor::isConvertibleBvTerm(TNode node) { + if (!node.getType().isBitVector() || + node.getType().getBitVectorSize() != 1) return false; Kind kind = node.getKind(); - if (kind == kind::CONST_BITVECTOR) { + if (kind == kind::CONST_BITVECTOR || + kind == kind::ITE || + kind == kind::BITVECTOR_AND || + kind == kind::BITVECTOR_OR || + kind == kind::BITVECTOR_NOT || + kind == kind::BITVECTOR_XOR) { return true; } - if (kind == kind::ITE) { - return isConvertibleBvTerm(node[1]) && isConvertibleBvTerm(node[2]); - } - - if (kind == kind::BITVECTOR_AND || kind == kind::BITVECTOR_OR || - kind == kind::BITVECTOR_NOT || kind == kind::BITVECTOR_XOR) { - for (unsigned i = 0; i < node.getNumChildren(); ++i) { - if (!isConvertibleBvTerm(node[i])) - return false; - } - return true; - } - if (kind == kind::VARIABLE) { - storeBvToBool(node, utils::mkNode(kind::EQUAL, node, utils::mkConst(BitVector(1, 1u)))); - return true; - } return false; } -Node BvToBoolVisitor::convertBvAtom(TNode node) { - Assert (node.getType().isBoolean()); - Kind kind = node.getKind(); - Node result; - switch(kind) { - case kind::BITVECTOR_ULT: { - Node a = getBoolForBvTerm(node[0]); - Node b = getBoolForBvTerm(node[1]); - Node a_eq_0 = utils::mkNode(kind::IFF, a, utils::mkFalse()); - Node b_eq_1 = utils::mkNode(kind::IFF, b, utils::mkTrue()); - result = utils::mkNode(kind::AND, a_eq_0, b_eq_1); - break; - } - case kind::BITVECTOR_ULE: { - Node a = getBoolForBvTerm(node[0]); - Node b = getBoolForBvTerm(node[1]); - Node a_eq_0 = utils::mkNode(kind::IFF, a, utils::mkFalse()); - Node b_eq_1 = utils::mkNode(kind::IFF, b, utils::mkTrue()); - Node a_lt_b = utils::mkNode(kind::AND, a_eq_0, b_eq_1); - Node a_eq_b = utils::mkNode(kind::IFF, a, b); - result = utils::mkNode(kind::OR, a_lt_b, a_eq_b); - break; - } - case kind::BITVECTOR_SLT: { - Node a = getBoolForBvTerm(node[0]); - Node b = getBoolForBvTerm(node[1]); - Node a_eq_1 = utils::mkNode(kind::IFF, a, utils::mkTrue()); - Node b_eq_0 = utils::mkNode(kind::IFF, b, utils::mkFalse()); - result = utils::mkNode(kind::AND, a_eq_1, b_eq_0); - break; - } - case kind::BITVECTOR_SLE: { - Node a = getBoolForBvTerm(node[0]); - Node b = getBoolForBvTerm(node[1]); - Node a_eq_1 = utils::mkNode(kind::IFF, a, utils::mkTrue()); - Node b_eq_0 = utils::mkNode(kind::IFF, b, utils::mkFalse()); - Node a_slt_b = utils::mkNode(kind::AND, a_eq_1, b_eq_0); - Node a_eq_b = utils::mkNode(kind::IFF, a, b); - result = utils::mkNode(kind::OR, a_slt_b, a_eq_b); - break; - } - case kind::EQUAL: { - Node a = getBoolForBvTerm(node[0]); - Node b = getBoolForBvTerm(node[1]); - result = utils::mkNode(kind::IFF, a, b); - break; - } - default: - Unhandled(); - } - Debug("bv-to-bool") << "BvToBoolVisitor::convertBvAtom " << node <<" => " << result << "\n"; - Assert (result != Node()); +Node BvToBoolPreprocessor::convertBvAtom(TNode node) { + Assert (node.getType().isBoolean() && + node.getKind() == kind::EQUAL); + Assert (utils::getSize(node[0]) == 1); + Assert (utils::getSize(node[1]) == 1); + Node a = convertBvTerm(node[0]); + Node b = convertBvTerm(node[1]); + Node result = utils::mkNode(kind::IFF, a, b); + Debug("bv-to-bool") << "BvToBoolPreprocessor::convertBvAtom " << node <<" => " << result << "\n"; + + ++(d_statistics.d_numAtomsLifted); return result; } -Node BvToBoolVisitor::convertBvTerm(TNode node) { +Node BvToBoolPreprocessor::convertBvTerm(TNode node) { Assert (node.getType().isBitVector() && node.getType().getBitVectorSize() == 1); - Kind kind = node.getKind(); + + if (hasBoolCache(node)) + return getBoolCache(node); + + if (!isConvertibleBvTerm(node)) { + ++(d_statistics.d_numTermsForcedLifted); + Node result = utils::mkNode(kind::EQUAL, node, d_one); + addToBoolCache(node, result); + Debug("bv-to-bool") << "BvToBoolPreprocessor::convertBvTerm " << node <<" => " << result << "\n"; + return result; + } if (node.getNumChildren() == 0) { - if (node.getKind() == kind::VARIABLE) { - return getBoolForBvTerm(node); - } - if (node.getKind() == kind::CONST_BITVECTOR) { - Node result = node == d_one ? utils::mkTrue() : utils::mkFalse(); - storeBvToBool(node, result); - return result; - } + Assert (node.getKind() == kind::CONST_BITVECTOR); + Node result = node == d_one ? utils::mkTrue() : utils::mkFalse(); + // addToCache(node, result); + Debug("bv-to-bool") << "BvToBoolPreprocessor::convertBvTerm " << node <<" => " << result << "\n"; + return result; } + + ++(d_statistics.d_numTermsLifted); + Kind kind = node.getKind(); if (kind == kind::ITE) { - Node cond = getCache(node[0]); - Node true_branch = getBoolForBvTerm(node[1]); - Node false_branch = getBoolForBvTerm(node[2]); + Node cond = liftNode(node[0]); + Node true_branch = convertBvTerm(node[1]); + Node false_branch = convertBvTerm(node[2]); Node result = utils::mkNode(kind::ITE, cond, true_branch, false_branch); - storeBvToBool(node, result); - Debug("bv-to-bool") << "BvToBoolVisitor::convertBvTerm " << node <<" => " << result << "\n"; + addToBoolCache(node, result); + Debug("bv-to-bool") << "BvToBoolPreprocessor::convertBvTerm " << node <<" => " << result << "\n"; + return result; + } + + Kind new_kind; + // special case for XOR as it has to be binary + // while BITVECTOR_XOR can be n-ary + if (kind == kind::BITVECTOR_XOR) { + new_kind = kind::XOR; + Node result = convertBvTerm(node[0]); + for (unsigned i = 1; i < node.getNumChildren(); ++i) { + Node converted = convertBvTerm(node[i]); + result = utils::mkNode(kind::XOR, result, converted); + } + Debug("bv-to-bool") << "BvToBoolPreprocessor::convertBvTerm " << node <<" => " << result << "\n"; return result; } - Kind new_kind; + switch(kind) { case kind::BITVECTOR_OR: new_kind = kind::OR; @@ -207,108 +177,69 @@ Node BvToBoolVisitor::convertBvTerm(TNode node) { NodeBuilder<> builder(new_kind); for (unsigned i = 0; i < node.getNumChildren(); ++i) { - builder << getBoolForBvTerm(node[i]); + builder << convertBvTerm(node[i]); } + Node result = builder; - storeBvToBool(node, result); - Debug("bv-to-bool") << "BvToBoolVisitor::convertBvTerm " << node <<" => " << result << "\n"; + addToBoolCache(node, result); + Debug("bv-to-bool") << "BvToBoolPreprocessor::convertBvTerm " << node <<" => " << result << "\n"; return result; } -void BvToBoolVisitor::check(TNode current, TNode parent) { - if (d_bvToBoolMap.find(current) != d_bvToBoolMap.end()) { - if (!isConvertibleBvTerm(parent) && !isConvertibleBvAtom(parent)) { - Debug("bv-to-bool") << "BvToBoolVisitor::check " << current << " in non boolean context: \n" - << " " << parent << "\n"; - } - } -} -void BvToBoolVisitor::visit(TNode current, TNode parent) { - Debug("bv-to-bool") << "BvToBoolVisitor visit (" << current << ", " << parent << ")\n"; - Assert (!alreadyVisited(current, parent) && - !hasCache(current)); +Node BvToBoolPreprocessor::liftNode(TNode current) { Node result; - // make sure that the bv terms we are replacing to not occur in other contexts - check(current, parent); - if (isConvertibleBvAtom(current)) { + if (hasLiftCache(current)) { + result = getLiftCache(current); + }else if (isConvertibleBvAtom(current)) { result = convertBvAtom(current); - addToCache(current, result); - } else if (isConvertibleBvTerm(current)) { - result = convertBvTerm(current); + addToLiftCache(current, result); } else { if (current.getNumChildren() == 0) { - result = current; + result = current; } else { NodeBuilder<> builder(current.getKind()); if (current.getMetaKind() == kind::metakind::PARAMETERIZED) { builder << current.getOperator(); } for (unsigned i = 0; i < current.getNumChildren(); ++i) { - Node converted = getCache(current[i]); + Node converted = liftNode(current[i]); Assert (converted.getType() == current[i].getType()); builder << converted; } result = builder; + addToLiftCache(current, result); } - addToCache(current, result); } Assert (result != Node()); - Debug("bv-to-bool") << " =>" << result <<"\n"; -} - - -BvToBoolVisitor::return_type BvToBoolVisitor::done(TNode node) { - Assert (hasCache(node)); - Node result = getCache(node); + Assert(result.getType() == current.getType()); + Debug("bv-to-bool") << "BvToBoolPreprocessor::liftNode " << current << " => \n" << result << "\n"; return result; } -bool BvToBoolVisitor::hasBoolTerm(TNode node) { - return d_bvToBoolMap.find(node) != d_bvToBoolMap.end(); -} -bool BvToBoolPreprocessor::matchesBooleanPatern(TNode current) { - // we are looking for something of the type (= (bvvar 1) (some predicate)) - if (current.getKind() == kind::IFF && - current[0].getKind() == kind::EQUAL && - current[0][0].getType().isBitVector() && - current[0][0].getType().getBitVectorSize() == 1 && - current[0][0].getKind() == kind::VARIABLE && - current[0][1].getKind() == kind::CONST_BITVECTOR) { - return true; - } - return false; -} - - -void BvToBoolPreprocessor::liftBoolToBV(const std::vector& assertions, std::vector& new_assertions) { - BvToBoolVisitor bvToBoolVisitor; - +void BvToBoolPreprocessor::liftBvToBool(const std::vector& assertions, std::vector& new_assertions) { for (unsigned i = 0; i < assertions.size(); ++i) { - if (matchesBooleanPatern(assertions[i])) { - TNode assertion = assertions[i]; - TNode bv_var = assertion[0][0]; - Assert (bv_var.getKind() == kind::VARIABLE && - bv_var.getType().isBitVector() && - bv_var.getType().getBitVectorSize() == 1); - Node bool_cond = NodeVisitor::run(bvToBoolVisitor, assertion[1]); - Assert (bool_cond.getType().isBoolean()); - if (!bvToBoolVisitor.hasBoolTerm(bv_var)) { - Debug("bv-to-bool") << "BBvToBoolPreprocessor::liftBvToBoolBV candidate: " << bv_var <<"\n"; - bvToBoolVisitor.storeBvToBool(bv_var, bool_cond); - } else { - Debug("bv-to-bool") << "BvToBoolPreprocessor::liftBvToBoolBV multiple def " << bv_var <<"\n"; - } - } - } - - for (unsigned i = 0; i < assertions.size(); ++i) { - Node new_assertion = NodeVisitor::run(bvToBoolVisitor, - assertions[i]); + Node new_assertion = liftNode(assertions[i]); new_assertions.push_back(new_assertion); Trace("bv-to-bool") << " " << assertions[i] <<" => " << new_assertions[i] <<"\n"; } } + +BvToBoolPreprocessor::Statistics::Statistics() + : d_numTermsLifted("theory::bv::BvToBoolPreprocess::NumberOfTermsLifted", 0) + , d_numAtomsLifted("theory::bv::BvToBoolPreprocess::NumberOfAtomsLifted", 0) + , d_numTermsForcedLifted("theory::bv::BvToBoolPreprocess::NumberOfTermsForcedLifted", 0) +{ + StatisticsRegistry::registerStat(&d_numTermsLifted); + StatisticsRegistry::registerStat(&d_numAtomsLifted); + StatisticsRegistry::registerStat(&d_numTermsForcedLifted); +} + +BvToBoolPreprocessor::Statistics::~Statistics() { + StatisticsRegistry::unregisterStat(&d_numTermsLifted); + StatisticsRegistry::unregisterStat(&d_numAtomsLifted); + StatisticsRegistry::unregisterStat(&d_numTermsForcedLifted); +} diff --git a/src/theory/bv/bv_to_bool.h b/src/theory/bv/bv_to_bool.h index b34923728..28501ba96 100644 --- a/src/theory/bv/bv_to_bool.h +++ b/src/theory/bv/bv_to_bool.h @@ -16,6 +16,7 @@ #include "cvc4_private.h" #include "theory/bv/theory_bv_utils.h" +#include "util/statistics_registry.h" #ifndef __CVC4__THEORY__BV__BV_TO_BOOL_H #define __CVC4__THEORY__BV__BV_TO_BOOL_H @@ -24,51 +25,43 @@ namespace CVC4 { namespace theory { namespace bv { -typedef __gnu_cxx::hash_set TNodeSet; -typedef __gnu_cxx::hash_map NodeNodeMap; +typedef __gnu_cxx::hash_map NodeNodeMap; -class BvToBoolVisitor { - NodeNodeMap d_bvToBoolMap; - NodeNodeMap d_cache; +class BvToBoolPreprocessor { + + struct Statistics { + IntStat d_numTermsLifted; + IntStat d_numAtomsLifted; + IntStat d_numTermsForcedLifted; + Statistics(); + ~Statistics(); + }; + + NodeNodeMap d_liftCache; + NodeNodeMap d_boolCache; Node d_one; Node d_zero; - void addToCache(TNode term, Node new_term); - Node getCache(TNode term) const; - bool hasCache(TNode term) const; + void addToBoolCache(TNode term, Node new_term); + Node getBoolCache(TNode term) const; + bool hasBoolCache(TNode term) const; + + void addToLiftCache(TNode term, Node new_term); + Node getLiftCache(TNode term) const; + bool hasLiftCache(TNode term) const; bool isConvertibleBvTerm(TNode node); bool isConvertibleBvAtom(TNode node); - Node getBoolForBvTerm(TNode node); Node convertBvAtom(TNode node); Node convertBvTerm(TNode node); - void check(TNode current, TNode parent); + Node liftNode(TNode current); + Statistics d_statistics; public: - typedef Node return_type; - BvToBoolVisitor() - : d_bvToBoolMap(), - d_cache(), - d_one(utils::mkConst(BitVector(1, 1u))), - d_zero(utils::mkConst(BitVector(1, 0u))) - {} - void start(TNode node); - bool alreadyVisited(TNode current, TNode parent); - void visit(TNode current, TNode parent); - return_type done(TNode node); - void storeBvToBool(TNode bv_term, TNode bool_term); - bool hasBoolTerm(TNode node); + BvToBoolPreprocessor(); + void liftBvToBool(const std::vector& assertions, std::vector& new_assertions); }; -class BvToBoolPreprocessor { - bool matchesBooleanPatern(TNode node); -public: - BvToBoolPreprocessor() - {} - ~BvToBoolPreprocessor() {} - void liftBoolToBV(const std::vector& assertions, std::vector& new_assertions); -}; - }/* CVC4::theory::bv namespace */ }/* CVC4::theory namespace */ diff --git a/src/theory/bv/eager_bitblaster.h b/src/theory/bv/eager_bitblaster.h new file mode 100644 index 000000000..da73c7f09 --- /dev/null +++ b/src/theory/bv/eager_bitblaster.h @@ -0,0 +1,163 @@ +/********************* */ +/*! \file eager_bitblaster.h + ** \verbatim + ** Original author: Liana Hadarean + ** Major contributors: none + ** Minor contributors (to current version): lianah + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2013 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief + ** + ** Bitblaster for the lazy bv solver. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__EAGER__BITBLASTER_H +#define __CVC4__EAGER__BITBLASTER_H + + +#include "bitblaster_template.h" +#include "theory/theory_registrar.h" +#include "prop/cnf_stream.h" +#include "prop/sat_solver_factory.h" +#include "theory/bv/options.h" + +namespace CVC4 { +namespace theory { +namespace bv { + + +class BitblastingRegistrar: public prop::Registrar { + EagerBitblaster* d_bitblaster; +public: + BitblastingRegistrar(EagerBitblaster* bb) + : d_bitblaster(bb) + {} + void preRegister(Node n) { + d_bitblaster->bbAtom(n); + }; + +};/* class Registrar */ + +EagerBitblaster::EagerBitblaster() + : TBitblaster() + , d_bbAtoms() +{ + d_bitblastingRegistrar = new BitblastingRegistrar(this); + d_nullContext = new context::Context(); + + d_satSolver = prop::SatSolverFactory::createMinisat(d_nullContext, "EagerBitblaster"); + d_cnfStream = new prop::TseitinCnfStream(d_satSolver, d_bitblastingRegistrar, d_nullContext); + + MinisatEmptyNotify* notify = new MinisatEmptyNotify(); + d_satSolver->setNotify(notify); +} + +EagerBitblaster::~EagerBitblaster() { + delete d_cnfStream; + delete d_satSolver; + delete d_nullContext; + delete d_bitblastingRegistrar; +} + +void EagerBitblaster::bbFormula(TNode node) { + d_cnfStream->convertAndAssert(node, false, false); +} + +/** + * Bitblasts the atom, assigns it a marker literal, adding it to the SAT solver + * NOTE: duplicate clauses are not detected because of marker literal + * @param node the atom to be bitblasted + * + */ +void EagerBitblaster::bbAtom(TNode node) { + node = node.getKind() == kind::NOT? node[0] : node; + if (node.getKind() == kind::BITVECTOR_BITOF) + return; + if (hasBBAtom(node)) { + return; + } + + Debug("bitvector-bitblast") << "Bitblasting node " << node <<"\n"; + + // the bitblasted definition of the atom + Node normalized = Rewriter::rewrite(node); + Node atom_bb = normalized.getKind() != kind::CONST_BOOLEAN ? + Rewriter::rewrite(d_atomBBStrategies[normalized.getKind()](normalized, this)) : + normalized; + // asserting that the atom is true iff the definition holds + Node atom_definition = utils::mkNode(kind::IFF, node, atom_bb); + + AlwaysAssert (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER); + storeBBAtom(node, atom_definition); + d_cnfStream->convertAndAssert(atom_definition, false, false); +} + +void EagerBitblaster::storeBBAtom(TNode atom, Node atom_bb) { + // no need to store the definition for the lazy bit-blaster + d_bbAtoms.insert(atom); +} + +bool EagerBitblaster::hasBBAtom(TNode atom) const { + return d_bbAtoms.find(atom) != d_bbAtoms.end(); +} + +void EagerBitblaster::bbTerm(TNode node, Bits& bits) { + if (hasBBTerm(node)) { + getBBTerm(node, bits); + return; + } + + Debug("bitvector-bitblast") << "Bitblasting node " << node <<"\n"; + + d_termBBStrategies[node.getKind()] (node, bits, this); + + Assert (bits.size() == utils::getSize(node)); + + storeBBTerm(node, bits); +} + +void EagerBitblaster::makeVariable(TNode var, Bits& bits) { + Assert(bits.size() == 0); + for (unsigned i = 0; i < utils::getSize(var); ++i) { + bits.push_back(utils::mkBitOf(var, i)); + } +} + +Node EagerBitblaster::getBBAtom(TNode node) const { + return node; +} + + +/** + * Calls the solve method for the Sat Solver. + * + * @return true for sat, and false for unsat + */ + +bool EagerBitblaster::solve() { + if (Trace.isOn("bitvector")) { + Trace("bitvector") << "EagerBitblaster::solve(). \n"; + } + Debug("bitvector") << "EagerBitblaster::solve(). \n"; + // TODO: clear some memory + // if (something) { + // NodeManager* nm= NodeManager::currentNM(); + // Rewriter::garbageCollect(); + // nm->reclaimZombiesUntil(options::zombieHuntThreshold()); + // } + return prop::SAT_VALUE_TRUE == d_satSolver->solve(); +} + + +} /*bv namespace */ +} /* theory namespace */ +} /* CVC4 namespace*/ + + + +#endif diff --git a/src/theory/bv/kinds b/src/theory/bv/kinds index cf83150e1..4b2bba741 100644 --- a/src/theory/bv/kinds +++ b/src/theory/bv/kinds @@ -8,7 +8,7 @@ theory THEORY_BV ::CVC4::theory::bv::TheoryBV "theory/bv/theory_bv.h" typechecker "theory/bv/theory_bv_type_rules.h" properties finite -properties check propagate presolve +properties check propagate presolve ppStaticLearn rewriter ::CVC4::theory::bv::TheoryBVRewriter "theory/bv/theory_bv_rewriter.h" @@ -70,6 +70,10 @@ operator BITVECTOR_SLE 2 "bit-vector signed less than or equal" operator BITVECTOR_SGT 2 "bit-vector signed greater than" operator BITVECTOR_SGE 2 "signed greater than or equal" +operator BITVECTOR_EAGER_ATOM 1 "formula to be treated as a bv atom via eager bit-blasting" +operator BITVECTOR_ACKERMANIZE_UDIV 1 "term to be treated as a variable; used for eager bitblasting ackerman expansion of bvudiv" +operator BITVECTOR_ACKERMANIZE_UREM 1 "term to be treated as a variable; used for eager bitblasting ackerman expansion of bvurem" + constant BITVECTOR_BITOF_OP \ ::CVC4::BitVectorBitOf \ ::CVC4::BitVectorBitOfHashFunction \ @@ -167,6 +171,10 @@ typerule BITVECTOR_SLE ::CVC4::theory::bv::BitVectorPredicateTypeRule typerule BITVECTOR_SGT ::CVC4::theory::bv::BitVectorPredicateTypeRule typerule BITVECTOR_SGE ::CVC4::theory::bv::BitVectorPredicateTypeRule +typerule BITVECTOR_EAGER_ATOM ::CVC4::theory::bv::BitVectorEagerAtomTypeRule +typerule BITVECTOR_ACKERMANIZE_UDIV ::CVC4::theory::bv::BitVectorAckermanizationUdivTypeRule +typerule BITVECTOR_ACKERMANIZE_UREM ::CVC4::theory::bv::BitVectorAckermanizationUremTypeRule + typerule BITVECTOR_EXTRACT_OP ::CVC4::theory::bv::BitVectorExtractOpTypeRule typerule BITVECTOR_EXTRACT ::CVC4::theory::bv::BitVectorExtractTypeRule typerule BITVECTOR_BITOF ::CVC4::theory::bv::BitVectorBitOfTypeRule diff --git a/src/theory/bv/lazy_bitblaster.h b/src/theory/bv/lazy_bitblaster.h new file mode 100644 index 000000000..013e230f6 --- /dev/null +++ b/src/theory/bv/lazy_bitblaster.h @@ -0,0 +1,503 @@ +/********************* */ +/*! \file lazy_bitblaster.h +** \verbatim +** Original author: Liana Hadarean +** Major contributors: none +** Minor contributors (to current version): lianah +** This file is part of the CVC4 project. +** Copyright (c) 2009-2013 New York University and The University of Iowa +** See the file COPYING in the top-level source directory for licensing +** information.\endverbatim +** +** \brief +** +** Bitblaster for the lazy bv solver. +**/ + +#include "cvc4_private.h" + +#ifndef __CVC4__LAZY__BITBLASTER_H +#define __CVC4__LAZY__BITBLASTER_H + + +#include "bitblaster_template.h" +#include "theory_bv_utils.h" +#include "theory/rewriter.h" +#include "prop/cnf_stream.h" +#include "prop/sat_solver.h" +#include "prop/sat_solver_factory.h" +#include "theory/bv/theory_bv.h" +#include "theory/bv/options.h" +#include "theory/theory_model.h" +#include "theory/bv/abstraction.h" + +namespace CVC4 { +namespace theory { +namespace bv { + +TLazyBitblaster::TLazyBitblaster(context::Context* c, bv::TheoryBV* bv, const std::string name, bool emptyNotify) + : TBitblaster() + , d_bv(bv) + , d_ctx(c) + , d_assertedAtoms(c) + , d_explanations(c) + , d_variables() + , d_bbAtoms() + , d_abstraction(NULL) + , d_emptyNotify(emptyNotify) + , d_name(name) + , d_statistics(name) { + d_satSolver = prop::SatSolverFactory::createMinisat(c, name); + d_nullRegistrar = new prop::NullRegistrar(); + d_nullContext = new context::Context(); + d_cnfStream = new prop::TseitinCnfStream(d_satSolver, + d_nullRegistrar, + d_nullContext); + + prop::BVSatSolverInterface::Notify* notify = d_emptyNotify ? + (prop::BVSatSolverInterface::Notify*) new MinisatEmptyNotify() : + (prop::BVSatSolverInterface::Notify*) new MinisatNotify(d_cnfStream, bv, this); + + d_satSolver->setNotify(notify); +} + +void TLazyBitblaster::setAbstraction(AbstractionModule* abs) { + d_abstraction = abs; +} + +TLazyBitblaster::~TLazyBitblaster() { + delete d_cnfStream; + delete d_nullRegistrar; + delete d_nullContext; + delete d_satSolver; +} + + +/** + * Bitblasts the atom, assigns it a marker literal, adding it to the SAT solver + * NOTE: duplicate clauses are not detected because of marker literal + * @param node the atom to be bitblasted + * + */ +void TLazyBitblaster::bbAtom(TNode node) { + node = node.getKind() == kind::NOT? node[0] : node; + + if (hasBBAtom(node)) { + return; + } + + // make sure it is marked as an atom + addAtom(node); + + Debug("bitvector-bitblast") << "Bitblasting node " << node <<"\n"; + ++d_statistics.d_numAtoms; + + /// if we are using bit-vector abstraction bit-blast the original interpretation + if (options::bvAbstraction() && + d_abstraction != NULL && + d_abstraction->isAbstraction(node)) { + // node must be of the form P(args) = bv1 + Node expansion = Rewriter::rewrite(d_abstraction->getInterpretation(node)); + + Node atom_bb; + if (expansion.getKind() == kind::CONST_BOOLEAN) { + atom_bb = expansion; + } else { + Assert (expansion.getKind() == kind::AND); + std::vector atoms; + for (unsigned i = 0; i < expansion.getNumChildren(); ++i) { + Node normalized_i = Rewriter::rewrite(expansion[i]); + Node atom_i = normalized_i.getKind() != kind::CONST_BOOLEAN ? + Rewriter::rewrite(d_atomBBStrategies[normalized_i.getKind()](normalized_i, this)) : + normalized_i; + atoms.push_back(atom_i); + } + atom_bb = utils::mkAnd(atoms); + } + Assert (!atom_bb.isNull()); + Node atom_definition = utils::mkNode(kind::IFF, node, atom_bb); + storeBBAtom(node, atom_bb); + d_cnfStream->convertAndAssert(atom_definition, false, false); + return; + } + + // the bitblasted definition of the atom + Node normalized = Rewriter::rewrite(node); + Node atom_bb = normalized.getKind() != kind::CONST_BOOLEAN ? + Rewriter::rewrite(d_atomBBStrategies[normalized.getKind()](normalized, this)) : + normalized; + // asserting that the atom is true iff the definition holds + Node atom_definition = utils::mkNode(kind::IFF, node, atom_bb); + storeBBAtom(node, atom_bb); + d_cnfStream->convertAndAssert(atom_definition, false, false); +} + +void TLazyBitblaster::storeBBAtom(TNode atom, Node atom_bb) { + // no need to store the definition for the lazy bit-blaster + d_bbAtoms.insert(atom); +} + +bool TLazyBitblaster::hasBBAtom(TNode atom) const { + return d_bbAtoms.find(atom) != d_bbAtoms.end(); +} + + +void TLazyBitblaster::makeVariable(TNode var, Bits& bits) { + Assert(bits.size() == 0); + for (unsigned i = 0; i < utils::getSize(var); ++i) { + bits.push_back(utils::mkBitOf(var, i)); + } + d_variables.insert(var); +} + +uint64_t TLazyBitblaster::computeAtomWeight(TNode node, NodeSet& seen) { + node = node.getKind() == kind::NOT? node[0] : node; + + Node atom_bb = Rewriter::rewrite(d_atomBBStrategies[node.getKind()](node, this)); + uint64_t size = utils::numNodes(atom_bb, seen); + return size; +} + +// cnf conversion ensures the atom represents itself +Node TLazyBitblaster::getBBAtom(TNode node) const { + return node; +} + +void TLazyBitblaster::bbTerm(TNode node, Bits& bits) { + + if (hasBBTerm(node)) { + getBBTerm(node, bits); + return; + } + + Debug("bitvector-bitblast") << "Bitblasting node " << node <<"\n"; + ++d_statistics.d_numTerms; + + d_termBBStrategies[node.getKind()] (node, bits,this); + + Assert (bits.size() == utils::getSize(node)); + + storeBBTerm(node, bits); +} +/// Public methods + +void TLazyBitblaster::addAtom(TNode atom) { + d_cnfStream->ensureLiteral(atom); + prop::SatLiteral lit = d_cnfStream->getLiteral(atom); + d_satSolver->addMarkerLiteral(lit); +} + +void TLazyBitblaster::explain(TNode atom, std::vector& explanation) { + prop::SatLiteral lit = d_cnfStream->getLiteral(atom); + + ++(d_statistics.d_numExplainedPropagations); + if (options::bvEagerExplanations()) { + Assert (d_explanations.find(lit) != d_explanations.end()); + const std::vector& literal_explanation = d_explanations[lit].get(); + for (unsigned i = 0; i < literal_explanation.size(); ++i) { + explanation.push_back(d_cnfStream->getNode(literal_explanation[i])); + } + return; + } + + std::vector literal_explanation; + d_satSolver->explain(lit, literal_explanation); + for (unsigned i = 0; i < literal_explanation.size(); ++i) { + explanation.push_back(d_cnfStream->getNode(literal_explanation[i])); + } +} + + +/* + * Asserts the clauses corresponding to the atom to the Sat Solver + * by turning on the marker literal (i.e. setting it to false) + * @param node the atom to be asserted + * + */ + +bool TLazyBitblaster::propagate() { + return d_satSolver->propagate() == prop::SAT_VALUE_TRUE; +} + +bool TLazyBitblaster::assertToSat(TNode lit, bool propagate) { + // strip the not + TNode atom; + if (lit.getKind() == kind::NOT) { + atom = lit[0]; + } else { + atom = lit; + } + + Assert (hasBBAtom(atom)); + + prop::SatLiteral markerLit = d_cnfStream->getLiteral(atom); + + if(lit.getKind() == kind::NOT) { + markerLit = ~markerLit; + } + + Debug("bitvector-bb") << "TheoryBV::TLazyBitblaster::assertToSat asserting node: " << atom <<"\n"; + Debug("bitvector-bb") << "TheoryBV::TLazyBitblaster::assertToSat with literal: " << markerLit << "\n"; + + prop::SatValue ret = d_satSolver->assertAssumption(markerLit, propagate); + + d_assertedAtoms.push_back(markerLit); + + return ret == prop::SAT_VALUE_TRUE || ret == prop::SAT_VALUE_UNKNOWN; +} + +/** + * Calls the solve method for the Sat Solver. + * passing it the marker literals to be asserted + * + * @return true for sat, and false for unsat + */ + +bool TLazyBitblaster::solve() { + if (Trace.isOn("bitvector")) { + Trace("bitvector") << "TLazyBitblaster::solve() asserted atoms "; + context::CDList::const_iterator it = d_assertedAtoms.begin(); + for (; it != d_assertedAtoms.end(); ++it) { + Trace("bitvector") << " " << d_cnfStream->getNode(*it) << "\n"; + } + } + Debug("bitvector") << "TLazyBitblaster::solve() asserted atoms " << d_assertedAtoms.size() <<"\n"; + return prop::SAT_VALUE_TRUE == d_satSolver->solve(); +} + +prop::SatValue TLazyBitblaster::solveWithBudget(unsigned long budget) { + if (Trace.isOn("bitvector")) { + Trace("bitvector") << "TLazyBitblaster::solveWithBudget() asserted atoms "; + context::CDList::const_iterator it = d_assertedAtoms.begin(); + for (; it != d_assertedAtoms.end(); ++it) { + Trace("bitvector") << " " << d_cnfStream->getNode(*it) << "\n"; + } + } + Debug("bitvector") << "TLazyBitblaster::solveWithBudget() asserted atoms " << d_assertedAtoms.size() <<"\n"; + return d_satSolver->solve(budget); +} + + +void TLazyBitblaster::getConflict(std::vector& conflict) { + prop::SatClause conflictClause; + d_satSolver->getUnsatCore(conflictClause); + + for (unsigned i = 0; i < conflictClause.size(); i++) { + prop::SatLiteral lit = conflictClause[i]; + TNode atom = d_cnfStream->getNode(lit); + Node not_atom; + if (atom.getKind() == kind::NOT) { + not_atom = atom[0]; + } else { + not_atom = NodeManager::currentNM()->mkNode(kind::NOT, atom); + } + conflict.push_back(not_atom); + } +} + +TLazyBitblaster::Statistics::Statistics(const std::string& prefix) : + d_numTermClauses("theory::bv::"+prefix+"::NumberOfTermSatClauses", 0), + d_numAtomClauses("theory::bv::"+prefix+"::NumberOfAtomSatClauses", 0), + d_numTerms("theory::bv::"+prefix+"::NumberOfBitblastedTerms", 0), + d_numAtoms("theory::bv::"+prefix+"::NumberOfBitblastedAtoms", 0), + d_numExplainedPropagations("theory::bv::"+prefix+"::NumberOfExplainedPropagations", 0), + d_numBitblastingPropagations("theory::bv::"+prefix+"::NumberOfBitblastingPropagations", 0), + d_bitblastTimer("theory::bv::"+prefix+"::BitblastTimer") +{ + StatisticsRegistry::registerStat(&d_numTermClauses); + StatisticsRegistry::registerStat(&d_numAtomClauses); + StatisticsRegistry::registerStat(&d_numTerms); + StatisticsRegistry::registerStat(&d_numAtoms); + StatisticsRegistry::registerStat(&d_numExplainedPropagations); + StatisticsRegistry::registerStat(&d_numBitblastingPropagations); + StatisticsRegistry::registerStat(&d_bitblastTimer); +} + + +TLazyBitblaster::Statistics::~Statistics() { + StatisticsRegistry::unregisterStat(&d_numTermClauses); + StatisticsRegistry::unregisterStat(&d_numAtomClauses); + StatisticsRegistry::unregisterStat(&d_numTerms); + StatisticsRegistry::unregisterStat(&d_numAtoms); + StatisticsRegistry::unregisterStat(&d_numExplainedPropagations); + StatisticsRegistry::unregisterStat(&d_numBitblastingPropagations); + StatisticsRegistry::unregisterStat(&d_bitblastTimer); +} + +bool TLazyBitblaster::MinisatNotify::notify(prop::SatLiteral lit) { + if(options::bvEagerExplanations()) { + // compute explanation + if (d_lazyBB->d_explanations.find(lit) == d_lazyBB->d_explanations.end()) { + std::vector literal_explanation; + d_lazyBB->d_satSolver->explain(lit, literal_explanation); + d_lazyBB->d_explanations.insert(lit, literal_explanation); + } else { + // we propagated it at a lower level + return true; + } + } + ++(d_lazyBB->d_statistics.d_numBitblastingPropagations); + TNode atom = d_cnf->getNode(lit); + return d_bv->storePropagation(atom, SUB_BITBLAST); +} + +void TLazyBitblaster::MinisatNotify::notify(prop::SatClause& clause) { + if (clause.size() > 1) { + NodeBuilder<> lemmab(kind::OR); + for (unsigned i = 0; i < clause.size(); ++ i) { + lemmab << d_cnf->getNode(clause[i]); + } + Node lemma = lemmab; + d_bv->d_out->lemma(lemma); + } else { + d_bv->d_out->lemma(d_cnf->getNode(clause[0])); + } +} + +void TLazyBitblaster::MinisatNotify::safePoint() { + d_bv->d_out->safePoint(); +} + +EqualityStatus TLazyBitblaster::getEqualityStatus(TNode a, TNode b) { + + // We don't want to bit-blast every possibly expensive term for the sake of equality checking + if (hasBBTerm(a) && hasBBTerm(b)) { + + Bits a_bits, b_bits; + getBBTerm(a, a_bits); + getBBTerm(b, b_bits); + theory::EqualityStatus status = theory::EQUALITY_TRUE_IN_MODEL; + for (unsigned i = 0; i < a_bits.size(); ++ i) { + if (d_cnfStream->hasLiteral(a_bits[i]) && d_cnfStream->hasLiteral(b_bits[i])) { + prop::SatLiteral a_lit = d_cnfStream->getLiteral(a_bits[i]); + prop::SatValue a_lit_value = d_satSolver->value(a_lit); + if (a_lit_value != prop::SAT_VALUE_UNKNOWN) { + prop::SatLiteral b_lit = d_cnfStream->getLiteral(b_bits[i]); + prop::SatValue b_lit_value = d_satSolver->value(b_lit); + if (b_lit_value != prop::SAT_VALUE_UNKNOWN) { + if (a_lit_value != b_lit_value) { + return theory::EQUALITY_FALSE_IN_MODEL; + } + } else { + status = theory::EQUALITY_UNKNOWN; + } + } { + status = theory::EQUALITY_UNKNOWN; + } + } else { + status = theory::EQUALITY_UNKNOWN; + } + } + + return status; + + } else { + return theory::EQUALITY_UNKNOWN; + } +} + + +bool TLazyBitblaster::isSharedTerm(TNode node) { + return d_bv->d_sharedTermsSet.find(node) != d_bv->d_sharedTermsSet.end(); +} + +bool TLazyBitblaster::hasValue(TNode a) { + Assert (hasBBTerm(a)); + Bits bits; + getBBTerm(a, bits); + for (int i = bits.size() -1; i >= 0; --i) { + prop::SatValue bit_value; + if (d_cnfStream->hasLiteral(bits[i])) { + prop::SatLiteral bit = d_cnfStream->getLiteral(bits[i]); + bit_value = d_satSolver->value(bit); + if (bit_value == prop::SAT_VALUE_UNKNOWN) + return false; + } else { + return false; + } + } + return true; +} +/** + * Returns the value a is currently assigned to in the SAT solver + * or null if the value is completely unassigned. + * + * @param a + * @param fullModel whether to create a "full model," i.e., add + * constants to equivalence classes that don't already have them + * + * @return + */ +Node TLazyBitblaster::getVarValue(TNode a, bool fullModel) { + if (!hasBBTerm(a)) { + Assert(isSharedTerm(a)); + return Node(); + } + Bits bits; + getBBTerm(a, bits); + Integer value(0); + for (int i = bits.size() -1; i >= 0; --i) { + prop::SatValue bit_value; + if (d_cnfStream->hasLiteral(bits[i])) { + prop::SatLiteral bit = d_cnfStream->getLiteral(bits[i]); + bit_value = d_satSolver->value(bit); + Assert (bit_value != prop::SAT_VALUE_UNKNOWN); + } else { + // the bit is unconstrainted so we can give it an arbitrary value + bit_value = prop::SAT_VALUE_FALSE; + } + Integer bit_int = bit_value == prop::SAT_VALUE_TRUE ? Integer(1) : Integer(0); + value = value * 2 + bit_int; + } + return utils::mkConst(BitVector(bits.size(), value)); +} + +void TLazyBitblaster::collectModelInfo(TheoryModel* m, bool fullModel) { + __gnu_cxx::hash_set::iterator it = d_variables.begin(); + for (; it!= d_variables.end(); ++it) { + TNode var = *it; + if (Theory::theoryOf(var) == theory::THEORY_BV || isSharedTerm(var)) { + Node const_value = getVarValue(var, fullModel); + if(const_value == Node()) { + if( fullModel ){ + // if the value is unassigned just set it to zero + const_value = utils::mkConst(BitVector(utils::getSize(var), 0u)); + } + } + if(const_value != Node()) { + Debug("bitvector-model") << "TLazyBitblaster::collectModelInfo (assert (= " + << var << " " + << const_value << "))\n"; + m->assertEquality(var, const_value, true); + } + } + } +} + +void TLazyBitblaster::clearSolver() { + Assert (d_ctx->getLevel() == 0); + delete d_satSolver; + delete d_cnfStream; + d_assertedAtoms = context::CDList(d_ctx); + d_explanations = ExplanationMap(d_ctx); + d_bbAtoms.clear(); + d_variables.clear(); + d_termCache.clear(); + + // recreate sat solver + d_satSolver = prop::SatSolverFactory::createMinisat(d_ctx); + d_cnfStream = new prop::TseitinCnfStream(d_satSolver, + new prop::NullRegistrar(), + new context::Context()); + + prop::BVSatSolverInterface::Notify* notify = d_emptyNotify ? + (prop::BVSatSolverInterface::Notify*) new MinisatEmptyNotify() : + (prop::BVSatSolverInterface::Notify*) new MinisatNotify(d_cnfStream, d_bv, this); + d_satSolver->setNotify(notify); +} + +} /*bv namespace */ +} /* theory namespace */ +} /* CVC4 namespace*/ + +#endif diff --git a/src/theory/bv/options b/src/theory/bv/options index 077299d1f..f59d675a7 100644 --- a/src/theory/bv/options +++ b/src/theory/bv/options @@ -5,28 +5,61 @@ module BV "theory/bv/options.h" Bitvector theory -option bitvectorEagerBitblast --bitblast-eager bool - eagerly bitblast the bitvectors to the main SAT solver +# Option to set the bit-blasting mode (lazy, eager, eager-aig) -option bitvectorShareLemmas --bitblast-share-lemmas bool - share lemmas from the bitblasting solver with the main solver +option bitblastMode --bitblast=MODE CVC4::theory::bv::BitblastMode :handler CVC4::theory::bv::stringToBitblastMode :default CVC4::theory::bv::BITBLAST_MODE_LAZY :read-write :include "theory/bv/bitblast_mode.h" :handler-include "theory/bv/options_handlers.h" + choose bitblasting mode, see --bitblast=help -option bitvectorEagerFullcheck --bitblast-eager-fullcheck bool - check the bitblasting eagerly +# Options for eager bit-blasting + +option bitvectorAig --bitblast-aig bool :default false :read-write :link --bitblast=eager + bitblast by first converting to AIG (only if --bitblast=eager) + +expert-option bitvectorAigSimplifications --bv-aig-simp=FILE std::string :default "" :read-write :link --bitblast-aig + abc command to run AIG simplifications + +# Options for lazy bit-blasting + +option bitvectorPropagate --bv-propagate bool :default true :read-write :link --bitblast=lazy + use bit-vector propagation in the bit-blaster + +option bitvectorEqualitySolver --bv-eq-solver bool :default true :read-write :link --bitblast=lazy + use the equality engine for the bit-vector theory (only if --bitblast=lazy) -option bitvectorInequalitySolver --bv-inequality-solver bool :default true - turn on the inequality solver for the bit-vector theory +option bitvectorEqualitySlicer --bv-eq-slicer=MODE CVC4::theory::bv::BvSlicerMode :handler CVC4::theory::bv::stringToBvSlicerMode :default CVC4::theory::bv::BITVECTOR_SLICER_OFF :read-write :include "theory/bv/bitblast_mode.h" :handler-include "theory/bv/options_handlers.h" :read-write :link --bv-eq-solver + turn on the slicing equality solver for the bit-vector theory (only if --bitblast=lazy) -option bitvectorCoreSolver --bv-core-solver bool - turn on the core solver for the bit-vector theory +option bitvectorInequalitySolver --bv-inequality-solver bool :default true :read-write :link --bitblast=lazy + turn on the inequality solver for the bit-vector theory (only if --bitblast=lazy) + +option bitvectorAlgebraicSolver --bv-algebraic-solver bool :default true :read-write :link --bitblast=lazy + turn on the algebraic solver for the bit-vector theory (only if --bitblast=lazy) + +expert-option bitvectorAlgebraicBudget --bv-algebraic-budget unsigned :default 1500 :read-write :link --bv-algebraic-solver + the budget allowed for the algebraic solver in number of SAT conflicts -option bvToBool --bv-to-bool bool +# General options + +option bitvectorToBool --bv-to-bool bool :default false :read-write lift bit-vectors of size 1 to booleans when possible -option bvPropagate --bv-propagate bool :default true - use bit-vector propagation in the bit-blaster +option bitvectorDivByZeroConst --bv-div-zero-const bool :default false + always return -1 on division by zero + +expert-option bvAbstraction --bv-abstraction bool :default false :read-write + mcm benchmark abstraction + +expert-option skolemizeArguments --bv-skolemize bool :default false :read-write + skolemize arguments for bv abstraction (only does something if --bv-abstraction is on) -option bvEquality --bv-eq bool :default true - use the equality engine for the bit-vector theory +expert-option bvNumFunc --bv-num-func=NUM unsigned :default 1 + number of function symbols in conflicts that are generalized + +expert-option bvEagerExplanations --bv-eager-explanations bool :default false :read-write + compute bit-blasting propagation explanations eagerly + +expert-option bitvectorQuickXplain --bv-quick-xplain bool :default false + minimize bv conflicts using the QuickXplain algorithm + endmodule diff --git a/src/theory/bv/options_handlers.h b/src/theory/bv/options_handlers.h new file mode 100644 index 000000000..bc01d4dc8 --- /dev/null +++ b/src/theory/bv/options_handlers.h @@ -0,0 +1,138 @@ +/********************* */ +/*! \file options_handlers.h + ** \verbatim + ** Original author: Liana Hadarean + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2013 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Custom handlers and predicates for TheoryBV options + ** + ** Custom handlers and predicates for TheoryBV options. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__BV__OPTIONS_HANDLERS_H +#define __CVC4__THEORY__BV__OPTIONS_HANDLERS_H + +#include "theory/bv/bitblast_mode.h" +#include "main/options.h" + +namespace CVC4 { +namespace theory { +namespace bv { + +static const std::string bitblastingModeHelp = "\ +Bit-blasting modes currently supported by the --bitblast option:\n\ +\n\ +lazy (default)\n\ ++ Separate boolean structure and term reasoning betwen the core\n\ + SAT solver and the bv SAT solver\n\ +\n\ +eager\n\ ++ Bitblast eagerly to bv SAT solver\n\ +\n\ +aig\n\ ++ Bitblast eagerly to bv SAT solver by converting to AIG\n\ +"; + +inline BitblastMode stringToBitblastMode(std::string option, std::string optarg, SmtEngine* smt) throw(OptionException) { + if(optarg == "lazy") { + if (!options::bitvectorPropagate.wasSetByUser()) { + options::bitvectorPropagate.set(true); + } + if (!options::bitvectorEqualitySolver.wasSetByUser()) { + options::bitvectorEqualitySolver.set(true); + } + if (!options::bitvectorEqualitySlicer.wasSetByUser()) { + if (options::incrementalSolving()) { + options::bitvectorEqualitySlicer.set(BITVECTOR_SLICER_OFF); + } else { + options::bitvectorEqualitySlicer.set(BITVECTOR_SLICER_AUTO); + } + } + + if (!options::bitvectorInequalitySolver.wasSetByUser()) { + options::bitvectorInequalitySolver.set(true); + } + if (!options::bitvectorAlgebraicSolver.wasSetByUser()) { + options::bitvectorAlgebraicSolver.set(true); + } + return BITBLAST_MODE_LAZY; + } else if(optarg == "eager") { + if (options::produceModels()) { + throw OptionException(std::string("Eager bit-blasting does not currently support model generation. \n\ + Try --bitblast=lazy")); + } + + if (options::incrementalSolving() && + options::incrementalSolving.wasSetByUser()) { + throw OptionException(std::string("Eager bit-blasting does not currently support incremental mode. \n\ + Try --bitblast=lazy")); + } + + if (!options::bitvectorAig.wasSetByUser()) { + options::bitvectorAig.set(true); + } + if (!options::bitvectorAigSimplifications.wasSetByUser()) { + // due to a known bug in abc switching to using drw instead of rw + options::bitvectorAigSimplifications.set("balance;drw"); + } + if (!options::bitvectorToBool.wasSetByUser()) { + options::bitvectorToBool.set(true); + } + + if (!options::bvAbstraction.wasSetByUser() && + !options::skolemizeArguments.wasSetByUser()) { + options::bvAbstraction.set(true); + options::skolemizeArguments.set(true); + } + return BITBLAST_MODE_EAGER; + } else if(optarg == "help") { + puts(bitblastingModeHelp.c_str()); + exit(1); + } else { + throw OptionException(std::string("unknown option for --bitblast: `") + + optarg + "'. Try --bitblast=help."); + } +} + +static const std::string bvSlicerModeHelp = "\ +Bit-vector equality slicer modes supported by the --bv-eq-slicer option:\n\ +\n\ +auto (default)\n\ ++ Turn slicer on if input has only equalities over core symbols\n\ +\n\ +on\n\ ++ Turn slicer on\n\ +\n\ +off\n\ ++ Turn slicer off\n\ +"; + +inline BvSlicerMode stringToBvSlicerMode(std::string option, std::string optarg, SmtEngine* smt) throw(OptionException) { + + if(optarg == "auto") { + return BITVECTOR_SLICER_AUTO; + } else if(optarg == "on") { + return BITVECTOR_SLICER_ON; + } else if(optarg == "off") { + return BITVECTOR_SLICER_OFF; + } else if(optarg == "help") { + puts(bitblastingModeHelp.c_str()); + exit(1); + } else { + throw OptionException(std::string("unknown option for --bv-eq-slicer: `") + + optarg + "'. Try --bv-eq-slicer=help."); + } +} + +}/* CVC4::theory::bv namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__BV__OPTIONS_HANDLERS_H */ diff --git a/src/theory/bv/slicer.cpp b/src/theory/bv/slicer.cpp index 56e0a479e..0644900fa 100644 --- a/src/theory/bv/slicer.cpp +++ b/src/theory/bv/slicer.cpp @@ -469,7 +469,7 @@ void Slicer::getBaseDecomposition(TNode node, std::vector& decomp) { low = utils::getExtractLow(node); top = node[0]; } - Assert (d_nodeToId.find(top) != d_nodeToId.end()); + AlwaysAssert (d_nodeToId.find(top) != d_nodeToId.end()); TermId id = d_nodeToId[top]; NormalForm nf(high-low+1); d_unionFind.getNormalForm(ExtractTerm(id, high, low), nf); @@ -498,7 +498,7 @@ bool Slicer::isCoreTerm(TNode node) { if (d_coreTermCache.find(node) == d_coreTermCache.end()) { Kind kind = node.getKind(); bool not_core; - if (options::bitvectorCoreSolver()) { + if (options::bitvectorEqualitySlicer()) { not_core = (kind != kind::BITVECTOR_EXTRACT && kind != kind::BITVECTOR_CONCAT); } else { not_core = true; diff --git a/src/theory/bv/theory_bv.cpp b/src/theory/bv/theory_bv.cpp index 5d5e0a97c..117a3e552 100644 --- a/src/theory/bv/theory_bv.cpp +++ b/src/theory/bv/theory_bv.cpp @@ -1,32 +1,36 @@ /********************* */ /*! \file theory_bv.cpp - ** \verbatim - ** Original author: Dejan Jovanovic - ** Major contributors: Morgan Deters, Liana Hadarean - ** Minor contributors (to current version): Tim King, Kshitij Bansal, Clark Barrett, Andrew Reynolds - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2013 New York University and The University of Iowa - ** See the file COPYING in the top-level source directory for licensing - ** information.\endverbatim - ** - ** \brief [[ Add one-line brief description here ]] - ** - ** [[ Add lengthier description here ]] - ** \todo document this file - **/ - +** \verbatim +** Original author: Dejan Jovanovic +** Major contributors: Morgan Deters, Liana Hadarean +** Minor contributors (to current version): Tim King, Kshitij Bansal, Clark Barrett, Andrew Reynolds +** This file is part of the CVC4 project. +** Copyright (c) 2009-2013 New York University and The University of Iowa +** See the file COPYING in the top-level source directory for licensing +** information.\endverbatim +** +** \brief [[ Add one-line brief description here ]] +** +** [[ Add lengthier description here ]] +** \todo document this file +**/ + +#include "smt/options.h" #include "theory/bv/theory_bv.h" #include "theory/bv/theory_bv_utils.h" #include "theory/bv/slicer.h" #include "theory/valuation.h" -#include "theory/bv/bitblaster.h" #include "theory/bv/options.h" #include "theory/bv/theory_bv_rewrite_rules_normalization.h" +#include "theory/bv/theory_bv_rewrite_rules_simplification.h" #include "theory/bv/bv_subtheory_core.h" #include "theory/bv/bv_subtheory_inequality.h" +#include "theory/bv/bv_subtheory_algebraic.h" #include "theory/bv/bv_subtheory_bitblast.h" +#include "theory/bv/bv_eager_solver.h" #include "theory/bv/theory_bv_rewriter.h" #include "theory/theory_model.h" +#include "theory/bv/abstraction.h" using namespace CVC4; using namespace CVC4::theory; @@ -48,24 +52,45 @@ TheoryBV::TheoryBV(context::Context* c, context::UserContext* u, OutputChannel& d_conflict(c, false), d_literalsToPropagate(c), d_literalsToPropagateIndex(c, 0), - d_propagatedBy(c) - { - if (options::bvEquality()) { - SubtheorySolver* core_solver = new CoreSolver(c, this); - d_subtheories.push_back(core_solver); - d_subtheoryMap[SUB_CORE] = core_solver; - } - if (options::bitvectorInequalitySolver()) { - SubtheorySolver* ineq_solver = new InequalitySolver(c, this); - d_subtheories.push_back(ineq_solver); - d_subtheoryMap[SUB_INEQUALITY] = ineq_solver; - } + d_propagatedBy(c), + d_eagerSolver(NULL), + d_abstractionModule(new AbstractionModule()), + d_isCoreTheory(false), + d_calledPreregister(false) +{ + + if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { + d_eagerSolver = new EagerBitblastSolver(); + return; + } - SubtheorySolver* bb_solver = new BitblastSolver(c, this); - d_subtheories.push_back(bb_solver); - d_subtheoryMap[SUB_BITBLAST] = bb_solver; + if (options::bitvectorEqualitySolver()) { + SubtheorySolver* core_solver = new CoreSolver(c, this); + d_subtheories.push_back(core_solver); + d_subtheoryMap[SUB_CORE] = core_solver; + } + + if (options::bitvectorInequalitySolver()) { + SubtheorySolver* ineq_solver = new InequalitySolver(c, this); + d_subtheories.push_back(ineq_solver); + d_subtheoryMap[SUB_INEQUALITY] = ineq_solver; } + if (options::bitvectorAlgebraicSolver()) { + SubtheorySolver* alg_solver = new AlgebraicSolver(c, this); + d_subtheories.push_back(alg_solver); + d_subtheoryMap[SUB_ALGEBRAIC] = alg_solver; + } + + BitblastSolver* bb_solver = new BitblastSolver(c, this); + if (options::bvAbstraction()) { + bb_solver->setAbstraction(d_abstractionModule); + } + d_subtheories.push_back(bb_solver); + d_subtheoryMap[SUB_BITBLAST] = bb_solver; +} + + TheoryBV::~TheoryBV() { for (unsigned i = 0; i < d_subtheories.size(); ++i) { delete d_subtheories[i]; @@ -73,7 +98,10 @@ TheoryBV::~TheoryBV() { } void TheoryBV::setMasterEqualityEngine(eq::EqualityEngine* eq) { - if (options::bvEquality()) { + if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { + return; + } + if (options::bitvectorEqualitySolver()) { dynamic_cast(d_subtheoryMap[SUB_CORE])->setMasterEqualityEngine(eq); } } @@ -84,7 +112,8 @@ TheoryBV::Statistics::Statistics(): d_solveTimer("theory::bv::solveTimer"), d_numCallsToCheckFullEffort("theory::bv::NumberOfFullCheckCalls", 0), d_numCallsToCheckStandardEffort("theory::bv::NumberOfStandardCheckCalls", 0), - d_weightComputationTimer("theory::bv::weightComputationTimer") + d_weightComputationTimer("theory::bv::weightComputationTimer"), + d_numMultSlice("theory::bv::NumMultSliceApplied", 0) { StatisticsRegistry::registerStat(&d_avgConflictSize); StatisticsRegistry::registerStat(&d_solveSubstitutions); @@ -92,6 +121,7 @@ TheoryBV::Statistics::Statistics(): StatisticsRegistry::registerStat(&d_numCallsToCheckFullEffort); StatisticsRegistry::registerStat(&d_numCallsToCheckStandardEffort); StatisticsRegistry::registerStat(&d_weightComputationTimer); + StatisticsRegistry::registerStat(&d_numMultSlice); } TheoryBV::Statistics::~Statistics() { @@ -101,6 +131,7 @@ TheoryBV::Statistics::~Statistics() { StatisticsRegistry::unregisterStat(&d_numCallsToCheckFullEffort); StatisticsRegistry::unregisterStat(&d_numCallsToCheckStandardEffort); StatisticsRegistry::unregisterStat(&d_weightComputationTimer); + StatisticsRegistry::unregisterStat(&d_numMultSlice); } Node TheoryBV::getBVDivByZero(Kind k, unsigned width) { @@ -133,6 +164,83 @@ Node TheoryBV::getBVDivByZero(Kind k, unsigned width) { } +void TheoryBV::collectNumerators(TNode term, TNodeSet& seen) { + if (seen.find(term) != seen.end()) + return; + if (term.getKind() == kind::BITVECTOR_ACKERMANIZE_UDIV) { + unsigned size = utils::getSize(term[0]); + if (d_BVDivByZeroAckerman.find(size) == d_BVDivByZeroAckerman.end()) { + d_BVDivByZeroAckerman[size] = TNodeSet(); + } + d_BVDivByZeroAckerman[size].insert(term[0]); + seen.insert(term); + } else if (term.getKind() == kind::BITVECTOR_ACKERMANIZE_UREM) { + unsigned size = utils::getSize(term[0]); + if (d_BVRemByZeroAckerman.find(size) == d_BVRemByZeroAckerman.end()) { + d_BVRemByZeroAckerman[size] = TNodeSet(); + } + d_BVRemByZeroAckerman[size].insert(term[0]); + seen.insert(term); + } + for (unsigned i = 0; i < term.getNumChildren(); ++i) { + collectNumerators(term[i], seen); + } +} + +void TheoryBV::mkAckermanizationAsssertions(std::vector& assertions) { + Debug("bv-ackermanize") << "TheoryBV::mkAckermanizationAsssertions\n"; + + Assert(options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER); + AlwaysAssert(!options::incrementalSolving()); + TNodeSet seen; + for (unsigned i = 0; i < assertions.size(); ++i) { + collectNumerators(assertions[i], seen); + } + + // process division UF + Debug("bv-ackermanize") << "Process division UF...\n"; + for (WidthToNumerators::const_iterator it = d_BVDivByZeroAckerman.begin(); it != d_BVDivByZeroAckerman.end(); ++it) { + const TNodeSet& numerators= it->second; + for (TNodeSet::const_iterator i = numerators.begin(); i != numerators.end(); ++i) { + TNodeSet::const_iterator j = i; + j++; + for (; j != numerators.end(); ++j) { + TNode arg1 = *i; + TNode arg2 = *j; + TNode acker1 = utils::mkNode(kind::BITVECTOR_ACKERMANIZE_UDIV, arg1); + TNode acker2 = utils::mkNode(kind::BITVECTOR_ACKERMANIZE_UDIV, arg2); + + Node arg_eq = utils::mkNode(kind::EQUAL, arg1, arg2); + Node acker_eq = utils::mkNode(kind::EQUAL, acker1, acker2); + Node lemma = utils::mkNode(kind::IMPLIES, arg_eq, acker_eq); + Debug("bv-ackermanize") << " " << lemma << "\n"; + assertions.push_back(lemma); + } + } + } + // process remainder UF + Debug("bv-ackermanize") << "Process remainder UF...\n"; + for (WidthToNumerators::const_iterator it = d_BVRemByZeroAckerman.begin(); it != d_BVRemByZeroAckerman.end(); ++it) { + const TNodeSet& numerators= it->second; + for (TNodeSet::const_iterator i = numerators.begin(); i != numerators.end(); ++i) { + TNodeSet::const_iterator j = i; + j++; + for (; j != numerators.end(); ++j) { + TNode arg1 = *i; + TNode arg2 = *j; + TNode acker1 = utils::mkNode(kind::BITVECTOR_ACKERMANIZE_UREM, arg1); + TNode acker2 = utils::mkNode(kind::BITVECTOR_ACKERMANIZE_UREM, arg2); + + Node arg_eq = utils::mkNode(kind::EQUAL, arg1, arg2); + Node acker_eq = utils::mkNode(kind::EQUAL, acker1, acker2); + Node lemma = utils::mkNode(kind::IMPLIES, arg_eq, acker_eq); + Debug("bv-ackermanize") << " " << lemma << "\n"; + assertions.push_back(lemma); + } + } + } +} + Node TheoryBV::expandDefinition(LogicRequest &logicRequest, Node node) { Debug("bitvector-expandDefinition") << "TheoryBV::expandDefinition(" << node << ")" << std::endl; @@ -147,15 +255,29 @@ Node TheoryBV::expandDefinition(LogicRequest &logicRequest, Node node) { case kind::BITVECTOR_UREM: { NodeManager* nm = NodeManager::currentNM(); unsigned width = node.getType().getBitVectorSize(); - Node divByZero = getBVDivByZero(node.getKind(), width); + + if (options::bitvectorDivByZeroConst()) { + Kind kind = node.getKind() == kind::BITVECTOR_UDIV ? kind::BITVECTOR_UDIV_TOTAL : kind::BITVECTOR_UREM_TOTAL; + return nm->mkNode(kind, node[0], node[1]); + } + TNode num = node[0], den = node[1]; Node den_eq_0 = nm->mkNode(kind::EQUAL, den, nm->mkConst(BitVector(width, Integer(0)))); - Node divByZeroNum = nm->mkNode(kind::APPLY_UF, divByZero, num); Node divTotalNumDen = nm->mkNode(node.getKind() == kind::BITVECTOR_UDIV ? kind::BITVECTOR_UDIV_TOTAL : kind::BITVECTOR_UREM_TOTAL, num, den); - node = nm->mkNode(kind::ITE, den_eq_0, divByZeroNum, divTotalNumDen); - logicRequest.widenLogic(THEORY_UF); - return node; + + if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { + // Ackermanize UF if using eager bit-blasting + Node ackerman_var = nm->mkNode(node.getKind() == kind::BITVECTOR_UDIV ? kind::BITVECTOR_ACKERMANIZE_UDIV : kind::BITVECTOR_ACKERMANIZE_UREM, num); + node = nm->mkNode(kind::ITE, den_eq_0, ackerman_var, divTotalNumDen); + return node; + } else { + Node divByZero = getBVDivByZero(node.getKind(), width); + Node divByZeroNum = nm->mkNode(kind::APPLY_UF, divByZero, num); + node = nm->mkNode(kind::ITE, den_eq_0, divByZeroNum, divTotalNumDen); + logicRequest.widenLogic(THEORY_UF); + return node; + } } break; @@ -169,13 +291,24 @@ Node TheoryBV::expandDefinition(LogicRequest &logicRequest, Node node) { void TheoryBV::preRegisterTerm(TNode node) { + d_calledPreregister = true; Debug("bitvector-preregister") << "TheoryBV::preRegister(" << node << ")" << std::endl; + + if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { + // the aig bit-blaster option is set heuristically + // if bv abstraction is not used + if (!d_eagerSolver->isInitialized()) { + d_eagerSolver->initialize(); + } - if (options::bitvectorEagerBitblast()) { - // don't use the equality engine in the eager bit-blasting - d_subtheoryMap[SUB_BITBLAST]->preRegister(node); - return; + if (node.getKind() == kind::BITVECTOR_EAGER_ATOM) { + Node formula = node[0]; + d_eagerSolver->assertFormula(formula); + } + // nothing to do for the other terms + return; } + for (unsigned i = 0; i < d_subtheories.size(); ++i) { d_subtheories[i]->preRegister(node); } @@ -212,8 +345,8 @@ void TheoryBV::checkForLemma(TNode fact) { TNode divisor = urem[1]; Node result_ult_div = mkNode(kind::BITVECTOR_ULT, result, divisor); Node divisor_eq_0 = mkNode(kind::EQUAL, - divisor, - mkConst(BitVector(getSize(divisor), 0u))); + divisor, + mkConst(BitVector(getSize(divisor), 0u))); Node split = utils::mkNode(kind::OR, divisor_eq_0, mkNode(kind::NOT, fact), result_ult_div); lemma(split); } @@ -224,10 +357,38 @@ void TheoryBV::checkForLemma(TNode fact) { void TheoryBV::check(Effort e) { Debug("bitvector") << "TheoryBV::check(" << e << ")" << std::endl; - if (options::bitvectorEagerBitblast()) { + + // if we are using the eager solver + if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { + // this can only happen on an empty benchmark + if (!d_eagerSolver->isInitialized()) { + d_eagerSolver->initialize(); + } + if (!Theory::fullEffort(e)) + return; + + std::vector assertions; + while (!done()) { + TNode fact = get().assertion; + Assert (fact.getKind() == kind::BITVECTOR_EAGER_ATOM); + assertions.push_back(fact); + } + Assert (d_eagerSolver->hasAssertions(assertions)); + + bool ok = d_eagerSolver->checkSat(); + if (!ok) { + if (assertions.size() == 1) { + d_out->conflict(assertions[0]); + return; + } + Node conflict = NodeManager::currentNM()->mkNode(kind::AND, assertions); + d_out->conflict(conflict); + return; + } return; } - + + if (Theory::fullEffort(e)) { ++(d_statistics.d_numCallsToCheckFullEffort); } else { @@ -241,6 +402,7 @@ void TheoryBV::check(Effort e) while (!done()) { TNode fact = get().assertion; + checkForLemma(fact); for (unsigned i = 0; i < d_subtheories.size(); ++i) { @@ -270,7 +432,7 @@ void TheoryBV::check(Effort e) void TheoryBV::collectModelInfo( TheoryModel* m, bool fullModel ){ Assert(!inConflict()); - // Assert (fullModel); // can only query full model + for (unsigned i = 0; i < d_subtheories.size(); ++i) { if (d_subtheories[i]->isComplete()) { d_subtheories[i]->collectModelInfo(m, fullModel); @@ -291,9 +453,8 @@ Node TheoryBV::getModelValue(TNode var) { void TheoryBV::propagate(Effort e) { Debug("bitvector") << indent() << "TheoryBV::propagate()" << std::endl; - - if (options::bitvectorEagerBitblast()) { - return; + if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { + return; } if (inConflict()) { @@ -321,22 +482,62 @@ void TheoryBV::propagate(Effort e) { Theory::PPAssertStatus TheoryBV::ppAssert(TNode in, SubstitutionMap& outSubstitutions) { switch(in.getKind()) { case kind::EQUAL: - - if (in[0].isVar() && !in[1].hasSubterm(in[0])) { - ++(d_statistics.d_solveSubstitutions); - outSubstitutions.addSubstitution(in[0], in[1]); - return PP_ASSERT_STATUS_SOLVED; - } - if (in[1].isVar() && !in[0].hasSubterm(in[1])) { - ++(d_statistics.d_solveSubstitutions); - outSubstitutions.addSubstitution(in[1], in[0]); - return PP_ASSERT_STATUS_SOLVED; + { + if (in[0].isVar() && !in[1].hasSubterm(in[0])) { + ++(d_statistics.d_solveSubstitutions); + outSubstitutions.addSubstitution(in[0], in[1]); + return PP_ASSERT_STATUS_SOLVED; + } + if (in[1].isVar() && !in[0].hasSubterm(in[1])) { + ++(d_statistics.d_solveSubstitutions); + outSubstitutions.addSubstitution(in[1], in[0]); + return PP_ASSERT_STATUS_SOLVED; + } + Node node = Rewriter::rewrite(in); + if ((node[0].getKind() == kind::BITVECTOR_EXTRACT && node[1].isConst()) || + (node[1].getKind() == kind::BITVECTOR_EXTRACT && node[0].isConst())) { + Node extract = node[0].isConst() ? node[1] : node[0]; + if (extract[0].getKind() == kind::VARIABLE) { + Node c = node[0].isConst() ? node[0] : node[1]; + + unsigned high = utils::getExtractHigh(extract); + unsigned low = utils::getExtractLow(extract); + unsigned var_bitwidth = utils::getSize(extract[0]); + std::vector children; + + if (low == 0) { + Assert (high != var_bitwidth - 1); + unsigned skolem_size = var_bitwidth - high - 1; + Node skolem = utils::mkVar(skolem_size); + children.push_back(skolem); + children.push_back(c); + } else if (high == var_bitwidth - 1) { + unsigned skolem_size = low; + Node skolem = utils::mkVar(skolem_size); + children.push_back(c); + children.push_back(skolem); + } else { + unsigned skolem1_size = low; + unsigned skolem2_size = var_bitwidth - high - 1; + Node skolem1 = utils::mkVar(skolem1_size); + Node skolem2 = utils::mkVar(skolem2_size); + children.push_back(skolem2); + children.push_back(c); + children.push_back(skolem1); + } + Node concat = utils::mkNode(kind::BITVECTOR_CONCAT, children); + Assert (utils::getSize(concat) == utils::getSize(extract[0])); + outSubstitutions.addSubstitution(extract[0], concat); + return PP_ASSERT_STATUS_SOLVED; + } + } } - // to do constant propagations - - break; - case kind::NOT: break; + case kind::BITVECTOR_ULT: + case kind::BITVECTOR_SLT: + case kind::BITVECTOR_ULE: + case kind::BITVECTOR_SLE: + default: // TODO other predicates break; @@ -346,28 +547,65 @@ Theory::PPAssertStatus TheoryBV::ppAssert(TNode in, SubstitutionMap& outSubstitu Node TheoryBV::ppRewrite(TNode t) { + Node res = t; if (RewriteRule::applies(t)) { Node result = RewriteRule::run(t); - return Rewriter::rewrite(result); - } - - if (options::bitvectorCoreSolver() && t.getKind() == kind::EQUAL) { + res = Rewriter::rewrite(result); + } else if (d_isCoreTheory && t.getKind() == kind::EQUAL) { std::vector equalities; Slicer::splitEqualities(t, equalities); - return utils::mkAnd(equalities); - } - - return t; + res = utils::mkAnd(equalities); + } + + // if(t.getKind() == kind::EQUAL && + // ((t[0].getKind() == kind::BITVECTOR_MULT && t[1].getKind() == kind::BITVECTOR_PLUS) || + // (t[1].getKind() == kind::BITVECTOR_MULT && t[0].getKind() == kind::BITVECTOR_PLUS))) { + // // if we have an equality between a multiplication and addition + // // try to express multiplication in terms of addition + // Node mult = t[0].getKind() == kind::BITVECTOR_MULT? t[0] : t[1]; + // Node add = t[0].getKind() == kind::BITVECTOR_PLUS? t[0] : t[1]; + // if (RewriteRule::applies(mult)) { + // Node new_mult = RewriteRule::run(mult); + // Node new_eq = Rewriter::rewrite(utils::mkNode(kind::EQUAL, new_mult, add)); + + // // the simplification can cause the formula to blow up + // // only apply if formula reduced + // if (d_subtheoryMap.find(SUB_BITBLAST) != d_subtheoryMap.end()) { + // BitblastSolver* bv = (BitblastSolver*)d_subtheoryMap[SUB_BITBLAST]; + // uint64_t old_size = bv->computeAtomWeight(t); + // Assert (old_size); + // uint64_t new_size = bv->computeAtomWeight(new_eq); + // double ratio = ((double)new_size)/old_size; + // if (ratio <= 0.4) { + // ++(d_statistics.d_numMultSlice); + // return new_eq; + // } + // } + + // if (new_eq.getKind() == kind::CONST_BOOLEAN) { + // ++(d_statistics.d_numMultSlice); + // return new_eq; + // } + // } + // } + + if (options::bvAbstraction() && t.getType().isBoolean()) { + d_abstractionModule->addInputAtom(res); + } + return res; } void TheoryBV::presolve() { Debug("bitvector") << "TheoryBV::presolve" << endl; } +static int prop_count = 0; + bool TheoryBV::storePropagation(TNode literal, SubTheory subtheory) { Debug("bitvector::propagate") << indent() << getSatContext()->getLevel() << " " << "TheoryBV::storePropagation(" << literal << ", " << subtheory << ")" << std::endl; - + prop_count++; + // If already in conflict, no more propagation if (d_conflict) { Debug("bitvector::propagate") << indent() << "TheoryBV::storePropagation(" << literal << ", " << subtheory << "): already in conflict" << std::endl; @@ -425,7 +663,7 @@ Node TheoryBV::explain(TNode node) { return utils::mkTrue(); } // return the explanation - Node explanation = mkAnd(assumptions); + Node explanation = utils::mkAnd(assumptions); Debug("bitvector::explain") << "TheoryBV::explain(" << node << ") => " << explanation << std::endl; return explanation; } @@ -434,7 +672,7 @@ Node TheoryBV::explain(TNode node) { void TheoryBV::addSharedTerm(TNode t) { Debug("bitvector::sharing") << indent() << "TheoryBV::addSharedTerm(" << t << ")" << std::endl; d_sharedTermsSet.insert(t); - if (!options::bitvectorEagerBitblast() && options::bvEquality()) { + if (options::bitvectorEqualitySolver()) { for (unsigned i = 0; i < d_subtheories.size(); ++i) { d_subtheories[i]->addSharedTerm(t); } @@ -444,10 +682,7 @@ void TheoryBV::addSharedTerm(TNode t) { EqualityStatus TheoryBV::getEqualityStatus(TNode a, TNode b) { - if (options::bitvectorEagerBitblast()) { - return EQUALITY_UNKNOWN; - } - + Assert (options::bitblastMode() == theory::bv::BITBLAST_MODE_LAZY); for (unsigned i = 0; i < d_subtheories.size(); ++i) { EqualityStatus status = d_subtheories[i]->getEqualityStatus(a, b); if (status != EQUALITY_UNKNOWN) { @@ -457,3 +692,43 @@ EqualityStatus TheoryBV::getEqualityStatus(TNode a, TNode b) return EQUALITY_UNKNOWN; ; } + +void TheoryBV::enableCoreTheorySlicer() { + Assert (!d_calledPreregister); + d_isCoreTheory = true; + if (d_subtheoryMap.find(SUB_CORE) != d_subtheoryMap.end()) { + CoreSolver* core = (CoreSolver*)d_subtheoryMap[SUB_CORE]; + core->enableSlicer(); + } +} + + +void TheoryBV::ppStaticLearn(TNode in, NodeBuilder<>& learned) {} + +bool TheoryBV::applyAbstraction(const std::vector& assertions, std::vector& new_assertions) { + bool changed = d_abstractionModule->applyAbstraction(assertions, new_assertions); + if (changed && + options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER && + options::bitvectorAig()) { + // disable AIG mode + AlwaysAssert (!d_eagerSolver->isInitialized()); + d_eagerSolver->turnOffAig(); + d_eagerSolver->initialize(); + } + return changed; +} + +void TheoryBV::setConflict(Node conflict) { + if (options::bvAbstraction()) { + Node new_conflict = d_abstractionModule->simplifyConflict(conflict); + + std::vector lemmas; + lemmas.push_back(new_conflict); + d_abstractionModule->generalizeConflict(new_conflict, lemmas); + for (unsigned i = 0; i < lemmas.size(); ++i) { + lemma(utils::mkNode(kind::NOT, lemmas[i])); + } + } + d_conflict = true; + d_conflictNode = conflict; +} diff --git a/src/theory/bv/theory_bv.h b/src/theory/bv/theory_bv.h index a5e2ac9ea..27b6b37c4 100644 --- a/src/theory/bv/theory_bv.h +++ b/src/theory/bv/theory_bv.h @@ -14,11 +14,10 @@ ** Bitvector theory. **/ -#include "cvc4_private.h" - #ifndef __CVC4__THEORY__BV__THEORY_BV_H #define __CVC4__THEORY__BV__THEORY_BV_H +#include "cvc4_private.h" #include "theory/theory.h" #include "context/context.h" #include "context/cdlist.h" @@ -34,8 +33,13 @@ namespace bv { class CoreSolver; class InequalitySolver; +class AlgebraicSolver; class BitblastSolver; +class EagerBitblastSolver; + +class AbstractionModule; + class TheoryBV : public Theory { /** The context we are using */ @@ -58,6 +62,8 @@ public: Node expandDefinition(LogicRequest &logicRequest, Node node); + void mkAckermanizationAsssertions(std::vector& assertions); + void preRegisterTerm(TNode n); void check(Effort e); @@ -71,9 +77,15 @@ public: std::string identify() const { return std::string("TheoryBV"); } PPAssertStatus ppAssert(TNode in, SubstitutionMap& outSubstitutions); + + void enableCoreTheorySlicer(); + Node ppRewrite(TNode t); + void ppStaticLearn(TNode in, NodeBuilder<>& learned); + void presolve(); + bool applyAbstraction(const std::vector& assertions, std::vector& new_assertions); private: class Statistics { @@ -81,9 +93,10 @@ private: AverageStat d_avgConflictSize; IntStat d_solveSubstitutions; TimerStat d_solveTimer; - IntStat d_numCallsToCheckFullEffort; - IntStat d_numCallsToCheckStandardEffort; + IntStat d_numCallsToCheckFullEffort; + IntStat d_numCallsToCheckStandardEffort; TimerStat d_weightComputationTimer; + IntStat d_numMultSlice; Statistics(); ~Statistics(); }; @@ -101,6 +114,9 @@ private: */ Node getBVDivByZero(Kind k, unsigned width); + typedef __gnu_cxx::hash_set TNodeSet; + void collectNumerators(TNode term, TNodeSet& seen); + /** * Maps from bit-vector width to divison-by-zero uninterpreted * function symbols. @@ -108,6 +124,14 @@ private: __gnu_cxx::hash_map d_BVDivByZero; __gnu_cxx::hash_map d_BVRemByZero; + /** + * Maps from bit-vector width to numerators + * of uninterpreted function symbol + */ + typedef __gnu_cxx::hash_map WidthToNumerators; + + WidthToNumerators d_BVDivByZeroAckerman; + WidthToNumerators d_BVRemByZeroAckerman; context::CDO d_lemmasAdded; @@ -130,6 +154,11 @@ private: typedef context::CDHashMap PropagatedMap; PropagatedMap d_propagatedBy; + EagerBitblastSolver* d_eagerSolver; + AbstractionModule* d_abstractionModule; + bool d_isCoreTheory; + bool d_calledPreregister; + bool wasPropagatedBySubtheory(TNode literal) const { return d_propagatedBy.find(literal) != d_propagatedBy.end(); } @@ -162,10 +191,7 @@ private: return indentStr; } - void setConflict(Node conflict = Node::null()) { - d_conflict = true; - d_conflictNode = conflict; - } + void setConflict(Node conflict = Node::null()); bool inConflict() { return d_conflict; @@ -176,12 +202,15 @@ private: void lemma(TNode node) { d_out->lemma(node); d_lemmasAdded = true; } void checkForLemma(TNode node); - - friend class Bitblaster; + + + friend class LazyBitblaster; + friend class TLazyBitblaster; friend class BitblastSolver; friend class EqualitySolver; friend class CoreSolver; - friend class InequalitySolver; + friend class InequalitySolver; + friend class AlgebraicSolver; };/* class TheoryBV */ }/* CVC4::theory::bv namespace */ diff --git a/src/theory/bv/theory_bv_rewrite_rules.h b/src/theory/bv/theory_bv_rewrite_rules.h index db48a1d05..9f01e46da 100644 --- a/src/theory/bv/theory_bv_rewrite_rules.h +++ b/src/theory/bv/theory_bv_rewrite_rules.h @@ -121,6 +121,8 @@ enum RewriteRuleId { NotUlt, NotUle, MultPow2, + MultSlice, + ExtractMultLeadingBit, NegIdemp, UdivPow2, UdivOne, @@ -133,6 +135,7 @@ enum RewriteRuleId { UltOne, SltZero, ZeroUlt, + MergeSignExtend, /// normalization rules ExtractBitwise, @@ -152,7 +155,7 @@ enum RewriteRuleId { PlusCombineLikeTerms, MultSimplify, MultDistribConst, - MultDistribVariable, + MultDistrib, SolveEq, BitwiseEq, AndSimplify, @@ -248,6 +251,8 @@ inline std::ostream& operator << (std::ostream& out, RewriteRuleId ruleId) { case XorOne : out << "XorOne"; return out; case XorZero : out << "XorZero"; return out; case MultPow2 : out << "MultPow2"; return out; + case MultSlice : out << "MultSlice"; return out; + case ExtractMultLeadingBit : out << "ExtractMultLeadingBit"; return out; case NegIdemp : out << "NegIdemp"; return out; case UdivPow2 : out << "UdivPow2"; return out; case UdivOne : out << "UdivOne"; return out; @@ -266,7 +271,6 @@ inline std::ostream& operator << (std::ostream& out, RewriteRuleId ruleId) { case PlusCombineLikeTerms: out << "PlusCombineLikeTerms"; return out; case MultSimplify: out << "MultSimplify"; return out; case MultDistribConst: out << "MultDistribConst"; return out; - case MultDistribVariable: out << "MultDistribConst"; return out; case SolveEq : out << "SolveEq"; return out; case BitwiseEq : out << "BitwiseEq"; return out; case NegMult : out << "NegMult"; return out; @@ -279,9 +283,12 @@ inline std::ostream& operator << (std::ostream& out, RewriteRuleId ruleId) { case UltOne : out << "UltOne"; return out; case SltZero : out << "SltZero"; return out; case ZeroUlt : out << "ZeroUlt"; return out; + case MergeSignExtend : out << "MergeSignExtend"; return out; + case UleEliminate : out << "UleEliminate"; return out; case BitwiseSlicing : out << "BitwiseSlicing"; return out; - case ExtractSignExtend : out << "ExtractSignExtend"; return out; + case ExtractSignExtend : out << "ExtractSignExtend"; return out; + case MultDistrib: out << "MultDistrib"; return out; default: Unreachable(); } @@ -473,6 +480,8 @@ struct AllRewriteRules { RewriteRule rule83; RewriteRule rule84; RewriteRule rule87; + RewriteRule rule85; + RewriteRule rule88; RewriteRule rule91; RewriteRule rule92; RewriteRule rule93; @@ -500,7 +509,7 @@ struct AllRewriteRules { RewriteRule rule115; RewriteRule rule116; RewriteRule rule117; - RewriteRule rule118; + RewriteRule rule118; }; template<> inline diff --git a/src/theory/bv/theory_bv_rewrite_rules_core.h b/src/theory/bv/theory_bv_rewrite_rules_core.h index 43acfef75..649af5ff9 100644 --- a/src/theory/bv/theory_bv_rewrite_rules_core.h +++ b/src/theory/bv/theory_bv_rewrite_rules_core.h @@ -270,7 +270,7 @@ Node RewriteRule::apply(TNode node) { template<> inline bool RewriteRule::applies(TNode node) { - return (node.getKind() == kind::EQUAL && node[0] < node[1]); + return (node.getKind() == kind::EQUAL && node[0] > node[1]); } template<> inline diff --git a/src/theory/bv/theory_bv_rewrite_rules_normalization.h b/src/theory/bv/theory_bv_rewrite_rules_normalization.h index bb5c94b1e..b13172bfa 100644 --- a/src/theory/bv/theory_bv_rewrite_rules_normalization.h +++ b/src/theory/bv/theory_bv_rewrite_rules_normalization.h @@ -431,35 +431,6 @@ Node RewriteRule::apply(TNode node) { return utils::mkNode(kind::BITVECTOR_MULT, children); } -template<> inline -bool RewriteRule::applies(TNode node) { - if (node.getKind() != kind::BITVECTOR_MULT || - node.getNumChildren() != 2) { - return false; - } - Assert(!node[0].isConst()); - if (!node[1].getNumChildren() == 0) { - return false; - } - TNode factor = node[0]; - return (factor.getKind() == kind::BITVECTOR_PLUS || - factor.getKind() == kind::BITVECTOR_SUB); -} - -template<> inline -Node RewriteRule::apply(TNode node) { - Debug("bv-rewrite") << "RewriteRule(" << node << ")" << std::endl; - TNode var = node[1]; - TNode factor = node[0]; - - std::vector children; - for(unsigned i = 0; i < factor.getNumChildren(); ++i) { - children.push_back(utils::mkNode(kind::BITVECTOR_MULT, factor[i], var)); - } - - return utils::mkNode(factor.getKind(), children); -} - template<> inline bool RewriteRule::applies(TNode node) { @@ -501,6 +472,40 @@ Node RewriteRule::apply(TNode node) { return utils::mkNode(factor.getKind(), children); } +template<> inline +bool RewriteRule::applies(TNode node) { + if (node.getKind() != kind::BITVECTOR_MULT || + node.getNumChildren() != 2) { + return false; + } + if (node[0].getKind() == kind::BITVECTOR_PLUS || + node[0].getKind() == kind::BITVECTOR_SUB) { + return node[1].getKind() != kind::BITVECTOR_PLUS && + node[1].getKind() != kind::BITVECTOR_SUB; + } + return node[1].getKind() == kind::BITVECTOR_PLUS || + node[1].getKind() == kind::BITVECTOR_SUB; +} + +template<> inline +Node RewriteRule::apply(TNode node) { + Debug("bv-rewrite") << "RewriteRule(" << node << ")" << std::endl; + + TNode factor = node[0].getKind() != kind::BITVECTOR_PLUS ? node[0] : node[1]; + TNode sum = node[0].getKind() == kind::BITVECTOR_PLUS? node[0] : node[1]; + Assert (factor.getKind() != kind::BITVECTOR_PLUS && + factor.getKind() != kind::BITVECTOR_SUB && + (sum.getKind() == kind::BITVECTOR_PLUS || + sum.getKind() == kind::BITVECTOR_SUB)); + + std::vector children; + for(unsigned i = 0; i < sum.getNumChildren(); ++i) { + children.push_back(utils::mkNode(kind::BITVECTOR_MULT, sum[i], factor)); + } + + return utils::mkNode(sum.getKind(), children); +} + template<> inline bool RewriteRule::applies(TNode node) { @@ -679,7 +684,7 @@ Node RewriteRule::apply(TNode node) { return utils::mkTrue(); } - if (newLeft < newRight) { + if (newLeft > newRight) { Assert((newRight == left && newLeft == right) || Rewriter::rewrite(newRight) != left || Rewriter::rewrite(newLeft) != right); diff --git a/src/theory/bv/theory_bv_rewrite_rules_simplification.h b/src/theory/bv/theory_bv_rewrite_rules_simplification.h index ff7d67cb0..db0d8bac8 100644 --- a/src/theory/bv/theory_bv_rewrite_rules_simplification.h +++ b/src/theory/bv/theory_bv_rewrite_rules_simplification.h @@ -786,6 +786,84 @@ Node RewriteRule::apply(TNode node) { return utils::mkConcat(extract, zeros); } +/** + * ExtractMultLeadingBit + * + * If the bit-vectors multiplied have enough leading zeros, + * we can determine that the top bits of the multiplication + * are zero and not compute them. Only apply for large bitwidths + * as this can interfere with other mult normalization rewrites such + * as flattening. + */ + +template<> inline +bool RewriteRule::applies(TNode node) { + if (node.getKind() != kind::BITVECTOR_EXTRACT) + return false; + unsigned low = utils::getExtractLow(node); + node = node[0]; + + if (node.getKind() != kind::BITVECTOR_MULT || + node.getNumChildren() != 2 || + utils::getSize(node) <= 64) + return false; + + if (node[0].getKind() != kind::BITVECTOR_CONCAT || + node[1].getKind() != kind::BITVECTOR_CONCAT || + !node[0][0].isConst() || + !node[1][0].isConst()) + return false; + + unsigned n = utils::getSize(node); + // count number of leading zeroes + const Integer& int1 = node[0][0].getConst().toInteger(); + const Integer& int2 = node[1][0].getConst().toInteger(); + unsigned zeroes1 = int1.isZero()? utils::getSize(node[0][0]) : + int1.length(); + + unsigned zeroes2 = int2.isZero()? utils::getSize(node[1][0]) : + int2.length(); + + // first k bits are not zero in the result + unsigned k = 2 * n - (zeroes1 + zeroes2); + + if (k > low) + return false; + + return true; +} + +template<> inline +Node RewriteRule::apply(TNode node) { + Debug("bv-rewrite") << "RewriteRule(" << node << ")" << std::endl; + + unsigned bitwidth = utils::getSize(node); + + // node = node[0]; + // const Integer& int1 = node[0][0].getConst().toInteger(); + // const Integer& int2 = node[1][0].getConst().toInteger(); + // unsigned zeroes1 = int1.isZero()? utils::getSize(node[0][0]) : + // int1.length(); + + // unsigned zeroes2 = int2.isZero()? utils::getSize(node[1][0]) : + // int2.length(); + // all bits >= k in the multiplier will have to be 0 + // unsigned n = utils::getSize(node); + // unsigned k = 2 * n - (zeroes1 + zeroes2); + // Node extract1 = utils::mkExtract(node[0], k - 1, 0); + // Node extract2 = utils::mkExtract(node[1], k - 1, 0); + // Node k_zeroes = utils::mkConst(n - k, 0u); + + // Node new_mult = utils::mkNode(kind::BITVECTOR_MULT, extract1, extract2); + // Node result = utils::mkExtract(utils::mkNode(kind::BITVECTOR_CONCAT, k_zeroes, new_mult), + // high, low); + + // since the extract is over multiplier bits that have to be 0, return 0 + Node result = utils::mkConst(bitwidth, 0u); + // std::cout << "MultLeadingBit " << node <<" => " << result <<"\n"; + return result; +} + /** * NegIdemp * @@ -989,6 +1067,93 @@ Node RewriteRule::apply(TNode node) { return utils::mkNode(kind::BITVECTOR_PLUS, children); } +template<> inline +bool RewriteRule::applies(TNode node) { + if (node.getKind() != kind::BITVECTOR_SIGN_EXTEND || + (node[0].getKind() != kind::BITVECTOR_SIGN_EXTEND && + node[0].getKind() != kind::BITVECTOR_ZERO_EXTEND)) + return false; + return true; +} + +template<> inline +Node RewriteRule::apply(TNode node) { + Debug("bv-rewrite") << "RewriteRule(" << node << ")" << std::endl; + unsigned ammount1 = node.getOperator().getConst().signExtendAmount; + + NodeManager* nm = NodeManager::currentNM(); + if (node[0].getKind() == kind::BITVECTOR_ZERO_EXTEND) { + unsigned ammount2 = node[0].getOperator().getConst().zeroExtendAmount; + if (ammount2 == 0) { + NodeBuilder<> nb(kind::BITVECTOR_SIGN_EXTEND); + Node op = nm->mkConst(BitVectorSignExtend(ammount1)); + nb << op << node[0][0]; + Node res = nb; + return res; + } + NodeBuilder<> nb(kind::BITVECTOR_ZERO_EXTEND); + Node op = nm->mkConst(BitVectorZeroExtend(ammount1 + ammount2)); + nb << op << node[0][0]; + Node res = nb; + return res; + } + Assert (node[0].getKind() == kind::BITVECTOR_SIGN_EXTEND); + unsigned ammount2 = node[0].getOperator().getConst().signExtendAmount; + NodeBuilder<> nb(kind::BITVECTOR_SIGN_EXTEND); + Node op = nm->mkConst(BitVectorSignExtend(ammount1+ ammount2)); + nb << op << node[0][0]; + Node res = nb; + return res; +} + + +template<> inline +bool RewriteRule::applies(TNode node) { + if (node.getKind() != kind::BITVECTOR_MULT) { + return false; + } + if (utils::getSize(node[0]) % 2 != 0) { + return false; + } + return true; +} + +/** + * Expressses the multiplication in terms of the top and bottom + * slices of the terms. Note increases circuit size, but could + * lead to simplifications (use wisely!). + * + * @param node + * + * @return + */ +template<> inline +Node RewriteRule::apply(TNode node) { + Debug("bv-rewrite") << "RewriteRule(" << node << ")" << std::endl; + unsigned bitwidth = utils::getSize(node[0]); + Node zeros = utils::mkConst(bitwidth/2, 0); + TNode a = node[0]; + Node bottom_a = utils::mkExtract(a, bitwidth/2 - 1, 0); + Node top_a = utils::mkExtract(a, bitwidth -1, bitwidth/2); + TNode b = node[1]; + Node bottom_b = utils::mkExtract(b, bitwidth/2 - 1, 0); + Node top_b = utils::mkExtract(b, bitwidth -1, bitwidth/2); + + Node term1 = utils::mkNode(kind::BITVECTOR_MULT, + utils::mkNode(kind::BITVECTOR_CONCAT, zeros, bottom_a), + utils::mkNode(kind::BITVECTOR_CONCAT, zeros, bottom_b)); + + Node term2 = utils::mkNode(kind::BITVECTOR_CONCAT, + utils::mkNode(kind::BITVECTOR_MULT, top_b, bottom_a), + zeros); + Node term3 = utils::mkNode(kind::BITVECTOR_CONCAT, + utils::mkNode(kind::BITVECTOR_MULT, top_a, bottom_b), + zeros); + return utils::mkNode(kind::BITVECTOR_PLUS, term1, term2, term3); +} + + + // /** // * // * diff --git a/src/theory/bv/theory_bv_rewriter.cpp b/src/theory/bv/theory_bv_rewriter.cpp index 0b09bce4d..6fd3bbf92 100644 --- a/src/theory/bv/theory_bv_rewriter.cpp +++ b/src/theory/bv/theory_bv_rewriter.cpp @@ -60,7 +60,14 @@ RewriteResponse TheoryBVRewriter::postRewrite(TNode node) { if(res.node!= node) { Debug("bitvector-rewrite") << "TheoryBV::postRewrite " << node << std::endl; Debug("bitvector-rewrite") << "TheoryBV::postRewrite to " << res.node << std::endl; + if (res.node.getKind() == kind::EQUAL) { + Assert (res.node[0] < res.node[1]); + } + } + if (res.node.getKind() == kind::EQUAL) { + Assert (res.node[0] < res.node[1]); } + // if (res.status == REWRITE_DONE) { // Node rewr = res.node; // Node rerewr = d_rewriteTable[rewr.getKind()](rewr, false).node; @@ -154,10 +161,10 @@ RewriteResponse TheoryBVRewriter::RewriteSge(TNode node, bool prerewrite){ RewriteResponse TheoryBVRewriter::RewriteNot(TNode node, bool prerewrite){ Node resultNode = node; - if(RewriteRule::applies(node)) { - resultNode = RewriteRule::run(node); - return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); - } + // // if(RewriteRule::applies(node)) { + // // resultNode = RewriteRule::run(node); + // // return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); + // // } resultNode = LinearRewriteStrategy < RewriteRule, RewriteRule @@ -179,27 +186,24 @@ RewriteResponse TheoryBVRewriter::RewriteExtract(TNode node, bool prerewrite) { return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); } - if (RewriteRule::applies(node)) { - resultNode = RewriteRule::run(node); - return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); - } - if (RewriteRule::applies(node)) { resultNode = RewriteRule::run(node); return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); } - if (RewriteRule::applies(node)) { - resultNode = RewriteRule::run(node); - return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); - } + // if (RewriteRule::applies(node)) { + // resultNode = RewriteRule::run(node); + // return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); + // } + resultNode = LinearRewriteStrategy < RewriteRule, RewriteRule, // We could get another extract over extract - RewriteRule + RewriteRule, // At this point only Extract-Whole could apply + RewriteRule >::apply(node); return RewriteResponse(REWRITE_DONE, resultNode); @@ -317,27 +321,18 @@ RewriteResponse TheoryBVRewriter::RewriteComp(TNode node, bool prerewrite) { RewriteResponse TheoryBVRewriter::RewriteMult(TNode node, bool prerewrite) { Node resultNode = node; - resultNode = LinearRewriteStrategy < RewriteRule, // flattens and sorts RewriteRule, // multiplies constant part and checks for 0 RewriteRule // replaces multiplication by a power of 2 by a shift - >::apply(node); + >::apply(resultNode); // only apply if every subterm was already rewritten if (!prerewrite) { - // distributes multiplication by constant over +, - and unary - - if(RewriteRule::applies(resultNode)) { - resultNode = RewriteRule::run(resultNode); - // creating new terms that might simplify further - return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); - } - if(RewriteRule::applies(resultNode)) { - resultNode = RewriteRule::run(resultNode); - // creating new terms that might simplify further - return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); - } - + resultNode = LinearRewriteStrategy + < RewriteRule + , RewriteRule + >::apply(resultNode); } if(resultNode == node) { @@ -540,10 +535,14 @@ RewriteResponse TheoryBVRewriter::RewriteZeroExtend(TNode node, bool prerewrite) RewriteResponse TheoryBVRewriter::RewriteSignExtend(TNode node, bool prerewrite) { Node resultNode = LinearRewriteStrategy - < RewriteRule + < RewriteRule + , RewriteRule >::apply(node); + - // return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); + if (resultNode != node) { + return RewriteResponse(REWRITE_AGAIN, resultNode); + } return RewriteResponse(REWRITE_DONE, resultNode); } diff --git a/src/theory/bv/theory_bv_type_rules.h b/src/theory/bv/theory_bv_type_rules.h index 12e258177..c1829ce69 100644 --- a/src/theory/bv/theory_bv_type_rules.h +++ b/src/theory/bv/theory_bv_type_rules.h @@ -114,6 +114,49 @@ public: } }; +class BitVectorEagerAtomTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + if( check ) { + TypeNode lhsType = n[0].getType(check); + if (!lhsType.isBoolean()) { + throw TypeCheckingExceptionPrivate(n, "expecting boolean term"); + } + } + return nodeManager->booleanType(); + } +}; + +class BitVectorAckermanizationUdivTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + TypeNode lhsType = n[0].getType(check); + if( check ) { + if (!lhsType.isBitVector()) { + throw TypeCheckingExceptionPrivate(n, "expecting bit-vector term"); + } + } + return lhsType; + } +}; + +class BitVectorAckermanizationUremTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + TypeNode lhsType = n[0].getType(check); + if( check ) { + if (!lhsType.isBitVector()) { + throw TypeCheckingExceptionPrivate(n, "expecting bit-vector term"); + } + } + return lhsType; + } +}; + + class BitVectorExtractTypeRule { public: inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) diff --git a/src/theory/bv/theory_bv_utils.cpp b/src/theory/bv/theory_bv_utils.cpp new file mode 100644 index 000000000..705a784f2 --- /dev/null +++ b/src/theory/bv/theory_bv_utils.cpp @@ -0,0 +1,102 @@ +/********************* */ +/*! \file theory_bv_utils.h + ** \verbatim + ** Original author: Dejan Jovanovic + ** Major contributors: Liana Hadarean + ** Minor contributors (to current version): Clark Barrett, Morgan Deters, Kshitij Bansal + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2013 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief [[ Add one-line brief description here ]] + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + +#include "theory/bv/theory_bv_utils.h" +#include "theory/decision_attributes.h" +#include "theory/theory.h" + +using namespace CVC4; +using namespace CVC4::theory; +using namespace CVC4::theory::bv; +using namespace CVC4::theory::bv::utils; + +bool CVC4::theory::bv::utils::isCoreTerm(TNode term, TNodeBoolMap& cache) { + term = term.getKind() == kind::NOT ? term[0] : term; + TNodeBoolMap::const_iterator it = cache.find(term); + if (it != cache.end()) { + return it->second; + } + + if (term.getNumChildren() == 0) + return true; + + if (theory::Theory::theoryOf(theory::THEORY_OF_TERM_BASED, term) == THEORY_BV) { + Kind k = term.getKind(); + if (k != kind::CONST_BITVECTOR && + k != kind::BITVECTOR_CONCAT && + k != kind::BITVECTOR_EXTRACT && + k != kind::EQUAL && + term.getMetaKind() != kind::metakind::VARIABLE) { + cache[term] = false; + return false; + } + } + + for (unsigned i = 0; i < term.getNumChildren(); ++i) { + if (!isCoreTerm(term[i], cache)) { + cache[term] = false; + return false; + } + } + + cache[term]= true; + return true; +} + +bool CVC4::theory::bv::utils::isEqualityTerm(TNode term, TNodeBoolMap& cache) { + term = term.getKind() == kind::NOT ? term[0] : term; + TNodeBoolMap::const_iterator it = cache.find(term); + if (it != cache.end()) { + return it->second; + } + + if (term.getNumChildren() == 0) + return true; + + if (theory::Theory::theoryOf(theory::THEORY_OF_TERM_BASED, term) == THEORY_BV) { + Kind k = term.getKind(); + if (k != kind::CONST_BITVECTOR && + k != kind::EQUAL && + term.getMetaKind() != kind::metakind::VARIABLE) { + cache[term] = false; + return false; + } + } + + for (unsigned i = 0; i < term.getNumChildren(); ++i) { + if (!isEqualityTerm(term[i], cache)) { + cache[term] = false; + return false; + } + } + + cache[term]= true; + return true; +} + + +uint64_t CVC4::theory::bv::utils::numNodes(TNode node, NodeSet& seen) { + if (seen.find(node) != seen.end()) + return 0; + + uint64_t size = 1; + for (unsigned i = 0; i < node.getNumChildren(); ++i) { + size += numNodes(node[i], seen); + } + seen.insert(node); + return size; +} diff --git a/src/theory/bv/theory_bv_utils.h b/src/theory/bv/theory_bv_utils.h index 95136598c..3650d3091 100644 --- a/src/theory/bv/theory_bv_utils.h +++ b/src/theory/bv/theory_bv_utils.h @@ -23,7 +23,7 @@ #include #include #include "expr/node_manager.h" -#include "theory/decision_attributes.h" + namespace CVC4 { namespace theory { @@ -65,7 +65,8 @@ inline Node mkFalse() { inline Node mkVar(unsigned size) { NodeManager* nm = NodeManager::currentNM(); - return nm->mkSkolem("bv", nm->mkBitVectorType(size), "is a variable created by the theory of bitvectors"); + + return nm->mkSkolem("BVSKOLEM$$", nm->mkBitVectorType(size), "is a variable created by the theory of bitvectors"); } @@ -435,8 +436,6 @@ inline Node mkConjunction(const std::vector& nodes) { - - // Turn a set into a string inline std::string setToString(const std::set& nodeSet) { std::stringstream out; @@ -498,26 +497,13 @@ inline T gcd(T a, T b) { return a; } +typedef __gnu_cxx::hash_map TNodeBoolMap; -typedef __gnu_cxx::hash_set TNodeSet; - -inline uint64_t numNodesAux(TNode node, TNodeSet& seen) { - if (seen.find(node) != seen.end()) - return 0; +bool isCoreTerm(TNode term, TNodeBoolMap& cache); +bool isEqualityTerm(TNode term, TNodeBoolMap& cache); +typedef __gnu_cxx::hash_set NodeSet; - uint64_t size = 1; - for (unsigned i = 0; i < node.getNumChildren(); ++i) { - size += numNodesAux(node[i], seen); - } - seen.insert(node); - return size; -} - -inline uint64_t numNodes(TNode node) { - TNodeSet seen; - uint64_t size = numNodesAux(node, seen); - return size; -} +uint64_t numNodes(TNode node, NodeSet& seen); } } diff --git a/src/theory/theory_engine.cpp b/src/theory/theory_engine.cpp index c63df83ee..2e80e1cda 100644 --- a/src/theory/theory_engine.cpp +++ b/src/theory/theory_engine.cpp @@ -51,8 +51,9 @@ #include "theory/quantifiers/first_order_model.h" #include "theory/uf/equality_engine.h" - //#include "theory/rewriterules/efficient_e_matching.h" +#include "theory/bv/theory_bv_utils.h" +#include "theory/bv/options.h" #include "proof/proof_manager.h" @@ -1445,8 +1446,49 @@ void TheoryEngine::conflict(TNode conflict, TheoryId theoryId) { } } +void TheoryEngine::staticInitializeBVOptions(const std::vector& assertions) { + bool useSlicer = true; + if (options::bitvectorEqualitySlicer() == bv::BITVECTOR_SLICER_ON) { + if (options::incrementalSolving()) + throw ModalException("Slicer does not currently support incremental mode. Use --bv-eq-slicer=off"); + if (options::produceModels()) + throw ModalException("Slicer does not currently support model generation. Use --bv-eq-slicer=off"); + useSlicer = true; + + } else if (options::bitvectorEqualitySlicer() == bv::BITVECTOR_SLICER_OFF) { + return; + + } else if (options::bitvectorEqualitySlicer() == bv::BITVECTOR_SLICER_AUTO) { + if (options::incrementalSolving() || + options::produceModels()) + return; + + useSlicer = true; + bv::utils::TNodeBoolMap cache; + for (unsigned i = 0; i < assertions.size(); ++i) { + useSlicer = useSlicer && bv::utils::isCoreTerm(assertions[i], cache); + } + } + + if (useSlicer) { + bv::TheoryBV* bv_theory = (bv::TheoryBV*)d_theoryTable[THEORY_BV]; + bv_theory->enableCoreTheorySlicer(); + } + +} + void TheoryEngine::ppBvToBool(const std::vector& assertions, std::vector& new_assertions) { - d_bvToBoolPreprocessor.liftBoolToBV(assertions, new_assertions); + d_bvToBoolPreprocessor.liftBvToBool(assertions, new_assertions); +} + +bool TheoryEngine::ppBvAbstraction(const std::vector& assertions, std::vector& new_assertions) { + bv::TheoryBV* bv_theory = (bv::TheoryBV*)d_theoryTable[THEORY_BV]; + return bv_theory->applyAbstraction(assertions, new_assertions); +} + +void TheoryEngine::mkAckermanizationAsssertions(std::vector& assertions) { + bv::TheoryBV* bv_theory = (bv::TheoryBV*)d_theoryTable[THEORY_BV]; + bv_theory->mkAckermanizationAsssertions(assertions); } Node TheoryEngine::ppSimpITE(TNode assertion) diff --git a/src/theory/theory_engine.h b/src/theory/theory_engine.h index 615598e44..946091167 100644 --- a/src/theory/theory_engine.h +++ b/src/theory/theory_engine.h @@ -795,8 +795,11 @@ private: /** For preprocessing pass lifting bit-vectors of size 1 to booleans */ theory::bv::BvToBoolPreprocessor d_bvToBoolPreprocessor; public: - + void staticInitializeBVOptions(const std::vector& assertions); void ppBvToBool(const std::vector& assertions, std::vector& new_assertions); + bool ppBvAbstraction(const std::vector& assertions, std::vector& new_assertions); + void mkAckermanizationAsssertions(std::vector& assertions); + Node ppSimpITE(TNode assertion); /** Returns false if an assertion simplified to false. */ bool donePPSimpITE(std::vector& assertions); diff --git a/src/theory/unconstrained_simplifier.cpp b/src/theory/unconstrained_simplifier.cpp index 7509c7f4f..c2fc9929a 100644 --- a/src/theory/unconstrained_simplifier.cpp +++ b/src/theory/unconstrained_simplifier.cpp @@ -280,8 +280,8 @@ void UnconstrainedSimplifier::processUnconstrained() case kind::BITVECTOR_SHL: case kind::BITVECTOR_LSHR: case kind::BITVECTOR_ASHR: - case kind::BITVECTOR_UDIV: - case kind::BITVECTOR_UREM: + case kind::BITVECTOR_UDIV_TOTAL: + case kind::BITVECTOR_UREM_TOTAL: case kind::BITVECTOR_SDIV: case kind::BITVECTOR_SREM: case kind::BITVECTOR_SMOD: { @@ -634,16 +634,13 @@ void UnconstrainedSimplifier::processUnconstrained() break; } - // These should have been rewritten up front - case kind::BITVECTOR_REPEAT: + // Do nothing + case kind::BITVECTOR_SIGN_EXTEND: case kind::BITVECTOR_ZERO_EXTEND: + case kind::BITVECTOR_REPEAT: case kind::BITVECTOR_ROTATE_LEFT: case kind::BITVECTOR_ROTATE_RIGHT: - Unreachable(); - break; - // Do nothing - case kind::BITVECTOR_SIGN_EXTEND: default: break; } diff --git a/src/util/bitvector.h b/src/util/bitvector.h index e826a6704..35b052531 100644 --- a/src/util/bitvector.h +++ b/src/util/bitvector.h @@ -197,7 +197,8 @@ public: CheckArgument(d_size == y.d_size, y); if (y.d_value == 0) { - return BitVector(d_size, 0u); + // under division by zero return -1 + return BitVector(d_size, Integer(1).oneExtend(1, d_size - 1)); } CheckArgument(d_value >= 0, this); CheckArgument(y.d_value > 0, y); @@ -210,7 +211,7 @@ public: BitVector unsignedRemTotal(const BitVector& y) const { CheckArgument(d_size == y.d_size, y); if (y.d_value == 0) { - return BitVector(d_size, 0u); + return BitVector(d_size, d_value); } CheckArgument(d_value >= 0, this); CheckArgument(y.d_value > 0, y); diff --git a/test/regress/regress0/bv/Makefile.am b/test/regress/regress0/bv/Makefile.am index 5d2a54b11..4c2b1c773 100644 --- a/test/regress/regress0/bv/Makefile.am +++ b/test/regress/regress0/bv/Makefile.am @@ -94,7 +94,7 @@ SMT_TESTS = \ smtcompbug.smt # Regression tests for SMT2 inputs -SMT2_TESTS = +SMT2_TESTS = divtest.smt2 # Regression tests for PL inputs CVC_TESTS = bvsimple.cvc sizecheck.cvc diff --git a/test/regress/regress0/bv/divtest.smt2 b/test/regress/regress0/bv/divtest.smt2 new file mode 100644 index 000000000..fe91cb87b --- /dev/null +++ b/test/regress/regress0/bv/divtest.smt2 @@ -0,0 +1,53 @@ +(set-logic QF_BV) +(set-info :status unsat) +(declare-fun x1 () (_ BitVec 12)) +(declare-fun x2 () (_ BitVec 12)) +(declare-fun x3 () (_ BitVec 12)) + +(declare-fun y1 () (_ BitVec 12)) +(declare-fun y2 () (_ BitVec 12)) +(declare-fun y3 () (_ BitVec 12)) + +(declare-fun z1 () (_ BitVec 12)) +(declare-fun z2 () (_ BitVec 12)) +(declare-fun z3 () (_ BitVec 12)) + +(declare-fun a () (_ BitVec 12)) + +(declare-fun x01 () (_ BitVec 10)) +(declare-fun x02 () (_ BitVec 10)) +(declare-fun x03 () (_ BitVec 10)) + +(declare-fun y01 () (_ BitVec 10)) +(declare-fun y02 () (_ BitVec 10)) +(declare-fun y03 () (_ BitVec 10)) + +(declare-fun z01 () (_ BitVec 10)) +(declare-fun z02 () (_ BitVec 10)) +(declare-fun z03 () (_ BitVec 10)) + +(declare-fun a0 () (_ BitVec 10)) + +(assert +(or +(and + (= a (_ bv0 12)) + (or (not (= (bvudiv x1 a) (bvudiv x2 a))) + (not (= (bvudiv x1 a) (bvudiv x3 a))) + (not (= (bvudiv x2 a) (bvudiv x3 a)))) + (or (and (= x1 y1) (= y1 x2)) + (and (= x1 z1) (= z1 x2))) + (or (and (= x2 y2) (= y2 x3)) + (and (= x2 z2) (= z2 x3)))) + +(and + (= a0 (_ bv0 10)) + (or (not (= (bvurem x01 a0) (bvurem x02 a0))) + (not (= (bvurem x01 a0) (bvurem x03 a0))) + (not (= (bvurem x02 a0) (bvurem x03 a0)))) + (or (and (= x01 y01) (= y01 x02)) + (and (= x01 z01) (= z01 x02))) + (or (and (= x02 y02) (= y02 x03)) + (and (= x02 z02) (= z02 x03)))))) + +(check-sat)