From de5f7c20e8058a63375c579b714f88e9ea1e47c5 Mon Sep 17 00:00:00 2001 From: Fahiem Bacchus Date: Wed, 12 Jan 2022 12:34:58 -0500 Subject: [PATCH] 2020 evaluation version --- Makefile | 8 +- maxhs/core/Assumptions.cc | 185 +- maxhs/core/Assumptions.h | 102 +- maxhs/core/Bvars.cc | 152 +- maxhs/core/Bvars.h | 252 ++- maxhs/core/Dimacs.h | 225 +-- maxhs/core/Graph.cc | 245 +++ maxhs/core/Graph.h | 137 ++ maxhs/core/Main.cc | 138 +- maxhs/core/MaxSolver.cc | 2983 +++++++++++++++++-------------- maxhs/core/MaxSolver.h | 368 ++-- maxhs/core/MaxSolverTypes.h | 3 + maxhs/core/Totalizer.cc | 282 +++ maxhs/core/Totalizer.h | 188 ++ maxhs/core/TotalizerAuxStruct.h | 102 ++ maxhs/core/TotalizerManager.cc | 1010 +++++++++++ maxhs/core/TotalizerManager.h | 300 ++++ maxhs/core/Wcnf.cc | 2080 ++++++++++----------- maxhs/core/Wcnf.h | 262 +-- maxhs/core/file | 3 + maxhs/ds/Packed.h | 220 +-- maxhs/ifaces/Cplex.cc | 1425 ++++++++------- maxhs/ifaces/Cplex.h | 310 ++-- maxhs/ifaces/GreedySolver.cc | 377 ++-- maxhs/ifaces/GreedySolver.h | 170 +- maxhs/ifaces/SatSolver.cc | 154 +- maxhs/ifaces/SatSolver.h | 424 +++-- maxhs/ifaces/greedySatSolver.cc | 255 ++- maxhs/ifaces/greedySatSolver.h | 148 +- maxhs/ifaces/miniSatSolver.cc | 304 ++-- maxhs/ifaces/miniSatSolver.h | 177 +- maxhs/ifaces/muser.cc | 501 +++--- maxhs/ifaces/muser.h | 155 +- maxhs/utils/Params.cc | 583 ++++-- maxhs/utils/Params.h | 33 +- maxhs/utils/hash.cc | 310 +--- maxhs/utils/hash.h | 65 +- maxhs/utils/io.h | 37 +- minisat/core/Dimacs.h | 0 minisat/core/Main.cc | 0 minisat/core/Solver.cc | 2274 +++++++++++------------ minisat/core/Solver.h | 980 ++++++---- minisat/core/SolverTypes.h | 787 ++++---- minisat/mtl/Alg.h | 0 minisat/mtl/Alloc.h | 0 minisat/mtl/Heap.h | 0 minisat/mtl/IntMap.h | 0 minisat/mtl/IntTypes.h | 0 minisat/mtl/Map.h | 0 minisat/mtl/Queue.h | 0 minisat/mtl/Rnd.h | 0 minisat/mtl/Sort.h | 0 minisat/mtl/Vec.h | 247 +-- minisat/mtl/XAlloc.h | 0 minisat/simp/Main.cc | 0 minisat/simp/SimpSolver.cc | 0 minisat/simp/SimpSolver.h | 0 minisat/utils/Options.cc | 0 minisat/utils/Options.h | 0 minisat/utils/ParseUtils.h | 0 minisat/utils/System.cc | 0 minisat/utils/System.h | 0 62 files changed, 11171 insertions(+), 7790 deletions(-) mode change 100644 => 100755 maxhs/core/Bvars.h mode change 100755 => 100644 maxhs/core/Dimacs.h create mode 100644 maxhs/core/Graph.cc create mode 100644 maxhs/core/Graph.h mode change 100755 => 100644 maxhs/core/Main.cc mode change 100755 => 100644 maxhs/core/MaxSolver.cc mode change 100755 => 100644 maxhs/core/MaxSolver.h mode change 100755 => 100644 maxhs/core/MaxSolverTypes.h create mode 100644 maxhs/core/Totalizer.cc create mode 100644 maxhs/core/Totalizer.h create mode 100644 maxhs/core/TotalizerAuxStruct.h create mode 100644 maxhs/core/TotalizerManager.cc create mode 100644 maxhs/core/TotalizerManager.h mode change 100755 => 100644 maxhs/core/Wcnf.cc mode change 100755 => 100644 maxhs/core/Wcnf.h create mode 100644 maxhs/core/file mode change 100755 => 100644 maxhs/ds/Packed.h mode change 100755 => 100644 maxhs/ifaces/Cplex.h mode change 100755 => 100644 maxhs/ifaces/GreedySolver.cc mode change 100755 => 100644 maxhs/ifaces/GreedySolver.h mode change 100755 => 100644 maxhs/ifaces/SatSolver.h mode change 100755 => 100644 maxhs/ifaces/greedySatSolver.cc mode change 100755 => 100644 maxhs/ifaces/greedySatSolver.h mode change 100755 => 100644 maxhs/ifaces/miniSatSolver.cc mode change 100755 => 100644 maxhs/ifaces/miniSatSolver.h mode change 100644 => 100755 minisat/core/Dimacs.h mode change 100644 => 100755 minisat/core/Main.cc mode change 100644 => 100755 minisat/core/Solver.cc mode change 100644 => 100755 minisat/core/Solver.h mode change 100644 => 100755 minisat/core/SolverTypes.h mode change 100644 => 100755 minisat/mtl/Alg.h mode change 100644 => 100755 minisat/mtl/Alloc.h mode change 100644 => 100755 minisat/mtl/Heap.h mode change 100644 => 100755 minisat/mtl/IntMap.h mode change 100644 => 100755 minisat/mtl/IntTypes.h mode change 100644 => 100755 minisat/mtl/Map.h mode change 100644 => 100755 minisat/mtl/Queue.h mode change 100644 => 100755 minisat/mtl/Rnd.h mode change 100644 => 100755 minisat/mtl/Sort.h mode change 100644 => 100755 minisat/mtl/Vec.h mode change 100644 => 100755 minisat/mtl/XAlloc.h mode change 100644 => 100755 minisat/simp/Main.cc mode change 100644 => 100755 minisat/simp/SimpSolver.cc mode change 100644 => 100755 minisat/simp/SimpSolver.h mode change 100644 => 100755 minisat/utils/Options.cc mode change 100644 => 100755 minisat/utils/Options.h mode change 100644 => 100755 minisat/utils/ParseUtils.h mode change 100644 => 100755 minisat/utils/System.cc mode change 100644 => 100755 minisat/utils/System.h diff --git a/Makefile b/Makefile index 4e0c62d..45393be 100644 --- a/Makefile +++ b/Makefile @@ -14,8 +14,8 @@ LINUX_CPLEXLIBDIR ?= /w/63/fbacchus/CPLEX_Studio1210/cplex/lib/x86-64_linux/st LINUX_CPLEXINCDIR ?= /w/63/fbacchus/CPLEX_Studio1210/cplex/include # #If you want to build on macos -DARWIN_CPLEXLIBDIR ?= /Users/fbacchus/Applications/IBM/ILOG/CPLEX_Studio128/cplex/lib/x86-64_osx/static_pic/ -DARWIN_CPLEXINCDIR ?= /Users/fbacchus/Applications/IBM/ILOG/CPLEX_Studio128/cplex/include +DARWIN_CPLEXLIBDIR ?= /Users/fbacchus/Applications/IBM/ILOG/CPLEX_Studio1210/cplex/lib/x86-64_osx/static_pic/ +DARWIN_CPLEXINCDIR ?= /Users/fbacchus/Applications/IBM/ILOG/CPLEX_Studio1210/cplex/include ifeq "$(shell uname)" "Linux" CPLEXLIBDIR =$(LINUX_CPLEXLIBDIR) @@ -30,7 +30,7 @@ endif BUILD_DIR ?= build # Include debug-symbols in release builds? -MAXHS_RELSYM ?= -g +#MAXHS_RELSYM ?= -g # Sat solver you can use minisat of glucose. minisat is faster.for maxhs SATSOLVER = minisat @@ -100,7 +100,7 @@ $(BUILD_DIR)/profile/%.o: MAXHS_CXXFLAGS +=$(MAXHS_PRF) -pg ## Build-type Link-flags: $(BUILD_DIR)/profile/bin/$(MAXHS): MAXHS_LDFLAGS += -pg ifeq "$(shell uname)" "Linux" -$(BUILD_DIR)/release/bin/$(MAXHS): MAXHS_LDFLAGS += -z muldefs +$(BUILD_DIR)/release/bin/$(MAXHS): MAXHS_LDFLAGS += --static -z muldefs endif $(BUILD_DIR)/release/bin/$(MAXHS): MAXHS_LDFLAGS += $(MAXHS_RELSYM) diff --git a/maxhs/core/Assumptions.cc b/maxhs/core/Assumptions.cc index b5f1888..e8ab29c 100644 --- a/maxhs/core/Assumptions.cc +++ b/maxhs/core/Assumptions.cc @@ -21,96 +21,165 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ***********/ - #include "maxhs/core/Assumptions.h" +#include "maxhs/core/Bvars.h" +#include "maxhs/core/TotalizerManager.h" +#include "maxhs/utils/Params.h" #ifdef GLUCOSE namespace Minisat = Glucose; #endif -using Minisat::l_True; using Minisat::l_False; +using Minisat::l_True; using Minisat::l_Undef; +using Minisat::lbool; +using Minisat::Lit; using Minisat::lit_Undef; +using Minisat::var; +Assumps::Assumps(MaxHS_Iface::SatSolver* s, Bvars& b, TotalizerManager* t) + : satsolver{s}, bvars(b), totalizers{t}, map(bvars.maxvar() + 1, -1) {} -void Assumps::init(const vector& ivals, CoreType coreType) { - //initialize assumptions with set of b-lits. coreType determines - //what kind of conflicts we are looking for. If looking for cores, - //add only noncore vars (conflict will be negation == core), etc. - - assumps.clear(); - for(auto l : ivals) { - assert(bvars.isBvar(l)); - if(satsolver->curVal(l) != l_True) - //Don't need true lits in assumption. - switch(coreType) { - case CoreType::cores: - if(bvars.isNonCore(l)) - assumps.push_back(l); - break; - case CoreType::nonCores: - if(bvars.isCore(l)) - assumps.push_back(l); - break; - case CoreType::mixed: - assumps.push_back(l); - } - if (satsolver->curVal(l) == l_False) - cout << "c WARNING assumptions being initialized with false lit\n"; - } - setMap(); +void Assumps::init(vector lits) { + // initialize assumptions with set of b-lits. coreType determines + // what kind of conflicts we are looking for. If looking for cores, + // add only noncore vars (conflict will be negation == core), etc. + assumps = std::move(lits); + if (params.verbosity > 1) + cout << "c Init assumptions with " << assumps.size() << "lits\n"; + // cout << "size of assumps: " << assumps.size() << "\n"; + // printAssumps(); + setMap(); + // printMap(); } void Assumps::all_softs_true() { - //harden all softs not yet forced. + // harden all softs not yet forced. assumps.clear(); - for(size_t i = 0; i < bvars.n_bvars(); i++) - if(satsolver->curVal(bvars.varOfCls(i)) == l_Undef) + for (size_t i = 0; i < bvars.n_bvars(); i++) + if (satsolver->curVal(bvars.varOfCls(i)) == l_Undef) assumps.push_back(~bvars.litOfCls(i)); setMap(); } - + +void Assumps::permute() { + std::random_shuffle(assumps.begin(), assumps.end()); + setMap(); +} + void Assumps::exclude(const vector& ex) { - //Unlike update exclude removes the lits without regard for its polarity. - for(auto l : ex) - if(getIndex(l) >= 0) - assumps[getIndex(l)] = lit_Undef; - auto isUndef = [](Lit l) { return l == lit_Undef; }; - auto p = std::remove_if(assumps.begin(), assumps.end(), isUndef); - assumps.erase(p, assumps.end()); + // Unlike update exclude removes the lits without regard for its polarity. + for (auto l : ex) + if (getIndex(l) >= 0) assumps[getIndex(l)] = lit_Undef; + removeUndefs(); setMap(); } void Assumps::update(const vector& conflict, bool rm) { - //Update assumptions with set of literals in conflict. Flip - //flip the assumptions--if using Fb or remove the assumptions if - //using Fbeq - if(rm) + // Update assumptions with set of literals in conflict. Flip + // flip the assumptions--if using Fb or remove the assumptions if + // using Fbeq + if (rm) remove(conflict); else flip(conflict); } void Assumps::flip(const vector& conflict) { - //conflict variables must be in assumps. Update assumptions to agree - //with conflict. - for(auto l : conflict) { - checkUpdate(l); - if(assumps[getIndex(l)] == l) - cout << "c WARNING conflict agrees with assumption---no real update in flip assumptions\n"; - assumps[getIndex(l)] = l; + // conflict variables must be in assumps. Update assumptions to agree + // with conflict. + for (auto l : conflict) { + if (checkUpdate(l)) { + if (assumps[getIndex(l)] == l) + cout << "c WARNING conflict agrees with assumption---no real update in " + "flip assumptions\n"; + assumps[getIndex(l)] = l; + } } } - + void Assumps::remove(const vector& conflict) { - //conflict variables must be in assumps. - //perserve order of assumps. - for(auto l : conflict) - if(checkUpdate(l)) - assumps[getIndex(l)] = lit_Undef; + // conflict variables must be in assumps. + // perserve order of assumps. + if(params.abstract_assumps == 0) { + for (auto l : conflict) + if (checkUpdate(l)) + assumps[getIndex(l)] = lit_Undef; + } + else if(params.abstract_assumps == 1) { + for (auto l : conflict) + if (checkUpdate(l)) { + assumps[getIndex(l)] = lit_Undef; + if (totalizers->isToutput(l)) { + Lit ln = totalizers->getNextOLit(l); + if (ln != lit_Undef) + assumps.push_back(~ln); // lit_Undef cannot be negated! + } + } + } + else if(params.abstract_assumps == 2) { + bool tot_relaxed {false}; + for (auto l : conflict) + if (checkUpdate(l)) { + if(bvars.isBvar(l)) + assumps[getIndex(l)] = lit_Undef; + else if (totalizers->isToutput(l) && !tot_relaxed) { + assumps[getIndex(l)] = lit_Undef; + Lit ln = totalizers->getNextOLit(l); + if (ln != lit_Undef) + assumps.push_back(~ln); + tot_relaxed = true; + } + } + } + removeUndefs(); + setMap(); +} + +void Assumps::clearIndex(Lit l) { + assert(var(l) < static_cast(map.size())); + map[var(l)] = -1; +} + +int Assumps::getIndex(Lit l) { + assert(var(l) < static_cast(map.size())); + return map[var(l)]; +} + +bool Assumps::removeUndefs() { auto isUndef = [](Lit l) { return l == lit_Undef; }; auto p = std::remove_if(assumps.begin(), assumps.end(), isUndef); - assumps.erase(p, assumps.end()); - setMap(); + if(p != assumps.end()) { + assumps.erase(p, assumps.end()); + return true; + } + return false; } + +void Assumps::setMap() { + std::fill(map.begin(), map.end(), -1); + for (size_t i = 0; i < assumps.size(); i++) { + if (static_cast(var(assumps[i])) > map.size()) + map.resize(var(assumps[i]) + 1, -1); + map[var(assumps[i])] = i; + } + } + +void Assumps::printMap() { + for (size_t i = 0; i < map.size(); i++) + std::cout << "a map[" << i << "] = " << map[i] << "\n"; + } + +bool Assumps::checkUpdate(Lit l) { + if (getIndex(l) < 0) { + cout << "c ERROR tried to update literal not in assumptions: " << l + << " to int: " << toInt(l) << "\n"; + if (totalizers->isToutput(l)) + cout << "c ERROR " << l << " is totalizer output\n"; + return false; + } + return true; + } + +void Assumps::printAssumps() { cout << "c assumps:\n" << assumps << "\n"; } diff --git a/maxhs/core/Assumptions.h b/maxhs/core/Assumptions.h index 885b722..171c864 100644 --- a/maxhs/core/Assumptions.h +++ b/maxhs/core/Assumptions.h @@ -25,80 +25,68 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef ASSUMPTIONS_h #define ASSUMPTIONS_h -#include #include -#include "maxhs/ifaces/SatSolver.h" -#include "maxhs/core/Wcnf.h" -#include "maxhs/core/Bvars.h" -#include "maxhs/utils/io.h" +#include + #include "maxhs/core/MaxSolverTypes.h" +#include "maxhs/ifaces/SatSolver.h" -#ifdef GLUCOSE -namespace Minisat = Glucose; -#endif +class Bvars; +class TotalizerManager; -using Minisat::Lit; -using Minisat::var; -using Minisat::lbool; +namespace MaxHS_Iface { +class Cplex; +class SatSolver; +} // namespace MaxHS_Iface + +using std::vector; class Assumps { - //MaxSolver helper class -public: - Assumps(MaxHS_Iface::SatSolver* s, Bvars& b) : satsolver{s}, bvars (b) { - map.resize(bvars.n_bvars(), -1); } - ~Assumps() {} - //initialize assumptions to make all softs true, or set of passed softs - void init(const vector& ivals, CoreType ctype); + // MaxSolver helper class + public: + Assumps(MaxHS_Iface::SatSolver* s, Bvars& b, TotalizerManager* t); + // initialize assumptions to make all softs true, or set of passed softs + void init(vector lits); void all_softs_true(); + void permute(); - //update requires that lits in conflict currently appear negated in - //assumptions Warns this is not the case. - //exclude ensures these variables corresponding to these lits are - //not part of assumption - void update(const vector& conflict, bool remove); - void exclude(const vector& ex); - - const vector& vec() const { return assumps; } + // update requires that lits in conflict currently appear negated in + // assumptions Warns this is not the case. + // exclude ensures these variables corresponding to these lits are + // not part of assumption + void exclude(const vector& ex); + void update(const vector& conflict, bool remove); + const vector& vec() const { return assumps; } template - void sort(Compare comp) { + void sort(Compare comp) { std::sort(assumps.begin(), assumps.end(), comp); setMap(); } - friend ostream& operator<<(ostream& os, const Assumps& a); + friend std::ostream& operator<<(std::ostream& os, const Assumps& a); -private: - vector assumps; - vector map; + private: MaxHS_Iface::SatSolver* satsolver; Bvars& bvars; -// const uint8_t inMxdvar = 2; -// const uint8_t inMxbvar = 1; -// vector& inMx; -// - void remove(const vector& conflict); - void flip(const vector& conflict); - void clearIndex(Lit l) { - map[bvars.toIndex(var(l))] = -1; - } - int getIndex(Lit l) { - return map[bvars.toIndex(var(l))]; - } - void setMap() { - std::fill(map.begin(), map.end(), -1); - for(size_t i = 0; i < assumps.size(); i++) - map[bvars.toIndex(var(assumps[i]))] = i; - } - bool checkUpdate(Lit l) { - if(getIndex(l) < 0) { - cout << "c ERROR tried to update literal not in assumptions\n"; - return false; - } - return true; - } + TotalizerManager* totalizers; + vector assumps; + vector map; + vector tot_index; + vector counts; + vector coeff; + void flip(const vector& conflict); + void remove(const vector& conflict); + + void clearIndex(Minisat::Lit l); + int getIndex(Minisat::Lit l); + bool removeUndefs(); + void setMap(); + void printMap(); + bool checkUpdate(Minisat::Lit l); + void printAssumps(); }; -inline ostream& operator<<(ostream& os, const Assumps& a) { - os << a.assumps; +inline std::ostream& operator<<(std::ostream& os, const Assumps& a) { + os << "Assumps" << a.assumps; return os; } diff --git a/maxhs/core/Bvars.cc b/maxhs/core/Bvars.cc index ebadafb..009a8a0 100644 --- a/maxhs/core/Bvars.cc +++ b/maxhs/core/Bvars.cc @@ -22,106 +22,128 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ***********/ -#include -#include #include "maxhs/core/Bvars.h" +#include +#include -Bvars::Bvars(const Wcnf* f) : - theWcnf {f}, - maxbvar {0}, - clsBlit {theWcnf->nSofts(), Minisat::lit_Undef}, - bvarCls {}, - var_types (theWcnf->nVars(), Var_type::not_in_theory), - mxNum (theWcnf->nVars(), -1) -{ - Var nxtbvar {theWcnf->nVars()}; - - for(auto& cls : theWcnf->hards()) - for(auto l : cls) - var_types[var(l)] = Var_type::original; - for(auto& cls : theWcnf->softs()) - for(auto l : cls) - var_types[var(l)] = Var_type::original; - - for(size_t i = 0; i < theWcnf->nSofts(); i++) { +Bvars::Bvars(const Wcnf* f) + : theWcnf{f}, + clsBlit{theWcnf->nSofts(), Minisat::lit_Undef}, + var_types(theWcnf->nVars(), Var_type::not_in_theory), + mxNum(theWcnf->nVars(), -1) { + Var nxtbvar{theWcnf->nVars()}; + + for (auto& cls : theWcnf->hards()) + for (auto l : cls) var_types[var(l)] = Var_type::original; + for (auto& cls : theWcnf->softs()) + for (auto l : cls) var_types[var(l)] = Var_type::original; + + for (size_t i = 0; i < theWcnf->nSofts(); i++) { auto scls = theWcnf->softs()[i]; - if(scls.size() == 1) - clsBlit[i] = ~scls[0]; //blit false means clause must be satisfied + if (scls.size() == 1) + clsBlit[i] = ~scls[0]; // blit false means clause must be satisfied else clsBlit[i] = mkLit(nxtbvar++); maxbvar = std::max(maxbvar, var(clsBlit[i])); } - bvarCls.resize(maxbvar+1, -1); - for(size_t i = 0; i < theWcnf->nSofts(); i++) - bvarCls[var(clsBlit[i])] = i; - - if(static_cast(maxvar()) >= var_types.size()) - var_types.resize(maxvar()+1, Var_type::not_in_theory); + bvarCls.resize(maxbvar + 1, -1); + for (size_t i = 0; i < theWcnf->nSofts(); i++) bvarCls[var(clsBlit[i])] = i; - for(size_t i = 0; i < theWcnf->nSofts(); i++) { + if (n_vars() > var_types.size()) + var_types.resize(n_vars(), Var_type::not_in_theory); + + for (size_t i = 0; i < theWcnf->nSofts(); i++) { auto blit = clsBlit[i]; - var_types[var(blit)] = var_types[var(blit)] | Var_type::bvar; - if(!sign(blit)) var_types[var(blit)] = var_types[var(blit)] | Var_type::core_is_pos; + var_types[var(blit)] |= Var_type::bvar; + if (!sign(blit)) var_types[var(blit)] |= Var_type::core_is_pos; } - - //process mutexes - mxNum.resize(maxvar()+1, -1); - for(size_t i =0; i < theWcnf->get_SCMxs().size(); i++) { + + // process mutexes + mxNum.resize(n_vars(), -1); + for (size_t i = 0; i < theWcnf->get_SCMxs().size(); i++) { auto& mx = theWcnf->get_SCMxs()[i]; - if(mx.encoding_lit() != Minisat::lit_Undef) { - var_types[var(mx.encoding_lit())] = var_types[var(mx.encoding_lit())] | Var_type::dvar; + if (mx.encoding_lit() != Minisat::lit_Undef) { + var_types[var(mx.encoding_lit())] |= Var_type::dvar; mxNum[var(mx.encoding_lit())] = i; } - for(auto l : mx.soft_clause_lits()) { - var_types[var(l)] = var_types[var(l)] | Var_type::in_mutex; - if(!sign(l)) - var_types[var(l)] = var_types[var(l)] | Var_type::orig_core_is_pos; + for (auto l : mx.soft_clause_lits()) { + var_types[var(l)] |= Var_type::in_mutex; + if (!sign(l)) var_types[var(l)] |= Var_type::orig_core_is_pos; mxNum[var(l)] = i; } } } +/* +void Bvars::setOutput(Lit tOutput, Lit next) { + if (!isTvar(tOutput)) { + cout << "c ERROR, Bvars::setOutput() setting next of a non-totalizer +literal\n"; exit(1); + } + if(!(isTvar(next) || next == lit_Undef)) { + cout << "c ERROR, Bvars::setOutput() next is invalid\n"; + exit(1); + } + int ind = var(tOutput); + if(nextOutputM.size() <= static_cast(ind)) + nextOutputM.resize(ind+1, lit_Undef); + nextOutputM[ind] = next; +} + +Lit Bvars::nextOutput(Lit tOutput) { + if (!isTvar(tOutput)) { + cout << "c ERROR, Bvars::nextOutput() asking for next output of a +non-totalizer\n"; exit(1); + } + int ind = var(tOutput); + if (nextOutputM.size() < static_cast(ind)) { + cout << "c ERROR, Bvars::nextOutput() asking for a non-set output +literal\n"; + } + return nextOutputM[ind]; +} +*/ + void Bvars::printVars() { - for(int i = 0; i <= maxvar(); i++) { - cout << "Var #" << i+1 << "." << var_types[i] << "\n"; - if(isBvar(i)) { + for (int i = 0; i <= maxvar(); i++) { + cout << "Var #" << i + 1 << "." << var_types[i] << "\n"; + cout << "is bvar: " << isBvar(i) << "\n"; + if (isBvar(i)) { auto clsi = clsIndex(i); Lit blit = litOfCls(clsi); - cout << "Clause " << clsi << ". blit = " << blit << " " << theWcnf->getSoft(clsi) << "\n"; + cout << "Clause " << clsi << ". blit = " << blit << " " + << theWcnf->getSoft(clsi) << "\n"; cout << "coreIsPos(" << blit << ") = " << coreIsPos(blit); cout << " sign(" << blit << ") = " << sign(blit); - cout << " isCore(" << blit << ") = " << isCore(blit) - << " isNonCore(" << blit << ") = " << isNonCore(blit) << "\n"; + cout << " isCore(" << blit << ") = " << isCore(blit) << " isNonCore(" + << blit << ") = " << isNonCore(blit) << "\n"; } - if(inMutex(i)) { - cout << "In mutex " << getMxNum(i) << " " << theWcnf->get_SCMxs()[getMxNum(i)] << "\n"; + if (inMutex(i)) { + cout << "In mutex " << getMxNum(i) << " " + << theWcnf->get_SCMxs()[getMxNum(i)] << "\n"; cout << "orig_IsCore(" << mkLit(i) << ") = " << orig_IsCore(mkLit(i)) - << " orig_IsNonCore(" << mkLit(i) << ") = " << orig_IsNonCore(mkLit(i)) << "\n"; + << " orig_IsNonCore(" << mkLit(i) + << ") = " << orig_IsNonCore(mkLit(i)) << "\n"; } cout << "\n"; } } void Bvars::print_var_types() { - for(size_t i=0; i < var_types.size(); i++) - cout << "Var " << i+1 << " type = " << var_types[i] << "\n"; + for (size_t i = 0; i < var_types.size(); i++) + cout << "Var " << i + 1 << " type = " << var_types[i] << "\n"; } ostream& operator<<(ostream& os, const Var_type& x) { - if(x == Var_type::not_in_theory) - os << "not_in_theory, "; - if(to_bool(x & Var_type::original)) - os << "original, "; - if(to_bool(x & Var_type::bvar)) - os << "blocking var, "; - if(to_bool(x & Var_type::core_is_pos)) - os << "positive is core, "; - if(to_bool(x & Var_type::in_mutex)) - os << "in mutex, "; - if(to_bool(x & Var_type::orig_core_is_pos)) + if (x == Var_type::not_in_theory) os << "not_in_theory, "; + if (to_bool(x & Var_type::original)) os << "original, "; + if (to_bool(x & Var_type::bvar)) os << "blocking var, "; + if (to_bool(x & Var_type::core_is_pos)) os << "positive is core, "; + if (to_bool(x & Var_type::in_mutex)) os << "in mutex, "; + if (to_bool(x & Var_type::orig_core_is_pos)) os << "positive is original core, "; - if(to_bool(x & Var_type::dvar)) - os << "defined var, "; + if (to_bool(x & Var_type::dvar)) os << "defined var, "; + if (to_bool(x & Var_type::totalizer)) os << "totalizer var "; return os; } diff --git a/maxhs/core/Bvars.h b/maxhs/core/Bvars.h old mode 100644 new mode 100755 index 42ff9ec..cc68088 --- a/maxhs/core/Bvars.h +++ b/maxhs/core/Bvars.h @@ -24,10 +24,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef BVARS_h #define BVARS_h -#include #include #include -#include "maxhs/ifaces/SatSolver.h" +#include #include "maxhs/core/Wcnf.h" #ifdef GLUCOSE @@ -35,160 +34,239 @@ namespace Minisat = Glucose; #endif using Minisat::Lit; -using Minisat::var; using Minisat::mkLit; using Minisat::sign; +using Minisat::var; - -//We no longer add new b-vars to unit clauses...this means that units -//are modeled more like in fbeq than fb. That is, for a unit soft (l) -//the b-lit is -l. (B-lit false means soft clause must be -//satisfied). In Fb, the b-lit true does not force the clause to be -//falsified...but for units it does even when Fb is used. - +// We no longer add new b-vars to unit clauses...this means that units +// are modeled more like in fbeq than fb. That is, for a unit soft (l) +// the b-lit is -l. (B-lit false means soft clause must be +// satisfied). In Fb, the b-lit true does not force the clause to be +// falsified...but for units it does even when Fb is used. enum class Var_type : uint8_t { - not_in_theory = 0, - original = 1, - bvar = 2, - core_is_pos = 4, - in_mutex = 8, - orig_core_is_pos = 16, - dvar = 32 }; - -constexpr Var_type operator| (Var_type x, Var_type y) { - return static_cast(static_cast(x) | static_cast(y)); -} -constexpr Var_type operator& (Var_type x, Var_type y) { - return static_cast(static_cast(x) & static_cast(y)); + not_in_theory = 0, + original = 1, + bvar = 2, + core_is_pos = 4, + in_mutex = 8, + orig_core_is_pos = 16, + dvar = 32, + totalizer = 64 +}; + +constexpr Var_type operator|(const Var_type x, const Var_type y) { + return static_cast(static_cast(x) | + static_cast(y)); } -constexpr bool to_bool(Var_type x) { - return x != Var_type::not_in_theory; + +constexpr Var_type& operator|=(Var_type& x, const Var_type y) { x = x | y; return x; } + +constexpr Var_type operator&(const Var_type x, const Var_type y) { + return static_cast(static_cast(x) & + static_cast(y)); } +constexpr bool to_bool(const Var_type x) { return x != Var_type::not_in_theory; } ostream& operator<<(ostream& os, const Var_type& x); class Bvars { - //Helper class to manage mapping of b-variables to soft clauses. -public: + // Helper class to manage mapping of b-variables to soft clauses. + public: Bvars(const Wcnf* f); ~Bvars() {} - - //number and sizes + + // number and sizes size_t n_bvars() const { return theWcnf->nSofts(); } - size_t n_vars() const { return theWcnf->nVars(); } - size_t n_blits() const { return n_bvars()*2; } + size_t n_blits() const { return n_bvars() * 2; } + size_t n_ovars() const { return theWcnf->nVars(); } Var maxBvar() const { return maxbvar; } - Var maxvar() const { return std::max(theWcnf->maxVar(), maxbvar); } + Var maxvar() const { + return std::max(maxTvar, std::max(theWcnf->maxVar(), maxbvar)); + } + size_t n_vars() const { return maxvar() + 1; } - //blit of soft clause + // blit of soft clause Var varOfCls(int i) const { return var(litOfCls(i)); } - //making litOfCls(i) true relaxes soft clause i---incurring a cost - //in fbeq it means that the clause is falsified - Lit litOfCls(int i) const { assert(static_cast(i) nSofts()); return clsBlit[i]; } + // making litOfCls(i) true relaxes soft clause i---incurring a cost + // in fbeq it means that the clause is falsified + Lit litOfCls(int i) const { + assert(static_cast(i) < theWcnf->nSofts()); + return clsBlit[i]; + } - //Soft clause index of a bvar/blit - int clsIndex(Var v) const { assert(isBvar(v)); return bvarCls[v]; } + // Soft clause index of a bvar/blit + int clsIndex(Var v) const { + assert(isBvar(v)); + return bvarCls[v]; + } int clsIndex(Lit l) const { return clsIndex(var(l)); } - //convert a b-variable to its core lit or non-core lit (core lit = True ==> relax corresponding soft clause) + // convert a b-variable to its core lit or non-core lit (core lit = True ==> + // relax corresponding soft clause) Lit coreLit(Var v) const { return litOfCls(clsIndex(v)); } Lit nonCoreLit(Var v) const { return ~coreLit(v); } - //Variable is a bvar + // Variable is a bvar bool isBvar(Var v) const { return to_bool(var_types[v] & Var_type::bvar); } bool isBvar(Lit l) const { return isBvar(var(l)); } - //return true if bvar appears positively/negatively in soft clause. - //I.e., setting to true/false incurs cost of sone soft clause. - bool coreIsPos(Var v) const { return isBvar(v) && to_bool(var_types[v] & Var_type::core_is_pos); } + // return true if bvar appears positively/negatively in soft clause. + // I.e., setting to true/false incurs cost of sone soft clause. + bool coreIsPos(Var v) const { + return isBvar(v) && to_bool(var_types[v] & Var_type::core_is_pos); + } bool coreIsPos(Lit l) const { return coreIsPos(var(l)); } bool coreIsNeg(Var v) const { return isBvar(v) && !coreIsPos(v); } bool coreIsNeg(Lit l) const { return coreIsNeg(var(l)); } - //Lit is a core bvar if it relaxes a clause when made true (falsifies clause in Fbeq) - //it can appear in a core constraint - //non core bvars can appear in a non-core constraint + // Lit is a core bvar if it relaxes a clause when made true (falsifies clause + // in Fbeq) it can appear in a core constraint non core bvars can appear in a + // non-core constraint bool isCore(Lit l) const { return isBvar(l) && (sign(l) != coreIsPos(l)); } - //bool isCore(Lit l) const { return isBvar(l) && l == litOfCls(clsIndex(l)); } + // bool isCore(Lit l) const { return isBvar(l) && l == litOfCls(clsIndex(l)); + // } bool isNonCore(Lit l) const { return isCore(~l); } - //is original variable (could also be a bvar!) - bool isOvar(Var v) const { return to_bool(var_types[v] & Var_type::original); } + // is original variable (could also be a bvar!) + bool isOvar(Var v) const { + return to_bool(var_types[v] & Var_type::original); + } bool isOvar(Lit l) const { return isOvar(var(l)); } - //appears in mutex - bool inMutex(Var v) const { return to_bool(var_types[v] & Var_type::in_mutex); } + // appears in mutex + bool inMutex(Var v) const { + return to_bool(var_types[v] & Var_type::in_mutex); + } bool inMutex(Lit l) const { return inMutex(var(l)); } - //var appears in a mutex and making it postive/negative incurred the cost of that - //original soft clause (i.e., before these softs were encoded into a - //mutex) - bool orig_coreIsPos(Var v) const { return inMutex(v) && to_bool(var_types[v] & Var_type::orig_core_is_pos); } + // var appears in a mutex and making it postive/negative incurred the cost of + // that original soft clause (i.e., before these softs were encoded into a + // mutex) + bool orig_coreIsPos(Var v) const { + return inMutex(v) && to_bool(var_types[v] & Var_type::orig_core_is_pos); + } bool orig_coreIsPos(Lit l) const { return orig_coreIsPos(var(l)); } bool orig_coreIsNeg(Var v) const { return inMutex(v) && !orig_coreIsPos(v); } bool orig_coreIsNeg(Lit l) const { return orig_coreIsNeg(var(l)); } - //This literal was originally a core or non-core bvar (before mx processing) - bool orig_IsCore(Lit l) const { return inMutex(l) && (sign(l) != orig_coreIsPos(l)); } + // This literal was originally a core or non-core bvar (before mx processing) + bool orig_IsCore(Lit l) const { + return inMutex(l) && (sign(l) != orig_coreIsPos(l)); + } bool orig_IsNonCore(Lit l) const { return orig_IsCore(~l); } - int getMxNum(Var v) const { return mxNum[v]; } - int getMxNum(Lit l) const { return getMxNum(var(l)); } + int getMxNum(Var v) const { return mxNum[v]; } + int getMxNum(Lit l) const { return getMxNum(var(l)); } - bool inCoreMx(Var v) const { return inMutex(v) && theWcnf->get_SCMxs()[getMxNum(v)].is_core(); } + Lit getMxDLit(Var v) const { + assert(inMutex(v)); + return theWcnf->get_SCMxs()[getMxNum(v)].encoding_lit(); + } + Lit getMxDLit(Lit l) const { return getMxDLit(var(l)); } + + bool inCoreMx(Var v) const { + return inMutex(v) && theWcnf->get_SCMxs()[getMxNum(v)].is_core(); + } bool inCoreMx(Lit l) const { return inCoreMx(var(l)); } - bool inNonCoreMx(Var v) const { return inMutex(v) && !theWcnf->get_SCMxs()[getMxNum(v)].is_core(); } + bool inNonCoreMx(Var v) const { + return inMutex(v) && !theWcnf->get_SCMxs()[getMxNum(v)].is_core(); + } bool inNonCoreMx(Lit l) const { return inNonCoreMx(var(l)); } bool isDvar(Var v) const { return to_bool(var_types[v] & Var_type::dvar); } bool isDvar(Lit l) const { return isDvar(var(l)); } + /*bool isTvar(Var v) const { + return to_bool(var_types[v] & Var_type::totalizer); + } + bool isTvar(Lit l) const { return isTvar(var(l)); } + + void setOutput(Lit tOutput, Lit next); + Lit nextOutput(Lit tOutput);*/ - //Map bvar or blit to a 0 based index---allowing - //storing vector based data about bvars/blits - int toIndex(Var v) const { assert(isBvar(v)); return clsIndex(v); } - int toIndex(Lit l) const { assert(isBvar(l)); return clsIndex(l)*2+(int)sign(l); } + // Map bvar or blit to a 0 based index---allowing + // storing vector based data about bvars/blits + int toIndex(Var v) const { + assert(isBvar(v)); + return clsIndex(v); + } + int toIndex(Lit l) const { + assert(isBvar(l)); + return clsIndex(l) * 2 + (int)sign(l); + } bool areSubsumedByMx(Lit l1, Lit l2) const { - //binary clause (l1, l2) is subsumed by mutex information. - return (inMutex(l1) && inMutex(l2) - && getMxNum(l1) == getMxNum(l2) - && ((orig_IsNonCore(l1) && orig_IsNonCore(l2) && inCoreMx(l1)) - || (orig_IsCore(l1) && orig_IsCore(l2) && inNonCoreMx(l1)))); + // binary clause (l1, l2) is subsumed by mutex information. + return (inMutex(l1) && inMutex(l2) && getMxNum(l1) == getMxNum(l2) && + ((orig_IsNonCore(l1) && orig_IsNonCore(l2) && inCoreMx(l1)) || + (orig_IsCore(l1) && orig_IsCore(l2) && inNonCoreMx(l1)))); } - //Weight of a bvar, blit. For blit this is the cost of making the - //literal true (zero if the lit hardens rather than relaxes the - //clause. - Weight wt(Var v) const { assert(isBvar(v)); return theWcnf->getWt(clsIndex(v)); } - Weight wtNcls(int i) const { assert((size_t)i < n_bvars()); return theWcnf->getWt(i); } + // Weight of a bvar, blit. For blit this is the cost of making the + // literal true (zero if the lit hardens rather than relaxes the + // clause. + Weight wt(Var v) const { + assert(isBvar(v)); + return theWcnf->getWt(clsIndex(v)); + } + Weight wtNcls(int i) const { + assert((size_t)i < n_bvars()); + return theWcnf->getWt(i); + } - Weight wt(Lit l) const { assert(isBvar(l)); return isCore(l) ? theWcnf->getWt(clsIndex(l)) : 0; } - int clsSize(Var v) const { assert(isBvar(v)); return theWcnf->softSize(clsIndex(v)); } - int clsSize(Lit l) const { assert(isBvar(l)); return clsSize(var(l)); } + Weight wt(Lit l) const { + assert(isBvar(l)); + return isCore(l) ? theWcnf->getWt(clsIndex(l)) : 0; + } + int clsSize(Var v) const { + assert(isBvar(v)); + return theWcnf->softSize(clsIndex(v)); + } + int clsSize(Lit l) const { + assert(isBvar(l)); + return clsSize(var(l)); + } Weight maxWt() const { return theWcnf->maxSftWt(); } Weight minWt() const { return theWcnf->minSftWt(); } - - //Return a vector containing all the bvars. + // Return a vector containing all the bvars. vector getvars() { vector vars(n_bvars()); - for(size_t i = 0; i < n_bvars(); i++) - vars[i] = varOfCls(i); + for (size_t i = 0; i < n_bvars(); i++) vars[i] = varOfCls(i); return vars; } + + // Return a vector containing all core lits---make these true and the soft + // clauses are falsified + vector getCoreLits() { + vector lits(n_bvars()); + for (size_t i = 0; i < n_bvars(); i++) lits[i] = litOfCls(i); + return lits; + } + void printVars(); void print_var_types(); -private: + void initMaxTvar() { maxTvar = maxvar(); } + + Var makeTvar() { + var_types.push_back(Var_type::totalizer); + return ++maxTvar; + } + + private: const Wcnf* theWcnf; - Var maxbvar; - vector clsBlit; //map from clause index to blit of clause - vector bvarCls; //map from b-var to clause index (sign determined by clsBlit) + Var maxbvar{}; + Var maxTvar{}; + vector clsBlit; // map from clause index to blit of clause + vector + bvarCls; // map from b-var to clause index (sign determined by clsBlit) vector var_types; - vector mxNum; //map from variables to mutex they are in + vector mxNum; // map from variables to mutex they are in + + vector nextOutputM; // map for the next outputLit }; #endif diff --git a/maxhs/core/Dimacs.h b/maxhs/core/Dimacs.h old mode 100755 new mode 100644 index 0f13df7..b1613c9 --- a/maxhs/core/Dimacs.h +++ b/maxhs/core/Dimacs.h @@ -2,173 +2,179 @@ Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson Copyright (c) 2007-2010, Niklas Sorensson -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, -sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or -substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT -NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT -OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. **************************************************************************************************/ #ifndef Minisat_Dimacs_h #define Minisat_Dimacs_h -#include #include +#include #include #ifdef GLUCOSE -#include "glucose/utils/ParseUtils.h" #include "glucose/core/SolverTypes.h" +#include "glucose/utils/ParseUtils.h" #else -#include "minisat/utils/ParseUtils.h" #include "minisat/core/SolverTypes.h" +#include "minisat/utils/ParseUtils.h" #endif #include "maxhs/core/MaxSolverTypes.h" -#include "maxhs/core/MaxSolver.h" #include "maxhs/core/Wcnf.h" - -using std::cout; using std::cerr; +using std::cout; #ifdef GLUCOSE namespace Minisat = Glucose; #endif -using namespace Minisat; -using namespace MaxHS; - - -template +template static double parseIntegerDouble(B& in) { - double val = 0; - bool neg = false; - skipWhitespace(in); - if (*in == '-') neg = true, ++in; - else if (*in == '+') ++in; - if (*in < '0' || *in > '9') - cerr << "PARSE ERROR! Unexpected char: " << *in << "\n" , exit(3); - while (*in >= '0' && *in <= '9') - val = val*10 + (*in - '0'), - ++in; - return neg ? -val : val; } + double val = 0; + bool neg = false; + skipWhitespace(in); + if (*in == '-') + neg = true, ++in; + else if (*in == '+') + ++in; + if (*in < '0' || *in > '9') + cerr << "PARSE ERROR! Unexpected char: " << *in << "\n", exit(3); + while (*in >= '0' && *in <= '9') val = val * 10 + (*in - '0'), ++in; + return neg ? -val : val; +} // JD -template +template static double parseDouble(B& in) { - double val = 0; - bool neg = false; - bool frac = false; - int exponent = 0; - int n = -1; - skipWhitespace(in); - if (*in == '-') neg = true, ++in; - else if (*in == '+') ++in; - if ((*in < '0' || *in > '9') && *in != '.' && *in != 'e' && *in != '-' && *in != '+') - cerr << "PARSE ERROR 3! Unexpected char: " << *in << "\n", exit(3); - while ((*in >= '0' && *in <= '9') || *in == '.' || *in == 'e' || *in == '-' || *in == '+') { - if (*in == '.') frac = true; - else if (*in == 'e') { - bool expNeg = false; - ++in; - while (*in == '-' || *in == '0' || *in == '+') { - if (*in == '-') expNeg = true; - ++in; - } - int intExp = parseInt(in); - exponent = expNeg ? -intExp : intExp; - } - else frac ? val = val + (*in - '0')*pow(10.0,n), --n : val = val*10 + (*in - '0'); + double val = 0; + bool neg = false; + bool frac = false; + int exponent = 0; + int n = -1; + skipWhitespace(in); + if (*in == '-') + neg = true, ++in; + else if (*in == '+') + ++in; + if ((*in < '0' || *in > '9') && *in != '.' && *in != 'e' && *in != '-' && + *in != '+') + cerr << "PARSE ERROR 3! Unexpected char: " << *in << "\n", exit(3); + while ((*in >= '0' && *in <= '9') || *in == '.' || *in == 'e' || *in == '-' || + *in == '+') { + if (*in == '.') + frac = true; + else if (*in == 'e') { + bool expNeg = false; + ++in; + while (*in == '-' || *in == '0' || *in == '+') { + if (*in == '-') expNeg = true; ++in; - } - val = val * pow(10, exponent); - return neg ? -val : val; } + } + int intExp = parseInt(in); + exponent = expNeg ? -intExp : intExp; + } else + frac ? val = val + (*in - '0') * pow(10.0, n), + --n : val = val * 10 + (*in - '0'); + ++in; + } + val = val * pow(10, exponent); + return neg ? -val : val; +} //================================================================================================= // DIMACS Parser: -template +template static void readClause(B& in, vector& lits) { - int parsed_lit, var; + int parsed_lit, var; lits.clear(); - for (;;){ + for (;;) { parsed_lit = parseInt(in); if (parsed_lit == 0) break; - var = abs(parsed_lit)-1; - lits.push_back( (parsed_lit > 0) ? mkLit(var) : ~mkLit(var) ); + var = abs(parsed_lit) - 1; + lits.push_back((parsed_lit > 0) ? mkLit(var) : ~mkLit(var)); } } // JD Read clauses with weights -template -static void readClause(B& in, vector& lits, Weight &outW) { +template +static void readClause(B& in, vector& lits, Weight& outW) { bool first_time = true; - int parsed_lit, var; + int parsed_lit, var; lits.clear(); - for (;;){ + for (;;) { if (first_time) { first_time = false; - outW = (Weight) parseDouble(in); + outW = (Weight)parseDouble(in); continue; } parsed_lit = parseInt(in); if (parsed_lit == 0) break; - var = abs(parsed_lit)-1; - lits.push_back( (parsed_lit > 0) ? mkLit(var) : ~mkLit(var) ); + var = abs(parsed_lit) - 1; + lits.push_back((parsed_lit > 0) ? mkLit(var) : ~mkLit(var)); } } /********************************************************/ -template -static bool parse_DIMACS_main(B& in, Wcnf *F) { +template +static bool parse_DIMACS_main(B& in, Wcnf* F) { vector lits; int clauses = 0; - // JD Note that partial maxsat clauses either have weight 1 or partial_Top + // JD Note that partial maxsat clauses either have weight 1 or partial_Top bool clausesHaveWeights = false; - - for (;;){ + + for (;;) { skipWhitespace(in); - if (*in == EOF) break; - else if (*in == 'p'){ + if (*in == EOF) + break; + else if (*in == 'p') { // JD it could be unweighted or weighted CNF - if (eagerMatch(in, "p cnf")){ - int nvars = parseInt(in); - clauses = parseInt(in); - F->set_dimacs_params(nvars, clauses); - // JD - }else if (eagerMatch(in, "wcnf")) { - clausesHaveWeights = true; - int nvars = parseInt(in); - clauses = parseInt(in); - if (! eagerMatch(in, "\n")) { - Weight top = parseIntegerDouble(in); - F->set_dimacs_params(nvars, clauses, top); - } - else - //no top => no hard clauses => no upper bound on soft clause weight. - F->set_dimacs_params(nvars, clauses); - }else{ - //not 'p cnf' or 'p wcnf' - return false; + if (eagerMatch(in, "p cnf")) { + int nvars = parseInt(in); + clauses = parseInt(in); + F->set_dimacs_params(nvars, clauses); + // JD + } else if (eagerMatch(in, "wcnf")) { + clausesHaveWeights = true; + int nvars = parseInt(in); + clauses = parseInt(in); + if (!eagerMatch(in, "\n")) { + Weight top = parseIntegerDouble(in); + F->set_dimacs_params(nvars, clauses, top); + } else { // no top => no hard clauses => no upper bound on soft clause + // weight. + F->set_dimacs_params(nvars, clauses); + } + } else { + // not 'p cnf' or 'p wcnf' + return false; } - } else if (*in == 'c' || *in == 'p') + } else if (*in == 'c') { skipLine(in); - else{ - // JD parse the weights of clauses as well (default weight 1) - Weight w = 1; + } else { + // JD parse the weights of clauses as well (default weight 1) + // printf("A| parse clause\n"); + Weight w = 1; clausesHaveWeights ? readClause(in, lits, w) : readClause(in, lits); - F->addDimacsClause(lits, w); + F->addDimacsClause(lits, w); } } return true; @@ -176,10 +182,9 @@ static bool parse_DIMACS_main(B& in, Wcnf *F) { // Inserts problem into solver. // -static bool parse_DIMACS(gzFile input_stream, Wcnf *F) -{ - StreamBuffer in(input_stream); - return parse_DIMACS_main(in, F); +static bool parse_DIMACS(gzFile input_stream, Wcnf* F) { + Minisat::StreamBuffer in(input_stream); + return parse_DIMACS_main(in, F); } #endif diff --git a/maxhs/core/Graph.cc b/maxhs/core/Graph.cc new file mode 100644 index 0000000..d0ce0dc --- /dev/null +++ b/maxhs/core/Graph.cc @@ -0,0 +1,245 @@ +/***********[Graph.cc] + Copyright (c) 2019 Fahiem Bacchus + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ***********/ + +#include "maxhs/core/Graph.h" +#include +#include +#include + +using std::cout; +using std::vector; + +double sq(double x) { return x * x; } + +int Graph::newNode(Var v) { + ++n_vars; + int nid = nodes.size(); + nodes.push_back({nid, {v}}); + return nid; +} + +int Graph::varToNid(Var v) { + if (static_cast(v) >= var_to_nid.size()) + var_to_nid.resize(v + 1, NONE); + if (var_to_nid[v] == NONE) { + var_to_nid[v] = newNode(v); + } + return var_to_nid[v]; +} + +void Graph::addCluster(const vector& cluster) { + if(cluster.empty()) + return; + n_vars += cluster.size(); + int nid = nodes.size(); + for(auto v : cluster) { + if(static_cast(v) >= var_to_nid.size()) + var_to_nid.resize(v+1, NONE); + var_to_nid[v] = nid; + } + nodes.push_back({nid, cluster}); + nodes.back().n_edges = params.abstract_min_cores; +} + +void Graph::addEdge(Var v1, Var v2, Weight w) { + auto nid1 = varToNid(v1); + auto nid2 = varToNid(v2); + insertEdge(nid1, nid2, w); + insertEdge(nid2, nid1, w); + if (nid1 != nid2) new_cross_node_edges = true; +} + +void Graph::insertEdge(int nid1, int nid2, Weight w) { + graph_total_edge_wt_times2 += w; + auto& node = nodes[nid1]; + node.total_edge_wt += w; + ++node.n_edges; + if (nid1 == nid2) + node.internal_edge_wt += w; + else + insert_into_edge_list(node.edges, {nid2, w}); +} + +void Graph::insert_into_edge_list(vector& edges, const Edge& edge) { + auto it = std::lower_bound(edges.begin(), edges.end(), edge); + if (it == edges.end()) + edges.push_back(edge); + else if (it->nid == edge.nid) + it->wt += edge.wt; + else + edges.insert(it, edge); +} + +double Graph::extractCommunities(vector>& communities) { + vector nodesToProcess; + double modularity_increase{0}; + communities.clear(); + if (new_cross_node_edges) { + if (params.verbosity > 0) + cout << "c extractCommunities #nodes =" << nodes.size() << "\n"; + // 1. Assign each node to its own component + for (auto& n : nodes) { + n.cid = components.size(); + components.push_back({n.internal_edge_wt, n.total_edge_wt, {n.nid}}); + if (n.can_be_clustered()) nodesToProcess.push_back(n.nid); + // else + // cout << "Can't cluster " << n.nid << " not enough edges\n"; + n.on_ToProcess = true; + } + // Louvain greedy modularity improvement + while (!nodesToProcess.empty()) { + auto nid = nodesToProcess.back(); + nodesToProcess.pop_back(); + nodes[nid].on_ToProcess = false; + modularity_increase += louvain_find_components(nid, nodesToProcess); + } + if (params.verbosity > 1) + cout << "c total mod increase = " << modularity_increase << "\n"; + + if (modularity_increase <= 0) + new_cross_node_edges = false; + else + rebuild_nodes(); + } else { + if (params.verbosity > 1) + cout << "c Skipping Louvain as no new cross node edges added since last " + "call\n"; + } + for (auto& n : nodes) communities.push_back(n.cnf_vars); + + for (auto& n : nodes) + if (!n.can_be_clustered() && n.is_clustered()) + cout << "ERROR node: " << n << "\n" + << " is clustered but doesn't have enough edges\n"; + return modularity_increase; +} + +double Graph::louvain_find_components(int nid, vector& nodesToProcess) { + vector comp_wts(components.size(), 0.0); + // cout << "louvain processing " << nid << "\n"; + auto& nd = nodes[nid]; + for (auto& i : comp_wts) i = 0.0; + for (auto& e : nd.edges) { + auto& connected_node = nodes[e.nid]; + if (connected_node.can_be_clustered()) comp_wts[connected_node.cid] += e.wt; + // else + // cout << "connected node " << connected_node.nid + // << " cannot be clustered\n"; + } + comp_wts[nd.cid] += nd.internal_edge_wt / 2; + + double best_mod_delta{0.0}; + auto m{graph_total_edge_wt_times2}; + int best_cid{nd.cid}; + + for (int cid = 0; static_cast(cid) != components.size(); ++cid) { + if (comp_wts[cid] == 0.0 || cid == nd.cid) continue; + double mod_delta = + 2 * ((comp_wts[cid] - comp_wts[nd.cid]) / m + + nd.total_edge_wt * + (components[nd.cid].total_edge_wt - + components[cid].total_edge_wt - nd.total_edge_wt) / + sq(m)); + if (mod_delta > best_mod_delta) { + best_mod_delta = mod_delta; + best_cid = cid; + } + } + + /*if (best_mod_delta > 0) + cout << "moving " << nid << " from " << nd.cid << " to " << best_cid + << " mod_delta " << best_mod_delta << "\n"; + else + cout << "not moving " << nid << "\n";*/ + + if (best_mod_delta > 0) { + // move nd to new component + assert(best_cid != nd.cid); + auto old_cid = nd.cid; + move_node(nd, best_cid, comp_wts); + nids_to_reprocess(nodesToProcess, old_cid, best_cid, nd.nid); + } + return best_mod_delta; +} + +void Graph::move_node(Node& nd, int to_cid, const vector& comp_wts) { + auto& cur_comp{components[nd.cid]}; + cur_comp.total_edge_wt -= nd.total_edge_wt; + cur_comp.internal_edge_wt -= 2 * comp_wts[nd.cid]; + auto& nid_list{cur_comp.nids}; + auto it = std::lower_bound(nid_list.begin(), nid_list.end(), nd.nid); + assert(it != nid_list.end()); + nid_list.erase(it); + + auto& new_comp{components[to_cid]}; + new_comp.total_edge_wt += nd.total_edge_wt; + new_comp.internal_edge_wt += 2 * comp_wts[to_cid]; + auto& new_nid_list{new_comp.nids}; + it = std::lower_bound(new_nid_list.begin(), new_nid_list.end(), nd.nid); + new_nid_list.insert(it, nd.nid); + + nd.cid = to_cid; +} + +void Graph::nids_to_reprocess(vector& nids, int old_cid, int new_cid, + int nid_moved) { + for (auto nid : components[old_cid].nids) + if (!nodes[nid].on_ToProcess) { + nids.push_back(nid); + nodes[nid].on_ToProcess = true; + } + for (auto nid : components[new_cid].nids) + if (nid != nid_moved && !nodes[nid].on_ToProcess) { + nids.push_back(nid); + nodes[nid].on_ToProcess = true; + } +} + +void Graph::rebuild_nodes() { + vector remap(components.size(), NONE); + int nxt{0}; + for (auto& n : nodes) + if (remap[n.cid] == NONE) { + remap[n.cid] = nxt; + ++nxt; + } + vector new_nodes(nxt); + for (auto& nd : nodes) { + auto& new_nd = new_nodes[remap[nd.cid]]; + new_nd.nid = remap[nd.cid]; + for (auto v : nd.cnf_vars) { + new_nd.cnf_vars.push_back(v); + var_to_nid[v] = new_nd.nid; + } + for (auto e : nd.edges) { + e.nid = remap[nodes[e.nid].cid]; + new_nd.total_edge_wt += e.wt; + if (e.nid == new_nd.nid) + new_nd.internal_edge_wt += e.wt; + else + insert_into_edge_list(new_nd.edges, e); + } + new_nd.n_edges += nd.n_edges; + } + nodes = new_nodes; +} diff --git a/maxhs/core/Graph.h b/maxhs/core/Graph.h new file mode 100644 index 0000000..8bce24c --- /dev/null +++ b/maxhs/core/Graph.h @@ -0,0 +1,137 @@ +/***********[Graph.h] +Copyright (c) 2019 Fahiem Bacchus + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +***********/ + +#ifndef __GRAPH__ +#define __GRAPH__ + +#include +#include +#include +#include +#include + +#ifdef GLUCOSE +namespace Minisat = Glucose; +#endif + +#ifdef GLUCOSE +#include "glucose/core/SolverTypes.h" +#else +#include "minisat/core/SolverTypes.h" +#endif +#include "maxhs/core/MaxSolverTypes.h" + +#include "maxhs/utils/Params.h" +#include "maxhs/utils/io.h" + +using Minisat::Lit; +using Minisat::Var; +using std::pair; +using std::vector; + +class Graph { + public: + double extractCommunities(vector>& communities); + void addEdge(Lit l1, Lit l2, Weight w) { addEdge(var(l1), var(l2), w); } + void addEdge(Var v1, Var v2, Weight w); + int get_n_vars() { return n_vars; } + void addCluster(const vector& cluster); + bool in_graph(Var v) { + return (static_cast(v) < var_to_nid.size() && + var_to_nid[v] != NONE); + } + + private: + const int NONE{-1}; + struct Edge { + int nid; + Weight wt; + }; + friend bool operator<(const Edge& e1, const Edge& e2); + friend std::ostream& operator<<(std::ostream&, const Edge&); + + struct Node { + public: + int nid; + vector cnf_vars; + vector edges{}; + Weight internal_edge_wt{0.0}; + Weight total_edge_wt{0.0}; + int cid{-1}; + int n_edges{0}; + bool on_ToProcess{false}; + bool is_clustered() { return cnf_vars.size() > 1; } + bool is_empty() { return cnf_vars.size() == 0; } + bool can_be_clustered() { return n_edges >= params.abstract_min_cores; } + }; + friend std::ostream& operator<<(std::ostream&, const Node&); + + struct Component { + public: + Weight internal_edge_wt{0.0}; + Weight total_edge_wt{0.0}; + vector nids; + }; + friend std::ostream& operator<<(std::ostream&, const Component&); + + int n_vars{0}; + int newNode(Var v); + int varToNid(Var v); + void insert_into_edge_list(vector& edges, const Edge& edge); + void insertEdge(int nid1, int nid2, Weight wt); + double louvain_find_components(int nid, vector& nodesToProcess); + void move_node(Node& n, int to_cid, const vector& comp_wts); + void nids_to_reprocess(vector& nids, int old_cid, int new_cid, + int nid_moved); + void rebuild_nodes(); + + vector nodes; + vector components; + vector var_to_nid; + Weight graph_total_edge_wt_times2{0.0}; + bool new_cross_node_edges{false}; +}; + +inline bool operator<(const Graph::Edge& e1, const Graph::Edge& e2) { + return e1.nid < e2.nid; +} + +inline std::ostream& operator<<(std::ostream& os, const Graph::Edge& e) { + std::cout << "Edge(" << e.nid << ", " << e.wt << ")"; + return os; +} + +inline std::ostream& operator<<(std::ostream& os, const Graph::Node& n) { + std::cout << "Node(" << n.nid << ", iwt=" << n.internal_edge_wt + << ", twt=" << n.total_edge_wt << " cid=" << n.cid + << ") cnf_vars =" << n.cnf_vars << " edges =" << n.edges; + return os; +} + +inline std::ostream& operator<<(std::ostream& os, const Graph::Component& c) { + std::cout << "comp(iwt=" << c.internal_edge_wt << ", twt=" << c.total_edge_wt + << ") nids =" << c.nids; + return os; +}; + +#endif diff --git a/maxhs/core/Main.cc b/maxhs/core/Main.cc old mode 100755 new mode 100644 index 17bf706..f93efef --- a/maxhs/core/Main.cc +++ b/maxhs/core/Main.cc @@ -27,61 +27,69 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #ifdef GLUCOSE -#include "glucose/utils/System.h" #include "glucose/utils/Options.h" +#include "glucose/utils/System.h" #else -#include "minisat/utils/System.h" #include "minisat/utils/Options.h" +#include "minisat/utils/System.h" #endif - #include "maxhs/core/MaxSolver.h" #include "maxhs/core/Wcnf.h" #include "maxhs/utils/Params.h" + using std::cout; -static MaxHS::MaxSolver* thesolver {}; +#ifdef GLUCOSE +namespace Minisat = Glucose; +#endif +using namespace Minisat; + +static MaxHS::MaxSolver* thesolver{}; static void SIGINT_exit(int signum) { - if (thesolver) { - thesolver->printStatsAndExit(signum, 1); - } else { - fflush(stdout); - fflush(stderr); - // Note that '_exit()' rather than 'exit()' has to be used. - // The reason is that 'exit()' calls destructors and may cause deadlocks - // if a malloc/free function happens to be running (these functions are guarded by - // locks for multithreaded use). - _exit(0); - } + if (thesolver) { + thesolver->printStatsAndExit(signum, 1); + } else { + fflush(stdout); + fflush(stderr); + // Note that '_exit()' rather than 'exit()' has to be used. + // The reason is that 'exit()' calls destructors and may cause deadlocks + // if a malloc/free function happens to be running (these functions are + // guarded by + // locks for multithreaded use). + _exit(0); + } } -const int majorVer {3}; -const int minorVer {2}; -const int update {1}; +constexpr int majorVer{4}; +constexpr int minorVer{0}; +constexpr int update{0}; int main(int argc, char** argv) { try { - setUsageHelp("USAGE: %s [options] \n where input may be either in plain or gzipped DIMACS.\n"); - -#if defined(__linux__) - fpu_control_t oldcw, newcw; - _FPU_GETCW(oldcw); newcw = (oldcw & ~_FPU_EXTENDED) | _FPU_DOUBLE; _FPU_SETCW(newcw); -#endif - - IntOption cpu_lim("A: General MaxHS", "cpu-lim","Limit on CPU time allowed in seconds.\n", INT32_MAX, IntRange(0, INT32_MAX)); - IntOption mem_lim("A: General MaxHS", "mem-lim","Limit on memory usage in megabytes.\n", INT32_MAX, IntRange(0, INT32_MAX)); - BoolOption version("A: General MaxHS", "version", "Print version number and exit", false); - + setUsageHelp( + "USAGE: %s [options] \n where input may be either in " + "plain or gzipped DIMACS.\n"); + + IntOption cpu_lim("A: General MaxHS", "cpu-lim", + "Limit on CPU time allowed in seconds.\n", INT32_MAX, + IntRange(0, INT32_MAX)); + IntOption mem_lim("A: General MaxHS", "mem-lim", + "Limit on memory usage in megabytes.\n", INT32_MAX, + IntRange(0, INT32_MAX)); + BoolOption version("A: General MaxHS", "version", + "Print version number and exit", false); + parseOptions(argc, argv, true); params.readOptions(); - if(version) { + if (version) { cout << "MaxHS " << majorVer << "." << minorVer << "." << update << "\n"; - return(0); + return (0); } cout << "c MaxHS " << majorVer << "." << minorVer << "." << update << "\n"; - cout << "c Instance: " << argv[argc-1] << "\n"; - if(params.printOptions) { + cout << "c Instance: " << argv[argc - 1] << "\n"; + if (params.printOptions) { cout << "c Parameter Settings\n"; cout << "c ============================================\n"; printOptionSettings("c ", cout); @@ -94,62 +102,60 @@ int main(int argc, char** argv) { signal(SIGSEGV, SIGINT_exit); signal(SIGTERM, SIGINT_exit); signal(SIGABRT, SIGINT_exit); - - if (cpu_lim != INT32_MAX){ + + if (cpu_lim != INT32_MAX) { rlimit rl; getrlimit(RLIMIT_CPU, &rl); - if (rl.rlim_max == RLIM_INFINITY || (rlim_t)cpu_lim < rl.rlim_max){ - rl.rlim_cur = cpu_lim; - if (setrlimit(RLIMIT_CPU, &rl) == -1) - cout << "c WARNING! Could not set resource limit: CPU-time.\n"; - } } - - if (mem_lim != INT32_MAX){ - rlim_t new_mem_lim = (rlim_t)mem_lim * 1024*1024; + if (rl.rlim_max == RLIM_INFINITY || (rlim_t)cpu_lim < rl.rlim_max) { + rl.rlim_cur = cpu_lim; + if (setrlimit(RLIMIT_CPU, &rl) == -1) + cout << "c WARNING! Could not set resource limit: CPU-time.\n"; + } + } + + if (mem_lim != INT32_MAX) { + rlim_t new_mem_lim = (rlim_t)mem_lim * 1024 * 1024; rlimit rl; getrlimit(RLIMIT_AS, &rl); - if (rl.rlim_max == RLIM_INFINITY || new_mem_lim < rl.rlim_max){ - rl.rlim_cur = new_mem_lim; - if (setrlimit(RLIMIT_AS, &rl) == -1) - cout << "c WARNING! Could not set resource limit: Virtual memory.\n"; - } } - - if(argc < 2) { + if (rl.rlim_max == RLIM_INFINITY || new_mem_lim < rl.rlim_max) { + rl.rlim_cur = new_mem_lim; + if (setrlimit(RLIMIT_AS, &rl) == -1) + cout << "c WARNING! Could not set resource limit: Virtual memory.\n"; + } + } + + if (argc < 2) { cout << "c ERROR: no input file specfied:\n" - "USAGE: %s [options] \n where input may be either in plain or gzipped DIMACS.\n"; + "USAGE: %s [options] \n where input may be either " + "in plain or gzipped DIMACS.\n"; exit(0); } - Wcnf theFormula {}; - if (!theFormula.inputDimacs(argv[1])) - return 1; + Wcnf theFormula{}; + if (!theFormula.inputDimacs(argv[1])) return 1; - /*cout << "Different wts " << theFormula.nDiffWts() << " = " << theFormula.getDiffWts() << "\n"; - cout << "Mutexes "; - for(size_t i=0; i < theFormula.getMxs().size(); i++) { - cout << "#" << i << ". "; + /*cout << "Different wts " << theFormula.nDiffWts() << " = " << + theFormula.getDiffWts() << "\n"; cout << "Mutexes "; for(size_t i=0; i < + theFormula.getMxs().size(); i++) { cout << "#" << i << ". "; if(theFormula.isCoreMx(i)) - cout << "Core Mx "; + cout << "Core Mx "; else - cout << "Non-Core Mx "; + cout << "Non-Core Mx "; cout << theFormula.getMxs()[i] << "\n"; } theFormula.printFormula();*/ - MaxHS::MaxSolver S(&theFormula); thesolver = &S; S.solve(); S.printStatsAndExit(-1, 0); - } - catch(std::bad_alloc) { + } catch (const std::bad_alloc&) { cout << "c Memory Exceeded\n"; thesolver->printStatsAndExit(100, 1); - } - catch (...) { + } catch (...) { cout << "c Unknown exception probably memory.\n"; thesolver->printStatsAndExit(200, 1); - } + } fflush(stdout); fflush(stderr); return 0; diff --git a/maxhs/core/MaxSolver.cc b/maxhs/core/MaxSolver.cc old mode 100755 new mode 100644 index 9b40468..67905e0 --- a/maxhs/core/MaxSolver.cc +++ b/maxhs/core/MaxSolver.cc @@ -22,19 +22,26 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ***********/ -#include +#include #include +#include +#include +#include #include #include -#include -#include -#include #include +#include "maxhs/core/Assumptions.h" #include "maxhs/core/MaxSolver.h" -#include "maxhs/utils/io.h" -#include "maxhs/ifaces/miniSatSolver.h" +#include "maxhs/core/TotalizerManager.h" +#include "maxhs/core/Wcnf.h" +#include "maxhs/ifaces/Cplex.h" #include "maxhs/ifaces/GreedySolver.h" +#include "maxhs/ifaces/SatSolver.h" +#include "maxhs/ifaces/miniSatSolver.h" +#include "maxhs/ifaces/muser.h" +#include "maxhs/utils/Params.h" +#include "maxhs/utils/io.h" #ifdef GLUCOSE #include "glucose/utils/System.h" @@ -45,203 +52,184 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using namespace MaxHS; using namespace MaxHS_Iface; -using std::endl; using std::cout; using std::pair; -MaxSolver::MaxSolver(Wcnf *f) : //many of are already default initialized...but for clarity - theWcnf {f}, - bvars {f}, - satsolver {new MaxHS_Iface::miniSolver{}}, - greedysolver {nullptr}, - // greedySatSolver {nullptr}, - muser {new MaxHS_Iface::Muser{theWcnf, bvars}}, - sat_wt {0.0}, - forced_wt {0.0}, - lower_bnd {0.0}, - absGap {params.tolerance}, - UBmodel (theWcnf->nVars(), l_Undef), - UBmodelSofts (theWcnf->nSofts(), l_False), - tmpModelSofts (theWcnf->nSofts(), l_False), - haveUBModel {false}, - have_new_UB_model {false}, - eqCvar {static_cast(bvars.maxvar()+1)}, - eqCvarPos (lit_Undef), - nextNewVar {eqCvar+1}, - bLitOccur (bvars.n_blits(), 0), - cplexClauses {}, - sftSatisfied {}, - cplexNU {}, - greedyNU {}, - muserNU {}, - satNU {}, - satBvarNU {}, - forcedWtNU {}, - amountConflictMin {0}, - globalStartTime {0}, - printStatsExecuted {false}, - solved {false}, - unsat {false}, - n_softs_forced_hard {0}, - n_softs_forced_relaxed {0}, - n_softs_forced_hard_not_in_cplex {0}, - nFailedLits {0}, - nForcedByBounds {0}, - allClausesSeeded {false}, - m_sum_reduced_frac {0}, - mtime {0.0}, - mcalls {0}, - doMin {true}, - blit_lt {&bLitOccur, bvars, false}, - blit_gt {&bLitOccur, bvars, true} -{ +MaxSolver::MaxSolver(Wcnf* f) + : // many of are already default initialized...but for clarity + theWcnf{f}, + bvars{f}, + satsolver{new MaxHS_Iface::miniSolver{}}, + muser{new MaxHS_Iface::Muser{theWcnf, bvars}}, + // greedySatSolver {nullptr}, + absGap{params.tolerance}, + UBmodel(bvars.n_vars(), l_Undef), + UBmodelSofts(theWcnf->nSofts(), l_False), + tmpModelSofts(theWcnf->nSofts(), l_False), + eqCvar{static_cast(bvars.maxvar() + 1)}, + eqCvarPos(lit_Undef), + nextNewVar{eqCvar + 1}, + bLitOccur(bvars.n_blits(), 0), + blit_lt{&bLitOccur, bvars, false}, + blit_gt{&bLitOccur, bvars, true} { params.instance_file_name = theWcnf->fileName(); - if(theWcnf->integerWts()) - absGap = 0.75; - cplex = new Cplex {bvars, UBmodelSofts, UBmodel, theWcnf->integerWts()}; - if(!cplex->is_valid()) + + if (theWcnf->integerWts()) absGap = 0.75; + totalizers = new TotalizerManager(bvars, this, muser, satsolver); + cplex = new Cplex{bvars, totalizers, UBmodelSofts, UBmodel, + theWcnf->integerWts()}; + if (!cplex->is_valid()) cout << "c ERROR. Problem initializing CPLEX solver\n"; /*if(theWcnf->getMxs().size() > 0) greedySatSolver = new MaxHS_Iface::GreedySatSolver{bvars}; else */ - greedysolver = new MaxHS_Iface::GreedySolver{bvars}; + greedysolver = new MaxHS_Iface::GreedySolver{bvars, totalizers}; // if(theWcnf->integerWts()) - cout << std::setprecision(8); + cout << std::setprecision(10); } MaxSolver::~MaxSolver() { - if (satsolver) - delete satsolver; - if (greedysolver) - delete greedysolver; - //if (greedySatSolver) + if (satsolver) delete satsolver; + if (greedysolver) delete greedysolver; + // if (greedySatSolver) // delete greedySatSolver; - if (cplex) - delete cplex; + if (totalizers) delete totalizers; + if (cplex) delete cplex; } +Weight MaxSolver::UB() { return theWcnf->totalClsWt() - sat_wt; } + bool MaxSolver::doPreprocessing() { - //test if we should do preprocessing. - //We have to freeze all unit softs. So if this spans all variables we won't - //be able to eliminate any variables in preprocessing. - if(!params.preprocess) - return false; + // test if we should do preprocessing. + // We have to freeze all unit softs. So if this spans all variables we won't + // be able to eliminate any variables in preprocessing. + if (!params.preprocess) return false; - const int not_seen {0}, seen {1}; - int toBeFrozen {0}, totalVars {0}; + const int not_seen{0}, seen{1}; + int toBeFrozen{0}, totalVars{0}; vector varStatus(theWcnf->nVars(), not_seen); - //Run through the non-unit soft and hard clauses, marking all - //variables as not-frozen, and count all newly seen variables - for(auto hc: theWcnf->hards()) - for(auto lt: hc) - if(varStatus[var(lt)] == not_seen) { + // Run through the non-unit soft and hard clauses, marking all + // variables as not-frozen, and count all newly seen variables + for (auto hc : theWcnf->hards()) + for (auto lt : hc) + if (varStatus[var(lt)] == not_seen) { ++totalVars; varStatus[var(lt)] = seen; } - for(auto sc: theWcnf->softs()) - if(sc.size() > 1) - for(auto lt: sc) - if(varStatus[var(lt)] == not_seen) { + for (auto sc : theWcnf->softs()) + if (sc.size() > 1) + for (auto lt : sc) + if (varStatus[var(lt)] == not_seen) { ++totalVars; varStatus[var(lt)] = seen; } - //Now go through the unit softs. Count those that appear in the - //non-unit softs and hards as to be frozen - for(auto sc: theWcnf->softs()) - if(sc.size() == 1) { + // Now go through the unit softs. Count those that appear in the + // non-unit softs and hards as to be frozen + for (auto sc : theWcnf->softs()) + if (sc.size() == 1) { Var v = var(sc[0]); - if(varStatus[v] == seen) { + if (varStatus[v] == seen) { ++toBeFrozen; - varStatus[v] = not_seen; //only units once + varStatus[v] = not_seen; // only units once } } - if(params.verbosity > 0) - cout << "c Total used vars = " << totalVars << " vars to be frozen = " << toBeFrozen << "\n"; + if (params.verbosity > 0) + cout << "c Total used vars = " << totalVars + << " vars to be frozen = " << toBeFrozen << "\n"; return toBeFrozen < totalVars; } +bool MaxSolver::check_termination(const std::string& location) { + if (fabs(UB() - LB()) <= absGap) { + optFound("c Solved by " + location + "."); + return true; + } + if (LB() > UB() + absGap) { + cout << "c ERROR after " << location << ". Lower bound " << LB() + << " exceeding upper bound " << UB() << "\n"; + return true; + } + return false; +} + void MaxSolver::solve() { globalStartTime = cpuTime(); - - if(theWcnf->isUnsat()) { + if (theWcnf->isUnsat()) { cout << "c Unsat Found by theWcnf\n"; unsatFound(); return; } - - if(!doPreprocessing()) - satsolver->eliminate(true); - + if (!doPreprocessing()) satsolver->eliminate(true); addHards(satsolver); addSofts(satsolver); - //add eq clauses if using fbeq. - if(params.fbeq) - addSoftEqs(satsolver, false); //permanent eq clauses - //Otherwise we will add forced negated b-vars to the sat solver - //after sat solving episodes---weaker than adding the eq clauses - if(params.fb) - initSftSatisfied(); - - if(params.preprocess) { + + // totalizer.current_var = bvars.maxvar(); + // cout<< "A maxvar= " << bvars.maxvar() << " maxbvar= " << bvars.maxBvar() << + // "\n"; cout<< "SAT solver variables: \n"; + + // add eq clauses if using fbeq. + if (params.fbeq) addSoftEqs(satsolver, false); // permanent eq clauses + + for (auto& mx : theWcnf->get_SCMxs()) + if (mx.encoding_lit() != lit_Undef) { + satsolver->freezeVar(var(mx.encoding_lit())); + } + + // Otherwise we will add forced negated b-vars to the sat solver + // after sat solving episodes---weaker than adding the eq clauses + if (params.fb) initSftSatisfied(); + + if (params.preprocess) { auto start = cpuTime(); satsolver->eliminate(true); - if(params.verbosity > 0) - cout << "c MiniSat Preprocess eliminated " << satsolver->nEliminated() << " variables. took " << cpuTime()-start << " sec.\n"; + if (params.verbosity > 0) + cout << "c MiniSat Preprocess eliminated " << satsolver->nEliminated() + << " variables. took " << cpuTime() - start << " sec.\n"; } - if(params.verbosity > 0) - cout << "c Before solving sat solver has " << satsolver->getNClauses(0) << " clauses and " - << satsolver->getNClauses(1) << " learnts\n"; + if (params.verbosity > 0) + cout << "c Before solving sat solver has " << satsolver->getNClauses(0) + << " clauses and " << satsolver->getNClauses(1) << " learnts\n"; - //1. Compute an initial lower and upper bound by solving the theory + // 1. Compute an initial lower and upper bound by solving the theory // Note only unsat if the hards are unsat, otherwise will set soft // clauses in an arbitary way. auto hards_are_sat = satsolver->solve(); - if(hards_are_sat == l_False) { + if (hards_are_sat == l_False) { unsatFound(); return; } - if(params.verbosity > 0) + if (params.verbosity > 0) cout << "c Init Bnds: SAT Time " << satsolver->solveTime() << "\n"; - if(params.improve_model) - improveModel(); - updateLB(getForcedWt()); //get forced weight. + if (params.improve_model) improveModel(); + updateLB(getForcedWt()); // get forced weight. updateUB(); - if(fabs(UB()-LB()) <= absGap) { - optFound("c Solved by Init Bnds."); - return; - } + if (check_termination("Init Bounds")) return; if (params.verbosity > 0) { cout << "c Init Bnds: LB = " << LB() << " UB = " << UB() << "\n"; cout << "c Init Bnds: Forced " << satsolver->nAssigns() << " literals.\n"; - cout << "c Init Bnds: after sat solver solver has " << satsolver->getNClauses(0) << " clauses and " + cout << "c Init Bnds: after sat solver solver has " + << satsolver->getNClauses(0) << " clauses and " << satsolver->getNClauses(1) << " learnts\n"; } - - if(fabs(UB()-LB()) <= absGap) { - optFound("c Solved by Initial Bnds."); - return; - } - - //DEBUG - //cout << "Init Bnds: Forced " << satsolver->nAssigns() << " literals.\n" + // DEBUG + // cout << "Init Bnds: Forced " << satsolver->nAssigns() << " literals.\n" // << satsolver->getForced(0) << "\n"; // - - if(params.fb) //add forced negated b-vars to sat solver + if (params.fb) // add forced negated b-vars to sat solver satSolverAddBvarsFromSofts(); - if(params.prepro_output) { - for(int isLearnt=0; isLearnt < 2; isLearnt++) { - for(int i =0; i < satsolver->getNClauses(isLearnt); i++) { + if (params.prepro_output) { + for (int isLearnt = 0; isLearnt < 2; isLearnt++) { + for (int i = 0; i < satsolver->getNClauses(isLearnt); i++) { auto clause = satsolver->getIthClause(i, isLearnt); cout << (isLearnt ? "l#" : "c#") << i << " [ "; for (auto l : clause) { cout << l; - if(bvars.isBvar(l)) + if (bvars.isBvar(l)) cout << " (B " << bvars.wt(l) << "), "; else cout << ", "; @@ -251,384 +239,763 @@ void MaxSolver::solve() { } } - //2. Seed CPLEX - if (!satsolver->simplify()) //remove satisfied clauses before seeding + // 2. Seed CPLEX + if (!satsolver->simplify()) // remove satisfied clauses before seeding printErrorAndExit("c ERROR: UNSAT detected when simplify clauses"); - if (params.seed_type) - seed_equivalence(); + // TEST + // totalizers->test(); - //4. Disjoint Phase - if(allClausesSeeded) - //when all clauses feed to cplex we will do a special greedy phase to - //get a better upper bound model then start up cplex. We do - //we want the greedy phase to start with only the disjoint cores. - //and we do not want the found greedy cores to be given to cplex. - greedyClauses.clear(); + if (params.seed_type) seed_equivalence(); - //3. Disjoint Phase - if (params.dsjnt_phase) - disjointPhase(); - if(fabs(UB()-LB()) <= absGap) { - optFound("c Solved by Disjoint phase."); - return; + // 4. Disjoint Phase + if (allClausesSeeded) { + // when all clauses feed to cplex we will do a special greedy phase to + // get a better upper bound model then start up cplex. We do + // we want the greedy phase to start with only the disjoint cores. + // and we do not want the found greedy cores to be given to cplex. + greedyClauses.clear(); + if (params.verbosity > 0) cout << "c All clauses seeded into CPLEX\n"; } - //4. mutexes processing. + + // 3. Disjoint Phase + if (params.dsjnt_phase) disjointPhase(); + if (check_termination("disjoint phase")) return; + + // 4. mutexes processing. processMutexes(); - //5. Solve. - seqOfSAT_maxsat(); + // 5. Solve. + if (allClausesSeeded) + allClausesSeeded_maxsat(); + else + seqOfSAT_maxsat(); } void MaxSolver::disjointPhase() { - Assumps assumps(satsolver, bvars); - int num {0}; - int len {0}; - Weight lb_weight {0.0}; - - if (params.verbosity > 0) - cout << "c Disjoint Phase\n"; - + Assumps assumps(satsolver, bvars, totalizers); + int num{0}; + int len{0}; + Weight lb_weight{0.0}; + if (params.verbosity > 0) cout << "c Disjoint Phase\n"; double beginTime = cpuTime(); - //at this stage the greedy solver has input and learnt cores of the - //theory via seeding. We don't want disjoint to rediscover these, - //initialize the assumed true softs with a greedy_solution. - //As added effect, if the greedy solver respect the mutex constraints - //then disjointPhase will always use assumptions repecting the mutexes. - auto greedy_solution = greedySoln(); - assumps.init(greedy_solution, CoreType::cores); - if(params.sort_assumps == 1) + /*// at this stage the greedy solver has input and learnt cores of the + // theory via seeding. We don't want disjoint to rediscover these, + // initialize the assumed true softs with a greedy_solution. + // As added effect, if the greedy solver respect the mutex constraints + // then disjointPhase will always use assumptions repecting the mutexes. + + // Must use greedy solver and must return a solution. + auto greedy_solution = greedySoln(true, true); + assumps.init(greedy_solution, CoreType::cores);*/ + assumps.all_softs_true(); + + if (params.sort_assumps == 1) assumps.sort(blit_gt); - else if(params.sort_assumps == 2) + else if (params.sort_assumps == 2) assumps.sort(blit_lt); vector conflict; lbool val; - while((val = satsolve_min(assumps, conflict, params.dsjnt_cpu_per_core, params.dsjnt_mus_cpu_lim)) - == l_False) { - if(params.verbosity > 1) { + while ( + (val = satsolve_min(assumps, conflict, + num == 0 ? params.noLimit : params.dsjnt_cpu_per_core, + params.dsjnt_mus_cpu_lim)) == l_False) { + // lbool val {l_False}; + // while(val == l_False) { + // cout << "Disjoint Phase satsolve. sat_cpu = " << + // params.dsjnt_cpu_per_core << " mus_cpu = " + // << params.dsjnt_mus_cpu_lim << "\n"; + // val = satsolve_min(assumps, conflict, params.dsjnt_cpu_per_core, + // params.dsjnt_mus_cpu_lim); + + if (params.verbosity > 1) { cout << "c Disjoint Conflict size = " << conflict.size() << "\n"; - if(params.verbosity > 2) - cout << "c conflict = " << conflict << "\n"; + if (params.verbosity > 2) cout << "c conflict = " << conflict << "\n"; } store_cplex_greedy_cls(conflict); - //Must remove assumptions now that we are adding forced negated bvars + // Must remove assumptions now that we are adding forced negated bvars assumps.update(conflict, true); num++; len += conflict.size(); - Weight min_wt {0}; - for(auto b : conflict) - if(min_wt == 0 || bvars.wt(b) < min_wt) - min_wt = bvars.wt(b); + Weight min_wt{std::numeric_limits::max()}; + for (auto b : conflict) + if (bvars.wt(b) < min_wt) min_wt = bvars.wt(b); lb_weight += min_wt; } - - //update Bounds (only UB could have changed) - if(val == l_True) { - if(params.improve_model) - improveModel(); + // update Bounds (only UB could have changed) + if (val == l_True) { + if (params.improve_model) improveModel(); updateUB(); } updateLB(lb_weight); if (params.verbosity > 0) { - cout << "c Dsjnt: #Cores " << num << " with total weight " << lb_weight << "\n"; - if(num > 0) { - cout << "c Dsjnt: Avg Core Size " << len/((double) num) << "\n"; - cout << "c Dsjnt: Time " << cpuTime() - beginTime << "\n"; + cout << "c Dsjnt: #Cores " << num << " with total weight " << lb_weight + << " LB " << LB() << " UB " << UB() << "\n"; + if (num > 0) { + cout << "c Dsjnt: Avg Core Size " << len / ((double)num) << "\n"; + cout << "c Dsjnt: Time " << cpuTime() - beginTime << "\n"; } } } -void MaxSolver::seqOfSAT_maxsat() { +vector MaxSolver::extract_costBoundinglits(const vector& soln) const { + // assuming unvalued non-cores bvars and totalizer outputs bounds + // cost of sat modele + vector cblits; + for (auto l : soln) + if (satsolver->curVal(l) == l_Undef && + (bvars.isNonCore(l) || totalizers->isToutput(l))) + cblits.push_back(l); + return cblits; +} + +vector MaxSolver::extract_NonCoreBvars(const vector& soln) const { + vector ncBlits; + for (auto l : soln) + if (satsolver->curVal(l) == l_Undef && bvars.isNonCore(l)) + ncBlits.push_back(l); + return ncBlits; +} + +vector MaxSolver::extract_UnValued(const vector& soln) const { + vector ncBvars; + for (auto l : soln) + if (satsolver->curVal(l) == l_Undef) ncBvars.push_back(l); + return ncBvars; +} + +bool MaxSolver::any_false(const vector& soln) const { + for (auto l : soln) + if (satsolver->curVal(l) == l_False) return true; + return false; +} + +bool MaxSolver::all_bvars(const vector& soln) const { + for (auto l : soln) + if (!bvars.isBvar(l)) return false; + return true; +} + +void MaxSolver::allClausesSeeded_maxsat() { + auto n_cplex = cplexAddNewForcedBvars(); + n_cplex += cplexAddNewClauses(); + + if (params.verbosity > 0 && n_cplex > 0) + cout << "c Initial solve: add to CPLEX " << n_cplex << " constraints\n."; + vector cplexsoln; + Weight cplexLB = cplex->solveBudget(cplexsoln, 0, 100.0); + + if (cplexLB < 0) { + params.cpu_per_exhaust = params.cpu_per_exhaust_all_clauses; + totalizers->compute_abstraction(); + if (haveUBModel && check_termination("building totalizers")) return; + + params.abstract_greedy_cores = 0; + if (get_greedy_conflicts( + params.seed_all_cpu, + params.max_cores_before_cplex + cplexClauses.size())) + return; + + get_ub_conflicts(); + if (check_termination("conflicts from UB model")) return; + + // compute abstraction requesting all vars be grouped into one + // cluster + totalizers->compute_abstraction(true); + if (haveUBModel && check_termination("building totalizers")) return; + + // Add new units but not new touts to cplex + n_cplex = cplexAddNewForcedBvars(); + n_cplex += cplexAddNewClauses(); + + if (params.verbosity > 0 && n_cplex > 0) + cout << "c Updated CPLEX model: add to CPLEX " << n_cplex + << " constraints\n"; + cplexsoln.clear(); + cplexLB = cplex->solve(cplexsoln, 0); + if (cplexLB < 0) { + printErrorAndExit("c ERROR: Cplex::solve() failed"); + } + } + + if (any_false(cplexsoln)) + printErrorAndExit("c ERROR cplex had full model returned false literal"); + + auto cplexSolnWt = getWtOfBvars(cplexsoln); // solution cost is UB + reportCplex(cplexLB, cplexSolnWt); + bool cplex_found_optimum = (fabs(cplexSolnWt - cplexLB) <= absGap); + if (params.verbosity > 0) { + cout << "c CPLEX solution cost = " << cplexSolnWt + << " lower bound = " << cplexLB << "\n"; + if (cplex_found_optimum) + cout << "c CPLEX found optimal solution to its current model\n"; + } + if (cplexLB > LB()) updateLB(cplexLB); + if (params.verbosity > 0) + cout << "c after CPLEX bnds: LB = " << LB() << " UB = " << UB() + << " gap = " << UB() - LB() << "\n"; + + if (haveUBModel && check_termination("LB >= UB")) return; + + // now check the cplex soln (should be valid since cplex has full + // model). + + Assumps assumps(satsolver, bvars, totalizers); + assumps.init(extract_NonCoreBvars(cplexsoln)); + vector conflict; - Assumps assumps(satsolver, bvars); - int iteration {0}; - Weight prev_cplex_solution_obj {0}; - Weight cplex_UB {0}; - bool cplex_found_optimum {false}; - auto n_conf = cplexClauses.size(); - bool first_time {true}; + auto val = satsolver->solve(assumps.vec(), conflict); + if (val == l_False) { + cout << "c ERROR cplex had full model but returned unsat " + "solution\n"; + } else if (val == l_Undef) { + cout << "c ERROR cplex had full model but sat solver could not " + " verify its solution\n"; + } else { + updateUB(); + if (!check_termination("CPLEX model")) + cout << "c ERROR cplex had full model but returned non-optimal" + " solution\n"; + } + return; +} +void MaxSolver::seqOfSAT_maxsat() { + vector cplexsoln; + vector conflict; + int iteration{-1}; + Weight prev_cplex_solution_obj{}; + Weight prev_lp_bnd{LB()}; + Weight cplex_UB{LB()}; + bool cplex_found_optimum{false}; while (1) { - //1. Update cplex model + // Update cplex model if (params.verbosity > 0) - cout << "c **********Iter: " << iteration++ << " Elapsed Time = " - << cpuTime() - globalStartTime << "\n"; - - int new_cplex_constraints {0}; + cout << "c **********Iter: " << ++iteration + << " Elapsed Time = " << cpuTime() - globalStartTime << "\n"; + int new_cplex_constraints{0}; new_cplex_constraints += cplexAddNewForcedBvars(); new_cplex_constraints += cplexAddNewClauses(); - //1.5 - if(!allClausesSeeded && params.lp_harden) - if(tryHarden()) { - optFound("c UB proved optimal by LP-Bound"); - return; - } + if (params.lp_harden && tryHarden() && check_termination("LP-Bound == UB")) + return; - if(first_time && allClausesSeeded) { - //note any improved ub model will be passed to cplex as a mip start - auto val = get_greedy_conflicts(); - if(val) { - optFound("c solved by initial greedy solve"); - return; - } - if(get_ub_conflicts()) { - optFound("c Solved by conflicts from UB model\n"); - return; - } - //don't want these clauses feed to cplex. - //if cplex already has full model. - //cplexClauses.clear(); - } - first_time=false; - - //2. Solve CPLEX model and update Bounds - if(allClausesSeeded) - //If CPLEX has full model, just let it run until it finds an optimal solution - cplex_UB = 0; - else if(cplex_found_optimum || prev_cplex_solution_obj == 0 || new_cplex_constraints) - //If the previous solve found an optimal solution (to its current model or - //there was no prior solve, or the cplex model has been improved (which must - //increase its cost) then stop cplex when it finds a better model - //than the current SAT UB model. + // Solve CPLEX model and update Bounds + // If the previous solve found an optimal solution (to its + // current model or there was no prior solve, or the cplex model + // has been improved (which must increase its cost) then stop + // cplex when it finds a better model than the current SAT UB + // model. Since we are aborting on non-optimal solutions we + // could have a sequence of non-optimal CPLEX solutions that + // satisfy the SAT model. In this case we want to ensure that + // when CPLEX is called again it returns a better model than the + // last time...to prevent cycling + if (cplex_found_optimum || prev_cplex_solution_obj == 0 || + new_cplex_constraints) cplex_UB = UB(); else - //Since we are aborting on non-optimal solutions we could have a sequence - //of non-optimal CPLEX solutions that satisfy the SAT model. - //In this case we want to ensure that when CPLEX is called again it returns - //a better model than the last time...to prevent cycling. cplex_UB = fmin(prev_cplex_solution_obj, UB()); - Weight cplexLB = cplex->solve(cplexsoln, cplex_UB); //cplexLB is LB + Weight cplexLB = cplex->solve(cplexsoln, cplex_UB); if (cplexLB < 0) { - printErrorAndExit("c WARNING: Cplex::solve() failed"); + printErrorAndExit("c ERROR: Cplex::solve() failed"); } - auto cplexSolnWt = getWtOfBvars(cplexsoln); //solution cost is UB - reportCplex(cplexLB, cplexSolnWt); + // solution cost is UB + auto cplexSolnWt = getWtOfBvars(cplexsoln); + prev_cplex_solution_obj = cplexSolnWt; + reportCplex(cplexLB, cplexSolnWt); cplex_found_optimum = (fabs(cplexSolnWt - cplexLB) <= absGap); - if(params.verbosity > 0) { - cout << "c CPLEX solution cost = " << cplexSolnWt << " lower bound = " << cplexLB << "\n"; - if(cplex_found_optimum) + + if (params.verbosity > 0) { + if (cplex_found_optimum) cout << "c CPLEX found optimal solution to its current model\n"; } - prev_cplex_solution_obj = cplexSolnWt; + if (cplexLB > LB()) updateLB(cplexLB); + if (params.verbosity > 0) + cout << "c after CPLEX bnds: LB = " << LB() << " UB = " << UB() + << " gap = " << UB() - LB() << "\n"; + if (check_termination("LB >= UB")) return; + + // Check to see if we need to abstract the b-vars + + if (params.abstract) { + vector lp_solvals; + vector lp_redvals; + vector cplex_vars; + Weight cur_lp_bnd = + cplex->solve_lp_relaxation(lp_solvals, lp_redvals, cplex_vars); + if (tryHarden_with_lp_soln(cur_lp_bnd, lp_redvals, cplex_vars) && + check_termination("LP bound == UB in tryharden")) + return; + + auto delta = cur_lp_bnd - prev_lp_bnd; + prev_lp_bnd = cur_lp_bnd; + if (delta < params.abstract_gap) { + if (params.verbosity > 0) + cout << "c LP bound delta = " << delta << " < " << params.abstract_gap + << " abstracting\n"; + totalizers->compute_abstraction(); + } + } + + // start iteration. + auto cplex_iter_max_time = cpuTime() + params.max_cpu_before_cplex; + int max_cores_remaining = params.max_cores_before_cplex; - if(cplexLB > LB()) - updateLB(cplexLB); + // generate conflicts from cplex solution + if (params.verbosity > 0) + cout << "c finding conflicts from cplex solution\n"; + if (get_cplex_conflicts(cplexsoln, 'c', cplex_iter_max_time, + max_cores_remaining)) + return; - if(params.verbosity >0) - cout << "c after CPLEX bnds: LB = " << LB() << " UB = " << UB() << "\n"; - if(haveUBModel) - if(fabs(UB()-LB()) <= absGap) { - optFound("c Solved by LB >= UB"); + // 4. Generate conflicts from greedy solutions. + max_cores_remaining = params.max_cores_before_cplex - cplexClauses.size(); + if (max_cores_remaining > 0 && cpuTime() < cplex_iter_max_time) + if (get_greedy_conflicts(cplex_iter_max_time, max_cores_remaining)) return; - } - //3. Generate conflicts from solution or from abstracted solution or both - // Abstracted solution: The - // CPLEX solution specifies a setting (satisfied/falsified) for - // each soft clause via a setting for the soft clause's - // corresponding b-var. An abstract solution does not specify - // the setting of all soft clauses. The property we need - // is that the abstract setting places the same constraint - // on the weight of any solution as the non-abstract solution. - // If our solution specifies that k-1 soft clauses in a core mx - // are satisfied, then we can remove that condition, as we know that - // it must always be the case that at least k-1 of these softs must be satisfied. - // If our solution specifies that k-1 soft clauses in a non-core mx must be - // be false, then we can remove that condition, as we know that it must - // always be the case that at least k-1 of these softs must be falsified. - - bool found_abstracted_solution {false}; - if(params.mx_hs_use_abstraction && theWcnf->n_mxes()) { - vector mxes_in_soln (theWcnf->n_mxes(), 0); - for(auto b : cplexsoln) - if((bvars.inCoreMx(b) && bvars.isNonCore(b)) - || (bvars.inNonCoreMx(b) && bvars.isCore(b))) - mxes_in_soln[bvars.getMxNum(b)]++; - - vector mxes_to_remove (theWcnf->n_mxes(), false); - for(size_t i=0; i < mxes_in_soln.size(); i++) - if(mxes_in_soln[i] == theWcnf->ith_mx_size(i)-1) { - mxes_to_remove[i] = true; - found_abstracted_solution = true; + // 5. if needed find some more conflicts by extracting other solutions + // from cplex + max_cores_remaining = params.max_cores_before_cplex - cplexClauses.size(); + if (max_cores_remaining > 0 && cpuTime() < cplex_iter_max_time) { + if (params.trypop > 1 || + (params.trypop > 0 && cplexClauses.size() < 35)) { + if (params.verbosity > 0) + cout << "c finding conflicts from cplex populated solutions\n"; + + int n_conf = cplexClauses.size(); + double gap = fabs(UB() - cplexSolnWt); + // want better solutions than UB + gap -= absGap; + if (gap <= absGap) gap = 0.0; + + int nsolns = cplex->populate(params.cplex_pop_cpu_lim, gap); + if (params.verbosity > 1) + cout << "c populate found " << nsolns << " solutions\n"; + for (int i = 0; i < nsolns; i++) { + cplex->getPopulatedSolution(i, cplexsoln); + if (cplexsoln.size() == 0) { + cout << "c WARNING Cplex populate could not find solution " << i + << "\n"; + continue; + } + if (params.verbosity > 1) + cout << "c getting conflicts from solution #" << i << "\n"; + if (get_cplex_conflicts(cplexsoln, 'p', cplex_iter_max_time, + max_cores_remaining)) + return; + + max_cores_remaining = + params.max_cores_before_cplex - cplexClauses.size(); + if (max_cores_remaining < 0 || cpuTime() > cplex_iter_max_time) break; } + if (params.verbosity > 0) + cout << "c populate found " << cplexClauses.size() - n_conf + << " conflicts\n"; + } + } - if(found_abstracted_solution) { - auto mx_remove = [&mxes_to_remove, this] (Lit b) { - return - ((bvars.inCoreMx(b) && bvars.isNonCore(b) && mxes_to_remove[bvars.getMxNum(b)]) - ||(bvars.inNonCoreMx(b) && bvars.isCore(b) && mxes_to_remove[bvars.getMxNum(b)])); - }; - - auto abscplexsoln = cplexsoln; //keep original - auto p = std::remove_if(abscplexsoln.begin(), abscplexsoln.end(), mx_remove); - abscplexsoln.erase(p, abscplexsoln.end()); - if(params.verbosity) - cout << "c CPLEX abstract solution reduced by " << cplexsoln.size() - abscplexsoln.size() - << " lits\n"; - n_conf = cplexClauses.size(); - if(get_conflicts(abscplexsoln, false)) { - optFound("c Solved by abstract CPLEX model"); - return; + // 6. if necessary and possible .. get conflicts from new UB + max_cores_remaining = params.max_cores_before_cplex - cplexClauses.size(); + if (max_cores_remaining > 0 && cpuTime() < cplex_iter_max_time) { + // cout << "have_new_UB_model = " << have_new_UB_model << " UB() = " << + // UB() + // << " conflicts_from_ub = " << params.conflicts_from_ub + // << " fabs(UB() - LB()) = " << fabs(UB() - LB()) + // << " cplexClauses.size() = " << cplexClauses.size() << "\n"; + if (have_new_UB_model && params.conflicts_from_ub > 0) + if (params.conflicts_from_ub > 1 || + fabs(UB() - LB()) <= 3 * theWcnf->minSftWt() || + cplexClauses.size() < 35) { + int prev_confs = cplexClauses.size(); + if (params.verbosity > 0) cout << "c trying UB conflicts\n"; + get_ub_conflicts(); + if (check_termination("conflicts from UB model")) return; + if (params.verbosity > 0) + cout << "c ub-conflicts found " << cplexClauses.size() - prev_confs + << " conflicts\n"; } - if(params.verbosity) - cout << "c CPLEX abstract solution yielded " << cplexClauses.size()-n_conf << " conflicts\n"; - } } - if(!found_abstracted_solution || params.mx_hs_use_abstraction > 1) { - n_conf = cplexClauses.size(); - if(get_conflicts(cplexsoln, false)) { - optFound("c Solved by CPLEX model"); - return; - } - if(params.verbosity > 0) - cout << "c CPLEX solution yielded " << cplexClauses.size()-n_conf << " conflicts\n"; + } +} + +bool MaxSolver::get_cplex_conflicts(const vector& cplexSoln, char type, + double timeout, int max_cores) { + // Input cplex solution and number of conflicts accumulated since CPLEX + // called. Return true if we found an optimal solution. I.e., one with cost + // equal to LB() that is SAT. If not optimal solution but is SAT update + // upper bound If UNSAT generate some conflicts to criticize this cplex + // solution. But check first to ensure that we don't have a conflict already + // criticizing this solution. + if (params.verbosity > 1) + cout << "c get_cplex_conflict processing " + << (type == 'p' ? "populated " : "cplex") << "solution of cost " + << getWtOfBvars(cplexSoln) << "\n"; + + // compute conflict from abstract soln if requested + bool found_abstract_solution{false}; + int n_conf{0}; + if (params.abstract_cplex_cores) { + auto abscplexsoln = abstractSolution(cplexSoln); + if (!abscplexsoln.empty()) { + n_conf = get_seq_of_conflicts( + abscplexsoln, + cplexClauses.empty() ? params.noLimit : params.optcores_cpu_per, + params.optcores_cpu_per, timeout, max_cores); + found_abstract_solution = n_conf > 0; + if (check_termination(type == 'p' ? "abstract populated CPLEX model" + : "abstract CPLEX model")) + return true; + if (params.verbosity > 0 && n_conf > 0) + cout << "c Cplex abstract solution yielded " << n_conf + << " new conflicts\n"; } - bool cplex_solution_was_unsat = !cplexClauses.empty(); + } - //4. Generate conflicts from greedy solutions. - n_conf = cplexClauses.size(); - if(cplex_solution_was_unsat && get_greedy_conflicts()) { - optFound("c Solved by greedy model"); - return; + max_cores -= n_conf; + if (!cplexClauses.empty() && (max_cores <= 0 || cpuTime() > timeout)) + return false; + + if (!found_abstract_solution || params.abstract_cplex_cores > 1) { + n_conf = get_seq_of_conflicts(cplexSoln, + (cplexClauses.empty() && cplexClauses.empty() + ? params.noLimit + : params.optcores_cpu_per), + params.optcores_cpu_per, timeout, max_cores); + if (check_termination(type == 'p' ? "populated CPLEX model" + : "CPLEX model")) + return true; + if (params.verbosity > 0 && n_conf > 0) + cout << "c Cplex concrete solution yielded " << n_conf + << " new conflicts\n"; + } + return false; +} + +bool MaxSolver::get_greedy_conflicts(double timeout, int max_cores) { + // accumulate more conflicts for CPLEX by criticizing sequence of greedy + // solutions. + int iter{0}; + int total_c_conflicts{0}; + int total_a_conflicts{0}; + if (params.verbosity > 0) cout << "c finding conflicts from greedy\n"; + + while (1) { + // Don't need to use greedy and don't need a solution + // if no new solution is available + if (max_cores <= 0 || cpuTime() > timeout) break; + int n_conf = 0; + bool iteration_succ = false; + + auto greedy_solution = greedySoln(false, false); + ++iter; + if (!greedy_solution.size()) break; + if (params.verbosity > 1) cout << "c Greedy Solution # " << iter << "\n"; + + if (params.abstract_greedy_cores != 1) { + n_conf = + get_seq_of_conflicts(greedy_solution, params.optcores_cpu_per, + params.optcores_cpu_per, timeout, max_cores); + max_cores -= n_conf; + total_c_conflicts += n_conf; + iteration_succ |= n_conf > 0; + if (check_termination("concrete greedy soln")) return true; + if (params.verbosity > 1) + cout << "c Greedy concrete solution yielded " << n_conf + << " new conflicts\n"; } - if(params.verbosity > 0) - cout << "c Greedy yielded " << cplexClauses.size()-n_conf << " conflicts\n"; - - //5. if needed find some more conflicts by extracting other solutions from cplex - n_conf = cplexClauses.size(); - if(cplex_solution_was_unsat && - (params.trypop > 1 || (params.trypop > 0 && cplexClauses.size() < 35))) { - double gap = fabs(UB()-cplexSolnWt); - //want better solutions than UB - if(theWcnf->integerWts()) - gap -= 1.0; - else - gap -= absGap; - if(gap <= absGap) - gap = 0.0; - - int nsolns = cplex->populate(params.cplex_pop_cpu_lim, gap); - if(params.verbosity > 1) - cout << "c populate found " << nsolns << " solutions\n"; - for(int i=0; i < nsolns; i++) { - cplex->getPopulatedSolution(i, cplexsoln); - if(cplexsoln.size() == 0) { - cout << "c WARNING Cplex populate could not find solution " << i << "\n"; - continue; - } - if(params.verbosity > 1) - cout << "c getting conflicts from solution #" << i << "\n"; - if(get_conflicts(cplexsoln, true)) { - optFound("c Solved by populated model"); - return; - } - //5.5 if the populated solution has cost <= cplex solution wt - //the keep track of its set of true blits...we need not try to - //harden these blits. - /*if(getWtOfBvars(cplexsoln) <= cplexSolnWt) { - for(auto l : cplexsoln) - if(bvars.isCore(l)) - cannot_exceed_bounds[bvars.clsIndex(l)] = 1; - }*/ + + if (max_cores <= 0 || cpuTime() > timeout) break; + + if (params.abstract_greedy_cores) { + auto absgreedysoln = abstractSolution(greedy_solution); + if (!absgreedysoln.empty()) { + n_conf = + get_seq_of_conflicts(absgreedysoln, params.optcores_cpu_per, + params.optcores_cpu_per, timeout, max_cores); + max_cores -= n_conf; + total_a_conflicts += n_conf; + iteration_succ |= n_conf > 0; + if (check_termination("abstract greedy soln")) return true; + if (params.verbosity > 1) + cout << "c Greedy abstract solution yielded " << n_conf + << " new conflicts\n"; } } - if(params.verbosity > 0) - cout << "c populate found " << cplexClauses.size() - n_conf << " conflicts\n"; - - //6. if necessary and possible get conflicts from new UB - n_conf = cplexClauses.size(); - - /*cout << "have_new_UB_model = " << have_new_UB_model << " UB() = " << UB() - << " conflicts_from_ub = " << params.conflicts_from_ub - << " fabs(UB() - LB()) = " << fabs(UB() - LB()) - << " cplexClauses.size() = " << cplexClauses.size() << "\n";*/ - - if(have_new_UB_model && params.conflicts_from_ub > 0) - if(params.conflicts_from_ub > 1 - || fabs(UB() - LB()) <= 3*theWcnf->minSftWt() - || cplexClauses.size() < 35) { - if(params.verbosity > 0) - cout << "c trying UB conflicts\n"; - if(get_ub_conflicts()) { - optFound("c Solved by conflicts from UB model\n"); - return; - } - } - if(params.verbosity > 0) - cout << "c ub-conflicts found " << cplexClauses.size() - n_conf << " conflicts\n"; + if (!iteration_succ) break; } + if (params.verbosity > 0) + cout << "c Greedy: iters=" << iter + << " concrete conflicts=" << total_c_conflicts + << " abstract conflicts=" << total_a_conflicts << "\n"; + + return false; +} + +bool MaxSolver::assumps_are_blocked(const vector& assumps) { + // check if soln falsifies an already found clause. If it does then + // the solution is already blocked so there is no need to generate + // new conflicts from it. + /* vector true_lits; + vector tinputs; + vector mxes_seen(theWcnf->n_mxes(), false); + for (auto l : soln) { + if (bvars.isCore(l)) + if (totalizers->isTinput(l)) + tinputs.push_back(l); + else if (bvars.inCoreMx(l) && !mxes_seen[bvars.getMxNum(l)]) { + mxes_seen[bvars.getMxNum(l)] = true; + if (totalizers->isTinput(bvars.getMxDLit(l))) + tinputs.push_back(bvars.getMxDLit(l)); + } else + true_lits.push_back(l); + } + totalizers->add_true_touts(tinputs, true_lits);*/ + + vector a(assumps); + std::sort(a.begin(), a.end()); + auto is_falsified_by_assumps = [&](Lit l) { + return std::binary_search(a.begin(), a.end(), ~l); + }; + for (auto cls : cplexClauses) + if (std::all_of(cls.begin(), cls.end(), is_falsified_by_assumps)) { + if (params.verbosity > 1) + cout << "c assumptions are blocked by previous clause\n"; + return true; + } + + return false; +} + +vector MaxSolver::abstractSolution(const vector& concrete_soln) { + // replace concreteSoln with abstract one, return empty vector if + // abstraction not possible. + // Code no longer works with non-encoded mutexes. Currently Wcnf + // encodes all mutexes it finds so unless non-encoded mutexes are + // restored this should not be a problem. + + //CURRENT VERSION DOES APPLY ABSTRACTION TO CORE MXES + //VERSION IN COMMENTS DOES BUT IT DOES NOT PERFORM AS WELL + + if (!totalizers->totalizers_active()) + // no abstraction possible + return vector{}; + + vector tot_n_true_inputs(totalizers->nb_totalizers(), 0); + vector tot_present(totalizers->nb_totalizers(), false); + bool can_abstract{false}; + + vector abs_soln(concrete_soln); + for (auto l : abs_soln) + if (totalizers->isTinput(l)) { + can_abstract = true; + tot_present[totalizers->get_Tinput_idx(l)] = true; + if (bvars.isCore(l)) + tot_n_true_inputs[totalizers->get_Tinput_idx(l)] += 1; + } + + if (!can_abstract) return vector{}; + + auto e = std::remove_if(abs_soln.begin(), abs_soln.end(), + [&](Lit l) { return totalizers->isTinput(l); }); + abs_soln.erase(e, abs_soln.end()); + + for (size_t tidx = 0; tidx < tot_present.size(); tidx++) + if (tot_present[tidx]) { + int n_true = + std::max(tot_n_true_inputs[tidx], totalizers->get_n_true_outs(tidx)); + Lit nxt_out = totalizers->get_next_Toutput(tidx, n_true); + if (n_true > 0) { + while (nxt_out != lit_Undef && satsolver->curVal(nxt_out) != l_Undef) + nxt_out = totalizers->get_next_Toutput(tidx, ++n_true); + if (nxt_out != lit_Undef) abs_soln.push_back(~nxt_out); + } else { + for (auto l : totalizers->get_ilits_from_tidx(tidx)) + abs_soln.push_back(~l); + } + } + + // if (!totalizers->totalizers_active() && !theWcnf->n_mxes()) + // // no abstraction possible + // return vector{}; + + // // set maximum number that can be asked to be true + // auto const& mxs = theWcnf->get_SCMxs(); + // vector mx_true_count(mxs.size()); + // for (size_t i = 0; i < mxs.size(); ++i) { + // mx_true_count[i] = mxs[i].soft_clause_lits().size(); + // for (auto l : mxs[i].soft_clause_lits()) + // if (satsolver->curVal(l) == l_False) mx_true_count[i] -= 1; + // } + + // vector tot_true_count(totalizers->nb_totalizers()); + // for (size_t i = 0; i < tot_true_count.size(); ++i) { + // tot_true_count[i] = totalizers->get_tot_size_from_tidx(i); + // for (auto l : totalizers->get_ilits_from_tidx(i)) + // if (satsolver->curVal(l) == l_False) tot_true_count[i] -= 1; + // } + + // // for each input input lit false in concrete_soln reduce + // // count of number to be assumed true, don't double count + // // lits that are already forced to be false (oppositive lit) + // vector abs_soln(concrete_soln); + // for (auto l : abs_soln) + // if (bvars.isNonCore(l)) { + // if (bvars.inCoreMx(l) && satsolver->curVal(~l) != l_False) + // mx_true_count[bvars.getMxNum(l)] -= 1; + // if (totalizers->isTinput(l) && satsolver->curVal(~l) != l_False) + // tot_true_count[totalizers->get_Tinput_idx(l)] -= 1; + // } + + // auto e = std::remove_if(abs_soln.begin(), abs_soln.end(), [&](Lit l) { + // return satsolver->curVal(l) != l_Undef || + // (bvars.inCoreMx(l) && mx_true_count[bvars.getMxNum(l)]) || + // (totalizers->isTinput(l) && + // tot_true_count[totalizers->get_Tinput_idx(l)]); + // }); + // abs_soln.erase(e, abs_soln.end()); + + // for (auto tidx : totalizers->get_top_level_tidxes()) { + // auto n_true = tot_true_count[tidx]; + // const vector& olits = totalizers->get_olits_from_tidx(tidx); + // if (n_true && n_true < static_cast(olits.size())) { + // auto val = satsolver->curVal(olits[n_true]); + // if (val == l_Undef) + // abs_soln.push_back(~olits[n_true]); + // else if (val == l_True) { + // while (n_true < static_cast(olits.size()) && + // satsolver->curVal(olits[n_true]) == l_True) + // ++n_true; + // if (n_true < static_cast(olits.size()) && + // satsolver->curVal(olits[n_true]) == l_Undef) + // abs_soln.push_back(~olits[n_true]); + // } + // } + // } + + return abs_soln; } bool MaxSolver::tryHarden() { - //Try to harden some of the soft clauses + // Try to harden some of the soft clauses vector lp_solvals; vector lp_redvals; - int hardened_count {n_softs_forced_hard}; - int relaxed_count {n_softs_forced_relaxed}; - int hardened_count_not_in_cplex {n_softs_forced_hard_not_in_cplex}; - bool some_forced {false}; + vector cplex_vars; + auto lp_objval = + cplex->solve_lp_relaxation(lp_solvals, lp_redvals, cplex_vars); + if (lp_objval <= 0) return false; + return tryHarden_with_lp_soln(lp_objval, lp_redvals, cplex_vars); +} - auto lp_objval = cplex->solve_lp_relaxation(lp_solvals, lp_redvals); - if(lp_objval <= 0) - return false; +bool MaxSolver::tryHarden_with_lp_soln(const Weight lp_objval, + vector& lp_redvals, + vector& cplex_vars) { + // Try to harden some of the soft clauses with information + // obtained from an lp solve. + + int hardened_softs_count{n_softs_forced_hard}; + int falsified_softs_count{n_softs_forced_false}; + int hardened_not_in_cplex_count{n_softs_forced_hard_not_in_cplex}; + int ovars_made_true_count{n_ovars_forced_true}; + int ovars_made_false_count{n_ovars_forced_false}; + int touts_made_true_count{n_touts_forced_true}; + int touts_made_false_count{n_touts_forced_false}; + + bool some_forced{false}; updateLB(lp_objval); - if(fabs(UB() - LB()) <= absGap) - return true; - if(lp_objval + bvars.maxWt() + 0.01 < UB()) - return false; - for(size_t i = 0; i < bvars.n_bvars(); i++) { - auto blit = bvars.litOfCls(i); - if(satsolver->curVal(blit) != l_Undef) { + if (fabs(UB() - LB()) <= absGap || LB() > UB() + absGap) return true; + + if (lp_objval + bvars.maxWt() + 0.01 < UB()) return false; + + for (auto blit : bvars.getCoreLits()) { + if (sign(blit)) cout << "c ERROR blits not all positive!\n"; + if (satsolver->curVal(blit) != l_Undef || cplex->lit_in_cplex(blit)) continue; + if (lp_objval + bvars.wt(blit) > UB() + 0.01 || + (fabs(lp_objval + bvars.wt(blit) - UB()) < 0.001 && + UBmodel[var(blit)] == l_False)) { + satsolver->addClause(~blit); + ++n_softs_forced_hard_not_in_cplex; } - if(lp_redvals[i] > 0) { - //var should be at its lower bound - if(lp_redvals[i] + lp_objval > UB() + 0.01 - || (fabs(lp_redvals[i] + lp_objval - UB()) < 0.001 && UBmodelSofts[i] == l_True)) { - //Lp is subject to rounding error. Greater than UB if gt by at least 0.01 - //equal to UB if within 0.001 + } + + for (size_t i = 0; i < cplex_vars.size(); i++) { + Var v = cplex_vars[i]; + if (satsolver->curVal(v) != l_Undef) { + continue; + } + if (lp_redvals[i] > 0) { + // var true incurs cost + if (lp_redvals[i] + lp_objval > UB() + 0.01 || + (fabs(lp_redvals[i] + lp_objval - UB()) < 0.001 && + UBmodel[v] == l_False)) { + // Lp is subject to rounding error. Greater than UB if gt by at least + // 0.01 equal to UB if within 0.001 // - //if we make blit true we must incur cost >= UB. If cost > - //UB force it false. If cost == UB and blit is false in UB - //model (the corresponding soft is true in the UB model) - //we can also force it false---no need to consider models - //with it true as they can't be better than the UB model. - satsolver->addClause({~blit}); - n_softs_forced_hard++; - if(!cplex->lit_in_cplex(blit)) - n_softs_forced_hard_not_in_cplex++; + // if we make blit true we must incur cost >= UB. If cost > + // UB force it false. If cost == UB and blit is false in UB + // model (the corresponding soft is true in the UB model) + // we can also force it false---no need to consider models + // with it true as they can't be better than the UB model. + satsolver->addClause(~mkLit(v)); + if (bvars.isBvar(v)) + ++n_softs_forced_hard; + else if (totalizers->isToutput(v)) + ++n_touts_forced_false; + else + ++n_ovars_forced_false; some_forced = true; } - } - else if(lp_redvals[i] < 0) { - //var should be at its upper bound - if(lp_objval - lp_redvals[i] > UB() + 0.01 - || (fabs(lp_objval - lp_redvals[i] - UB()) < 0.001 && UBmodelSofts[i] == l_False)) { - //|| (lp_objval - lp_redvals[i] == UB() && UBmodelSofts[i] == l_False)) { - satsolver->addClause({blit}); - n_softs_forced_relaxed++; + } else if (lp_redvals[i] < 0) { + // var should be at its upper bound + if (lp_objval - lp_redvals[i] > UB() + 0.01 || + (fabs(lp_objval - lp_redvals[i] - UB()) < 0.001 && + UBmodel[v] == l_True)) { + satsolver->addClause(mkLit(v)); + if (bvars.isBvar(v)) + ++n_softs_forced_false; + else if (totalizers->isToutput(v)) + ++n_touts_forced_true; + else + ++n_ovars_forced_true; some_forced = true; } } } - if(some_forced) { - cout << "c tryharden: hardened " - << n_softs_forced_hard - hardened_count << "\n" - << "c tryharden: relaxed " - << n_softs_forced_relaxed - relaxed_count << "\n" - << "c tryharden: hardened not in model " - << n_softs_forced_hard_not_in_cplex - hardened_count_not_in_cplex << "\n"; - cplexAddNewForcedBvars(); - } + if (some_forced && params.verbosity) { + if (n_softs_forced_hard - hardened_softs_count > 0) + cout << "c tryharden: softs hardened " + << n_softs_forced_hard - hardened_softs_count << "\n"; + if (n_softs_forced_false - falsified_softs_count > 0) + cout << "c tryharden: softs falsified " + << n_softs_forced_false - falsified_softs_count << "\n"; + if (n_softs_forced_hard_not_in_cplex - hardened_not_in_cplex_count > 0) + cout << "c tryharden: softs not in cplex hardened " + << n_softs_forced_hard_not_in_cplex - hardened_not_in_cplex_count + << "\n"; + if (n_ovars_forced_true - ovars_made_true_count) + cout << "c tryharden: ordinary vars made true " + << n_ovars_forced_true - ovars_made_true_count << "\n"; + if (n_ovars_forced_false - ovars_made_false_count) + cout << "c tryharden: ordinary vars made false " + << n_ovars_forced_false - ovars_made_false_count << "\n"; + if (n_touts_forced_true - touts_made_true_count) + cout << "c tryharden: touts made true " + << n_touts_forced_true - touts_made_true_count << "\n"; + if (n_touts_forced_false - touts_made_false_count) + cout << "c tryharden: touts made false " + << n_touts_forced_false - touts_made_false_count << "\n"; + } + + cplexAddNewForcedBvars(); /*cout << "blit " << blit; if(lp_solvals[i] > 0) @@ -642,340 +1009,177 @@ bool MaxSolver::tryHarden() { return false; } -bool MaxSolver::get_conflicts(const vector& cplexSoln, bool check_if_blocked) { - //Input cplex solution and number of conflicts accumulated since CPLEX called. - //Return true if we found an optimal solution. - //I.e., one with cost equal to LB() that is SAT. If not optimal - //solution but is SAT update upper bound If UNSAT generate some - //conflicts to criticize this cplex solution. But check first to - //ensure that we don't have a conflict already criticizing this - //solution. - - if(params.verbosity > 1) - cout << "c get_conflict processing solution of cost " << getWtOfBvars(cplexSoln) << "\n"; - - //1. Check if already blocked by previous conflict. - if(!cplexClauses.empty() && check_if_blocked) { - vector solnMap(bvars.n_bvars(), lit_Undef); - for(auto a : cplexSoln) - solnMap[bvars.clsIndex(a)] = a; - - bool isBlocked; - for(auto con: cplexClauses) { - isBlocked = true; - for(auto a: con) - if(solnMap[bvars.clsIndex(a)] == a) { - //passed solution satisfies this conflict clause - isBlocked = false; - break; - } - if(isBlocked) { - if(params.verbosity > 1) - cout << "c get_conflict conflict is already blocked\n"; - return false; - } - } - } - - //Not Blocked, get conflicts from this solution - auto n_conf = get_seq_of_conflicts(cplexSoln, params.noLimit, params.optcores_cpu_per); - if(params.verbosity > 1) - cout << "c Solution yielded " << n_conf << " new conflicts\n"; - if(fabs(UB()-LB()) < absGap) { - return true; - } - else - return false; -} - -bool MaxSolver::get_ub_conflicts() { - //try reducing UB model---should be a new UB model - //generate conflicts from each reduction (or improve UB!) +void MaxSolver::get_ub_conflicts() { + // try reducing UB model---should be a new UB model + // generate conflicts from each reduction (or improve UB!) + // No longer have non-cores double start_time = cpuTime(); - if(params.verbosity > 0) - cout << "c Finding conflicts from UB\n"; - vector ub_false; //store true b-lits == set of falsified clauses in UB-model - vector ub_true; //store false b-lits == set of true clauses in UB-model - Assumps assumps(satsolver, bvars); + if (params.verbosity > 0) cout << "c Finding conflicts from UB\n"; + vector ub_false, ub_true; // store true and false b-lits (true and false + // softs in UB-model) + Assumps assumps(satsolver, bvars, totalizers); auto n_conf = cplexClauses.size(); vector conflict; - vector ub_true_has_ncore_mx; - vector ncore_mxes_in_ub_true (theWcnf->n_mxes(), false); - - //UBmodel does not store the bvars---so we have to retrieve them from UBmodelSofts - //which unfortunately has a reversed logic to the bvars---soft clause i is l_True - //means that the bvar of this clause is l_False. - for(size_t i=0; i < UBmodelSofts.size(); i++) - if(UBmodelSofts[i] == l_False) { - //this soft is falsified but not forced. (If forced - //it is pointless to try to satisfy it). - if(satsolver->curVal(bvars.litOfCls(i)) == l_Undef) - ub_false.push_back(bvars.litOfCls(i)); - } - else if(satsolver->curVal(bvars.litOfCls(i)) == l_Undef) { - ub_true.push_back(~bvars.litOfCls(i)); - if(bvars.inNonCoreMx(ub_true.back())) { - if(ub_true_has_ncore_mx[bvars.getMxNum(ub_true.back())]) - cout << "c ERROR get_ub_conflicts UB model makes more than one non-core mx true\n"; - ub_true_has_ncore_mx[bvars.getMxNum(ub_true.back())] = true; - } + + // init true and false softs in ubmodel + for (size_t i = 0; i < bvars.n_bvars(); i++) { + Lit blit = bvars.litOfCls(i); // blit = false <==> soft is true + if (satsolver->curVal(blit) == l_Undef) { + if (UBmodel[var(blit)] == + (sign(blit) ? l_True : l_False)) // soft is true + ub_true.push_back(~blit); + else + ub_false.push_back(blit); } + } - auto respects_mx_constraints = [&ub_true_has_ncore_mx, this](Lit b) { - return !bvars.inNonCoreMx(b) || !ub_true_has_ncore_mx[bvars.getMxNum(b)]; + // try to make false soft clauses true in order of their weight (process + // from the back) + auto wtLt = [this](const Lit bx, const Lit by) { + return bvars.wt(bx) < bvars.wt(by); }; - - //try to make false soft clauses true in order of their weight (process from the back) - auto wtLt = [this](const Lit bx, const Lit by) { return bvars.wt(bx) < bvars.wt(by); }; std::sort(ub_false.begin(), ub_false.end(), wtLt); - if(params.verbosity > 0) - cout << "c UB has " << ub_true.size() << " true softs (false blits) and " - << ub_false.size() << " unforced false softs (true blits)\n"; - - while(!ub_false.empty()) { - //try to make one more soft clause true in UB-model. - /*cout << "c UB has " << ub_true.size() << " true softs (false blits) and " - << ub_false.size() << " unforced false softs (true blits)\n";* - - cout << "at top of loop ub_false = " << ub_false;*/ - - if(satsolver->curVal(ub_false.back()) == l_Undef && - (!params.mx_constrain_hs || respects_mx_constraints(ub_false.back()))) { - ub_true.push_back(~ub_false.back()); - ub_false.pop_back(); - } - else { - ub_false.pop_back(); + if (params.verbosity > 0) + cout << "c UB has " << ub_true.size() + << " unforced true softs (false blits) and " << ub_false.size() + << " unforced false softs (true blits)\n"; + + while (!ub_false.empty()) { + // try to make one more soft clause true in UB-model. + auto blit = ub_false.back(); + ub_false.pop_back(); + + if (satsolver->curVal(blit) != l_Undef) continue; + if (UBmodel[var(blit)] == (sign(blit) ? l_True : l_False)) { + // soft is true in current model + ub_true.push_back(~blit); continue; } - - auto isforced = [this](Lit j) { return satsolver->curVal(j) != l_Undef; }; - auto p = std::remove_if(ub_true.begin(), ub_true.end(), isforced); - ub_true.erase(p, ub_true.end()); - - /*for(size_t i = 0; i < ub_true.size(); i++) - if(satsolver->curVal(ub_true[i]) == l_False) - cout << "Problem with ub_true " << ub_true[i] << " is already false index = " << i << "\n";*/ - - assumps.init(ub_true, params.coreType); - auto val = satsolve_min(assumps, conflict, params.optcores_cpu_per, params.mus_cpu_lim); - if(val == l_True) { - if(params.verbosity > 0) + // else try to make it true + ub_true.push_back(~blit); + + assumps.init(extract_UnValued(ub_true)); + auto val = satsolve_min(assumps, conflict, params.optcores_cpu_per, + params.mus_cpu_lim); + if (val == l_True) { + if (params.verbosity > 0) cout << "c get_ub_conflicts found SAT==better UB\n"; - if(params.improve_model) - improveModel(); - auto w = updateUB(); - if(params.verbosity > 1) - cout << "c New SAT solution found by UB_CONFLICTS cost = " << w << "\n"; - if(fabs(UB() - LB()) <= absGap) - break; - size_t cur_size = 0; - for(size_t examine = 0; examine < ub_false.size(); examine++) { - //remove from set of true blits all newly falsified blits (i.e., corresponding soft is true - //in new UB model) and all newly forced false blits (no need to test these) - auto blit = ub_false[examine]; - if(UBmodelSofts[bvars.clsIndex(blit)] == l_True) - //this false soft is now true. - ub_true.push_back(~blit); - else if(satsolver->curVal(blit) != l_True) - ub_false[cur_size++] = ub_false[examine]; - } - ub_false.resize(cur_size); - if(params.verbosity>1) - cout << "c UB has " << ub_true.size() << " true softs (false blits) and " - << ub_false.size() << " unforced false softs (true blits)\n"; - } - else { - if (val == l_False) - store_cplex_greedy_cls(conflict); + if (params.improve_model) improveModel(); + updateUB(); + if (fabs(UB() - LB()) <= absGap) break; + if (params.verbosity > 1) + cout << "c UB has " << ub_true.size() + << " true softs (false blits) and " << ub_false.size() + << " unforced false softs (true blits)\n"; + } else { + if (val == l_False) store_cplex_greedy_cls(conflict); ub_true.pop_back(); } - if(cpuTime() - start_time > params.max_cpu_before_cplex) - //this should be fixed...it doesn't account for time spent in greedy and populate. + + if (cpuTime() - start_time > params.max_cpu_before_cplex) + // this doesn't account for time spent in greedy + // and populate. break; } - if(params.verbosity >0) - cout << "c get_ub_conflicts found " << cplexClauses.size() - n_conf << " new conflicts\n"; + if (params.verbosity > 0) + cout << "c get_ub_conflicts found " << cplexClauses.size() - n_conf + << " new conflicts\n"; have_new_UB_model = false; - - if(fabs(UB() - LB()) <= absGap) - return true; - return false; } -bool MaxSolver::get_greedy_conflicts() { - //accumulate more conflicts for CPLEX by criticizing sequence of greedy solutions. - double start_time = cpuTime(); - int iter {0}; - while(1) { - if(cplexClauses.size() > static_cast(params.max_cores_before_cplex) - || cpuTime() - start_time > params.max_cpu_before_cplex) - return false; - - if(params.verbosity > 0) - cout << "c Greedy Solution # " << iter << "\n"; - auto greedy_solution = greedySoln(); - auto n_conf = get_seq_of_conflicts(greedy_solution, params.optcores_cpu_per, params.optcores_cpu_per); - if(fabs(UB() - LB()) <= absGap) - return true; - if(params.verbosity > 0) - cout << "c Greedy Solution yielded " << n_conf << " conflicts\n"; - if(!n_conf) - return false; - iter++; - } -} - -int MaxSolver::get_seq_of_conflicts(const vector& solution, double first_core_cpu_lim, double other_cores_cpu_lim) { - //Just found a candidate solution (via cplex or greedy or...) - //check if the solution satisfies whole theory, and if not generate sequence - //of conflicts from it. Return number of conflicts found. - Assumps assumps(satsolver, bvars); - assumps.init(solution, params.coreType); +int MaxSolver::get_seq_of_conflicts(const vector& solution, + double first_core_cpu_lim, + double other_cores_cpu_lim, double timeout, + int max_cores) { + // Just found a candidate solution (via cplex or greedy or...) + // check if the solution satisfies whole theory, and if not generate + // sequence of conflicts from it. Return number of conflicts found. + // Solution might contain totalizer output variables + Assumps assumps(satsolver, bvars, totalizers); + + assumps.init(extract_costBoundinglits(solution)); + if (assumps_are_blocked(assumps.vec())) return 0; + assumps.permute(); + + // cout << "get_seq_of_conflicts init soln " << solution << "\n"; + // cout << "get_seq_of_conflicts init assumptions " << assumps.vec() << + // "\n"; vector conflict; - int n_conf {0}; - - while(1) { - auto val = satsolve_min(assumps, conflict, (n_conf ? other_cores_cpu_lim: first_core_cpu_lim), params.mus_cpu_lim); - if(val == l_True) { - if(params.improve_model) - improveModel(); + int n_conf{0}; + // cout << "satsolve_min\n"; + while (1) { + // cout << "get_seq_of_conflicts satsolve. sat_cpu = " + // << (n_conf ? other_cores_cpu_lim: first_core_cpu_lim) + // << " n_conf = " << n_conf + // << " other_cores_cpu_lim = " << other_cores_cpu_lim + // << " first_core_cpu_lim = " << first_core_cpu_lim + // << " mus_cpu = " + // << params.mus_cpu_lim << "\n"; + + // cout << "Assumptions " << assumps.vec() << "\n"; + auto val = satsolve_min(assumps, conflict, + (n_conf ? other_cores_cpu_lim : first_core_cpu_lim), + params.mus_cpu_lim); + // cout << "val: " << val << "\n"; + if (val == l_True) { + if (params.improve_model) improveModel(); auto w = updateUB(); - if(params.verbosity > 1) - cout << "c New SAT solution found (get_seq_of_conflicts), cost = " << w << "\n"; + if (params.verbosity > 1) + cout << "c New SAT solution found (get_seq_of_conflicts), cost = " << w + << "\n"; + // cout << "c UnSat weights = " << unw << "\n"; + // auto wt = checkSolWt(solution); return n_conf; - } - else if (val == l_False) { - n_conf++; + } else if (val == l_False) { + ++n_conf; + --max_cores; + // cout << "got conflict: " << conflict << "\n"; store_cplex_greedy_cls(conflict); assumps.update(conflict, true); - } - else - //timed out + } else + // timed out return n_conf; - } -} - -int MaxSolver::feedCplex(int gIter, Assumps& assumps, - int nSoFar, size_t sizeSoFar) { - //accumulate some conflicts for CPLEX. Return number found. - //Pre: Assumps is not know to be SAT. - // last two parameters are for collecting statistics only. - - vector conflict; - int iter {1}; //On call we already found one constraint for CPLEX - int nCon {nSoFar}; - int nSinceGreedy {1}; //Count CPLEX as a greedy call - int64_t totalCnfSize { static_cast(sizeSoFar) }; - - double start_time = cpuTime(); - while(1) { - iter++; - if(iter > params.max_cores_before_cplex - || cpuTime() - start_time > params.max_cpu_before_cplex) - return nCon; - - lbool val = satsolve_min(assumps, conflict, params.optcores_cpu_per, params.mus_cpu_lim); - if(val == l_True) { - if(params.improve_model) - improveModel(); - Weight w = updateUB(); - if(params.verbosity > 0) { - cout << "c New SAT solution found, cost = " << w - << " UB =" << UB() << " LB = " << LB() << "\n"; - } - - if(fabs(UB()-LB()) <= absGap) { - optFound("c Solved by Sat Solution equal to LB"); - return nCon; - } - else if(nSinceGreedy == 0) //greedy solution was solvable. No - return nCon; //more clauses to feed to cplex - - else { //start with new set of assumptions - auto tmp = greedySoln(); - assumps.init(tmp, params.coreType); - if(params.sort_assumps == 1) - assumps.sort(blit_gt); - else if(params.sort_assumps == 2) - assumps.sort(blit_lt); - - if(params.verbosity > 3) { - cout << "Greedy Solution: " << tmp << "\n"; - cout << "Assumps after Greedy Solution" << assumps.vec() << "\n"; - } - nSinceGreedy = 0; - } - } - else if(val == l_False) { - nCon++; - nSinceGreedy++; - totalCnfSize += conflict.size(); - store_cplex_greedy_cls(conflict); - if (params.verbosity > 1 - || (params.verbosity > 0 && (iter % 50) == 0)) - cout << "c FeedCplex: " << gIter << "." << iter - << " (#cons = " << nCon << " ave size = " - << totalCnfSize/nCon << ").\n"; - - auto tmp = getAssumpUpdates(nCon, conflict); - //Must remove assumptions now that we are adding forced negated bvars - //assumps.update(tmp, params.fbeq); - assumps.update(tmp, true); - - if(params.verbosity > 2) { - cout << "c FeedCplex conflict: " << conflict << "\n"; - cout << "c FeedCplex Conflict updates: " << tmp << "\n"; - if(params.verbosity > 3) - cout << "c FeedCplex Updated Assumps: " << assumps.vec() << "\n"; - } - } - else { //timed out - if(params.verbosity > 0) - cout << "c feedCplex finding new core timedout\n"; - return nCon; - } + if (max_cores <= 0 || cpuTime() > timeout) return n_conf; } } - -//This routine must be called right after an optimal cplex solution is -//computed. We know that that solution satisfies all current cores +// This routine must be called right after an optimal cplex solution is +// computed. We know that that solution satisfies all current cores //(and non-cores). A cplex solution is a complete setting of Bvars: -//i.e., it is a model. If each of the new solutions in the pool are -//optimal, then all of them also satisfy all prior cores. - -//We can stop any redundant cores from being generated by making sure -//that every solution we examine satisfies all new conflicts -//generated. On entry we have one new conflict C0---the conflict that -//refutes the original optimal CPLEX solution. So we can discard a -//solution from the pool S0 if it fails to satisfy C0---C0 will block S0 -//as well. Otherwise we can refute S0---or if S0 is optimal and -//satisfiable we can declare S to be a maxsat solution. - -//In refuting S0 we obtain another conflict C1. Now for the next -//solution from the pool S1, we should really check that S1 is not -//blocked by either C0 or C1. Similary for Sk from the solution pool -//we should check that it is not blocked by all prior conflicts -//generated in this routine. - -//We preform this test as cplex often generated multiple copies of the -//same solution in the solution pool. +// i.e., it is a model. If each of the new solutions in the pool are +// optimal, then all of them also satisfy all prior cores. + +// We can stop any redundant cores from being generated by making sure +// that every solution we examine satisfies all new conflicts +// generated. On entry we have one new conflict C0---the conflict that +// refutes the original optimal CPLEX solution. So we can discard a +// solution from the pool S0 if it fails to satisfy C0---C0 will block S0 +// as well. Otherwise we can refute S0---or if S0 is optimal and +// satisfiable we can declare S to be a maxsat solution. + +// In refuting S0 we obtain another conflict C1. Now for the next +// solution from the pool S1, we should really check that S1 is not +// blocked by either C0 or C1. Similary for Sk from the solution pool +// we should check that it is not blocked by all prior conflicts +// generated in this routine. + +// We preform this test as cplex often generated multiple copies of the +// same solution in the solution pool. void MaxSolver::tryPopulate(vector& conflict, double cplexOptSolnWt) { - //Got conflict for first cplex solution. Try populate and see if we can't - //get some more conflicts. Return the last one to initiate greedy feeding - //of cplex. - - //Allow for non-optimal solution in solution pool. But - //if solution is optimal (equal to the passed optimal solution weight - //computed from an optimal solve) we can use it to declare that - //a solution has been found. - if(params.verbosity > 0) - cout << "c Trying populate\n"; + // Got conflict for first cplex solution. Try populate and see if we can't + // get some more conflicts. Return the last one to initiate greedy feeding + // of cplex. + + // Allow for non-optimal solution in solution pool. But + // if solution is optimal (equal to the passed optimal solution weight + // computed from an optimal solve) we can use it to declare that + // a solution has been found. + if (params.verbosity > 0) cout << "c Trying populate\n"; int nsolns = cplex->populate(params.cplex_pop_cpu_lim); + Assumps assumps(satsolver, bvars, totalizers); vector cplexsoln; vector> foundConflicts; vector newConflict; @@ -984,209 +1188,270 @@ void MaxSolver::tryPopulate(vector& conflict, double cplexOptSolnWt) { int nOpt = 0; int nOptAdded = 0; - foundConflicts.push_back(std::move(conflict)); //empties conflict must restore. + // must restore conflict later. + foundConflicts.push_back(std::move(conflict)); - for(int i = 0; i < nsolns; i++) { + for (int i = 0; i < nsolns; i++) { cplex->getPopulatedSolution(i, cplexsoln); - if(cplexsoln.size() == 0) { + if (cplexsoln.size() == 0) { cout << "c WARNING Cplex populate could not find solution " << i << "\n"; continue; } auto solnwt = getWtOfBvars(cplexsoln); bool isOpt = (solnwt == cplexOptSolnWt); - if(isOpt) - ++nOpt; + if (isOpt) ++nOpt; - for(auto l : cplexsoln) + bool isBlocked = false; + for (auto l : cplexsoln) { + if (satsolver->curVal(l) == l_False) { + // invalid cplex solution (sat solver has negated one of its lits) + isBlocked = true; + break; + } solnMap[bvars.clsIndex(l)] = l; - bool isBlocked=false; - for(auto con: foundConflicts) { - isBlocked = true; - for(auto l: con) - if(solnMap[bvars.clsIndex(l)] == l) - isBlocked = false; - if(isBlocked) break; } - if(!isBlocked) { //can obtain new conflict from this solution + + if (!isBlocked) + for (auto con : foundConflicts) { + isBlocked = true; + for (auto l : con) + if (solnMap[bvars.clsIndex(l)] == l) { + isBlocked = false; + break; + } + if (isBlocked) break; + } + + if (!isBlocked) { // can obtain new conflict from this solution ++nadded; - if(isOpt) ++nOptAdded; - //Assumps assumps(satsolver, bvars, inMx); - Assumps assumps(satsolver, bvars); - assumps.init(cplexsoln, params.coreType); - if(params.sort_assumps == 1) + if (isOpt) ++nOptAdded; + // Assumps assumps(satsolver, bvars, inMx); + + assumps.init(extract_NonCoreBvars(cplexsoln)); + if (params.sort_assumps == 1) assumps.sort(blit_gt); - else if(params.sort_assumps == 2) + else if (params.sort_assumps == 2) assumps.sort(blit_lt); - if(satsolve_min(assumps, newConflict, params.noLimit, params.mus_cpu_lim) == l_True) { + + // cout << "tryPopulate satsolve. sat_cpu = " << params.noLimit << " + // mus_cpu = " + // << params.mus_cpu_lim << "\n"; + + if (satsolve_min(assumps, newConflict, params.noLimit, + params.mus_cpu_lim) == l_True) { updateUB(); - if(isOpt) { + if (isOpt) { optFound("c Solved by one of Cplex populated models"); return; - } - else + } else cout << "c Non optimal CPLEX populated model was SAT\n"; - } - else { + } else { store_cplex_greedy_cls(newConflict); foundConflicts.push_back(std::move(newConflict)); } } } - if(params.verbosity>0) + if (params.verbosity > 0) cout << "c populate added " << nadded << " conflicts\n"; conflict = std::move(foundConflicts[0]); } vector MaxSolver::getAssumpUpdates(int nSinceCplex, vector& core) { - //return a subset of the core to update assumptions. Core should be - //a conflict returned by trying to see if assumptions are - //satisfiable...so it will contain only negations of the assumption - //literals. Uses core for computation---modifies it! + // return a subset of the core to update assumptions. Core should be + // a conflict returned by trying to see if assumptions are + // satisfiable...so it will contain only negations of the assumption + // literals. Uses core for computation---modifies it! assert(!(params.coreType == CoreType::cores) || vec_isCore(core)); - switch(params.coreRelaxFn) { - case CoreRelaxFn::maxoccur: - return vector { maxOccurring(core) }; - case CoreRelaxFn::frac: - return fracOfCore(nSinceCplex, core); - case CoreRelaxFn::dsjn: - return core; - default: - return vector { core[0] }; + switch (params.coreRelaxFn) { + case CoreRelaxFn::maxoccur: + return vector{maxOccurring(core)}; + case CoreRelaxFn::frac: + return fracOfCore(nSinceCplex, core); + case CoreRelaxFn::dsjn: + return core; + default: + return vector{core[0]}; } } -vector MaxSolver::fracOfCore(int nSinceCplex, vector &core) { +vector MaxSolver::fracOfCore(int nSinceCplex, vector& core) { double fracToReturn = params.frac_to_relax; - int nToReturn {1}; - if(nSinceCplex > params.frac_rampup_start) { - if(nSinceCplex < params.frac_rampup_end) - fracToReturn *= (nSinceCplex - params.frac_rampup_start)/(1.0 * (params.frac_rampup_end - params.frac_rampup_start)); - nToReturn = ceil(fracToReturn*((double) core.size())); - } - if(nToReturn == 1) - return vector { maxOccurring(core) }; - - //auto maxOccur = [this](Lit l1, Lit l2) { return bLitOccur[bvars.toIndex(l1)] < bLitOccur[bvars.toIndex(l2)]; }; - //use a heap here instead of fully sorting the vector. - //std::make_heap(core.begin(), core.end(), maxOccur); + int nToReturn{1}; + if (nSinceCplex > params.frac_rampup_start) { + if (nSinceCplex < params.frac_rampup_end) + fracToReturn *= + (nSinceCplex - params.frac_rampup_start) / + (1.0 * (params.frac_rampup_end - params.frac_rampup_start)); + nToReturn = ceil(fracToReturn * ((double)core.size())); + } + if (nToReturn == 1) return vector{maxOccurring(core)}; + + // auto maxOccur = [this](Lit l1, Lit l2) { return + // bLitOccur[bvars.toIndex(l1)] < bLitOccur[bvars.toIndex(l2)]; }; use a + // heap here instead of fully sorting the vector. + // std::make_heap(core.begin(), core.end(), maxOccur); std::make_heap(core.begin(), core.end(), blit_lt); vector retVec; for (int i = 0; i < nToReturn; i++) { retVec.push_back(core.front()); - //std::pop_heap(core.begin(), core.end()-i, maxOccur); - std::pop_heap(core.begin(), core.end()-i, blit_lt); + // std::pop_heap(core.begin(), core.end()-i, maxOccur); + std::pop_heap(core.begin(), core.end() - i, blit_lt); } - if(params.verbosity > 1) - cout << "c fracOfCore returns a relaxation of size " << retVec.size() << "\n"; + if (params.verbosity > 1) + cout << "c fracOfCore returns a relaxation of size " << retVec.size() + << "\n"; return retVec; } Lit MaxSolver::maxOccurring(const vector& core) { - //return literal of core that appears in largest number of accumulated CPLEX clauses + // return literal of core that appears in largest number of accumulated + // CPLEX clauses Lit max = core[0]; - for(auto l : core) - //if(bLitOccur[bvars.toIndex(l)] > bLitOccur[bvars.toIndex(max)]) + for (auto l : core) + // if(bLitOccur[bvars.toIndex(l)] > bLitOccur[bvars.toIndex(max)]) // - if(blit_gt(l, max)) //Note blit_lt(max, l) is not same as blit_gt(l, max) + if (blit_gt(l, + max)) // Note blit_lt(max, l) is not same as blit_gt(l, max) max = l; return max; } - -void MaxSolver::incrBLitOccurrences(const vector &core) { - //all lits in core should be bvars! - for (auto l : core) +void MaxSolver::incrBLitOccurrences(const vector& core) { + // all lits in core should be bvars! + if (core.size() > static_cast(top_freq)) { + top_freq = core.size(); + } + for (auto l : core) { bLitOccur[bvars.toIndex(l)] += 1; + } } - -//TODO: Consider unifying the add clause/add new forced bvar +// TODO: Consider unifying the add clause/add new forced bvar // across all solvers (including CPLEX). // Perhaps a solver class that CPLEX and SAT derive from. // -bool MaxSolver::cplexAddCls(vector&& cls) { - bool isNonCore = cplex->add_clausal_constraint(cls); - if(params.verbosity > 2) { +void MaxSolver::cplexAddCls(vector&& cls) { + cplex->add_clausal_constraint(cls); + if (params.verbosity > 2) { cout << "c Added clause to Cplex:\n"; outputConflict(cls); } - return isNonCore; } -//TODO Better abstraction to facilitate sharing of unit or other clauses between solvers. +// TODO Better abstraction to facilitate sharing of unit or other clauses +// between solvers. // int MaxSolver::cplexAddNewForcedBvars() { cplexNU.update(satsolver); - vector& forced = cplexNU.forced; - int nf {0}; - for(auto l : forced) - if(bvars.isBvar(l) || cplex->lit_in_cplex(l)) { + vector forced{cplexNU.forced}; + int nf{0}; + int tv{0}; + + auto high_touts_first = [&](const Lit l1, const Lit l2) { + if (totalizers->isToutput(l1) && totalizers->isToutput(l2)) { + if (totalizers->isNegativeToutput(l1) && + totalizers->isNegativeToutput(l2)) + return totalizers->get_olit_index(l1) < totalizers->get_olit_index(l2); + else if (totalizers->isNegativeToutput(l1)) + return true; + else if (totalizers->isNegativeToutput(l2)) + return false; + else + return totalizers->get_olit_index(l1) > totalizers->get_olit_index(l2); + } else if (totalizers->isToutput(l1)) + return true; + else if (totalizers->isToutput(l2)) + return false; + else + return l1 < l2; + }; + std::sort(forced.begin(), forced.end(), high_touts_first); + + /*cout << "FORCED:\n"; + for (auto l : forced) { + if (totalizers->isToutput(l)) + cout << "(" << l << "," << totalizers->get_olit_idx(l) << ":" + << totalizers->get_olit_index(l) << ")\n"; + }*/ + + for (auto l : forced) { + if (totalizers->isToutput(l)) { + tv++; + } + if (bvars.isBvar(l) || totalizers->isToutput(l) || cplex->lit_in_cplex(l)) { nf++; cplexAddCls({l}); + // cout << "cplex add clause\n"; } + } if (params.verbosity > 0 && nf > 0) - cout << "c Add to CPLEX " << nf << " Forced bvars.\n"; + cout << "c Add to CPLEX " << nf << " Forced bvars. " << tv + << " are forced totalizer outputs\n"; return nf; } -void MaxSolver::greedyAddNewForcedBvars() { - //We pass only forced b-vars to the greedy solver +int MaxSolver::greedyAddNewForcedBvars() { + // We pass only forced b-vars to the greedy solver greedyNU.update(satsolver); vector& forced = greedyNU.forced; - int nf {0}; - for(auto l : forced) - if(bvars.isBvar(l)) { + int nf{0}; + // bvars.printVars(); + for (auto l : forced) { + if (bvars.isBvar(l)) { nf++; greedysolver->addClause({l}); } + } if (params.verbosity > 0 && nf > 0) - cout << "c Add to greedysolver " << nf << " Forced bvars.\n"; + cout << "c Add to greedysolver " << nf << " Forced bvars.\n"; + return nf; } void MaxSolver::muserAddNewForcedVars() { - //We pass on ordinary variables and b-variables to the muser. - //Note the sat solver might have addition control variables that - //have been forced on its trail e.g., eqCVar. These should not be - //sent to the muser. + // We pass on ordinary variables and b-variables to the muser. + // Note the sat solver might have addition control variables that + // have been forced on its trail e.g., eqCVar. These should not be + // sent to the muser. muserNU.update(satsolver); vector& forced = muserNU.forced; - if(params.verbosity > 2) + if (params.verbosity > 2) cout << "Adding forced units to MUSER " << forced << "\n"; - int nf {0}; - for(auto l : forced) - //Note muser might not know about l, but we inform it of l and its value - //in case later on the muser has to deal with clauses involving l. - //don't add forced control variables - if(muser->curVal(l) == l_Undef) { + int nf{0}; + for (auto l : forced) { + // Note muser might not know about l, but we inform it of l and its value + // in case later on the muser has to deal with clauses involving l. + // don't add forced control variables + if (muser->curVal(l) == l_Undef) { nf++; - if(!muser->addClause({l})) + if (!muser->addClause({l})) cout << "c ERROR: Adding unit to MUSER " << l << " current value " << muser->curVal(l) << " Caused UNSAT\n"; } + } if (params.verbosity > 0 && nf > 0) - cout << "c Add to muser " << nf << " newly forced vars.\n"; + cout << "c Add to muser " << nf << " newly forced vars.\n"; } void MaxSolver::satSolverAddNewForcedVars() { - //We pass on ordinary variables and b-variables from the muser to the sat solver. - //muser control variables are ignored (They shouldn't be any that get through - //the getForced interface in any event). + // We pass on ordinary variables and b-variables from the muser to the sat + // solver. muser control variables are ignored (They shouldn't be any that + // getthrough the getForced interface in any event). satNU.update(muser); vector& forced = satNU.forced; - if(params.verbosity > 2) + if (params.verbosity > 2) cout << "Adding forced units to satsolver " << forced << "\n"; - int nf {0}; - for(auto l : forced) - if(satsolver->curVal(l) == l_Undef) { + int nf{0}; + for (auto l : forced) + if (satsolver->curVal(l) == l_Undef) { nf++; satsolver->addClause({l}); } @@ -1195,100 +1460,80 @@ void MaxSolver::satSolverAddNewForcedVars() { } void MaxSolver::satSolverAddBvarsFromSofts() { - //If we are using only the Fb theory, when a soft is satisfied by forced literals - //the corresponding b-variable will not be set. Use this routine to add these forced - //b-variables to the sat solver. (This allows the sat solver to delete these softs - //And also to pass the forced b-vars to CPLEX, the muser and the greedy solver - //perhaps helping them as well.) Note we can only process ordinary variables - //from the sat solver. + // If we are using only the Fb theory, when a soft is satisfied by forced + // literals the corresponding b-variable will not be set. Use this routine + // to add these forced b-variables to the sat solver. (This allows the sat + // solver to delete these softs And also to pass the forced b-vars to CPLEX, + // the muser and the greedy solver perhaps helping them as well.) Note we + // can only process ordinary variables from the sat solver. satBvarNU.update(satsolver); vector& forced = satBvarNU.forced; /*static int notSeen {0}; - vector forced { satsolver->getForced(notSeen) }; //Get unprocessed forced - notSeen += forced.size();*/ - - if(params.verbosity > 2) - cout << "Processing new forced units for adding satisfied softs to satsolver " << forced << "\n"; - - int nf {0}; - for(auto l : forced) - if(bvars.isOvar(l)) { - for(auto sftIdx : sftSatisfied[toInt(l)]) { + vector forced { satsolver->getForced(notSeen) }; //Get unprocessed + forced notSeen += forced.size();*/ + + if (params.verbosity > 2) + cout << "Processing new forced units for adding satisfied softs to " + "satsolver " + << forced << "\n"; + + int nf{}; + for (auto l : forced) + if (bvars.isOvar(l)) { + for (auto sftIdx : sftSatisfied[toInt(l)]) { Lit blit = bvars.litOfCls(sftIdx); - if(satsolver->curVal(blit) == l_Undef) { + if (satsolver->curVal(blit) == l_Undef) { ++nf; satsolver->addClause({~blit}); - //debug - //cout << "Adding forced negated blit to satsolver " << ~blit << "\n"; + // debug + // cout << "Adding forced negated blit to satsolver " << ~blit << + // "\n"; } } } - if(params.verbosity > 0 && nf > 0) + if (params.verbosity > 0 && nf > 0) cout << "c Add to satsolver " << nf << " forced negated bvars.\n"; } int MaxSolver::cplexAddNewClauses() { - auto n_new_constraints {cplexClauses.size()}; + auto n_new_constraints = cplexClauses.size(); if (params.verbosity > 0) { double totalLits = cplexClauses.total_size(); - cout << "c CPLEX: += " << cplexClauses.size() << " Clauses. Average size =" - << totalLits/cplexClauses.size() << "\n"; + cout << "c CPLEX: += " << cplexClauses.size(); + if (cplexClauses.size()) + cout << " Clauses. Average size =" << totalLits / cplexClauses.size(); + cout << "\n"; } - for(size_t i = 0; i < cplexClauses.size(); i++) + for (size_t i = 0; i < cplexClauses.size(); i++) { cplexAddCls(cplexClauses.getVec(i)); + } cplexClauses.clear(); return n_new_constraints; } -void MaxSolver::greedyAddNewClauses() { - if (params.verbosity > 0) { +int MaxSolver::greedyAddNewClauses() { + auto n_new_constraints = greedyClauses.size(); + + if (params.verbosity > 1) { double totalLits = greedyClauses.total_size(); - cout << "c GREEDY: += " << greedyClauses.size() << " Clauses. Average size =" - << totalLits/greedyClauses.size() << "\n"; + cout << "c GREEDY: += " << greedyClauses.size(); + if (greedyClauses.size()) + cout << " Clauses. Average size =" << totalLits / greedyClauses.size(); + cout << "\n"; } - for(size_t i = 0; i < greedyClauses.size(); i++) + for (size_t i = 0; i < greedyClauses.size(); i++) { greedysolver->addClause(greedyClauses.getVec(i)); - greedyClauses.clear(); + } + greedyClauses.clear(); + return n_new_constraints; } void MaxSolver::processMutexes() { - auto& mxs = theWcnf->get_SCMxs(); - /*For each mutex. - (a) if mutex was transformed and cplex was seeded with some of the - mx lits, then encode to clplex the relation between the mx - encoding lit and the mx lits. - Note1. the greedy solver does not get any such clauses, it - only gets b-lit cores) - Note2. cplex will be seeded with some mx lits only if we - have params.mx_seed_originals - - (b) Otherwise there is no encoding lit so the mx is over blits. - If params.mx_constrain_hs is true we inform cplex and the greedy - solver of the mx over these blits - */ - - vector in_cplex (bvars.maxvar()+1, 0); - for(auto cls : cplexClauses) //not yet feed into cplex - for(auto l : cls) - in_cplex[var(l)] = 1; - for(size_t i=0; i < in_cplex.size(); i++) //already in cplex - if(cplex->var_in_cplex(i)) - in_cplex[i] = 1; - - for(auto& mx : mxs) { - if(mx.encoding_lit() != lit_Undef) { - bool mx_lits_in_cplex {false}; - for(auto l : mx.soft_clause_lits()) - mx_lits_in_cplex |= in_cplex[var(l)]; - if(mx_lits_in_cplex) - cplex->add_mutex_constraint(mx); - } - else if(params.mx_constrain_hs) { - cplex->add_mutex_constraint(mx); - greedysolver->addMutexConstraint(mx); - } + for (auto& mx : theWcnf->get_SCMxs()) { + cplex->add_mutex_constraint(mx); + greedysolver->addMutexConstraint(mx); } } @@ -1310,44 +1555,50 @@ void MaxSolver::seed_equivalence() { /*TODO this is way too complicated---should be simplified*/ - vector>> cplex_cores (2); - vector>> cplex_ncores (2); - vector>> cplex_mixed (2); - vector>> cplex_ordinary (2); + vector>> cplex_cores(2); + vector>> cplex_ncores(2); + vector>> cplex_mixed(2); + vector>> cplex_ordinary(2); bool is_core, is_ncore, is_mixed; - bool seed_all { params.seed_all_limit >= theWcnf->dimacsNvars() }; + bool seed_all{params.seed_all_limit >= theWcnf->dimacsNvars()}; allClausesSeeded = true; - for(size_t isLearnt=0; isLearnt < (params.seed_learnts ? 2 : 1); isLearnt++) { - for(int i =0; i < satsolver->getNClauses(isLearnt); i++) { + for (size_t isLearnt = 0; isLearnt < (params.seed_learnts ? 2 : 1); + isLearnt++) { + for (int i = 0; i < satsolver->getNClauses(isLearnt); i++) { auto seedingCls = satsolver->getIthClause(i, isLearnt); is_core = true; is_ncore = true; is_mixed = true; - //cout << (isLearnt ? "l#" : "c#") << i << ". " << seedingCls << "\n"; - for(auto l : seedingCls) { - is_core &= bvars.isCore(l) || (bvars.orig_IsCore(l) && params.mx_seed_originals); - is_ncore &= bvars.isNonCore(l) || (bvars.orig_IsNonCore(l) && params.mx_seed_originals); - is_mixed &= bvars.isBvar(l) || (bvars.inMutex(l) && params.mx_seed_originals); + // cout << (isLearnt ? "l#" : "c#") << i << ". " << seedingCls << "\n"; + for (auto l : seedingCls) { + is_core &= bvars.isCore(l) || + (bvars.orig_IsCore(l) && params.mx_seed_originals); + is_ncore &= bvars.isNonCore(l) || + (bvars.orig_IsNonCore(l) && params.mx_seed_originals); + is_mixed &= + bvars.isBvar(l) || (bvars.inMutex(l) && params.mx_seed_originals); } - bool keep = seed_all || is_core || (is_ncore && params.seed_type > 1) || (is_mixed && params.seed_type > 2); - if(!keep) { + bool keep = seed_all || theWcnf->orig_all_lits_are_softs() || is_core || + (is_ncore && params.seed_type > 1) || + (is_mixed && params.seed_type > 2); + if (!keep) { seedingCls.clear(); - if(!isLearnt) - allClausesSeeded = false; + if (!isLearnt) allClausesSeeded = false; } - if(seedingCls.size() == 2 - && bvars.areSubsumedByMx(seedingCls[0], seedingCls[1])) + if (seedingCls.size() == 2 && + bvars.areSubsumedByMx(seedingCls[0], seedingCls[1])) { seedingCls.clear(); + } if (seedingCls.size() > 0) { - if(is_core) + if (is_core) cplex_cores[isLearnt].push_back(seedingCls); - else if(is_ncore) + else if (is_ncore) cplex_ncores[isLearnt].push_back(seedingCls); - else if(is_mixed) + else if (is_mixed) cplex_mixed[isLearnt].push_back(seedingCls); else cplex_ordinary[isLearnt].push_back(seedingCls); @@ -1355,10 +1606,11 @@ void MaxSolver::seed_equivalence() { } } - if(params.verbosity > 0) { - for(size_t isLearnt = 0; isLearnt < (params.seed_learnts ? 2 : 1); isLearnt++) { + if (params.verbosity > 0) { + for (size_t isLearnt = 0; isLearnt < (params.seed_learnts ? 2 : 1); + isLearnt++) { auto n = cplex_cores[isLearnt].size() + cplex_ncores[isLearnt].size() + - cplex_mixed[isLearnt].size() + cplex_ordinary[isLearnt].size(); + cplex_mixed[isLearnt].size() + cplex_ordinary[isLearnt].size(); cout << "c EqSeed: found " << n << " seedable constraints from " << (isLearnt ? " learnts\n" : "input clauses\n"); @@ -1369,69 +1621,67 @@ void MaxSolver::seed_equivalence() { } } - //Now add the accumulated seed constraints up to the supplied limit. - //prioritize. cores before nonCores before Mixed. - //Short constraints before long ones. + // Now add the accumulated seed constraints up to the supplied limit. + // prioritize. cores before nonCores before Mixed. + // Short constraints before long ones. - auto mergeClauses = [](vector>> &v) { - vector> clauses {}; - for(auto cls_set : v) - for(auto cls : cls_set) - clauses.push_back(std::move(cls)); + auto mergeClauses = [](vector>>& v) { + vector> clauses{}; + for (auto cls_set : v) + for (auto cls : cls_set) clauses.push_back(std::move(cls)); return clauses; }; - auto cores {mergeClauses(cplex_cores)}; - auto ncores {mergeClauses(cplex_ncores)}; - auto mixed {mergeClauses(cplex_mixed)}; - auto ordinary {mergeClauses(cplex_ordinary)}; + auto cores = mergeClauses(cplex_cores); + auto ncores = mergeClauses(cplex_ncores); + auto mixed = mergeClauses(cplex_mixed); + auto ordinary = mergeClauses(cplex_ordinary); - auto vecSizeLt = [](const vector &v1, const vector& v2){ return v1.size() < v2.size(); }; + auto vecSizeLt = [](const vector& v1, const vector& v2) { + return v1.size() < v2.size(); + }; sort(cores.begin(), cores.end(), vecSizeLt); sort(ncores.begin(), ncores.end(), vecSizeLt); sort(mixed.begin(), mixed.end(), vecSizeLt); sort(ordinary.begin(), ordinary.end(), vecSizeLt); - auto addUpTo = [this](vector> &c, int &lim, size_t &nAdd) { - //add up to lim constraints to cplex. Update lim and number - //added, return total length of constraints added - double tl {}; - int i {}; - for(i = 0; static_cast(i) < c.size() && i < lim; i++) { + auto addUpTo = [this](vector>& c, int& lim, size_t& nAdd) { + // add up to lim constraints to cplex. Update lim and number + // added, return total length of constraints added + double tl{}; + int i{}; + for (i = 0; static_cast(i) < c.size() && i < lim; i++) { tl += c[i].size(); store_cplex_greedy_cls(c[i]); } nAdd = i; - lim -= i; + lim -= c.size(); return tl; }; - size_t n_cores {}, n_ncores {}, n_mixed {}, n_ordinary {}; - double l_cores {}, l_ncores {}, l_mixed {}, l_ordinary {}; - int limit {params.seed_max}; + size_t n_cores{}, n_ncores{}, n_mixed{}, n_ordinary{}; + double l_cores{}, l_ncores{}, l_mixed{}, l_ordinary{}; + int limit{params.seed_max}; - l_cores = addUpTo(cores, limit, n_cores); - l_ncores = addUpTo(ncores, limit, n_ncores); - l_mixed = addUpTo(mixed, limit, n_mixed); + l_cores = addUpTo(cores, limit, n_cores); + l_ncores = addUpTo(ncores, limit, n_ncores); + l_mixed = addUpTo(mixed, limit, n_mixed); l_ordinary = addUpTo(ordinary, limit, n_ordinary); - if(limit < 0) - allClausesSeeded = false; + if (limit < 0) allClausesSeeded = false; if (params.verbosity > 0) { cout << "c EqSeed: #seeded constraints " << params.seed_max - limit << "\n"; - cout << "c EqSeed: cores " << n_cores - << " Ave length " << (n_cores > 0 ? l_cores/n_cores : 0.0) << "\n"; - cout << "c EqSeed: non-cores " << n_ncores - << " Ave length " << (n_ncores > 0 ? l_ncores/n_ncores : 0.0) << "\n"; - cout << "c EqSeed: mixed cores " << n_mixed - << " Ave length " << (n_mixed > 0 ? l_mixed/n_mixed : 0.0) << "\n"; - cout << "c EqSeed: ordinary clauses " << n_ordinary - << " Ave length " << (n_ordinary > 0 ? l_ordinary/n_ordinary : 0.0) << "\n"; - - if(allClausesSeeded) - cout << "c CPLEX has full input theory\n"; + cout << "c EqSeed: cores " << n_cores << " Ave length " + << (n_cores > 0 ? l_cores / n_cores : 0.0) << "\n"; + cout << "c EqSeed: non-cores " << n_ncores << " Ave length " + << (n_ncores > 0 ? l_ncores / n_ncores : 0.0) << "\n"; + cout << "c EqSeed: mixed cores " << n_mixed << " Ave length " + << (n_mixed > 0 ? l_mixed / n_mixed : 0.0) << "\n"; + cout << "c EqSeed: ordinary clauses " << n_ordinary << " Ave length " + << (n_ordinary > 0 ? l_ordinary / n_ordinary : 0.0) << "\n"; + if (allClausesSeeded) cout << "c CPLEX has full input theory\n"; } } @@ -1439,83 +1689,106 @@ void MaxSolver::printStatsAndExit(int signum, int exitType) { if (!printStatsExecuted) { printStatsExecuted = true; double cpu_time = cpuTime(); - double mem_used = memUsedPeak(); + double mem_used = Minisat::memUsedPeak(); - if(signum >= 0) { - cout << "c INTERRUPTED signal " << signum << "\n"; + if (signum >= 0) { + cout << "c INTERRUPTED signal " << signum << "\n"; } - if(!solved) { + if (!solved) { cout << "c unsolved\n"; - cout << "c Best LB Found: " << LB()+theWcnf->baseCost() << "\n"; - cout << "c Best UB Found: " << UB()+theWcnf->baseCost() << "\n"; + cout << "c Best LB Found: " << LB() + theWcnf->baseCost() << "\n"; + cout << "c Best UB Found: " << UB() + theWcnf->baseCost() << "\n"; Weight solCost = UB() + theWcnf->baseCost(); Weight intPart; auto p = cout.precision(); - if(modf(solCost, &intPart) > 0) + if (modf(solCost, &intPart) > 0) cout << "o " << solCost << "\n"; else cout << "o " << std::fixed << std::setprecision(0) << solCost << "\n"; cout.precision(p); cout.unsetf(std::ios::fixed); cout << "s UNKNOWN\n"; - if(params.printBstSoln) { + if (params.printBstSoln) { cout << "c Best Model Found:\n"; - if(haveUBModel) { - theWcnf->rewriteModelToInput(UBmodel); - for(size_t i=0; i < UBmodel.size(); i++) - if(UBmodel[i] == l_Undef) - UBmodel[i] = l_True; //set unset vars to arbitary value (true) + if (haveUBModel) { printSolution(UBmodel); - cout << "\n"; int nfalseSofts; - if(theWcnf->checkModelFinal(UBmodel, nfalseSofts) != UB() + theWcnf->baseCost()) //we cannot use wcnf after this! - cout << "c ERROR incorrect model reported" << endl; + if (theWcnf->checkModelFinal(UBmodel, nfalseSofts) != + UB() + theWcnf->baseCost()) // we cannot use wcnf after this! + cout << "c ERROR incorrect model reported" << std::endl; else - cout << "c Solved: Number of falsified softs = " << nfalseSofts << "\n"; - } - else + cout << "c Solved: Number of falsified softs = " << nfalseSofts + << "\n"; + } else cout << "c No Model of hard clauses found\n"; } } - cout << "c CPLEX: #constraints " << cplex->nCnstr() << "\n"; - cout << "c CPLEX: Avg constraint size " - << cplex->totalCnstrSize()/(double)cplex->nCnstr() << "\n"; - cout << "c CPLEX: #non-core constraints " << cplex->nNonCores() << "\n"; - cout << "c CPLEX: Ave non-core size " - << (cplex->totalNonCore())/((double) (cplex->nNonCores())) << "\n"; - cout << "c SAT: #calls " << satsolver->nSolves() << "\n"; - cout << "c SAT: Total time " << satsolver->total_time() + muser->total_time() << "\n"; + cout << "c SAT: Total time " + << satsolver->total_time() + muser->total_time() << "\n"; cout << "c SAT: #muser calls " << muser->nSolves() << " (" - << muser->nSucc_Solves()*100.0/muser->nSolves() << " % successful)\n"; - cout << "c SAT: Minimize time " << muser->total_time() - << " (" << muser->total_time()*100/(satsolver->total_time()+muser->total_time()) << "%)\n"; + << muser->nSucc_Solves() * 100.0 / muser->nSolves() + << " % successful)\n"; + cout << "c SAT: Minimize time " << muser->total_time() << " (" + << muser->total_time() * 100 / + (satsolver->total_time() + muser->total_time()) + << "%)\n"; cout << "c SAT: Avg constraint minimization " - << amountConflictMin/(double) cplex->nCnstr() << "\n"; - cout << "c CPLEX: #calls " << cplex->nSolves() << "\n"; - cout << "c CPLEX: Total time " << cplex->total_time() << "\n"; - cout << "c GREEDY: #calls " << greedysolver->nSolves() << "\n";; - cout << "c GREEDY: Total time " << greedysolver->total_time() << "\n"; - - if(params.find_forced) { - cout << "c FindForced: Total failed lits detecte " << nFailedLits << "\n"; - cout << "c FindForced: Total lits forced by bounds " << nForcedByBounds << "\n"; + << amountConflictMin / (double)cplex->nCnstr() << "\n"; + if (params.find_forced) { + cout << "c FindForced: Total failed lits detecte " << nFailedLits + << "\n"; + cout << "c FindForced: Total lits forced by bounds " << nForcedByBounds + << "\n"; } - - if(params.lp_harden) { - cout << "c LP-Bounds: Hardened " << n_softs_forced_hard << " softs "; - cout << n_softs_forced_hard_not_in_cplex << " because not in cplex\n"; - cout << "c LP-Bounds: Relaxed " << n_softs_forced_relaxed << " softs\n"; - cout << "c LP-Bounds: Total time " << cplex->total_lp_time() << "\n"; - cout << "c LP-Bounds: #calls " << cplex->nLPSolves() << "\n"; + cout << "c GREEDY: #calls " << greedysolver->nSolves() << "\n"; + ; + cout << "c GREEDY: Total time " << greedysolver->total_time() << "\n"; cout << "c CPLEX: #calls " << cplex->nSolves() << "\n"; cout << "c CPLEX: Total time " << cplex->total_time() << "\n"; - + cout << "c CPLEX: #constraints " << cplex->nCnstr() << "\n"; + if (cplex->nCnstr()) + cout << "c CPLEX: Avg constraint size " + << cplex->totalCnstrSize() / (double)cplex->nCnstr() << "\n"; + cout << "c CPLEX: #non-core constraints " << cplex->nNonCores() << "\n"; + if (cplex->nNonCores()) + cout << "c CPLEX: Ave non-core size " + << (cplex->totalNonCore()) / ((double)(cplex->nNonCores())) << "\n"; + + cout << "c LP-Bounds: Total time " << cplex->total_lp_time() << "\n"; + cout << "c LP-Bounds: #calls " << cplex->nLPSolves() << "\n"; + if (params.lp_harden) { + cout << "c LP-Bounds: Forced " + << n_softs_forced_hard + n_softs_forced_false + + n_softs_forced_hard_not_in_cplex + n_ovars_forced_true + + n_ovars_forced_false + n_touts_forced_true + + n_touts_forced_false + << " variables\n"; + if (n_softs_forced_hard > 0) + cout << "c hardned softs: " << n_softs_forced_hard + << "\n"; + if (n_softs_forced_false > 0) + cout << "c falsified softs: " << n_softs_forced_false + << "\n"; + if (n_softs_forced_hard_not_in_cplex > 0) + cout << "c hardned softs not in cplex: " + << n_softs_forced_hard_not_in_cplex << "\n"; + if (n_ovars_forced_true > 0) + cout << "c ordinary vars made true: " << n_ovars_forced_true + << "\n"; + if (n_ovars_forced_false > 0) + cout << "c ordinary vars made false: " << n_ovars_forced_false + << "\n"; + if (n_touts_forced_true > 0) + cout << "c touts made true: " << n_touts_forced_true + << "\n"; + if (n_touts_forced_false > 0) + cout << "c touts made false: " << n_touts_forced_false + << "\n"; } - cout << "c MEM MB: " << mem_used << "\n"; cout << "c CPU: " << cpu_time << "\n"; } @@ -1526,62 +1799,68 @@ void MaxSolver::printStatsAndExit(int signum, int exitType) { void MaxSolver::reportCplex(Weight cplexLB, Weight solnWt) { if (params.verbosity > 0) { - cout <<"c CPLEX returns incumbent with cost " << solnWt << " and lower bound of " << cplexLB + cout << "c CPLEX returns incumbent with cost " << solnWt + << " and lower bound of " << cplexLB << " time = " << cplex->solveTime() << "\n"; } } -void MaxSolver::reportSAT_min(lbool result, double iTime, size_t orig_size, int nMins, double mTime, size_t final_size) { +void MaxSolver::reportSAT_min(lbool result, double iTime, size_t orig_size, + int nMins, double mTime, size_t final_size) { if (params.verbosity > 1) { cout << "c SAT_MIN: " << result; - if(result==l_False) - cout << ", In cnf size = " << orig_size << ", reduction = " << orig_size - final_size; - cout << ", Init Sat time: " << iTime - << ", Min calls: " << nMins - << ", Min time: " << mTime << "\n"; + if (result == l_False) + cout << ", In cnf size = " << orig_size + << ", reduction = " << orig_size - final_size; + cout << ", Init Sat time: " << iTime << ", Min calls: " << nMins + << ", Min time: " << mTime << "\n"; } } -void MaxSolver::reportForced(const vector &forced, Weight wt) { +void MaxSolver::reportForced(const vector& forced, Weight wt) { if (params.verbosity > 0 && forced.size() > 0) { - cout << "c Forced Soft Clauses (#" << forced.size() << ", wt = " << wt << "\n"; - if (params.verbosity > 2) - outputConflict(forced); + cout << "c Forced Soft Clauses (#" << forced.size() << ", wt = " << wt + << "\n"; + if (params.verbosity > 2) outputConflict(forced); } } -lbool MaxSolver::satsolve_min(const Assumps& inAssumps, vector& outConflict, - double sat_cpu_lim, double mus_cpu_lim) { - double isatTime {0}; - //satsolver->pruneLearnts(); - lbool result = (sat_cpu_lim > 0) ? - satsolver->solveBudget(inAssumps.vec(), outConflict, sat_cpu_lim) : - satsolver->solve(inAssumps.vec(), outConflict); - - if(params.fb) - satSolverAddBvarsFromSofts(); - - size_t orig_size { outConflict.size() }; - +lbool MaxSolver::satsolve_min(const Assumps& inAssumps, + vector& outConflict, double sat_cpu_lim, + double mus_cpu_lim) { + double isatTime{0}; + // cout << " satsolve_min\n"; + // satsolver->pruneLearnts(); + lbool result = + (sat_cpu_lim > 0) + ? satsolver->solveBudget(inAssumps.vec(), outConflict, sat_cpu_lim) + : satsolver->solve(inAssumps.vec(), outConflict); + // cout << "after solve\n"; + if (params.fb) satSolverAddBvarsFromSofts(); + size_t orig_size{outConflict.size()}; isatTime = satsolver->solveTime(); muser->startTimer(); + size_t origsize = outConflict.size(); if (result == l_False && params.min_type == 1 && outConflict.size() > 2) { - int origsize = outConflict.size(); - minimize_muser(outConflict, mus_cpu_lim); + minimize_muser(outConflict, mus_cpu_lim); // slow down amountConflictMin += origsize - outConflict.size(); } - - if(outConflict.size() == 1 && satsolver->curVal(outConflict[0]) == l_Undef) { - //probably quite rare...but since units are propagated to all solvers why not? - satsolver->addClause( {outConflict[0]} ); - cout << "c NOTE: added new unit via minimization\n"; + // cout << "after minimize_muser\n"; + if (outConflict.size() <= 3 || origsize > outConflict.size()) { + // add short conflicts and clauses reduced by the muser back into the sat + // solver. + satsolver->addClause(outConflict); + if (params.verbosity > 1) + cout << "c added new clause of size " << outConflict.size() + << ": from conflict\n"; } + reportSAT_min(result, isatTime, orig_size, muser->nSatSolvesSinceTimer(), + muser->elapTime(), outConflict.size()); - reportSAT_min(result, isatTime, orig_size, muser->nSatSolvesSinceTimer(), muser->elapTime(), outConflict.size()); return result; } -void MaxSolver::minimize_muser(vector &con, double mus_cpu_lim) { +void MaxSolver::minimize_muser(vector& con, double mus_cpu_lim) { /* Muser tries to remove lits from end of con first. Sort so that better lits to remove are later in the vector. */ @@ -1596,54 +1875,56 @@ void MaxSolver::minimize_muser(vector &con, double mus_cpu_lim) { };*/ muserAddNewForcedVars(); - if(!doMin) - return; - - size_t reduction {0}; - size_t osize {0}; + if (!doMin) return; - if(doMin) { + size_t reduction{0}; + size_t osize{0}; + if (doMin) { osize = con.size(); - //std::sort(con.begin(), con.end(), minConf); + // cout << "issue with sort\n"; + // std::sort(con.begin(), con.end(), minConf); std::sort(con.begin(), con.end(), blit_lt); - if(mus_cpu_lim > 0) + // cout << "not issue with sort\n"; + if (mus_cpu_lim > 0) muser->musBudget(con, mus_cpu_lim); else muser->mus(con); - //debug - //check_mus(con); + // debug + // check_mus(con); satSolverAddNewForcedVars(); - if(params.fb) - satSolverAddBvarsFromSofts(); + if (params.fb) satSolverAddBvarsFromSofts(); reduction = osize - con.size(); } - if(osize > 1) { + if (osize > 1) { mtime += muser->solveTime(); ++mcalls; - m_sum_reduced_frac += static_cast(reduction)/static_cast(osize); + m_sum_reduced_frac += + static_cast(reduction) / static_cast(osize); } doMin = params.mus_min_red <= 0 - //|| (mtime < 124.0) //give muser some time to allow rate computation to be more accurate - //|| mcalls < 64 - || (mtime < 64.0) //give muser some time to allow rate computation to be more accurate - || (m_sum_reduced_frac/mcalls) > params.mus_min_red; - - if(params.verbosity > 1) + //|| (mtime < 124.0) //give muser some time to allow rate computation + // to be more accurate + //|| mcalls < 64 + || (mtime < 64.0) // give muser some time to allow rate computation + // to be more accurate + || (m_sum_reduced_frac / mcalls) > params.mus_min_red; + + if (params.verbosity > 1) cout << "c doMin = " << doMin << " mtime = " << mtime - << " average reduction = " << m_sum_reduced_frac/mcalls << "\n"; + << " average reduction = " << m_sum_reduced_frac / mcalls << "\n"; } -void MaxSolver::check_mus(vector &con) { - //Test to see if con is a MUS (DEBUGGING) +void MaxSolver::check_mus(vector& con) { + // Test to see if con is a MUS (DEBUGGING) size_t orig_size = con.size(); vector assumps; vector critical; vector tmp; auto litHash = [](const Lit l) { return std::hash()(toInt(l)); }; - std::unordered_set inCon(con.size(), litHash); - auto findInCon = [&inCon](Lit l){ return inCon.find(l) == inCon.end(); }; + std::unordered_set inCon(con.size(), litHash); + auto findInCon = [&inCon](Lit l) { return inCon.find(l) == inCon.end(); }; // For sorting the conflict before it is minimized // Can't make all lits in conflict true. Neg bvars: @@ -1667,45 +1948,41 @@ void MaxSolver::check_mus(vector &con) { // || (sign(i) && sign(j) && (bvars.wt(var(i)) < bvars.wt(var(j)))) // || (!sign(i) && !sign(j) && (bvars.wt(var(i)) > bvars.wt(var(j)))); }; - //std::sort(con.begin(), con.end(), mConf); + // std::sort(con.begin(), con.end(), mConf); assumps.clear(); - for (size_t i = 0; i < con.size(); i++) - assumps.push_back(~con[i]); - if(satsolver->solve(assumps,tmp) == l_True) + for (size_t i = 0; i < con.size(); i++) assumps.push_back(~con[i]); + if (satsolver->solve(assumps, tmp) == l_True) cout << "check_mus: ERROR mus not unsatisfiable\n"; - while(con.size() > 0) { + while (con.size() > 0) { Lit min = con.back(); con.pop_back(); assumps.clear(); for (size_t i = 0; i < critical.size(); i++) assumps.push_back(~critical[i]); - for (size_t i = 0; i < con.size(); i++) - assumps.push_back(~con[i]); + for (size_t i = 0; i < con.size(); i++) assumps.push_back(~con[i]); - if(satsolver->solve(assumps, tmp) == l_True) { - //min was critical + if (satsolver->solve(assumps, tmp) == l_True) { + // min was critical critical.push_back(min); - } - else { + } else { inCon.clear(); - for(auto lt : tmp) - inCon.insert(lt); + for (auto lt : tmp) inCon.insert(lt); auto p = std::remove_if(con.begin(), con.end(), findInCon); con.erase(p, con.end()); } } con = critical; - if(con.size() != orig_size) { + if (con.size() != orig_size) { cout << "check_mus: ERROR mus not minimal orig size = " << orig_size << " reduced size = " << con.size() << "\n"; cout << " conflict = " << con << "\n"; } } -void MaxSolver::outputConflict(const vector &con) { +void MaxSolver::outputConflict(const vector& con) { cout << "conflict clause (" << con.size() << "): "; for (size_t i = 0; i < con.size(); i++) { satsolver->printLit(con[i]); @@ -1714,15 +1991,14 @@ void MaxSolver::outputConflict(const vector &con) { cout << "\n"; } -//Currently not used. But if we call satsolver->pruneLearnts -//This function will be used to determine which learnts to delete -//Potentially inefficient if called frequently...requires copying -//Sat solver's clause into passed std:vector. -bool MaxSolver::deleteLearntTest(const vector &c) const { - if (c.size() <= 3) - return false; - //keep hard learnts - //if (!params.delete_hard_learnts) { +// Currently not used. But if we call satsolver->pruneLearnts +// This function will be used to determine which learnts to delete +// Potentially inefficient if called frequently...requires copying +// Sat solver's clause into passed std:vector. +bool MaxSolver::deleteLearntTest(const vector& c) const { + if (c.size() <= 3) return false; + // keep hard learnts + // if (!params.delete_hard_learnts) { bool containsBVar = false; for (size_t i = 0; i < c.size(); i++) { if (bvars.isBvar(c[i])) { @@ -1748,12 +2024,12 @@ void MaxSolver::unsatFound() { fflush(stdout); } -void MaxSolver::optFound(std::string reason) { +void MaxSolver::optFound(const std::string& reason) { cout << reason << "\n"; Weight solCost = UB() + theWcnf->baseCost(); Weight intPart; auto p = cout.precision(); - if(modf(solCost, &intPart) > 0) + if (modf(solCost, &intPart) > 0) cout << "o " << solCost << "\n"; else cout << "o " << std::fixed << std::setprecision(0) << solCost << "\n"; @@ -1761,34 +2037,26 @@ void MaxSolver::optFound(std::string reason) { cout.unsetf(std::ios::fixed); cout << "s OPTIMUM FOUND\n"; solved = true; - theWcnf->rewriteModelToInput(UBmodel); - - for(size_t i=0; i < UBmodel.size(); i++) - if(UBmodel[i] == l_Undef) - UBmodel[i] = l_True; //set unset vars to arbitary value (true) - printSolution(UBmodel); int nfalseSofts; - if(theWcnf->checkModelFinal(UBmodel, nfalseSofts) != UB() + theWcnf->baseCost()) //we cannot use wcnf after this! - cout << "c ERROR incorrect model reported" << endl; + if (theWcnf->checkModelFinal(UBmodel, nfalseSofts) != + UB() + theWcnf->baseCost()) // we cannot use wcnf after this! + cout << "c ERROR incorrect model reported" << std::endl; else cout << "c Solved: Number of falsified softs = " << nfalseSofts << "\n"; } - void MaxSolver::printCurClause(const vector& cls) { cout << "["; - for (size_t i = 0; i < cls.size()-1; i++) { + for (size_t i = 0; i < cls.size() - 1; i++) { satsolver->printLit(cls[i]); cout << ", "; } - if (cls.size() > 0) - satsolver->printLit(cls.back()); + if (cls.size() > 0) satsolver->printLit(cls.back()); cout << "]"; } - -void MaxSolver::printErrorAndExit(const char *msg) { +void MaxSolver::printErrorAndExit(const char* msg) { cout << msg << "\n"; printStatsAndExit(-1, 1); } @@ -1800,59 +2068,72 @@ void MaxSolver::printSolution(const vector& model) { // Typically model is filled in before calling, but if model is // incomplete (contains l_Undef) these vars will be printed as false // in the code below. - if(!params.printSoln) - return; - - cout << "v"; - for (size_t i = 0; i < model.size(); i++) { - cout << " " << (model[i] == l_True ? "" : "-") << i+1; + if (!params.printSoln) return; + vector external_model = theWcnf->rewriteModelToInput(model); + + if (params.printNewFormat) { + std::string vline(external_model.size()+2, ' '); + vline[0] = 'v', vline[1] = ' '; + for (size_t i = 0; i < external_model.size(); i++) + vline[i+2] = (external_model[i] == l_True ? '1' : '0'); + cout << vline << std::endl; + // cout << "v "; + // for (size_t i = 0; i < external_model.size(); i++) { + // cout << (external_model[i] == l_True ? "1" : "0"); + // } + //cout << std::endl; + } else { + cout << "v"; + for (size_t i = 0; i < external_model.size(); i++) { + cout << " " << (external_model[i] == l_True ? "" : "-") << i + 1; + } + cout << std::endl; } - cout << endl; } void MaxSolver::configBvar(Var bvar, SatSolver* slv) { - //Configure any blits after they have been added to the sat solver - //Should be passed literal appearing in soft clause (i.e., - //blit=false ==> enforce clause) - if(params.preprocess) + // Configure any blits after they have been added to the sat solver + // Should be passed literal appearing in soft clause (i.e., + // blit=false ==> enforce clause) + if (params.preprocess) { slv->freezeVar(bvar); + } - if(!params.bvarDecisions && !bvars.isOvar(bvar)) - //keep unit b-vars (ordinary vars as decisions) + if (false && !params.bvarDecisions && !bvars.isOvar(bvar)) + // keep unit b-vars (ordinary vars as decisions) slv->setDecisionVar(bvar, false); - //setPolarity determines if sign should be true! So if bvar appears - //positively in clause, we want to set its default sign to true + // setPolarity determines if sign should be true! So if bvar appears + // positively in clause, we want to set its default sign to true //(making it false and activating the clause by defaults) - slv->setPolarity(bvar, - (bvars.coreIsPos(bvar) || bvars.orig_coreIsPos(bvar)) ? - l_True : l_False); + slv->setPolarity(bvar, (bvars.coreIsPos(bvar) || bvars.orig_coreIsPos(bvar)) + ? l_True + : l_False); } void MaxSolver::addHards(SatSolver* slv) { for (size_t i = 0; i < theWcnf->nHards(); i++) { + // printCurClause(theWcnf->getHard(i)); + // cout << "\n"; if (!slv->addClause(theWcnf->getHard(i))) { cout << "c Adding hard clauses caused unsat.\n"; return; } - for(auto lt : theWcnf->hards()[i]) - if(bvars.isBvar(lt) || - (params.mx_seed_originals && bvars.inMutex(lt))) + for (auto lt : theWcnf->hards()[i]) + if (bvars.isBvar(lt) || (params.mx_seed_originals && bvars.inMutex(lt))) configBvar(var(lt), slv); } } - void MaxSolver::addHards(SatSolver* slv, const vector& indicies) { - for(auto i : indicies) { + for (auto i : indicies) { if (!slv->addClause(theWcnf->getHard(i))) { cout << "c Adding hard clauses caused unsat.\n"; return; } - for(auto lt : theWcnf->hards()[i]) - if(bvars.isBvar(lt) || - (params.mx_seed_originals && bvars.inMutex(lt))) + for (auto lt : theWcnf->hards()[i]) + if (bvars.isBvar(lt) || (params.mx_seed_originals && bvars.inMutex(lt))) configBvar(var(lt), slv); } } @@ -1864,18 +2145,20 @@ void MaxSolver::addSofts(SatSolver* slv) { /*cout << "Soft clause " << i << " " << theWcnf->getSoft(i) << "\n" << "blit = " << blit << "\n";*/ - if(theWcnf->softSize(i) > 1) { - //Only non-unit softs get added to solver (units are handled in the assumptions) - vector sftCls {theWcnf->getSoft(i)}; + if (theWcnf->softSize(i) > 1) { + // Only non-unit softs get added to solver (units are handled in the + // assumptions) + vector sftCls{theWcnf->getSoft(i)}; sftCls.push_back(blit); - if(!slv->addClause(sftCls)) { + // printCurClause(sftCls); + // cout << "\n"; + if (!slv->addClause(sftCls)) { cout << "c ERROR: soft clause " << i << " caused solver UNSAT state!\n"; printCurClause(sftCls); return; } - for(auto lt : sftCls) - if(bvars.isBvar(lt) || - (params.mx_seed_originals && bvars.inMutex(lt))) + for (auto lt : sftCls) + if (bvars.isBvar(lt) || (params.mx_seed_originals && bvars.inMutex(lt))) configBvar(var(lt), slv); } } @@ -1884,44 +2167,43 @@ void MaxSolver::addSofts(SatSolver* slv) { void MaxSolver::addSofts(SatSolver* slv, const vector& indicies) { for (auto i : indicies) { Lit blit = bvars.litOfCls(i); - if(theWcnf->softSize(i) > 1) { - //Only non-unit softs get added to solver (units are handled in the assumptions) - vector sftCls {theWcnf->getSoft(i)}; + if (theWcnf->softSize(i) > 1) { + // Only non-unit softs get added to solver (units are handled in the + // assumptions) + vector sftCls{theWcnf->getSoft(i)}; sftCls.push_back(blit); - if(!slv->addClause(sftCls)) { + if (!slv->addClause(sftCls)) { cout << "c ERROR: soft clause " << i << " caused solver UNSAT state!\n"; printCurClause(sftCls); return; } - for(auto lt : sftCls) - if(bvars.isBvar(lt) || - (params.mx_seed_originals && bvars.inMutex(lt))) + for (auto lt : sftCls) + if (bvars.isBvar(lt) || (params.mx_seed_originals && bvars.inMutex(lt))) configBvar(var(lt), slv); } } } void MaxSolver::addSoftEqs(SatSolver* slv, bool removable) { - //add eqs to solver. Optionally make them removable - //Note all softs processed here should have already been added - //to the solver to config their b-vars. + // add eqs to solver. Optionally make them removable + // Note all softs processed here should have already been added + // to the solver to config their b-vars. vector eqcls(removable ? 3 : 2, lit_Undef); for (size_t i = 0; i < theWcnf->nSofts(); i++) - if(theWcnf->softSize(i) > 1) { - if(removable && eqCvarPos == lit_Undef) { - slv->newControlVar(eqCvar, false); //not a decision variable + if (theWcnf->softSize(i) > 1) { + if (removable && eqCvarPos == lit_Undef) { + slv->newControlVar(eqCvar, false); // not a decision variable eqCvarPos = mkLit(eqCvar, false); } Lit bneg = ~bvars.litOfCls(i); - for(auto l: theWcnf->softs()[i]) { - eqcls[0] = ~l; eqcls[1] = bneg; - if(removable) eqcls[2] = eqCvarPos; - //note addClause modifies eqcls - if(!slv->addClause(eqcls)) { - cout << "c ERROR: equality clause (" << eqcls[0] << " " - << eqcls[1]; - if(removable) - cout << " " << eqcls[2]; + for (auto l : theWcnf->softs()[i]) { + eqcls[0] = ~l; + eqcls[1] = bneg; + if (removable) eqcls[2] = eqCvarPos; + // note addClause modifies eqcls + if (!slv->addClause(eqcls)) { + cout << "c ERROR: equality clause (" << eqcls[0] << " " << eqcls[1]; + if (removable) cout << " " << eqcls[2]; cout << ") causes solver UNSAT state!\n"; return; } @@ -1931,25 +2213,24 @@ void MaxSolver::addSoftEqs(SatSolver* slv, bool removable) { void MaxSolver::addSoftEqs(SatSolver* slv, bool removable, const vector& indicies) { - //Note all softs processed here should have already been added - //to the solver to config their b-vars. + // Note all softs processed here should have already been added + // to the solver to config their b-vars. vector eqcls(removable ? 3 : 2, lit_Undef); for (auto i : indicies) - if(theWcnf->softSize(i) > 1) { - if(removable && eqCvarPos == lit_Undef) { - slv->newControlVar(eqCvar, false); //not a decision variable + if (theWcnf->softSize(i) > 1) { + if (removable && eqCvarPos == lit_Undef) { + slv->newControlVar(eqCvar, false); // not a decision variable eqCvarPos = mkLit(eqCvar, false); } Lit bneg = ~bvars.litOfCls(i); - for(auto l: theWcnf->softs()[i]) { - eqcls[0] = ~l; eqcls[1] = bneg; - if(removable) eqcls[2] = eqCvarPos; - //note addClause modifies eqcls - if(!slv->addClause(eqcls)) { - cout << "c ERROR: equality clause (" << eqcls[0] << " " - << eqcls[1]; - if(removable) - cout << " " << eqcls[2]; + for (auto l : theWcnf->softs()[i]) { + eqcls[0] = ~l; + eqcls[1] = bneg; + if (removable) eqcls[2] = eqCvarPos; + // note addClause modifies eqcls + if (!slv->addClause(eqcls)) { + cout << "c ERROR: equality clause (" << eqcls[0] << " " << eqcls[1]; + if (removable) cout << " " << eqcls[2]; cout << ") causes satsolver UNSAT state!\n"; return; } @@ -1958,117 +2239,136 @@ void MaxSolver::addSoftEqs(SatSolver* slv, bool removable, } void MaxSolver::initSftSatisfied() { - vector> satByLit(theWcnf->nVars()*2, vector{}); - for(size_t i = 0; i < theWcnf->nSofts(); i++) - for(auto lt: theWcnf->softs()[i]) - satByLit[toInt(lt)].push_back(i); - for(auto& v : satByLit) - sftSatisfied.addVec(v); - //Debug + vector> satByLit(theWcnf->nVars() * 2, vector{}); + for (size_t i = 0; i < theWcnf->nSofts(); i++) + for (auto lt : theWcnf->softs()[i]) satByLit[toInt(lt)].push_back(i); + for (auto& v : satByLit) sftSatisfied.addVec(v); + // Debug /*cout << "Softs\n"; cout << theWcnf->softs(); cout << "\nFinal occurance list\n"; cout << sftSatisfied;*/ } - void MaxSolver::setNoBvarDecisions() { for (size_t i = 0; i < theWcnf->nSofts(); i++) { Var b = bvars.varOfCls(i); - if(!bvars.isOvar(b)) //don't change ordinary b-vars (from units)? + if (false && + !bvars.isOvar(b)) // don't change ordinary b-vars (from units)? satsolver->setDecisionVar(b, false); } } void MaxSolver::improveModel() { - //Sat solver has found a model. Try to improve the cost of that model by computing a muc. + // Sat solver has found a model. Try to improve the cost of that model by + // computing a muc. vector assumps; vector branchLits; - Weight prevWt {0}; - if(params.verbosity > 0) { + Weight prevWt{0}; + if (params.verbosity > 0) { cout << "c Trying to improve Model\n"; - //cout << "totalClsWt() = " << theWcnf->totalClsWt() << " getSatClsWt() = " << getSatClsWt() << "\n"; + // cout << "totalClsWt() = " << theWcnf->totalClsWt() << " getSatClsWt() = + // " + // << getSatClsWt() << "\n"; prevWt = theWcnf->totalClsWt() - getSatClsWt(); cout << "c Current Model weight = " << prevWt << "\n"; } - for(size_t i = 0; i < bvars.n_bvars(); i++) { + for (size_t i = 0; i < bvars.n_bvars(); i++) { /*cout << "soft clause #" << i << " litofCls = " << bvars.litOfCls(i) - << " model value = " << satsolver->modelValue(bvars.litOfCls(i)) << "\n";*/ - if(satsolver->curVal(bvars.litOfCls(i)) == l_Undef) { //only try to change un-forced b's + << " model value = " << satsolver->modelValue(bvars.litOfCls(i)) << + "\n";*/ + if (satsolver->curVal(bvars.litOfCls(i)) == + l_Undef) { // only try to change un-forced b's auto tval = satsolver->modelValue(bvars.litOfCls(i)); - if(tval == l_True) - branchLits.push_back(~bvars.litOfCls(i)); //try to negate these bvars + if (tval == l_True) + branchLits.push_back(~bvars.litOfCls(i)); // try to negate these bvars else - assumps.push_back(~bvars.litOfCls(i)); //these bvars must remain false + assumps.push_back(~bvars.litOfCls(i)); // these bvars must remain false } } - if(params.verbosity > 1) { - cout << "c assumptions size = " << assumps.size() << " branchlits size = " << branchLits.size() << "\n"; + if (params.verbosity > 1) { + cout << "c assumptions size = " << assumps.size() + << " branchlits size = " << branchLits.size() << "\n"; /*cout << "Assumps = " << assumps << "\n"; cout << "branchlits = " << branchLits << "\n";*/ } - //if(params.improve_model_max_size > 0 && static_cast(branchLits.size()) > params.improve_model_max_size) + // if(params.improve_model_max_size > 0 && + // static_cast(branchLits.size()) > params.improve_model_max_size) // return; - auto val = satsolver->relaxSolve(assumps, branchLits, params.improve_model_cpu_lim); + auto val = + satsolver->relaxSolve(assumps, branchLits, params.improve_model_cpu_lim); - if(params.verbosity > 0) { + if (params.verbosity > 0) { cout << "c Relaxation search yielded " << val << "\n"; Weight newWt = theWcnf->totalClsWt() - getSatClsWt(); - cout << "c Old wt = " << prevWt << " new wt = " << newWt << " improvement = " << prevWt - newWt << "\n"; + cout << "c Old wt = " << prevWt << " new wt = " << newWt + << " improvement = " << prevWt - newWt << "\n"; } } Weight MaxSolver::updateUB() { - //return weight of new model...update UB if it is a better model. + // return weight of new model...update UB if it is a better model. Weight w = getSatClsWt(); if (!haveUBModel || w > sat_wt) { sat_wt = w; - if(params.verbosity > 0) { + if (params.verbosity > 0) { cout << "c New UB found " << UB() << "\n"; cout << "c Elapsed time " << cpuTime() - globalStartTime << "\n"; } have_new_UB_model = true; setUBModel(); - if(params.find_forced) - findForced(); + if (params.find_forced) findForced(); } return theWcnf->totalClsWt() - w; } void MaxSolver::setUBModel() { - //copy sat solvers model to UBmodel; Note sat solver's model might - //be incomplete resulting in l_Undef as the value of some variables + // copy sat solvers model to UBmodel; Note sat solver's model might + // be incomplete resulting in l_Undef as the value of some variables haveUBModel = true; - for(int i = 0; i < theWcnf->nVars(); i++) + if (bvars.n_vars() > UBmodel.size()) UBmodel.resize(bvars.n_vars(), l_Undef); + for (size_t i = 0; i < bvars.n_vars(); i++) UBmodel[i] = satsolver->modelValue(i); - for(size_t i = 0; i < theWcnf->nSofts(); i++) + for (size_t i = 0; i < theWcnf->nSofts(); i++) { UBmodelSofts[i] = tmpModelSofts[i]; + if (UBmodelSofts[i] == l_True) { + // check if blit is set to true (it shouldn't be) + auto blit = bvars.litOfCls(i); + if (UBmodel[var(blit)] == (sign(blit) ? l_False : l_True)) { + cout << "c WARNING blit in model not set to false when soft is " + "satisfied\n"; + } else if (UBmodel[var(blit)] == l_Undef) { + cout << "c setting unset blit in UBmodel\n"; + UBmodel[var(blit)] = sign(blit) ? l_True : l_False; + } + } + } } Weight MaxSolver::getSatClsWt() { - //return sum of weight of satisfied clauses. - //Note sat solver's model might be incomplete. - //and bvars might not be set. - Weight w {0.0}; - for(size_t i = 0; i < theWcnf->nSofts(); i++) { + // return sum of weight of satisfied clauses. + // Note sat solver's model might be incomplete. + // and bvars might not be set. + Weight w{0.0}; + for (size_t i = 0; i < theWcnf->nSofts(); i++) { bool is_sat = false; - //calling theWcnf->softs() avoids copying soft clauses. - for(auto ell : theWcnf->softs()[i]) - if(satsolver->modelValue(ell) == l_True) { + // calling theWcnf->softs() avoids copying soft clauses. + for (auto ell : theWcnf->softs()[i]) + if (satsolver->modelValue(ell) == l_True) { is_sat = true; break; } - if(is_sat) { + if (is_sat) { w += theWcnf->getWt(i); tmpModelSofts[i] = l_True; - } - else + } else { tmpModelSofts[i] = l_False; + } /*else { cout << i << "-th soft falsified bvar = " << bvars.litOfCls(i) << " value = " @@ -2080,102 +2380,132 @@ Weight MaxSolver::getSatClsWt() { cout << "]\n"; }*/ } - //cout << "Computed wt = " << w << " total wt = " << theWcnf->totalClsWt() << "\n"; + // cout << "Computed wt = " << w << " total wt = " << theWcnf->totalClsWt() + // << + // "\n"; return w; } Weight MaxSolver::getForcedWt() { - //return weight of forced b-vars. Note that - //this works with both fb and fbeq. In fb the solver could set some - //b-vars arbitrarily. But it can't force them to be true. - + // return weight of forced b-vars. Note that + // this works with both fb and fbeq. In fb the solver could set some + // b-vars arbitrarily. But it can't force them to be true. forcedWtNU.update(satsolver); vector& forced = forcedWtNU.forced; - /*static int notSeen {0}; vector forced { satsolver->getForced(notSeen) }; notSeen += forced.size();*/ - for(auto l : forced) { - if(bvars.isBvar(l)) - forced_wt += bvars.wt(l); //If l is negated b-var its wt is zero. + for (auto l : forced) { + if (bvars.isBvar(l)) { + forced_wt += bvars.wt(l); // If l is negated b-var its wt is zero. + } } return forced_wt; } Weight MaxSolver::getWtOfBvars(const vector& blits) { - Weight w {0}; - for(auto b : blits) { - if(bvars.isBvar(b)) + Weight w{0}; + // cout << "true b lits: "; + for (auto b : blits) { + if (bvars.isBvar(b)) { w += bvars.wt(b); + /*if (bvars.wt(b) > 0) + cout << b << " ";*/ + } } + // cout << "\n"; return w; } void MaxSolver::store_cplex_greedy_cls(const vector& cls) { - //clauses of size 1 are forced in sat solver and will - //be added by AddNewForcedBvars; Only cores are added to the greedy - //solver - if(cls.size() <= 1) - return; + // clauses of size 1 are forced in sat solver and will + // be added by AddNewForcedBvars; Only cores are added to the greedy + // solver + if (cls.size() <= 1) return; cplexClauses.addVec(cls); - bool core {true}; - for(auto lt : cls) + bool core{true}, core_totalizer{true}; + for (auto lt : cls) { core &= bvars.isCore(lt); - if(core) { + core_totalizer &= (bvars.isCore(lt) || totalizers->isToutput(lt)); + } + if (core) { incrBLitOccurrences(cls); greedyClauses.addVec(cls); } + if (params.abstract && core_totalizer) totalizers->add_core(cls); } -vector MaxSolver::greedySoln() { - //return a greedy solution of the clauses fed (and pending) to CPLEX - if(params.verbosity > 0) { - cout << "c Computing Greedy Solution\n"; - } +vector MaxSolver::greedySoln(bool useGreedy, bool need_soln) { + // Compute a new greedy solution. Must use the greedy solver + // if useGreedy, and must return a solution if need_soln. + // Otherwise if we are using the greedy solver and no + // new conficts have been added to it we do not return a solution + //(return an empty vector) + vector soln; - greedyAddNewForcedBvars(); - greedyAddNewClauses(); - //if(greedysolver) - soln = greedysolver->solve(); + bool greedy{useGreedy || params.cplexgreedy == 0 || top_freq == 0 || + (params.cplexgreedy == 2 && !totalizers->totalizers_active())}; + if (greedy) { + int nc{0}; + nc += greedyAddNewForcedBvars(); + nc += greedyAddNewClauses(); + if (need_soln || nc) { + soln = greedysolver->solve(); + if (params.verbosity > 1) { + cout << "c Greedy: soln cost = " << getWtOfBvars(soln) << "\n"; + } + } else if (params.verbosity > 0) { + cout << "c no new constraints for greedy skipping call to solver\n"; + } + } else { // cplex + int nc{0}; + nc += cplexAddNewForcedBvars(); + nc += cplexAddNewClauses(); + if (need_soln || nc) { + cplex->greedySolution(soln, top_freq); + if (params.verbosity > 0) { + cout << "c Cplex Greedy: soln cost = " << getWtOfBvars(soln) << "\n"; + } + } else if (params.verbosity > 0) { + cout << "c no new constraints for greedy skipping call to cplex\n"; + } + } /*else { auto val = greedySatSolver->solve(); if(val == l_False) - cout << "c ERROR. CPLEX clauses have no solution (or greedy solver couldn't find one)\n"; - for(size_t i = 0; i < bvars.n_bvars(); i++) { - Var v = bvars.varOfCls(i); - lbool val = greedySatSolver->modelValue(v); - if(val == l_True) - soln.push_back(mkLit(v, false)); - else if(val == l_False) + cout << "c ERROR. CPLEX clauses have no solution (or greedy solver + couldn't find one)\n"; for(size_t i = 0; i < bvars.n_bvars(); i++) { Var v + = bvars.varOfCls(i); lbool val = greedySatSolver->modelValue(v); if(val == + l_True) soln.push_back(mkLit(v, false)); else if(val == l_False) soln.push_back(mkLit(v, true)); else if(val == l_Undef) soln.push_back(~bvars.litOfCls(i)); //harden clause if no value assigned } }*/ - if(params.verbosity > 0) { - cout << "c Greedy: soln cost = " << getWtOfBvars(soln) << "\n"; - } + /*cout<< "a Greedy sol cost:\n"; + for (Lit l: soln) + cout << "a Lit : " << l << "\n"; + */ return soln; } bool MaxSolver::vec_isCore(const vector& core) { - for(auto l : core) - if(bvars.isNonCore(l)) - return(false); + for (auto l : core) + if (bvars.isNonCore(l)) return (false); return true; } void MaxSolver::findForced() { - //Check if new UB allows any variables to be forced. - //Try ordinary variables if there are few enough of them - //else try only b-vars. Does not account for future forced lits changing the implication wt. + // Check if new UB allows any variables to be forced. + // Try ordinary variables if there are few enough of them + // else try only b-vars. Does not account for future forced lits changing + // the implication wt. auto nf = nFailedLits; auto nb = nForcedByBounds; - for(size_t i = 0; i < satsolver->nVars(); i++) { - if(!satsolver->activeVar(i)) - continue; - for(int sign = 0; sign < 2; sign++) { + for (size_t i = 0; i < satsolver->nVars(); i++) { + if (!satsolver->activeVar(i)) continue; + for (int sign = 0; sign < 2; sign++) { Lit l = mkLit(i, sign); /*cout << " Active Lit " << l; @@ -2186,20 +2516,16 @@ void MaxSolver::findForced() { else if(!impWtIsUnk(l)) cout << " weight = " << getImpWt(l) << "\n";*/ - if(impWtIsUnk(l)) { //not computed. Compute now - if(findImpWt(l)) - break; //was forced - } - else if(impWtIsUB(l)) { - if(getImpWt(l) > UB()) { - //potentially forced. Refine now - if(findImpWt(l)) - break; + if (impWtIsUnk(l)) { // not computed. Compute now + if (findImpWt(l)) break; // was forced + } else if (impWtIsUB(l)) { + if (getImpWt(l) > UB()) { + // potentially forced. Refine now + if (findImpWt(l)) break; } - } - else {//is exact + } else { // is exact assert(impWtIsExact(l)); - if(getImpWt(l) > UB()) { + if (getImpWt(l) > UB()) { satsolver->addClause({~l}); ++nForcedByBounds; break; @@ -2207,72 +2533,79 @@ void MaxSolver::findForced() { } } } - if(params.verbosity>0) - cout << "c findForced found " << nFailedLits-nf << " failed lits and " - << nForcedByBounds-nb << " lits forced by bounding\n"; + if (params.verbosity > 0) + cout << "c findForced found " << nFailedLits - nf << " failed lits and " + << nForcedByBounds - nb << " lits forced by bounding\n"; } bool MaxSolver::findImpWt(Lit l) { vector imps; - //compute implied weight of l by UP. Return true if l was forced - if(!satsolver->findImplications(l, imps)) { + // compute implied weight of l by UP. Return true if l was forced + if (!satsolver->findImplications(l, imps)) { satsolver->addClause({~l}); ++nFailedLits; return true; } auto wt = getWtOfBvars(imps); - if(wt + getForcedWt() > UB()) { + if (wt + getForcedWt() > UB()) { satsolver->addClause({~l}); ++nForcedByBounds; return true; } setImpWt(l, wt, impWtExact); - for(auto x : imps) - if(impWtIsUnk(x) || (impWtIsUB(x) && wt < getImpWt(x))) + for (auto x : imps) + if (impWtIsUnk(x) || (impWtIsUB(x) && wt < getImpWt(x))) setImpWt(x, wt, impWtUB); return false; } -bool MaxSolver::BLitOrderLt::operator() (const Lit l1, const Lit l2) const { - //must be strict weak order: - - int oc1 = (*occurCount)[bvars.toIndex(l1)]; - int oc2 = (*occurCount)[bvars.toIndex(l2)]; - double wt1 = bvars.wt(l1); - double wt2 = bvars.wt(l2); - bool retval {0}; - - if(wt1 == 0 && oc1 > 0) { //l1 is super---satisfies cores at zero cost - if(wt2 == 0 && oc2 > 0) //so is l2 - if(oc1 == oc2) - retval = (l1 < l2); //order lits the same for gt and lt - //when discriminators are equal - else - retval = (oc1 < oc2) ^ gt; //XOR: we know that oc1 != - //oc2. So if (oc1 < oc2) == 0, - //then l1 > l2. - else //l2 is not super - retval = gt; //l1 > l2 - } - else if(wt2 == 0 && oc2 > 0) //l1 is not super but l2 is - retval = !gt; //l1 < l2 +bool MaxSolver::BLitOrderLt::operator()(const Lit l1, const Lit l2) const { + // must be strict weak order: + // if either L1 or L2 is a Tvar order it infront + bool retval{0}; + if (!bvars.isBvar(l1) && !bvars.isBvar(l2)) + retval = (l1 < l2) ^ gt; + else if (!bvars.isBvar(l1)) + retval = 1 ^ gt; + else if (!bvars.isBvar(l2)) + retval = 0 ^ gt; else { - //Neither is super compute merit - double m1 {0.0}; - double m2 {0.0}; + int oc1 = (*occurCount)[bvars.toIndex(l1)]; + int oc2 = (*occurCount)[bvars.toIndex(l2)]; + double wt1 = bvars.wt(l1); + double wt2 = bvars.wt(l2); + // bool retval {0}; + + if (wt1 == 0 && oc1 > 0) { // l1 is super---satisfies cores at zero cost + if (wt2 == 0 && oc2 > 0) // so is l2 + if (oc1 == oc2) + retval = (l1 < l2); // order lits the same for gt and lt + // when discriminators are equal + else + retval = (oc1 < oc2) ^ gt; // XOR: we know that oc1 != + // oc2. So if (oc1 < oc2) == 0, + // then l1 > l2. + else // l2 is not super + retval = gt; // l1 > l2 + } else if (wt2 == 0 && oc2 > 0) // l1 is not super but l2 is + retval = !gt; // l1 < l2 + else { + // Neither is super compute merit + double m1{0.0}; + double m2{0.0}; - //note since lits not super wt == 0 ==> oc == 0; no division by 0 - m1 = (oc1==0) ? -wt1 : oc1/wt1; - m2 = (oc2==0) ? -wt2 : oc2/wt2; + // note since lits not super wt == 0 ==> oc == 0; no division by 0 + m1 = (oc1 == 0) ? -wt1 : oc1 / wt1; + m2 = (oc2 == 0) ? -wt2 : oc2 / wt2; - if(m1 == m2) { - if(bvars.clsSize(l1) == bvars.clsSize(l2)) - retval = (l1 < l2); - else - retval = (bvars.clsSize(l1) < bvars.clsSize(l2)) ^ gt; + if (m1 == m2) { + if (bvars.clsSize(l1) == bvars.clsSize(l2)) + retval = (l1 < l2); + else + retval = (bvars.clsSize(l1) < bvars.clsSize(l2)) ^ gt; + } else + retval = (m1 < m2) ^ gt; } - else - retval = (m1 < m2) ^ gt; } return retval; } diff --git a/maxhs/core/MaxSolver.h b/maxhs/core/MaxSolver.h old mode 100755 new mode 100644 index 4c7e1f9..68d1541 --- a/maxhs/core/MaxSolver.h +++ b/maxhs/core/MaxSolver.h @@ -36,265 +36,303 @@ using std::vector; #include "minisat/core/SolverTypes.h" #endif -#include "maxhs/core/MaxSolverTypes.h" -#include "maxhs/ifaces/SatSolver.h" -#include "maxhs/ifaces/GreedySolver.h" -#include "maxhs/ifaces/muser.h" -#include "maxhs/ifaces/Cplex.h" -#include "maxhs/core/Wcnf.h" #include "maxhs/core/Bvars.h" -#include "maxhs/utils/Params.h" -#include "maxhs/core/Assumptions.h" +#include "maxhs/core/MaxSolverTypes.h" #include "maxhs/ds/Packed.h" -using namespace MaxHS_Iface; - -#ifdef GLUCOSE -namespace Minisat = Glucose; -#endif - -using namespace Minisat; +class Wcnf; +class Assumps; +class TotalizerManager; -namespace MaxHS { +namespace MaxHS_Iface { +class Cplex; +class SatSolver; +class Muser; +class GreedySolver; +} // namespace MaxHS_Iface +namespace MaxHS { class MaxSolver { -public: - MaxSolver(Wcnf *f); + public: + MaxSolver(Wcnf* f); ~MaxSolver(); - void solve(); //Solve the initialized Wcnf - Weight UB() { return theWcnf->totalClsWt() - sat_wt; } + void solve(); // Solve the initialized Wcnf + Weight UB(); Weight LB() { return lower_bnd; } bool isSolved() { return solved; } bool isUnsat() { return unsat; } const vector& getBestModel() { return UBmodel; } const Wcnf* getWcnf() { return theWcnf; } - - //Not private but not for general users. - //Used by interrupt trap + + // Not private but not for general users. + // Used by interrupt trap void printStatsAndExit(int signum, int exitType); // Called by the SAT solver to tell whether or not a learnt clause // can be deleted bool deleteLearntTest(const vector& c) const; + Weight updateUB(); + void updateLB(Weight wt) { + if (wt > lower_bnd) lower_bnd = wt; + } -protected: + protected: Wcnf* theWcnf; Bvars bvars; - SatSolver* satsolver; - GreedySolver* greedysolver; -// GreedySatSolver* greedySatSolver; - Muser* muser; - Cplex* cplex; - - //PREPROCESSING - bool doPreprocessing(); //test if preprocessing should be skipped. - - //BOUNDS - Weight sat_wt; //wt of soft clauses known to be satisfiable. - Weight forced_wt; - - Weight lower_bnd; //lower bound on wt false soft clauses. - Weight absGap; //Stop when (UB() - LB()) <= absGap (absGap will be zero for integer weights). - void improveModel(); //try to improve the cost of a sat model. - void updateLB(Weight wt) { if (wt > lower_bnd) lower_bnd = wt; } - Weight updateUB(); - Weight getForcedWt(); //wt of softs falsified by forced units - Weight getSatClsWt(); //wt of satisfied softs in current model - Weight getWtOfBvars(const vector& blits); //sum a vector of wts. - vector UBmodel; //Holds the current best (upper bound) model - vector UBmodelSofts; //Holds true/false for satisfied softs in the UB model. - vector tmpModelSofts; //Temp vector for holding satisfied status of softs in latest model. - - bool haveUBModel; - bool have_new_UB_model; + MaxHS_Iface::SatSolver* satsolver{}; + MaxHS_Iface::GreedySolver* greedysolver{}; + MaxHS_Iface::Muser* muser{}; + MaxHS_Iface::Cplex* cplex{}; + TotalizerManager* totalizers{}; + int top_freq{}; + + // PREPROCESSING + bool doPreprocessing(); // test if preprocessing should be skipped. + + // BOUNDS + Weight sat_wt{}; // wt of soft clauses known to be satisfiable. + Weight forced_wt{}; + + Weight lower_bnd{}; // lower bound on wt false soft clauses. + Weight absGap; // Stop when (UB() - LB()) <= absGap (absGap will be zero for + // integer weights). + void improveModel(); // try to improve the cost of a sat model. + bool check_termination(const std::string& location); + Weight getForcedWt(); // wt of softs falsified by forced units + Weight getSatClsWt(); // wt of satisfied softs in current model + Weight checkSolWt(const vector& sol); + Weight getWtOfBvars(const vector& blits); // sum a vector of wts. + vector UBmodel; // Holds the current best (upper bound) model + vector + UBmodelSofts; // Holds true/false for satisfied softs in the UB model. + vector tmpModelSofts; // Temp vector for holding satisfied status of + // softs in latest model. + + bool haveUBModel{false}; + bool have_new_UB_model{false}; void setUBModel(); - //SAT solver interaction + // SAT solver interaction // set up bvars - void configBvar(Var, SatSolver*); + void configBvar(Var, MaxHS_Iface::SatSolver*); void setNoBvarDecisions(); - //add clauses to sat solver - void addHards(SatSolver*); - void addHards(SatSolver*, const vector& indicies); - void addSofts(SatSolver*); - void addSofts(SatSolver*, const vector&); - - //b-var equivalent clauses for Fbeq (potentially removable). - //removable b-vars implemented by adding extra control variable to all eq clauses. - //If the eq clauses are specified as being removable one must - //assume the literal returned by "activateSoftEqLit" during sat solving. - //rmSoftEqs removes "removable" eqs. - void addSoftEqs(SatSolver*, bool removable); - void addSoftEqs(SatSolver*, bool removable, const vector& indicies); - Var eqCvar; //control variable for b-var equivalences. - Lit eqCvarPos; //Postive literal of eqCvar - void rmSoftEqs(SatSolver* slv) { - //assert eqCvar and thus remove all eq clauses after - //a call to simplify + // add clauses to sat solver + void addHards(MaxHS_Iface::SatSolver*); + void addHards(MaxHS_Iface::SatSolver*, const vector& indicies); + void addSofts(MaxHS_Iface::SatSolver*); + void addSofts(MaxHS_Iface::SatSolver*, const vector&); + + // b-var equivalent clauses for Fbeq (potentially removable). + // removable b-vars implemented by adding extra control variable to all eq + // clauses. If the eq clauses are specified as being removable one must assume + // the literal returned by "activateSoftEqLit" during sat solving. rmSoftEqs + // removes "removable" eqs. + void addSoftEqs(MaxHS_Iface::SatSolver*, bool removable); + void addSoftEqs(MaxHS_Iface::SatSolver*, bool removable, + const vector& indicies); + Var eqCvar; // control variable for b-var equivalences. + Lit eqCvarPos; // Postive literal of eqCvar + void rmSoftEqs(MaxHS_Iface::SatSolver* slv) { + // assert eqCvar and thus remove all eq clauses after + // a call to simplify slv->freeVar(mkLit(eqCvar, false)); } - Lit activateSoftEqLit() { - //return assumption that activates the soft equivalent clauses - return mkLit(eqCvar, true); } + Lit activateSoftEqLit() { + // return assumption that activates the soft equivalent clauses + return mkLit(eqCvar, true); + } - //If not using Fbeq allow forcing negated b-vars after each sat call. - void satSolverAddBvarsFromSofts(); //with Fb force negated b-vars when soft is satisfied. - void initSftSatisfied(); //for var --> satisfied softs map for forcing neg b-vars in Fb + // If not using Fbeq allow forcing negated b-vars after each sat call. + void satSolverAddBvarsFromSofts(); // with Fb force negated b-vars when soft + // is satisfied. + void initSftSatisfied(); // for var --> satisfied softs map for forcing neg + // b-vars in Fb - //New control variables (better support needed for more extensive uses of new variables) - int nextNewVar; //for adding additional variables beyond the b-vars - //+ original vars + eqCvar to the maxsat theory.; + // New control variables (better support needed for more extensive uses of new + // variables) + int nextNewVar; // for adding additional variables beyond the b-vars + //+ original vars + eqCvar to the maxsat theory.; int getNextNewVar() { return nextNewVar++; } - //Manage Cplex and Greedy solver Clauses. - vector bLitOccur; - Packed_vecs cplexClauses; - Packed_vecs greedyClauses; - Packed_vecs sftSatisfied; //map from ordinary var --> satisfied sft clauses + // Manage Cplex and Greedy solver Clauses. + vector bLitOccur; + Packed_vecs cplexClauses{}; + Packed_vecs greedyClauses{}; + Packed_vecs + sftSatisfied{}; // map from ordinary var --> satisfied sft clauses int cplexAddNewClauses(); - void greedyAddNewClauses(); - bool cplexAddCls(vector&& cls); //moves it argument in. Can only input temporaries. + int greedyAddNewClauses(); + void cplexAddCls(vector&& cls); void store_cplex_greedy_cls(const vector& cls); - //Transfer units between sub-solvers. - void greedyAddNewForcedBvars(); + // Transfer units between sub-solvers. + int greedyAddNewForcedBvars(); int cplexAddNewForcedBvars(); void satSolverAddNewForcedVars(); void muserAddNewForcedVars(); struct GetNewUnits { int not_seen; vector forced; - template - void update(S* slv) { forced = slv->getForced(not_seen); not_seen += forced.size(); } - GetNewUnits() : not_seen {0}, forced{} {} + template + void update(S* slv) { + forced = slv->getForced(not_seen); + not_seen += forced.size(); + } + GetNewUnits() : not_seen{0}, forced{} {} }; - GetNewUnits cplexNU, greedyNU, muserNU, satNU, satBvarNU, forcedWtNU; + GetNewUnits cplexNU{}, greedyNU{}, muserNU{}, satNU{}, satBvarNU{}, + forcedWtNU{}; // Statistics - int amountConflictMin; - double globalStartTime; + int amountConflictMin{}; + double globalStartTime{}; - //output routines - void printErrorAndExit(const char *msg); + // output routines + void printErrorAndExit(const char* msg); void printSolution(const vector& model); - void printCurClause(const vector &cls); + void printCurClause(const vector& cls); void reportCplex(Weight cplexLB, Weight solnWt); - void reportSAT_min(lbool result, double iTime, size_t orig_size, int nMins, double mTime, size_t final_size); - void reportForced(const vector &forced, Weight wt); - void outputConflict(const vector &conf); - void optFound(std::string reason); + void reportSAT_min(lbool result, double iTime, size_t orig_size, int nMins, + double mTime, size_t final_size); + void reportForced(const vector& forced, Weight wt); + void outputConflict(const vector& conf); + void optFound(const std::string& reason); void unsatFound(); - bool printStatsExecuted; + bool printStatsExecuted{false}; - //status flags - bool solved; //True when finished solving. - bool unsat; //Hards are UNSAT or no solution of cost < dimacs top + // status flags + bool solved{false}; // True when finished solving. + bool unsat{false}; // Hards are UNSAT or no solution of cost < dimacs top - //internal Subroutines + // internal Subroutines void disjointPhase(); + void allClausesSeeded_maxsat(); void seqOfSAT_maxsat(); - int feedCplex(int gIter, Assumps& a, int nSoFar, size_t sizeSoFar); + vector extract_costBoundinglits(const vector& soln) const; + vector extract_NonCoreBvars(const vector& soln) const; + vector extract_UnValued(const vector& soln) const; + bool any_false(const vector& soln) const; + bool all_bvars(const vector& soln) const; + + vector abstractSolution(const vector&); bool tryHarden(); - int n_softs_forced_hard; - int n_softs_forced_relaxed; - int n_softs_forced_hard_not_in_cplex; - - void tryPopulate(vector &, double); - bool get_conflicts(const vector &, bool check_if_blocked); - bool get_greedy_conflicts(); - bool get_ub_conflicts(); - int get_seq_of_conflicts(const vector&, double, double); - - //For forced by bounding or failed lit tests - vector impWt; //holds the implied weight of a literal - vector impWtType; //holds the type of weight stored in impliedWt - int nFailedLits; - int nForcedByBounds; - + bool tryHarden_with_lp_soln(const Weight lp_objval, + vector& lp_redvals, + vector& cplex_vars); + + int n_softs_forced_hard_not_in_cplex{}; + int n_softs_forced_hard{}; + int n_softs_forced_false{}; + + int n_ovars_forced_true{}; + int n_ovars_forced_false{}; + int n_touts_forced_true{}; + int n_touts_forced_false{}; + + void tryPopulate(vector&, double); + bool get_cplex_conflicts(const vector&, char type, double timeout, + int max_cores); + bool get_greedy_conflicts(double timeout, int max_cores); + void get_ub_conflicts(); + int get_seq_of_conflicts(const vector&, double first_core_cpu_lim, + double other_core_cpu_lim, double timeout, + int max_cores); + bool assumps_are_blocked(const vector&); + + // For forced by bounding or failed lit tests + vector impWt; // holds the implied weight of a literal + vector impWtType; // holds the type of weight stored in impliedWt + int nFailedLits{}; + int nForcedByBounds{}; + void findForced(); bool findImpWt(Lit); - const lbool impWtUnk {l_Undef}, impWtExact {l_True}, impWtUB{l_False}; + const lbool impWtUnk{l_Undef}, impWtExact{l_True}, impWtUB{l_False}; bool impWtIsUnk(Lit l) { - return toInt(l) >= static_cast(impWt.size()) || impWtType[toInt(l)] == impWtUnk; + return toInt(l) >= static_cast(impWt.size()) || + impWtType[toInt(l)] == impWtUnk; } bool impWtIsExact(Lit l) { - return toInt(l) < static_cast(impWt.size()) && impWtType[toInt(l)] == impWtExact; + return toInt(l) < static_cast(impWt.size()) && + impWtType[toInt(l)] == impWtExact; } bool impWtIsUB(Lit l) { - return toInt(l) < static_cast(impWt.size()) && impWtType[toInt(l)] == impWtUB; + return toInt(l) < static_cast(impWt.size()) && + impWtType[toInt(l)] == impWtUB; } void setImpWt(Lit l, Weight w, const lbool type) { - if(static_cast(toInt(l)) >= impWt.size()) { - impWt.resize(toInt(l)+1); - impWtType.resize(toInt(l)+1, impWtUnk); + if (static_cast(toInt(l)) >= impWt.size()) { + impWt.resize(toInt(l) + 1); + impWtType.resize(toInt(l) + 1, impWtUnk); } impWt[toInt(l)] = w; impWtType[toInt(l)] = type; } Weight getImpWt(Lit l) { - assert((size_t) toInt(l) < impWt.size()); + assert((size_t)toInt(l) < impWt.size()); return static_cast(toInt(l)) >= impWt.size() ? 0 : impWt[toInt(l)]; } - - //mutexes + // mutexes void processMutexes(); const uint8_t inMxdvar = 2; const uint8_t inMxbvar = 1; vector inMx; void setInMutex(Lit b, int type) { ensureInMx(b); - inMx[bvars.toIndex(b)] = (uint8_t) type; + inMx[bvars.toIndex(b)] = (uint8_t)type; } void ensureInMx(Lit b) { - if(static_cast(bvars.toIndex(b)) >= inMx.size()) - inMx.resize(bvars.toIndex(b) + 1, (uint8_t) 0); + if (static_cast(bvars.toIndex(b)) >= inMx.size()) + inMx.resize(bvars.toIndex(b) + 1, (uint8_t)0); } // Noncore stuff void seed_equivalence(); - bool allClausesSeeded; + bool allClausesSeeded{false}; bool vec_isCore(const vector& core); // Accumulate Cores vector getAssumpUpdates(int sinceCplex, vector& core); - vector greedySoln(); - vector fracOfCore(int nCplex, vector &core); + vector greedySoln(bool useGreedy, bool need_soln); + vector fracOfCore(int nCplex, vector& core); Lit maxOccurring(const vector& core); - void incrBLitOccurrences(const vector &core); - lbool satsolve_min(const Assumps &inAssumps, vector &outConflict, double sat_cpu_lim, double mus_cpu_lim); - void minimize_muser(vector &con, double mus_cpu_lim); - double m_sum_reduced_frac; - double mtime; - int mcalls; - bool doMin; - - void check_mus(vector &con); //debugging - - //Functor to sort blits for assumptions, minimization, and - //agumenting the hitting set during Karp. + void incrBLitOccurrences(const vector& core); + lbool satsolve_min(const Assumps& inAssumps, vector& outConflict, + double sat_cpu_lim, double mus_cpu_lim); + void minimize_muser(vector& con, double mus_cpu_lim); + double m_sum_reduced_frac{}; + double mtime{}; + int mcalls{}; + bool doMin{true}; + + void check_mus(vector& con); // debugging + + // Functor to sort blits for assumptions, minimization, and + // agumenting the hitting set during Karp. struct BLitOrderLt { - //on function call (b1, b2) return true if b1 satisfies fewer - //cores per unit weight, or if it satifies the same number of - //cores per unit weight and its corresponding soft clause is - //longer. If b1 < b2 then relaxing b1's soft clause is not as - //good as relaxing b2's soft clause for making the formula - //satisfiable at lower cost. + // on function call (b1, b2) return true if b1 satisfies fewer + // cores per unit weight, or if it satifies the same number of + // cores per unit weight and its corresponding soft clause is + // longer. If b1 < b2 then relaxing b1's soft clause is not as + // good as relaxing b2's soft clause for making the formula + // satisfiable at lower cost. - const vector* const occurCount; //How many cores will blit relax. - Bvars& bvars; //reference to solver's bvar structure - bool gt; //true: > ordering; false < ordering + const vector* const occurCount; // How many cores will blit relax. + Bvars& bvars; // reference to solver's bvar structure + bool gt; // true: > ordering; false < ordering - BLitOrderLt(const vector* const o, Bvars& b, bool g) : occurCount {o}, bvars (b), gt {g} {} + BLitOrderLt(const vector* const o, Bvars& b, bool g) + : occurCount{o}, bvars(b), gt{g} {} - bool operator() (const Lit l1, const Lit l2) const; + bool operator()(const Lit l1, const Lit l2) const; }; - + BLitOrderLt blit_lt; BLitOrderLt blit_gt; }; -} //namespace +} // namespace MaxHS #endif diff --git a/maxhs/core/MaxSolverTypes.h b/maxhs/core/MaxSolverTypes.h old mode 100755 new mode 100644 index d896918..f0bc704 --- a/maxhs/core/MaxSolverTypes.h +++ b/maxhs/core/MaxSolverTypes.h @@ -30,4 +30,7 @@ typedef double Weight; enum class CoreType { cores, nonCores, mixed }; enum class CoreRelaxFn { rand, maxoccur, frac, dsjn }; +enum { _ERROR_ = 50 }; +enum { _INCREMENTAL_NONE_ = 0 }; +enum { _CARD_TOTALIZER_ = 0 }; #endif diff --git a/maxhs/core/Totalizer.cc b/maxhs/core/Totalizer.cc new file mode 100644 index 0000000..3fc8a46 --- /dev/null +++ b/maxhs/core/Totalizer.cc @@ -0,0 +1,282 @@ +/***********[Totalizers.cc] +Copyright (c) 2019, Fahiem Bacchus + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Some code and ideas from + + Open-WBO, Copyright (c) 2013-2017, Ruben Martins, Vasco Manquinho, Ines Lynce + +have been used; + Open-WBO is subject to the following LICENSE + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +***********/ + +#include "Totalizer.h" +#include +#include +#include +#include +#include "TotalizerManager.h" + +using std::vector; + +vector Totalizer::all_totalizers{}; + +TRef Totalizer::makeTotalizer(TotalizerManager& tm, + const vector& inputLits, Weight wt) { + assert(!inputLits.empty()); + Totalizer t0{tm, inputLits, wt}; + Totalizer::all_totalizers.push_back(t0); + return TRef{Totalizer::all_totalizers.size() - 1}; +} + +TRef Totalizer::makeTotalizer(TotalizerManager& tm, + const vector& inputLits, size_t start, + size_t end, Weight wt) { + assert(!inputLits.empty()); + Totalizer t0{tm, inputLits, start, end, wt}; + Totalizer::all_totalizers.push_back(t0); + return TRef{Totalizer::all_totalizers.size() - 1}; +} + +TRef Totalizer::makeTotalizer(TotalizerManager& tm, TRef rT, TRef lT) { + Totalizer::all_totalizers.emplace_back(tm, rT, lT); + return TRef{Totalizer::all_totalizers.size() - 1}; +} + +Totalizer& Totalizer::getTotalizer(TRef idx) { + assert(0 <= idx && idx < static_cast(all_totalizers.size())); + return all_totalizers[idx]; +} + +Totalizer::Totalizer(TotalizerManager& tm, const vector& inputLits, + Weight wt) + : Totalizer{tm, inputLits, 0, inputLits.size(), wt} {} + +Totalizer::Totalizer(TotalizerManager& tm, const vector& inputLits, + size_t start, size_t end, Weight wt) + : weight{wt} { + assert(end <= inputLits.size()); + assert(start < inputLits.size()); + nb_inputs = static_cast(end - start); + assert(nb_inputs != 0); + _maxFalse = 0; + _maxTrue = 0; + + if (nb_inputs == 1) { + rightT = NullTRef; + leftT = NullTRef; + output_lits.push_back( + inputLits[start]); // no needed to connect output and input lits. + _maxFalse = 1; + _maxTrue = 1; + } else { + size_t mid = start + nb_inputs / 2; + leftT = makeTotalizer(tm, inputLits, start, mid, wt); + rightT = makeTotalizer(tm, inputLits, mid, end, wt); + for (int i = 0; i < nb_inputs; i++) + output_lits.push_back(tm.getNewOutputLit()); + } +} + +Totalizer::Totalizer(TotalizerManager& tm, TRef lT, TRef rT) { + assert(rT != NullTRef && lT != NullTRef); + rightT = rT; + leftT = lT; + nb_inputs = (*rightT).nb_inputs + (*leftT).nb_inputs; + weight = (*rightT).weight; + _maxFalse = 0; + _maxTrue = 0; + for (int i = 0; i < nb_inputs; i++) + output_lits.push_back(tm.getNewOutputLit()); +} + +void Totalizer::increaseMaxFalse(int maxFalse, vector>& clauses) { + // maxFalse is max number of outputs that must constraint inputs when false. + // We must ensure that nOutTrue for the right and the left totalizer childen + // have been properly set! Note that maxFalse means that the totalizer must + // sum up maxFalse true inputs (so that when we set the maxFalse output to + // false we allow < maxFalse inputs to be true) + + if (rightT == NullTRef && leftT == NullTRef) return; + assert(rightT != NullTRef && leftT != NullTRef); + maxFalse = std::min(maxFalse, nb_inputs); + // we already know that nOutTrue outs are true...so _maxFalse can't + // be smaller than this + _maxFalse = std::max(_maxFalse, nOutTrue); + if (maxFalse <= _maxFalse) return; + + // give children a chance to update in case they have a lower + // current _maxFalse. Given that left contributes k true inputs + // right only has to sum its true inputs up to maxFalse-k (if it + // sums that many true inputs this will be sufficent to sum the + // parent to maxFalse. + + (*rightT).increaseMaxFalse(maxFalse - (*leftT).nOutTrue, clauses); + (*leftT).increaseMaxFalse(maxFalse - (*rightT).nOutTrue, clauses); + getAtLeastCNF(clauses, _maxFalse + 1, maxFalse); + _maxFalse = maxFalse; +} + +void Totalizer::increaseMaxTrue(int maxTrue, vector>& clauses) { + // TODO account for know false outputs. + // maxTrue is max number of outputs that must constrain input when true + if (rightT == NullTRef && leftT == NullTRef) return; + assert(rightT != NullTRef && leftT != NullTRef); + maxTrue = std::min(maxTrue, nb_inputs); + if (maxTrue <= _maxTrue) return; + + // give children a chance to update their UB in case they have a lower + // current UB than this parent + (*rightT).increaseMaxTrue(maxTrue, clauses); + (*leftT).increaseMaxTrue(maxTrue, clauses); + + // Now update ours and generate our new clauses + getAtMostCNF(clauses, _maxTrue + 1, maxTrue); + _maxTrue = maxTrue; +} + +void Totalizer::getCNF(vector>& clauses) const { + if (rightT == NullTRef && leftT == NullTRef) return; + + assert(rightT != NullTRef && leftT != NullTRef); + + (*rightT).getCNF(clauses); // encode right outputs + (*leftT).getCNF(clauses); // encode left ouputs + + getAtLeastCNF(clauses, 1, _maxFalse); // encode this totalizers outputs. + getAtMostCNF(clauses, 1, _maxTrue); +} + +void Totalizer::getAtLeastCNF(vector>& clauses, int low, + int high) const { + // Return clauses capturing if j inputs are true then the j-th + // output must be true (i.e. output_lits[j-1]=l_true), for values of + // j in range [low, high] inclusive. By the contrapositive + // -output_lits[j-1] for j in [low, high] implies that less + // than j inputs are true. These clauses capture that the + // output is as least as large as the number of true inputs. + assert(low > 0); + assert(high <= static_cast(output_lits.size())); + if (low > high) return; + + Totalizer& right = *rightT; + Totalizer& left = *leftT; + auto& r_outs = right.output_lits; + auto& l_outs = left.output_lits; + int r_max = r_outs.size(); + int l_max = l_outs.size(); + + // A) if right (left) has j true outputs then the j-th output is true. + for (int r = low; r <= r_max && r <= high; r++) + clauses.emplace_back( + std::initializer_list{~r_outs[r - 1], output_lits[r - 1]}); + + for (int l = low; l <= l_max && l <= high; l++) + clauses.emplace_back( + std::initializer_list{~l_outs[l - 1], output_lits[l - 1]}); + + // B) output m (m >= low, m <=high) is true if right has j>=1 true + // outputs and left has k>=1 true outputs where j+k = m. (right or + // left with 0 true outputs are handled by case A) + for (int m = low; m <= high; m++) + for (int r = 1; r <= r_max && r < m; r++) { + int l = m - r; + if (l <= l_max) + clauses.emplace_back(std::initializer_list{ + ~l_outs[l - 1], ~r_outs[r - 1], output_lits[m - 1]}); + } +} + +void Totalizer::getAtMostCNF(vector>& clauses, int low, + int high) const { + // Return clauses capturing if < j inputs are true then output_lit[j-1] + // must be false for j in range [low, high] inclusive. These + // clauses capture that the output is at most large as the number of + // true inputs. By the contrapositive output_lits[j-1] for j in [low,high] + // 1-based implies that >= j inputs are true. + assert(low > 0); + assert(high <= static_cast(output_lits.size())); + if (low > high) return; + + Totalizer& right = *rightT; + Totalizer& left = *leftT; + auto& r_outs = right.output_lits; + auto& l_outs = left.output_lits; + int r_max = r_outs.size(); + int l_max = l_outs.size(); + + // A) At most left.nb_inputs can be true from the left. Therefore + // if the r-th right output is false, then for this totalizer + // at most (r-1)+left.nb_inputs can be true over both children + // So the m-th output with m = r+left.nb_inputs must be false. + // Similar reasoning applies wrt to the l-th output of the left child. + + for (int m = low; m <= high; m++) { + int r = m - left.nb_inputs; + if (r >= 1 && r <= r_max) + clauses.emplace_back( + std::initializer_list{r_outs[r - 1], ~output_lits[m - 1]}); + int l = m - right.nb_inputs; + if (l >= 1 && l <= l_max) + clauses.emplace_back( + std::initializer_list{l_outs[l - 1], ~output_lits[m - 1]}); + } + + // B) output m-1 (m-1 >= low, m-1 <=high) is false if right has + // < r true inputs and left has < l true inputs where r+l = + // m. In particular, at most r-1 + l-1 = r+l-2 inputs can be + // true, so at most r+l-2 ouputs can be true. Therefore output + // r+l-1 = m-1 must be false. + for (int m = low + 1; m <= high + 1; m++) + for (int r = 1; r <= r_max && r < m; r++) { + int l = m - r; + if (l <= l_max && m >= 2) + clauses.emplace_back(std::initializer_list{ + l_outs[l - 1], r_outs[r - 1], ~output_lits[m - 2]}); + } +} + +void Totalizer::getInputs(vector& inputs) const { + if (rightT == NullTRef && leftT == NullTRef) { + for (auto l : output_lits) inputs.push_back(l); + } else { + assert(rightT != NullTRef && leftT != NullTRef); + (*rightT).getInputs(inputs); + (*leftT).getInputs(inputs); + } +} diff --git a/maxhs/core/Totalizer.h b/maxhs/core/Totalizer.h new file mode 100644 index 0000000..7541df0 --- /dev/null +++ b/maxhs/core/Totalizer.h @@ -0,0 +1,188 @@ +/***********[Totalizers.h] +Copyright (c) 2019, Fahiem Bacchus + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Some code and ideas from + + Open-WBO, Copyright (c) 2013-2017, Ruben Martins, Vasco Manquinho, Ines Lynce + +have been used; + Open-WBO is subject to the following LICENSE + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +***********/ + +#ifndef Totalizer_h +#define Totalizer_h + +#include +#include + +#include "maxhs/core/MaxSolverTypes.h" +#include "maxhs/core/TotalizerAuxStruct.h" +#include "maxhs/utils/io.h" + +#ifdef GLUCOSE +namespace Minisat = Glucose; +#endif + +using Minisat::Lit; +using Minisat::Var; +using std::vector; + +class Totalizer { + public: + // use only these static makeTotalizer routines to make totalizers. + // due to internal storage of all totalizers. + + // 1. Build the totalizer over inputLits. + static TRef makeTotalizer(TotalizerManager& tm, const vector& inputLits, + Weight w); + + // 2. Build totalizer by joining two previous totalizers referred to rightTRef + // and leftTRef + static TRef makeTotalizer(TotalizerManager& tm, TRef rT, TRef lT); + + static TRef makeTotalizer(TotalizerManager& tm, const vector& inputLits, + size_t start, size_t end, Weight w); + + static Totalizer& getTotalizer(TRef idx); + static size_t getNbTotalizers() { return all_totalizers.size(); } + + // Update maxTrue/maxFalse to a higher value and return new clauses + // generated clauses to allow the additional outputs to constrain the inputs + vector> increaseMaxFalse(int maxFalse) { + vector> clauses{}; + increaseMaxFalse(maxFalse, clauses); + return clauses; + } + + vector> increaseMaxTrue(int maxTrue) { + vector> clauses{}; + increaseMaxTrue(maxTrue, clauses); + return clauses; + } + + // get full CNF representation of totalizer current state of totalizer. + // Usually called after constructing totalizer with specific inputLits + // as that constructor does not return the CNF encoding. + vector> getCNF() const { + vector> clauses; + getCNF(clauses); + return clauses; + } + + const vector& getOutputs() const { return output_lits; } + + vector getInputs() const { + vector inputs; + getInputs(inputs); + return inputs; + } + + void getInputs(vector& inputs) const; + int getSize() const { return nb_inputs; }; + int getMaxFalse() const { return _maxFalse; } + int getMaxTrue() const { return _maxTrue; } + int getOutTrue() const { return nOutTrue; } + int getOutFalse() const { return nOutFalse; } + void setNOutTrue(int t) { nOutTrue = t; } + void setNOutFalse(int t) { nOutFalse = t; } + Weight getWeight() const { return weight; } + bool isTopLevel() const { return toplevel; } + void setTopLevel() { toplevel = true; } + void unsetTopLevel() { toplevel = false; } + + bool isLeaf() const { + assert((rightT != NullTRef && leftT != NullTRef) || + (rightT == NullTRef && leftT == NullTRef)); + return (rightT == NullTRef && leftT == NullTRef); + } + TRef getRight() const { return rightT; } + TRef getLeft() const { return leftT; } + + Totalizer(TotalizerManager& tm, const vector& inputLits, Weight w); + Totalizer(TotalizerManager& tm, TRef rT, TRef lT); + Totalizer(TotalizerManager& tm, const vector& inputLits, size_t start, + size_t end, Weight w); + + friend std::ostream& operator<<(std::ostream& os, const Totalizer& tot); + + private: + // return incremental clauses required to ensure setting + // output[maxFalse-1] = False or output[maxTrue-1] = True properly + // constrains the inputs. maxFalse, maxTrue are COUNTS of the number + // of false/true outputs + void increaseMaxFalse(int maxFalse, vector>& clauses); + void increaseMaxTrue(int maxTrue, vector>& clauses); + + void getCNF(vector>& clauses) const; + void getAtLeastCNF(vector>& clauses, int low, int high) const; + void getAtMostCNF(vector>& clauses, int low, int high) const; + TRef rightT; + TRef leftT; + bool toplevel{false}; + vector output_lits; + int _maxFalse{0}, _maxTrue{0}, nOutTrue{0}, nOutFalse{0}; + int nb_inputs{0}; + Weight weight; + static vector all_totalizers; +}; + +// Interface with Totalizer class +inline Totalizer& operator*(TRef t) { return Totalizer::getTotalizer(t); } + +inline std::ostream& operator<<(std::ostream& os, const Totalizer& tot) { + std::cout << "Totalizer: weight = " << tot.weight + << " _maxFalse=" << tot._maxFalse << " _maxTrue=" << tot._maxTrue + << " trueOuts = " << tot.nOutTrue + << " falseOuts = " << tot.nOutFalse << "\n" + << " outputs = " << tot.output_lits << "\n" + << " inputs = " << tot.getInputs() << "\n" + << " leftT = " << tot.leftT << "\n" + << " rightT = " << tot.rightT << "\n"; + /*if (tot.leftT == NullTRef) + std::cout << "LEFT: NullTRef\n"; + else + std::cout << "LEFT:" << (*tot.leftT) << "\n"; + if (tot.rightT == NullTRef) + std::cout << "RIGHT: NullTRef\n"; + else + std::cout << "RIGHT:" << (*tot.rightT);*/ + return os; +} + +#endif diff --git a/maxhs/core/TotalizerAuxStruct.h b/maxhs/core/TotalizerAuxStruct.h new file mode 100644 index 0000000..027e05b --- /dev/null +++ b/maxhs/core/TotalizerAuxStruct.h @@ -0,0 +1,102 @@ +/***********[TotalizerAuxStruct.h] +Copyright (c) 2019, Jeremias Berg, Fahiem Bacchus + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Some code and ideas from + + Open-WBO, Copyright (c) 2013-2017, Ruben Martins, Vasco Manquinho, Ines Lynce + +have been used; + Open-WBO is subject to the following LICENSE + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +***********/ + +#ifndef __TOTALIZER_AUXSTRUCT__ +#define __TOTALIZER_AUXSTRUCT__ + +#include +#include "maxhs/utils/io.h" +class TotalizerManager; +#ifdef GLUCOSE +namespace Minisat = Glucose; +#endif + +#ifdef GLUCOSE +#include "glucose/core/SolverTypes.h" +#else +#include "minisat/core/SolverTypes.h" +#endif + +using Minisat::Lit; +using Minisat::lit_Undef; + +struct TRef { + /* class for storing revefrences to totalizers */ + TRef(size_t i) : idx{static_cast(i)} {} + TRef(int i) : idx{i} {} + TRef() : idx{-1} {} + int idx; + operator int() const { return idx; } +}; +const TRef NullTRef{}; + +inline bool operator==(const TRef& t1, const TRef& t2) { + return t1.idx == t2.idx; +} + +inline std::ostream& operator<<(std::ostream& os, const TRef& t) { + std::cout << "TRef(" << t.idx << ")"; + return os; +} + +// auxilary struct. +struct TOut { + // as we build sub-totalizers we make some of their outputs assumptions + // this struct holds information about these output literal + Lit out_lit; // output lit + int toutIdx; // output index + TRef tr; // reference to the totalizer out_lit is an output of + TOut() : out_lit{lit_Undef}, toutIdx{0}, tr{NullTRef} {}; + TOut(Lit o, int idx, TRef t) : out_lit{o}, toutIdx{idx}, tr{t} {}; +}; + +inline std::ostream& operator<<(std::ostream& os, const TOut& t) { + std::cout << "TOut(" << t.out_lit << "," << t.toutIdx << "," << t.tr << ")"; + return os; +} + +#endif diff --git a/maxhs/core/TotalizerManager.cc b/maxhs/core/TotalizerManager.cc new file mode 100644 index 0000000..b76b590 --- /dev/null +++ b/maxhs/core/TotalizerManager.cc @@ -0,0 +1,1010 @@ +/***********[TotalizerManager.cc] +Copyright (c) 2019, Jeremias Berg, Fahiem Bacchus + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Some code and ideas from + + Open-WBO, Copyright (c) 2013-2017, Ruben Martins, Vasco Manquinho, Ines Lynce + +have been used; + Open-WBO is subject to the following LICENSE + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +***********/ + +#include "TotalizerManager.h" +#include "Totalizer.h" + +#include +#include +#include +#include +#include +#include "maxhs/ifaces/muser.h" +#include "maxhs/utils/Params.h" + +using std::map; +using std::vector; + +using MaxHS::MaxSolver; +using MaxHS_Iface::Muser; +using MaxHS_Iface::SatSolver; + +TotalizerManager::TotalizerManager(Bvars& b, MaxSolver* maxhs, Muser* m, + SatSolver* s) + : bvars{b}, maxsolver{maxhs}, muser{m}, satsolver{s} {} + +void TotalizerManager::add_core(const vector& cnst) { + // requires that cnst only contain core bvars (true ===> soft clause is + // falsified) or totalizer output (true ===> some number of soft clauses are + // falsified) + if (!params.abstract) return; + if (cnst.size() > static_cast(params.abstract_max_core_size)) { + if (params.verbosity > 1) + cout << "c adding large core to graph (size = " << cnst.size() + << " taking smaller set\n"; + // could select a random subset using std::sample (c++17) + // but for now just grab the first k elements + potential_cores.addVec(vector( + cnst.begin(), cnst.begin() + params.abstract_max_core_size)); + } else + potential_cores.addVec(cnst); +} + +void TotalizerManager::add_core_mutex(const vector& mx) { + if(mx.size() <= 1) return; + if (!madeTvars) { + bvars.initMaxTvar(); + madeTvars = true; + } + vector mxvars; + for(auto l: mx) { + if (!bvars.isCore(l)) return; + if (!isSameWt(l, mx[0])) return; + auto v = var(l); + if (core_structure.in_graph(v)) return; + mxvars.push_back(v); + } + core_structure.addCluster(mxvars); + TRef t = build_mx_totalizer(mx); + if(t != NullTRef) { + if (params.verbosity > 0) + cout << "c Built mx totalizer: " << getTSize(t) + << " inputs, " << getOutFalse(t) << " false\n"; + totalizer_list.push_back(t); + update_maps(t, totalizer_list.size() - 1); + } +} + +void TotalizerManager::compute_abstraction(bool full) { + if (params.verbosity > 0) + cout << "c Computing an abstraction with " << potential_cores.size() + << " new cores\n"; + size_t n_edges{0}; + vector core; + double ave_size = (1.0*potential_cores.total_size())/potential_cores.size(); + + /*cout << "ave_size = " << ave_size << " cores_total_size = " << potential_cores.total_size() + << " num cores = " << potential_cores.size() << "\n";*/ + + if(ave_size > params.abstract_max_ave_size) { + if(params.verbosity > 0) + cout << "c cores too large not abstracting\n"; + potential_cores.clear(); + return; + } + + for (auto cnst : potential_cores) { + // cout << "Core size: " << cnst.size() << endl; + core.clear(); + for (Lit l : cnst) { + if (isToutput(l)) + core.push_back(output_to_firstinput(l)); + else if (bvars.isCore(l) || bvars.isDvar(l)) + core.push_back(l); + } + if (core.size() == 0 && params.verbosity > 0) { + if (params.verbosity > 0) + cout << "c After processing, orig size " << cnst.size() + << " no literals left\n"; + continue; + } + for (size_t i = 0; i < core.size(); i++) + for (size_t j = i + 1; j < core.size(); j++) + if (isSameWt(core[i], core[j])) { + core_structure.addEdge(core[i], core[j], 1.0); + ++n_edges; + } + } + potential_cores.clear(); + if (n_edges > 0) { + if (params.verbosity > 0) + cout << "c added " << n_edges << " new edges. Abstracting\n"; + abstract_cores(full); + maxsolver->updateLB(computeTotLB()); + } else if (params.verbosity > 0) + cout << "c No new edges added skipping abstraction\n"; +} + +void TotalizerManager::abstract_cores(bool full) { + if (!madeTvars) { + bvars.initMaxTvar(); + madeTvars = true; + } + vector> clusters; + int iter{0}; + bool last_iter{false}; + while (true) { + auto mod_increase = core_structure.extractCommunities(clusters); + if (mod_increase <= 0 && !full) break; + if (mod_increase <= 0) { + auto n_clusters = clusters.size(); + combine_same_wt_clusters(clusters); + if (n_clusters == clusters.size()) break; + else last_iter = true; + } + ++iter; + double tots_built{0}; + double tot_sum_size{}; + double tot_sum_outs_true{}; + int n_tots{0}; + if (params.verbosity > 0) + cout << "c Cluster Iter " << iter << ". " << clusters.size() + << " clusters (mod " << mod_increase << ")\n"; + + if (params.verbosity > 1) { + int n_vars_in_clusters{0}; + for (auto& cluster : clusters) n_vars_in_clusters += cluster.size(); + cout << "c Nvars in graph = " << core_structure.get_n_vars() + << " nvars in cluster = " << n_vars_in_clusters << "\n"; + } + + for (auto& cluster : clusters) { + if (cluster.size() < static_cast(params.abstract_min_size)) { + continue; + } + if (params.verbosity > 1) + cout << "c building totalizer from cluster of size " << cluster.size() + << "\n"; + if (params.verbosity > 0 && cluster.size() > 200) + cout << "c large cluster: " << cluster.size() << "\n"; + + TRef t = build_totalizer(cluster); + if (t != NullTRef) { + ++tots_built; + tot_sum_size += getTSize(t); + tot_sum_outs_true += getOutTrue(t); + if (params.verbosity > 1) + cout << "c " << ++n_tots << ". built totalizer: " << getTSize(t) + << " inputs, " << getOutTrue(t) << " true\n"; + totalizer_list.push_back(t); + update_maps(t, totalizer_list.size() - 1); + } + } + + if (params.verbosity > 0) { + cout << "c " << tots_built << " new totalizers (out of " + << clusters.size() << " ), ave. size = " << tot_sum_size / tots_built + << " ave. true outs " << tot_sum_outs_true / tots_built << "\n"; + } + if (last_iter) break; + } + if (params.verbosity > 0) cout << "c " << iter << ". clustering complete\n"; +} + +void TotalizerManager::combine_same_wt_clusters(vector>& clusters) { + for (auto i = clusters.begin(); i != clusters.end(); ++i) + for (auto j = clusters.begin(); j != i; j++) { + if (i->empty() || j->empty()) continue; + if (isSameWt((*i)[0], (*j)[0])) { + for (auto& v : *i) j->push_back(v); + i->clear(); + } + } + clusters.erase(std::remove_if(clusters.begin(), clusters.end(), + [&](vector& c) { return c.empty(); }), + clusters.end()); +} + +TRef TotalizerManager::build_totalizer(const vector& cluster) { + // cout << "TEST: build_totalizer called with cluster size " << + // cluster.size() + // << "\n"; + // every var in cluster should be a bvar + assert(std::find_if(cluster.begin(), cluster.end(), [&](Var v) { + return !bvars.isBvar(v); + }) == cluster.end()); + vector inputs; // convert variable cluster to core literals. + for (auto v : cluster) inputs.push_back(bvars.coreLit(v)); + // every lit should have the same weight. + assert(allSameWt(inputs)); + inputs.erase( + std::remove_if(inputs.begin(), inputs.end(), + [&](Lit l) { return (satsolver->curVal(l) != l_Undef); }), + inputs.end()); + if (inputs.size() <= 1) { + return NullTRef; + } + if (!madeTvars) { + bvars.initMaxTvar(); + madeTvars = true; + } + return build_totalizer_1(inputs); +} + +TRef TotalizerManager::build_totalizer_1(const vector& inputs) { + vector assumps; + vector conflict; + vector inProcess_touts; // contains all active sub-totalizers that + // remain to be joined into other + // totalizers. + for (auto l : inputs) { + if (!isTinput(l)) + assumps.push_back(~l); + else { + auto l_tr = get_ilit_tref(l); + if (std::find_if(inProcess_touts.begin(), inProcess_touts.end(), + [&](TOut& tout) { return tout.tr == l_tr; }) == + inProcess_touts.end()) { + auto l_tr_ntrue = getOutTrue(l_tr); + if (static_cast(l_tr_ntrue) < getOLits(l_tr).size()) { + auto l_out_lit = getOLits(l_tr)[l_tr_ntrue]; + inProcess_touts.push_back({l_out_lit, l_tr_ntrue, l_tr}); + assumps.push_back(~l_out_lit); + } + } + } + } + if (assumps.size() <= 1) { + if (params.verbosity > 1) + cout << "c build_totalizer_1 already a cluster skipping\n"; + return NullTRef; + } + + // an invariant in this loop is that every active sub-totalizer + // (i.e. sub-totalizer not yet absorbed into another totalizer) has a + // negated output lit in assumps, and every original literal not yet + // absorbed into a totalizer has its negation in assumps. + // When all original lits and sub-totalizers have been absorbed into + // a totalizer, assumps will become empty. + + TOut t0; + while (!assumps.empty()) { + conflict.clear(); + if (sat_solve_update_assumps(assumps, conflict) == l_False) { + if (params.verbosity > 1) + cout << "c build_totalizer_1 found conflict. Size = " << conflict.size() + << "\n"; + if (conflict.empty()) { + cout << "c ERROR, build_totalizer found empty conflict\n"; + return NullTRef; + } + // constuct single totalizer from the conflict original lits + // and sub-totalizer outputs + t0 = totalizer_from_conflict(conflict, inProcess_touts); + if (t0.tr != NullTRef && !assumps.empty()) { + inProcess_touts.push_back(t0); + assumps.push_back(~t0.out_lit); + } + } else { // remaining assumptions are satisfiable + assert(!assumps.empty()); + conflict.clear(); + for (auto l : assumps) { + if (satsolver->curVal(l) == l_Undef) conflict.push_back(~l); + } + assumps.clear(); + t0 = totalizer_from_conflict(conflict, inProcess_touts); + } + } + return t0.tr; +} + +TRef TotalizerManager::build_mx_totalizer(const vector& mx) { + Weight wt{get_weight_of_lit(mx[0])}; + TRef t = Totalizer::makeTotalizer(*this, mx, wt); + const vector& oLits = getOLits(t); + + cout << " Built mx totalizer outlits = " << oLits << "\n"; + + satsolver->addClause(~oLits[1]); + setValuedOuts(t); + addClausesToSolvers(increaseMaxFalse(t, 2)); + addClausesToSolvers(increaseMaxTrue(t, 1)); + return t; +} + +lbool TotalizerManager::sat_solve_update_assumps(vector& assumps, + vector& conflict) { + // sat solve and remove conflict (if found from assumptions) + conflict.clear(); + lbool sat_result; + if(params.cpu_per_exhaust < 0) + sat_result = satsolver->solve(assumps, conflict); + else if(params.cpu_per_exhaust > 0) + sat_result = satsolver->solveBudget(assumps, conflict, params.cpu_per_exhaust); + else + sat_result = l_Undef; + + if (sat_result == l_False) { + if (params.min_type == 1 && conflict.size() > 2) { + if (params.mus_cpu_lim > 0) { + muser->musBudget(conflict, params.mus_cpu_lim); + } else { + muser->mus(conflict); + } + } + satsolver->addClause(conflict); + std::sort(conflict.begin(), conflict.end()); + auto e = std::remove_if(assumps.begin(), assumps.end(), [&](Lit l) { + return (satsolver->curVal(l) != l_Undef || + std::binary_search(conflict.begin(), conflict.end(), ~l)); + }); + assumps.erase(e, assumps.end()); + } else if (sat_result == l_True) { + maxsolver->updateUB(); + } + return sat_result; +} + +TOut TotalizerManager::totalizer_from_conflict(const vector& conflict, + vector& inProcess_touts) { + // Build a totalizer from the lits in conflict (some of which might be + // output lits from active sub-totalizer in inProcess_touts. + assert(!conflict.empty()); + vector conf_orig_lits; + vector conf_touts; + + // 1. split conflict + for (auto l : conflict) { + auto it = std::find_if(inProcess_touts.begin(), inProcess_touts.end(), + [&](TOut t) { return t.out_lit == l; }); + if (it != inProcess_touts.end()) { + // move the found sub totalizer from inprocess to conf This + // sub-totalizer will be joined into the final totalizer + // generated by the conflict so it is no longer going to be + // active + conf_touts.push_back(*it); + *it = inProcess_touts.back(); + inProcess_touts.pop_back(); + } else + conf_orig_lits.push_back(l); + } + + // 2. build and exhaust a totalizer over the orig lits + if (conflict.size() == 1) { + if (conf_orig_lits.size() == 1) return TOut{}; + } + if (!conf_orig_lits.empty()) { + TOut t0 = make_and_exhaust_base_totalizer(conf_orig_lits); + if (t0.tr != NullTRef) conf_touts.push_back(t0); + } + // 3. join and exhaust the totalizer over the orig lits + // and all other sub-totalizers found in the conflict. + // Return a TOut structure from this totalizer + return join_and_exhaust_totalizers(conf_touts); +} + +/*TEST*/ +void TotalizerManager::test() { + /*vector v; + vector> cnf; + for (int i = 0; i < 6; i++) { + v.push_back(mkLit(i)); + } + cnf.clear(); + Totalizer::makeTotalizer(*this, v, cnf, 0, 0, 1.0); + cout << "Totalizer of size " << v.size() << " cnf = " << cnf << "\n"; + exit(0);*/ + auto vars{bvars.getvars()}; + TRef t = build_totalizer(vars); + if (t != NullTRef) { + totalizer_list.push_back(t); + update_maps(t, totalizer_list.size() - 1); + const vector& iLits = getILits(t); + const vector& oLits = getOLits(t); + for (auto l : iLits) + if (!bvars.isCore(l)) { + cout << "ERROR input to final totalizer is not a core\n"; + exit(1); + } + for (auto l : oLits) + if (!isToutput(l)) { + cout << "ERROR output to final totalizer is not TOut\n"; + exit(1); + } + } +} +/*TEST*/ + +TOut TotalizerManager::make_and_exhaust_base_totalizer( + const vector& conf_orig_lits) { + // build a totalizer over this set of lits, then exhaust it. + // if is_clause, this set of lits are a clause so their totalizer must + // have its first output set to true. + assert(!conf_orig_lits.empty()); + assert(allSameWt(conf_orig_lits)); + Weight wt{get_weight_of_lit(conf_orig_lits[0])}; + auto t0 = Totalizer::makeTotalizer(*this, conf_orig_lits, wt); + int lb = exhaustTotalizer(t0); + if (lb >= static_cast(getTSize(t0))) + return TOut{}; + else + return TOut{getOLits(t0)[lb], lb, t0}; +} + +TOut TotalizerManager::join_and_exhaust_totalizers(vector& conf_touts) { + // join these totalizers into one. The final totalizer must have one extra + // output true if uncounted_true + if (conf_touts.empty()) return TOut{}; + TRef t0{NullTRef}; + for (size_t i = 0; i < conf_touts.size(); i++) { + int lb{0}; + if (t0 == NullTRef) { + t0 = conf_touts[i].tr; + lb = exhaustTotalizer(t0); + } else { + t0 = Totalizer::makeTotalizer(*this, t0, conf_touts[i].tr); + lb = exhaustTotalizer(t0); + } + if (lb >= getTSize(t0)) t0 = NullTRef; + } + if (t0 == NullTRef) + return TOut{}; + else { + int nTrue{getOutTrue(t0)}; + return TOut{getOLits(t0)[nTrue], nTrue, t0}; + } +} + +int TotalizerManager::exhaustTotalizer(TRef tot) { + // cout << "TEST: exhausting totalizer " << (*tot) << "\n"; + const vector& oLits = getOLits(tot); + setValuedOuts(tot); + int lb = getOutTrue(tot); + // cout << "TEST: lb = " << lb << "\n"; + vector assumps(1); + while (lb < static_cast(getOLits(tot).size())) { + addClausesToSolvers(increaseMaxFalse(tot, lb + 1)); + assumps[0] = ~oLits[lb]; + vector discard; + lbool res; + if(params.cpu_per_exhaust < 0) + res = satsolver->solve(assumps, discard); + else if(params.cpu_per_exhaust > 0) + res = satsolver->solveBudget(assumps, discard, params.cpu_per_exhaust); + else + res = l_Undef; + // cout << "TEST in exhaust loop sat solver returns " << res << "\n"; + // cout << " lb = " << lb << "\n"; + if (res == l_False) { + satsolver->addClause(oLits[lb]); + ++lb; + } else { + if (res == l_True) maxsolver->updateUB(); + break; + } + } + (*tot).setNOutTrue(lb); + addClausesToSolvers(increaseMaxTrue(tot, lb)); + return lb; +} + +/*void TotalizerManager::initTotalizer(TRef tot, int ntrue) { + // call if you have an initial lb bound on totalizer + setValuedOuts(tot); + if (ntrue > getOutTrue(tot)) { + const vector& oLits = getOLits(tot); + for (int i = getOutTrue(tot); i < ntrue && i < getTSize(tot); ++i) + if (satsolver->curVal(oLits[i]) != l_True) { + satsolver->addClause(oLits[i]); + } + int lb = getOutTrue(tot); + addClausesToSolvers(increaseMaxFalse(tot, lb + 1)); + } + }*/ + +void TotalizerManager::addClausesToSolvers(const vector> cnf) { + for (const auto& clause : cnf) { + satsolver->addClause(clause); + muser->addClause(clause); + } +} + +void TotalizerManager::update_maps(TRef t, size_t tidx) { + // t is a new top level totalizer + resizeMaps(); + setTopLevel(t); + + auto iLits = getILits(t); + auto oLits = getOLits(t); + + for (Lit l : iLits) { + assert(bvars.isBvar(l)); + if(input_var_to_tref[toInt(var(l))] != -1) + unsetTopLevel(input_var_to_tref[toInt(var(l))]); + input_var_to_tref[toInt(var(l))] = t; + input_var_to_tidx[toInt(var(l))] = tidx; + } + + for (size_t j = 0; j < oLits.size(); j++) { + Lit o = oLits[j]; + output_var_to_tref[toInt(var(o))] = t; + output_var_to_tout_index[toInt(var(o))] = j; + } +} + +// void TotalizerManager::compute_totalizer_count( +// TRef tot, const vector& bvars_in_sol_counts, vector& counts) { +// /* set counts[i] to be the maximum number of true inputs possible +// for totalizer i. +// */ +// if (counts[tot] >= 0) return; +// auto ilit{getILits(tot)}; +// // bvars_in_sol_counts does not include inputs that are outputs of other +// // totalizers +// int num_falsified{bvars_in_sol_counts[tot]}; +// for (Lit l : ilit) { +// if (isToutput(l)) { +// // check if this input (that is an output of another totalizer is +// // also falsified) +// TRef tref_o = get_olit_tref(l); +// int tref_o_index = get_olit_index(l); +// if (counts[tref_o] < 0) +// compute_totalizer_count(tref_o, bvars_in_sol_counts, counts); +// if (counts[tref_o] <= tref_o_index) { +// num_falsified++; +// } +// } +// } +// int baseCount = +// std::max(static_cast(ilit.size()) - num_falsified, getOutTrue(tot)); +// // cout << "Index " << index << " Base count " << baseCount << "\n"; +// counts[tot] = baseCount; +// } + +Lit TotalizerManager::getNextOLit(Lit l) { + assert(isToutput(l)); + TRef tot = get_olit_tref(l); + int nextOutputIdx = get_olit_index(l) + 1; + if (nextOutputIdx >= static_cast(getOLits(tot).size())) + return lit_Undef; + else { + auto cnf{increaseMaxFalse(tot, nextOutputIdx + 1)}; + addClausesToSolvers(cnf); + return getOLits(tot)[nextOutputIdx]; + } +} + +void TotalizerManager::resizeMaps() { + size_t sz = bvars.n_vars(); + if (input_var_to_tref.size() < sz) { + input_var_to_tref.resize(sz, -1); + input_var_to_tidx.resize(sz, -1); + output_var_to_tref.resize(sz, -1); + output_var_to_tout_index.resize(sz, -1); + output_added_to_cplex.resize(sz, 0); + } +} + +// TODO change code so that TRef objects have a -> operator +// so that, e.g., t->getSize() can be used instead of (*t).getSize() +// But this will require refactoring the headers since -> operators +// must be member functions of the TRef class. + +void TotalizerManager::setValuedOuts(TRef t) { + // update the ouputs of t to their forced values. + int lrfalse{0}, lrtrue{0}; + TRef r{(*t).getRight()}; + TRef l{(*t).getLeft()}; + + if (r != NullTRef) { + setValuedOuts(r); + lrtrue += getOutTrue(r); + lrfalse += getOutFalse(r); + } + if (l != NullTRef) { + setValuedOuts(l); + lrtrue += getOutTrue(l); + lrfalse += getOutFalse(l); + } + const vector& oLits = getOLits(t); + int ntrue{0}; + for (; ntrue < lrtrue; ++ntrue) { + if (satsolver->curVal(oLits[ntrue]) != l_True) + satsolver->addClause(oLits[ntrue]); + } + for (; ntrue < static_cast(oLits.size()); ntrue++) { + if (satsolver->curVal(oLits[ntrue]) != l_True) break; + } + (*t).setNOutTrue(ntrue); + + int nfalse{0}; + auto last{oLits.size() - 1}; + for (; nfalse < lrfalse; nfalse++) { + if (satsolver->curVal(oLits[last - nfalse]) != l_False) + satsolver->addClause(~oLits[last - nfalse]); + } + for (; nfalse < static_cast(oLits.size()); nfalse++) { + if (satsolver->curVal(oLits[last - nfalse]) != l_False) break; + } + (*t).setNOutFalse(nfalse); + + assert(checkOutputs(t)); +} + +Weight TotalizerManager::computeTotLB() { + Weight w{}; + for(auto t : totalizer_list) + if(isTopLevel(t)) + w += getOutTrue(t) * getWeight(t); + return w; +} + +vector TotalizerManager::get_top_level_tidxes() const { + vector tidxs; + for(size_t i = 0; i < totalizer_list.size(); ++i) + if(isTopLevel(totalizer_list[i])) + tidxs.push_back(i); + return tidxs; +} + +bool TotalizerManager::checkOutputs(TRef t) { + // outputs should be true first undef next false last. + auto oLits{getOLits(t)}; + int type{-1}, nxt_type{-1}; + for (int i = 0; i < static_cast(oLits.size()); i++) { + auto val = satsolver->curVal(oLits[0]); + if (val == l_True) + nxt_type = 0; + else if (val == l_Undef) + nxt_type = 1; + else if (val == l_False) + nxt_type = 3; + if (nxt_type < type) return false; + type = nxt_type; + } + return true; +} + +int TotalizerManager::getTSize(TRef t) const { return (*t).getSize(); } +bool TotalizerManager::isLeafT(TRef t) const { return (*t).isLeaf(); } + +int TotalizerManager::getOutTrue(TRef t) const { return (*t).getOutTrue(); } + +int TotalizerManager::getOutFalse(TRef t) const { return (*t).getOutFalse(); } + +vector TotalizerManager::getILits(TRef t) const { + return (*t).getInputs(); +} +const vector& TotalizerManager::getOLits(TRef t) const { + return (*t).getOutputs(); +} + +const vector& TotalizerManager::getROLits(TRef t) const { + auto rt = (*t).getRight(); + return (*rt).getOutputs(); +} + +const vector& TotalizerManager::getLOLits(TRef t) const { + auto lt = (*t).getLeft(); + return (*lt).getOutputs(); +} + +Weight TotalizerManager::getWeight(TRef t) const { return (*t).getWeight(); } +vector> TotalizerManager::increaseMaxFalse(TRef t, int maxFalse) { + return (*t).increaseMaxFalse(maxFalse); +} +vector> TotalizerManager::increaseMaxTrue(TRef t, int maxTrue) { + return (*t).increaseMaxFalse(maxTrue); +} + +bool TotalizerManager::isTopLevel(TRef t) const { return (*t).isTopLevel(); } +void TotalizerManager::setTopLevel(TRef t) { return (*t).setTopLevel(); } +void TotalizerManager::unsetTopLevel(TRef t) { return (*t).unsetTopLevel(); } + +/*TRef TotalizerManager::build_totalizer_2(vector& orig_inputs) { + // We look for a conflict and try to build sub-totalizers over these + // conflicts finally joining up the sub-totalizers. + vector assumps(orig_inputs.size()); + std::transform(orig_inputs.begin(), orig_inputs.end(), assumps.begin(), + [](Lit l) { return ~l; }); + + vector conflict; + // conflict will be partitioned into original lits + // and created sub-totalizer outputs + TOut current_T{}; + + while (!assumps.empty()) { + // cout << "TEST: assumps.size() = " << assumps.size() << "\n"; + conflict.clear(); + if (sat_solve_update_assumps(assumps, conflict) == l_False) { + if (params.verbosity > 0) + cout << "c build_totalizer_2 found conflict. Size = " << + conflict.size() + << "\n"; + if (conflict.empty()) { + cout << "c ERROR, build_totalizer_2 found empty conflict\n"; + return NullTRef; + } else if (conflict.size() == 1) { + if (params.verbosity > 0) cout << "c in build_totalizer_2 found + unit\n"; + // satsolver->addClause(conflict[0]); + if (conflict[0] == current_T.out_lit) { + if (params.verbosity > 0) + cout << "c conflict is current totalizer output index " + << current_T.toutIdx << "\n"; + auto tot{current_T.tr}; + auto oLits{getOLits(tot)}; + initTotalizer(tot, current_T.toutIdx); + auto nxt_idx{current_T.toutIdx + 1}; + if (nxt_idx < getTSize(tot)) { + current_T = TOut{oLits[nxt_idx], nxt_idx, tot}; + assumps.push_back(~current_T.out_lit); + } else { + current_T = TOut{}; // exhausted this totalizer + } + } + } else { // non-unit conflict of size > 1 + auto it = + std::remove(conflict.begin(), conflict.end(), current_T.out_lit); + if (params.verbosity > 0) { + if (it == conflict.end()) + cout << "c found conflict not including current totalizer\n"; + else + cout << "c found conflict including current totalizer\n"; + } + conflict.erase(it, conflict.end()); + + // build new totalizer and then join with current totalizer. + auto tout = make_and_exhaust_base_totalizer(conflict); + auto t0 = tout.tr; + auto lb = getOutTrue(t0); + if (current_T.tr != NullTRef) { + auto nTrue = getOutTrue(current_T.tr) + getOutTrue(t0); + t0 = Totalizer::makeTotalizer(*this, current_T.tr, t0); + initTotalizer(t0, nTrue); + lb = exhaustTotalizer(t0); + } + assumps.erase( + std::remove(assumps.begin(), assumps.end(), ~current_T.out_lit), + assumps.end()); + if (static_cast(lb) < getOLits(t0).size()) { + current_T = TOut{getOLits(t0)[lb], lb, t0}; + assumps.push_back(~current_T.out_lit); + } else { + current_T = TOut{}; + } + } + } else { + assert(!assumps.empty()); + // the sat solver did not find a new conflict. Collect remaining + // lits into a totalizer and then join with current totalizer + if (assumps.size() == 1) { + assert(assumps[0] == ~current_T.out_lit); + assumps.clear(); + } else { + conflict.clear(); + for (auto l : assumps) { + if (satsolver->curVal(l) == l_Undef && l != ~current_T.out_lit) + conflict.push_back(~l); + } + auto tout = make_and_exhaust_base_totalizer(conflict); + auto t0 = tout.tr; + auto lb = getOutTrue(t0); + if (current_T.tr != NullTRef) { + auto nTrue = getOutTrue(current_T.tr) + getOutTrue(t0); + t0 = Totalizer::makeTotalizer(*this, current_T.tr, t0); + initTotalizer(t0, nTrue); + lb = exhaustTotalizer(t0); + } + assumps.clear(); + if (static_cast(lb) < getOLits(t0).size()) { + current_T = TOut{getOLits(t0)[lb], lb, t0}; + } else { + current_T = TOut{}; + } + } + } + } + return current_T.tr; + }*/ + +/*TRef TotalizerManager::build_totalizer_3(vector& orig_inputs) { + if (params.verbosity > 0) + cout << "c build_totalizer type 3 on " << orig_inputs.size() << " + inputs\n"; vector assumps(orig_inputs.size()); + std::transform(orig_inputs.begin(), orig_inputs.end(), assumps.begin(), + [](Lit l) { return ~l; }); + + vector conflict; + TOut current_T{}; + while (!assumps.empty()) { + if (current_T.tr == NullTRef) { + find_initial_totalizer(assumps, current_T); + } else { + grow_totalizer(assumps, current_T); + } + } + return current_T.tr; + }*/ + +/*void TotalizerManager::find_initial_totalizer(vector& assumps, + TOut& current_T) { + vector conflict{}; + while (!assumps.empty()) { + if (sat_solve_update_assumps(assumps, conflict) == l_False) { + if (params.verbosity > 0) + cout << "c initial_totalizer found conflict. Size = " << + conflict.size() + << "\n"; + if (conflict.empty()) { + cout << "c ERROR, build_totalizer found empty conflict\n"; + current_T = TOut{}; + assumps.clear(); + return; + } else if (conflict.size() == 1) { + // satsolver->addClause(conflict[0]); + } else { + Weight wt{get_weight_of_lit(conflict[0])}; + auto t0 = Totalizer::makeTotalizer(*this, conflict, wt); + // this totalizer already has one true input. + initTotalizer(t0, 1); + auto lb = exhaustTotalizer(t0); + // if all inputs of totalizer are forced to be true we don't + // need the totalizer + if (lb >= getTSize(t0)) + current_T = TOut{}; + else { + current_T = TOut{getOLits(t0)[lb], lb, t0}; + return; + } + } + } else { // no conflict in this call. Build final totalizer. + if (params.verbosity > 0) + cout << "c initial_totalizer found sat over remaining " + << assumps.size() << " assumptions\n"; + current_T = TOut{}; + if (assumps.size() > 1) { + conflict.clear(); + for (auto l : assumps) { + if (satsolver->curVal(l) == l_Undef) conflict.push_back(~l); + } + Weight wt{get_weight_of_lit(conflict[0])}; + auto t0 = Totalizer::makeTotalizer(*this, conflict, wt); + initTotalizer(t0, 0); + current_T = TOut{getOLits(t0)[0], 0, t0}; + } + assumps.clear(); + return; + } + } + }*/ + +/*void TotalizerManager::grow_totalizer(vector& assumps, TOut& current_T) + { + // grow the totalizer in current_T by successively adding literals from + // assumps that are falsified by the current model. + vector incremental_assumps; + vector conflict; + incremental_assumps.push_back(~current_T.out_lit); + while (!assumps.empty()) { + auto it = std::find_if(assumps.begin(), assumps.end(), [&](Lit l) { + return satsolver->modelValue(l) != l_True; + }); + if (it == assumps.end()) { + if (params.verbosity > 0) + cout << "c grow_totalizer found sat over " + << assumps.size() + incremental_assumps.size() + << " remaining assumptions (" << assumps.size() << "," + << incremental_assumps.size() << ")\n"; + conflict.clear(); + for (auto l : assumps) { + if (satsolver->curVal(l) == l_Undef) conflict.push_back(~l); + } + for (auto l : incremental_assumps) + if (satsolver->curVal(l) == l_Undef) conflict.push_back(~l); + Weight wt{get_weight_of_lit(conflict[0])}; + auto t0 = Totalizer::makeTotalizer(*this, conflict, wt); + initTotalizer(t0, 0); + t0 = Totalizer::makeTotalizer(*this, current_T.tr, t0); + initTotalizer(t0, getOutTrue(current_T.tr)); + current_T = TOut{getOLits(t0)[getOutTrue(t0)], getOutTrue(t0), t0}; + assumps.clear(); + return; + } else { // test for new conflict + incremental_assumps.push_back(*it); + *it = assumps.back(); + assumps.pop_back(); + if (sat_solve_update_assumps(incremental_assumps, conflict) == l_False) + { if (params.verbosity > 0) cout << "c grow totalizer found conflict. Size = + " << conflict.size() + << "\n"; + if (conflict.empty()) { + cout << "c ERROR, build_totalizer found empty conflict\n"; + current_T = TOut{}; + assumps.clear(); + return; + } else if (conflict.size() == 1) { + // satsolver->addClause(conflict[0]); + if (conflict[0] == current_T.out_lit) { + if (params.verbosity > 0) + cout << "c grow_totalizer conflict is totalizer output\n"; + auto tot = current_T.tr; + ++current_T.toutIdx; + if (current_T.toutIdx < getTSize(tot)) { + current_T.out_lit = getOLits(tot)[current_T.toutIdx]; + incremental_assumps[0] = ~current_T.out_lit; + } else { + // this totalizer is exhausted return to find new + // totalizer over remaining assumptions + for (size_t i = 1; i < incremental_assumps.size(); i++) + assumps.push_back(incremental_assumps[i]); + current_T = TOut{}; + return; + } + } else { + if (params.verbosity > 0) + cout << "c grow_totalizer found ordinary unit\n"; + } + } else { // non-unit conflict + auto it = + std::remove(conflict.begin(), conflict.end(), + current_T.out_lit); bool disjoint_conflict = (it == conflict.end()); + conflict.erase(it, conflict.end()); + Weight wt{get_weight_of_lit(conflict[0])}; + auto t0 = Totalizer::makeTotalizer(*this, conflict, wt); + initTotalizer(t0, (disjoint_conflict ? 1 : 0)); + t0 = Totalizer::makeTotalizer(*this, current_T.tr, t0); + auto lb = exhaustTotalizer(t0); + if (lb > getTSize(t0)) { + for (size_t i = 1; i < incremental_assumps.size(); i++) + assumps.push_back(incremental_assumps[i]); + current_T = TOut{}; + return; + } else { + current_T = TOut{getOLits(t0)[lb], lb, t0}; + incremental_assumps[0] = ~current_T.out_lit; + } + } + } else { + if (params.verbosity > 0) + cout << "c grow_totalizer satisfied incremental assumption " + "growing " + "incrementals\n"; + } + } + } + }*/ diff --git a/maxhs/core/TotalizerManager.h b/maxhs/core/TotalizerManager.h new file mode 100644 index 0000000..1d90f96 --- /dev/null +++ b/maxhs/core/TotalizerManager.h @@ -0,0 +1,300 @@ +/***********[TotalizerManager.h] +Copyright (c) 2019, Jeremias Berg, Fahiem Bacchus + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Some code and ideas from + + Open-WBO, Copyright (c) 2013-2017, Ruben Martins, Vasco Manquinho, Ines Lynce + +have been used; + Open-WBO is subject to the following LICENSE + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +***********/ + +#ifndef __TOTALIZER_MANAGER__ +#define __TOTALIZER_MANAGER__ + +#include +#include +#include +#include +#include +#include + +#include "maxhs/core/Bvars.h" +#include "maxhs/core/Graph.h" +#include "maxhs/core/MaxSolver.h" +#include "maxhs/core/MaxSolverTypes.h" +#include "maxhs/core/TotalizerAuxStruct.h" +#include "maxhs/ds/Packed.h" +#include "maxhs/ifaces/SatSolver.h" + +using Minisat::toInt; +using std::map; +using std::vector; + +#ifdef GLUCOSE +namespace Minisat = Glucose; +#endif + +class Bvars; +class Graph; +namespace MaxHS_Iface { +class SatSolver; +class Muser; +} // namespace MaxHS_Iface + +class TotalizerManager { + public: + TotalizerManager(Bvars& b, MaxHS::MaxSolver* maxhs, MaxHS_Iface::Muser* m, + MaxHS_Iface::SatSolver* s); + bool totalizers_active() const { return !totalizer_list.empty(); } + size_t nb_totalizers() const { return totalizer_list.size(); } + // void clear_cores() { potential_cores.clear(); } + void add_core(const vector& core); + void add_core_mutex(const vector& mx); + void compute_abstraction(bool full = false); + Lit getNextOLit(Lit l); + + /*int get_olit_coef(Lit l) { + assert(isToutput(l)); + return get_olit_index(l)p + 1; + }*/ + + int get_olit_index(Lit l) const { + assert(isToutput(l)); + return output_var_to_tout_index[toInt(var(l))]; + } + + bool olit_set_in_cplex(Lit l) const { + assert(isToutput(l)); + return output_added_to_cplex[toInt(var(l))]; + } + + void set_olit_in_cplex(Lit l) { + assert(isToutput(l)); + output_added_to_cplex[toInt(var(l))] = 1; + } + + vector get_ilits_from_olit(Lit l) { + assert(isToutput(l)); + return getILits(get_olit_tref(l)); + } + + const vector& get_olits_from_olit(Lit l) const { + assert(isToutput(l)); + return getOLits(get_olit_tref(l)); + } + + vector get_ilits_from_tidx(int tidx) { + return getILits(totalizer_list[tidx]); + } + + const vector& get_olits_from_tidx(int tidx) { + return getOLits(totalizer_list[tidx]); + } + + int get_tot_size_from_tidx(int tidx) { + return getTSize(totalizer_list[tidx]); + } + + int get_n_true_outs(int tidx) const { + return getOutTrue(totalizer_list[tidx]); + } + + vector get_top_level_tidxes() const; + + bool isTinput(Lit l) const { + return (static_cast(toInt(var(l))) < input_var_to_tref.size()) && + input_var_to_tref[toInt(var(l))] >= 0; + } + + int get_Tinput_idx(Lit l) const { + assert(isTinput(l)); + return input_var_to_tidx[toInt(var(l))]; + } + + Lit get_next_Toutput(int idx, int n_true) { + assert(static_cast(idx) < totalizer_list.size()); + auto tref = totalizer_list[idx]; + if (static_cast(n_true) < getOLits(tref).size()) { + auto cnf{increaseMaxFalse(tref, n_true + 1)}; + addClausesToSolvers(cnf); + return getOLits(tref)[n_true]; + } else + return lit_Undef; + } + + bool isToutput(Lit l) const { return isToutput(var(l)); } + + bool isToutput(Var v) const { + return (static_cast(toInt(v)) < output_var_to_tref.size()) && + output_var_to_tref[toInt(v)] >= 0; + } + + bool isPositiveToutput(Lit l) const { return isToutput(l) && !sign(l); } + bool isNegativeToutput(Lit l) const { return isToutput(l) && sign(l); } + + /*TEST*/ + void test(); + /*TEST*/ + + private: + friend class Totalizer; + + void abstract_cores(bool full); + TRef build_totalizer(const vector&); + void combine_same_wt_clusters(vector>&); + TRef build_totalizer_1(const vector&); + TRef build_mx_totalizer(const vector& mx); + // TRef build_totalizer_2(vector&); + // TRef build_totalizer_3(vector&); + TOut totalizer_from_conflict(const vector& conflict, + vector& inProcess_touts); + TOut make_and_exhaust_base_totalizer(const vector& conf_orig_lits); + TOut join_and_exhaust_totalizers(vector& conf_touts); + + + // void find_initial_totalizer(vector&, TOut&); + // void grow_totalizer(vector&, TOut&); + lbool sat_solve_update_assumps(vector&, vector&); + + TRef get_ilit_tref(Lit l) const { + assert(isTinput(l)); + return input_var_to_tref[toInt(var(l))]; + } + + TRef get_olit_tref(Lit l) const { return get_olit_tref(var(l)); } + + TRef get_olit_tref(Var v) const { + assert(isToutput(v)); + return output_var_to_tref[toInt(v)]; + } + + Weight get_weight_of_lit(Lit l) const { + if (isToutput(l)) { + return getWeight(get_olit_tref(l)); + } else { + return bvars.wt(l); + } + } + + Weight get_weight_of_var(Var v) const { + if (isToutput(v)) { + return getWeight(get_olit_tref(v)); + } else { + return bvars.wt(v); + } + } + + void addClausesToSolvers(const vector> cnf); + int exhaustTotalizer(TRef t); + void initTotalizer(TRef, int); + void update_maps(TRef t, size_t tidx); + Lit output_to_firstinput(Lit l) { return get_ilits_from_olit(l)[0]; } + Weight computeTotLB(); + // void compute_totalizer_count(TRef t, const vector& counts_in_sol, + // vector& counts); + + void addNewNodesToGraph(vector& toAdd); + void updateExistingNodesinGraph(vector& lits_to_add); + void resizeMaps(); + + int getTSize(TRef t) const; + bool isLeafT(TRef tp) const; + vector getILits(TRef t) const; + const vector& getOLits(TRef t) const; + const vector& getROLits(TRef t) const; + const vector& getLOLits(TRef t) const; + int getOutTrue(TRef t) const; + int getOutFalse(TRef t) const; + bool isTopLevel(TRef t) const; + void setTopLevel(TRef t); + void unsetTopLevel(TRef t); + + Weight getWeight(TRef t) const; + vector> increaseMaxFalse(TRef t, int maxFalse); + vector> increaseMaxTrue(TRef t, int maxTrue); + void setValuedOuts(TRef t); + bool checkOutputs(TRef t); + + bool isSameWt(Lit l1, Lit l2) { + return get_weight_of_lit(l1) == get_weight_of_lit(l2); + } + + bool isSameWt(Var v1, Var v2) { + return get_weight_of_var(v1) == get_weight_of_var(v2); + } + + bool allSameWt(const vector& set) { + return set.size() < 2 || std::find_if(set.begin(), set.end(), [&](Lit l) { + return !isSameWt(l, set[0]); + }) == set.end(); + } + + Lit getNewOutputLit() { return mkLit(bvars.makeTvar()); } + + bool madeTvars{false}; + Bvars& bvars; + Packed_vecs potential_cores; + + // TO add clauses + MaxHS::MaxSolver* maxsolver; + MaxHS_Iface::Muser* muser; + MaxHS_Iface::SatSolver* satsolver; + Graph core_structure; + + vector totalizer_list; + // + // MAPS + // var --> totalizer that var is an input of + vector input_var_to_tref; + // var --> totalizer that var is an output of + vector output_var_to_tref; + // var --> i such that lit is the i-th output of its totalizer + vector output_var_to_tout_index; + // var --> true if this totalizer output var has been added to cplex + vector output_added_to_cplex; + // var --> index in totalizer_list. Internal totalizers are not in + // totalizer_list. + vector input_var_to_tidx; + + // stats for output + int seen_totalizers{0}; +}; + +#endif diff --git a/maxhs/core/Wcnf.cc b/maxhs/core/Wcnf.cc old mode 100755 new mode 100644 index ec63327..532935b --- a/maxhs/core/Wcnf.cc +++ b/maxhs/core/Wcnf.cc @@ -22,60 +22,42 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ***********/ -#include -#include #include -#include +#include +#include #include +#include +#include #ifdef GLUCOSE -#include "glucose/utils/System.h" #include "glucose/core/SolverTypes.h" +#include "glucose/utils/System.h" #else -#include "minisat/utils/System.h" #include "minisat/core/SolverTypes.h" +#include "minisat/utils/System.h" #endif -#include "maxhs/core/Wcnf.h" -#include "maxhs/utils/io.h" +#include "maxhs/core/Bvars.h" #include "maxhs/core/Dimacs.h" +#include "maxhs/core/Wcnf.h" #include "maxhs/ifaces/miniSatSolver.h" -#include "maxhs/utils/hash.h" #include "maxhs/utils/Params.h" -#include "maxhs/core/Bvars.h" +#include "maxhs/utils/hash.h" +#include "maxhs/utils/io.h" #ifdef GLUCOSE namespace Minisat = Glucose; #endif -using Minisat::sign; +using MaxHS_Iface::miniSolver; +using MaxHS_Iface::SatSolver_uniqp; using Minisat::lbool; using Minisat::mkLit; +using Minisat::sign; using Minisat::toInt; using Minisat::toLit; using Minisat::var_Undef; -using MaxHS_Iface::SatSolver_uniqp; -Wcnf::Wcnf() : - maxorigvar{0}, - maxvar{0}, - dimacs_nvars{0}, - dimacs_nclauses{0}, - ms_type{MStype::undef}, - parsing_time{0}, - total_cls_wt {0}, - base_cost {0}, - dimacs_top{std::numeric_limits::max()}, - wt_var {0}, - wt_mean {0}, - wt_min {0}, - wt_max {0}, - unsat {false}, - noDups {true}, - intWts {true}, - nhard_units {0} -{} - void Wcnf::set_dimacs_params(int nvars, int nclauses, Weight top) { dimacs_nvars = nvars; dimacs_nclauses = nclauses; @@ -83,32 +65,34 @@ void Wcnf::set_dimacs_params(int nvars, int nclauses, Weight top) { } bool Wcnf::inputDimacs(std::string filename, bool verify) { - //verify == reading input 2nd time to verify result (don't apply preprocessing) + // verify == reading input 2nd time to verify result (don't apply + // preprocessing) instance_file_name = filename; double start_time = Minisat::cpuTime(); gzFile input = gzopen(filename.c_str(), "rb"); if (input == NULL) { - cout << "c ERROR: problem opening input file: " << instance_file_name << "\n"; + cout << "c ERROR: problem opening input file: " << instance_file_name + << "\n"; return false; } - if(!parse_DIMACS(input, this)) { - cout << "c ERROR: Parsing error on input file: " << instance_file_name << "\n"; + if (!parse_DIMACS(input, this)) { + cout << "c ERROR: Parsing error on input file: " << instance_file_name + << "\n"; return false; } - if(!verify) { + if (!verify) { computeWtInfo(); parsing_time = Minisat::cpuTime() - start_time; printFormulaStats(); simplify(); - if(params.verbosity>0) - printSimpStats(); + if (params.verbosity > 0) printSimpStats(); } return true; } -void Wcnf::addDimacsClause(vector &lits, Weight w) { - //This routine needs to know dimacs_top (so set_dimacs_params should - //have been called first) to determine if the clause is soft or hard +void Wcnf::addDimacsClause(vector& lits, Weight w) { + // This routine needs to know dimacs_top (so set_dimacs_params should + // have been called first) to determine if the clause is soft or hard if (w >= dimacs_top) addHardClause(lits); else @@ -116,15 +100,14 @@ void Wcnf::addDimacsClause(vector &lits, Weight w) { } bool Wcnf::prepareClause(vector& lits) { - if(lits.size() > 1) { + if (lits.size() > 1) { std::sort(lits.begin(), lits.end()); size_t cur_size, examine; - for(cur_size = examine = 1; examine < lits.size(); examine++) { - if(lits[cur_size-1] == ~lits[examine]) { - return false; //Tautologies + for (cur_size = examine = 1; examine < lits.size(); examine++) { + if (lits[cur_size - 1] == ~lits[examine]) { + return false; // Tautologies } - if(lits[cur_size-1] != lits[examine]) - lits[cur_size++] = lits[examine]; + if (lits[cur_size - 1] != lits[examine]) lits[cur_size++] = lits[examine]; } lits.resize(cur_size); } @@ -132,9 +115,9 @@ bool Wcnf::prepareClause(vector& lits) { } void Wcnf::update_maxorigvar(vector& lits) { - for(auto l : lits) - if(var(l) > maxorigvar) - maxorigvar = maxvar = var(l); + for (auto l : lits) + if (var(l) > maxorigvar) maxorigvar = var(l); + if (maxorigvar > maxvar) maxvar = maxorigvar; } void Wcnf::addHardClause(vector& lits) { @@ -143,193 +126,243 @@ void Wcnf::addHardClause(vector& lits) { } void Wcnf::_addHardClause(vector& lits) { - //Use this routine when adding a clause not contained in the - //original formula, e.g., adding preprocessing clause - if(unsat) return; - if(!prepareClause(lits)) return; //skip tautologies + // Use this routine when adding a clause not contained in the + // original formula, e.g., adding preprocessing clause + if (unsat) return; + if (!prepareClause(lits)) return; // skip tautologies + for (auto l : lits) + if (maxvar < var(l)) maxvar = var(l); hard_cls.addVec(lits); - for(auto l : lits) - if(maxvar < var(l)) - maxvar = var(l); - noDups=false; + noDups = false; } -void Wcnf::addSoftClause(vector &lits, Weight w) { - //zero weight clauses discarded by this interface function. +void Wcnf::addSoftClause(vector& lits, Weight w) { + // zero weight clauses discarded by this interface function. if (w < 0) cout << "c ERROR: soft clause cannot have negative weight: " << w << "\n"; - else if(w > 0) { + else if (w > 0) { update_maxorigvar(lits); _addSoftClause(lits, w); } } -void Wcnf::_addSoftClause(vector &lits, Weight w) { - //Use this routine when adding a clause not contained in the - //original formula, e.g., adding preprocessing clause - if(unsat) return; - if(!prepareClause(lits)) return; //skip tautologies +void Wcnf::_addSoftClause(vector& lits, Weight w) { + // Use this routine when adding a clause not contained in the + // original formula, e.g., adding preprocessing clause + if (unsat) return; + if (!prepareClause(lits)) return; // skip tautologies Weight intPart; - if(lits.size() > 0) { - if(modf(w, &intPart) > 0) - intWts = false; + if (lits.size() > 0) { + if (modf(w, &intPart) > 0) intWts = false; soft_cls.addVec(lits); soft_clswts.push_back(w); total_cls_wt += w; - for(auto l : lits) - if(maxvar < var(l)) - maxvar = var(l); - } - else base_cost += w; - noDups=false; + for (auto l : lits) + if (maxvar < var(l)) maxvar = var(l); + } else + base_cost += w; + noDups = false; } void Wcnf::simplify() { - //We allow a Wcnf to transform itself in various model equivalent - //ways. Only the remaining hard and soft clauses after - //simplification will be passed to the solver. After the solver has - //found a model for the transformed wcnf, it will need to invoke the - //method 'rewriteModelToInput' to convert that model to a model of - //the original input formula. (E.g., if eliminated variables must be - //added back). - - //1. wcnf_harden --- test some softs can be hardened because of their high weight - if(params.wcnf_harden) - simpleHarden(); - - //2. Look for equalities implied by the hard clauses. Replace y by x if x==y. - // Also simplify by hard units. - bool found_eqs {false}; - if(params.wcnf_eqs) - found_eqs = subEqs(); - - //3. Although eq-processing does unit reduction, we might find some - // additional units via eq-processing if equalities were found. - if(params.wcnf_units && (!params.wcnf_eqs || found_eqs)) - rmUnits(); - - //4. New b-variables are not added to soft units, e.g., (x). Instead - // we reuse the literal in the soft unit as its own b-variable. In this case - // we need to ensure that we have no duplicate softs for correctness. - remDupCls(); - - //5. Find groups of mutuall exclusive b-variables. Both positive \sum bi <= 1 and - // negative \sum -bi <= 1. - // These are given a special treatment. - if(params.mx_find_mxes) - mxBvars(); - - //6. Some of these transformations might increase the forced (base) cost of the WCNF - // and changed the weights + // We allow a Wcnf to transform itself in various model equivalent + // ways. Only the remaining hard and soft clauses after + // simplification will be passed to the solver. After the solver has + // found a model for the transformed wcnf, it will need to invoke the + // method 'rewriteModelToInput' to convert that model to a model of + // the original input formula. (E.g., if eliminated variables must be + // added back). + + orig_all_lits_soft = test_all_lits_are_softs(); + + // wcnf_harden --- test some softs can be hardened because of their high + // weight + if (params.wcnf_harden) simpleHarden(); + + // Look for units and/or equalities implied by the hard clauses. + // Simplify by hard units and replace y by x if x==y. + if (params.wcnf_eqs || params.wcnf_units) subEqsAndUnits(); + + // New b-variables are not added to soft units, e.g., (x). Instead + // we reuse the literal in the soft unit as its own b-variable. In this case + // we need to ensure that we have no duplicate softs for correctness. + remDupCls(); + // Find groups of mutually exclusive b-variables. Both positive + // \sum bi <= 1 and negative \sum -bi <= 1. + // These are given a special treatment. + if (params.mx_find_mxes) { + if (params.mx_find_mxes == 3) { + params.mx_find_mxes = 2; + mxBvars(); + params.mx_find_mxes = 1; + mxBvars(); + } else + mxBvars(); + } + + // Some of these transformations might increase the forced (base) cost of + // the WCNF and change the weights computeWtInfo(); - if(params.simplify_and_exit) { - //write simplified wcnf to cout then exit. + // remap the variables + remapVars(); + + if (params.simplify_and_exit) { + // write simplified wcnf to cout then exit. printDimacs(cout); - exit(0); } - if(params.prepro_output) - printFormula(); + if (params.prepro_output) printFormula(); } -bool Wcnf::subEqs() { - if(unsat) return false; +bool Wcnf::test_all_lits_are_softs() { + vector is_soft(nVars(), false); + for (const auto cls : soft_cls) { + if (cls.size() == 1) is_soft[var(cls[0])] = true; + } + for (const auto cls : hard_cls) + for (auto l : cls) + if (!is_soft[var(l)]) return false; + for (const auto cls : soft_cls) + for (auto l : cls) + if (!is_soft[var(l)]) return false; + return true; +} + +void Wcnf::subEqsAndUnits() { + if (unsat) return; - //Find equalities implied by the hard clauses. - //if x <==> y then replace all occurances of y by x + // reduce formula by hard units. Optionally find + // equalities implied by the hard clauses and + // if x <==> y then replace all occurances of y by x // - during replace shrink the clause if this results in duplicates. // - remove the clause if this results in a tautology - //After all y's have been replaced, add the clauses (-x, y) and (-y, x) - //to the set of hards...so that the final solution will set y appropriately. + // After all y's have been replaced, add the clauses (-x, y) and (-y, x) + // to the set of hards...so that the final solution will set y appropriately. //( 'y' longer appears elsewhere in the theory). - //finding equalities might also force some units so simplify the - //theory with those as well. + // finding equalities might also force some units + // so in general we need propagation over equalities and units. + // but for now do incomplete prop. int ph = hard_cls.size(); int ph_lits = hard_cls.total_size(); int ps = soft_cls.size(); int ps_lits = soft_cls.total_size(); - //1. Find current units in the hard clauses and then find/binaries - //among the hard clauses reduced by those units. - SatSolver_uniqp sat_solver {new miniSolver}; - sat_solver->eliminate(true); //no preprocessing - for(size_t i = 0; i < nHards(); i++) - if(!sat_solver->addClause(getHard(i))) { + // 1. Find current units in the hard clauses and then find/binaries + // among the hard clauses reduced by those units. + + SatSolver_uniqp sat_solver{new MaxHS_Iface::miniSolver}; + sat_solver->eliminate(true); // no preprocessing + for (size_t i = 0; i < nHards(); i++) + if (!sat_solver->addClause(getHard(i))) { unsat = true; - return false; + return; + } + + hard_units = sat_solver->getForced(0); + + if (params.wcnf_eqs) { + vector binaries = get_binaries(sat_solver); + vector> edges; + for (size_t i = 0; i < binaries.size(); i += 2) { + Lit x = binaries[i]; + Lit y = binaries[i + 1]; + size_t max_index = std::max(std::max(toInt(x), toInt(~x)), + std::max(toInt(y), toInt(~y))); + if (max_index >= edges.size()) edges.resize(max_index + 1); + edges[toInt(x)].push_back(y); + edges[toInt(y)].push_back(x); } - vector hard_units = sat_solver->getForced(0); - vector binaries = get_binaries(sat_solver); - - //2. Build adjacency map to represent the binaries - vector> edges; - for(size_t i = 0; i < binaries.size(); i+=2) { - Lit x = binaries[i]; - Lit y = binaries[i+1]; - size_t max_index = std::max(std::max(toInt(x), toInt(~x)), - std::max(toInt(y), toInt(~y))); - if(max_index >= edges.size()) - edges.resize(max_index+1); - edges[toInt(x)].push_back(y); - edges[toInt(y)].push_back(x); + all_scc = binary_scc(edges); + } else + all_scc.clear(); + + // debug + // cout << "hard_units" << hard_units << "\n"; + // cout << "all_scc " << all_scc << "\n"; + + // modify the wcnf by the detected equivalents and units + if (!all_scc.empty() || !hard_units.empty()) { + hard_cls = reduce_by_eqs_and_units(hard_cls, false); + soft_cls = reduce_by_eqs_and_units(soft_cls, true); } - //3. Compute SCCs. - auto all_sccs = binary_scc(edges); - bool found_eqs = !all_sccs.empty(); - - /*//DEBUG - cout << "Found " << all_sccs.size() << " components \n"; - if(all_sccs.size()) cout << all_sccs << "\n";*/ - - //4. modify the wcnf by the detected equivalents and units - hard_cls = reduce_by_eqs_and_units(hard_cls, false, all_sccs, hard_units); - soft_cls = reduce_by_eqs_and_units(soft_cls, true, all_sccs, hard_units); - - //5. Now add hard clauses to enforce the units and equalities. - // E.g., if x==y, in step 4 we replace all instances of y by x. - // then in step 5 we add (-x, y) and (x, -y). Now - // y must have the same truth value as x and y only appears - // in these two hard clauses. - - for(auto l : hard_units) - _addHardClause(l); - - vector bin(2); - for(auto& scc : all_sccs) - //note all lits in scc replaced by scc[0] by replace_by_eqs_and_units - for(size_t i = 1; i < scc.size(); i++) { - bin[0] = ~scc[0]; bin[1] = scc[i]; - _addHardClause(bin); - bin[0] = scc[0]; bin[1] = ~scc[i]; - _addHardClause(bin); + // check for pures and units. + auto n_hards{hard_units.size()}; + int pures{0}; + + vector appears(nVars(), 0); + for (const auto cls : hard_cls) { + if (cls.size() == 1) { + hard_units.push_back(cls[0]); + // debug + // cout << "Hard unit after reduction " << cls.getVec() << "\n"; + } + for (auto l : cls) appears[var(l)] |= sign(l) ? 0b01 : 0b10; + } + for (auto cls : soft_cls) + for (auto l : cls) appears[var(l)] |= sign(l) ? 0b01 : 0b10; + + for (int v = 0; v != nVars(); v++) + if (appears[v] == 0b01) { // pure -v + ++pures; + // debug + // cout << "pure unit after reduction " << mkLit(v, true) << "\n"; + hard_units.push_back(mkLit(v, true)); + } else if (appears[v] == 0b10) { // pure + ++pures; + // debug + // cout << "pure unit after reduction " << mkLit(v, false) << "\n"; + hard_units.push_back(mkLit(v, false)); } - if(params.verbosity > 0) { + if (hard_units.size() > n_hards) { + if (params.verbosity > 0) + cout << "c WCNF eqs: found additional units after scc " + << hard_units.size() - n_hards << " pures " << pures << "\n"; + for (auto l : hard_units) + if (!sat_solver->addClause(l)) { + unsat = true; + return; + } + hard_units = sat_solver->getForced(0); + // debug + // cout << "New hard units = " << hard_units << "\n"; + + hard_cls = reduce_by_eqs_and_units(hard_cls, false); + soft_cls = reduce_by_eqs_and_units(soft_cls, true); + } + + if (params.verbosity > 0) { int nvars_removed = hard_units.size(); - for(size_t i = 0; i < all_sccs.size(); i++) - nvars_removed += all_sccs[i].size() - 1; + for (size_t i = 0; i < all_scc.size(); i++) + nvars_removed += all_scc[i].size() - 1; cout << "c WCNF units: found " << hard_units.size() << " units\n" - << "c WCNF SCCs: found " << all_sccs.size() << " strongly connected components\n" + << "c WCNF SCCs: found " << all_scc.size() + << " strongly connected components\n" << "c WCNF removed: " << nvars_removed << " variables\n" - << "c WCNF removed: " << ph - static_cast(hard_cls.size()) << " hard clauses\n" - << "c WCNF removed: " << ph_lits - static_cast(hard_cls.total_size()) << " lits from hard clauses\n" - << "c WCNF removed: " << ps - static_cast(soft_cls.size()) << " soft clauses\n" - << "c WCNF removed: " << ps_lits - static_cast(soft_cls.total_size()) << " lits from softs clauses\n"; + << "c WCNF removed: " << ph - static_cast(hard_cls.size()) + << " hard clauses\n" + << "c WCNF removed: " + << ph_lits - static_cast(hard_cls.total_size()) + << " lits from hard clauses\n" + << "c WCNF removed: " << ps - static_cast(soft_cls.size()) + << " soft clauses\n" + << "c WCNF removed: " + << ps_lits - static_cast(soft_cls.total_size()) + << " lits from softs clauses\n"; } - return found_eqs; } - + vector Wcnf::get_units() { - //feed hard clauses of wcnf into a sat solver, do unit prop, and then - //return the found units - SatSolver_uniqp sat_solver {new miniSolver}; - sat_solver->eliminate(true); //no preprocessing - for(size_t i = 0; i < nHards(); i++) - if(!sat_solver->addClause(getHard(i))) { + // feed hard clauses of wcnf into a sat solver, do unit prop, and then + // return the found units + SatSolver_uniqp sat_solver{new miniSolver}; + sat_solver->eliminate(true); // no preprocessing + for (size_t i = 0; i < nHards(); i++) + if (!sat_solver->addClause(getHard(i))) { unsat = true; return {}; } @@ -337,32 +370,31 @@ vector Wcnf::get_units() { } vector Wcnf::get_binaries(SatSolver_uniqp& sat_solver) { - vector binaries {}; - for(auto& clause : hard_cls) { + vector binaries{}; + for (auto& clause : hard_cls) { int nlits = 0; - for(auto l : clause) { + for (auto l : clause) { auto truth_value = sat_solver->curVal(l); - if(truth_value == l_Undef) - nlits++; + if (truth_value == l_Undef) nlits++; - if(truth_value == l_True) - //treat satisfied clauses as if they are too big. + if (truth_value == l_True) + // treat satisfied clauses as if they are too big. nlits = 3; - if(nlits > 2) - break; + if (nlits > 2) break; } - - if(nlits == 2) { - int sz {}; - for(auto l : clause) - if(sat_solver->curVal(l) == l_Undef) { + + if (nlits == 2) { + int sz{}; + for (auto l : clause) + if (sat_solver->curVal(l) == l_Undef) { binaries.push_back(l); sz++; } - if(sz != 2) { - cout << "c ERROR in WCNF get_binaries...binary of size " << sz << "\n ["; - for(auto l : clause) { + if (sz != 2) { + cout << "c ERROR in WCNF get_binaries...binary of size " << sz + << "\n ["; + for (auto l : clause) { sat_solver->printLit(l); cout << ", "; } @@ -373,300 +405,214 @@ vector Wcnf::get_binaries(SatSolver_uniqp& sat_solver) { return binaries; } -Packed_vecs Wcnf::reduce_by_eqs_and_units( - Packed_vecs& cls, bool softs, - vector>& all_sccs, vector units) { - - if(unsat) - return Packed_vecs {}; +Packed_vecs Wcnf::reduce_by_eqs_and_units(const Packed_vecs& clauses, + bool softs) { + if (unsat) return Packed_vecs{}; - vector truth_vals(2*nVars(), l_Undef); - vector eqLit(2*nVars()); + vector truth_vals(2 * nVars(), l_Undef); + vector eqLit(2 * nVars()); - for(int i = 0; i < nVars(); i++) { + for (int i = 0; i < nVars(); i++) { auto lt = mkLit(i); eqLit[toInt(lt)] = lt; eqLit[toInt(~lt)] = ~lt; } - - for(auto lt : units) { + + for (auto lt : hard_units) { truth_vals[toInt(lt)] = l_True; truth_vals[toInt(~lt)] = l_False; } - for(auto scc : all_sccs) - for(size_t i = 0; i < scc.size(); i++) { + for (const auto& scc : all_scc) + for (size_t i = 0; i < scc.size(); i++) { eqLit[toInt(scc[i])] = scc[0]; eqLit[toInt(~scc[i])] = ~scc[0]; } - + Packed_vecs tmp; size_t j = 0; vector c; - for(size_t i = 0; i < cls.size(); i++) { + for (size_t i = 0; i < clauses.size(); i++) { c.clear(); - bool isSat {false}; - for(auto l : cls[i]) { + bool isSat{false}; + for (auto l : clauses[i]) { auto eqL = eqLit[toInt(l)]; - if(truth_vals[toInt(eqL)] == l_Undef) - c.push_back(eqL); - if(truth_vals[toInt(eqL)] == l_True) { + if (truth_vals[toInt(eqL)] == l_Undef) c.push_back(eqL); + if (truth_vals[toInt(eqL)] == l_True) { isSat = true; break; } } - if(isSat) + if (isSat) continue; - else if(c.empty()) { - if(!softs) { - //empty hards should be caught when clauses added to sat solver. + else if (c.empty()) { + if (!softs) { + // empty hards should be caught when clauses added to sat solver. cout << "c ERROR: Wcnf::reduce_by_units found empty hard clause\n"; unsat = true; - return Packed_vecs {}; + return Packed_vecs{}; } base_cost += soft_clswts[i]; - } - else if(prepareClause(c)) { + } else if (prepareClause(c)) { tmp.addVec(c); - //Note equality replacement might generate new units. But all units passed in - //hard_units will be satisfied...do not added to the updated clauses. - if(softs) - soft_clswts[j++] = soft_clswts[i]; + // Note equality replacement might generate new units. But all units + // passed in hard_units will be satisfied...do not added to the updated + // clauses. + if (softs) soft_clswts[j++] = soft_clswts[i]; } } - - if(softs) { + + if (softs) { soft_clswts.resize(j); soft_clswts.shrink_to_fit(); + total_cls_wt = 0; + for (auto wt : soft_clswts) total_cls_wt += wt; } return tmp; } vector> Wcnf::binary_scc(vector>& edges) { - //find strongly connected components of binary implication graph + // find strongly connected components of binary implication graph //(BIG) of size > 1. The BIG has duality. So if x is in an SCC -x - //will be in a dual SCC. Only return the first of these dual SCCs - //found. - //We ignore the possibility that the BIG might have implied - //units. These could be checked for after scc detection if wanted. - //Edges are an adjacency map edges[toInt(l)] = {y1, y2, ..., yk} iff - //the binary clauses (l, y1), ..., (l, yk) exist. + // will be in a dual SCC. Only return the first of these dual SCCs + // found. + // We ignore the possibility that the BIG might have implied + // units. These could be checked for after scc detection if wanted. + // Edges are an adjacency map edges[toInt(l)] = {y1, y2, ..., yk} iff + // the binary clauses (l, y1), ..., (l, yk) exist. // - // The nodes of the graph can be viewed to be + // The nodes of the graph can be viewed to be // literals l, where toInt(l) is index of l in edges - // OR indicies i into edges, where toLit(i) is the literal of that node. + // OR indicies i into edges, where toLit(i) is the literal of that node. // We use both--dependent on what is convenient. - //We use a stack for dfs. Push when a node is first explored, pop - //when a node is finished. + // We use a stack for dfs. Push when a node is first explored, pop + // when a node is finished. - //stacks to hold the tentative sccs (store nodes as indicies) + // stacks to hold the tentative sccs (store nodes as indicies) vector unfinished; vector roots; - //dfsnum---DFS visit order of a node (as an index). -1 not yet visited + // dfsnum---DFS visit order of a node (as an index). -1 not yet visited vector dfsnum(edges.size(), -1); - int dfscount {0}; + int dfscount{0}; - //component number---scc number node (as an index) is in. -1 not yet processed + // component number---scc number node (as an index) is in. -1 not yet + // processed vector comp_num(edges.size(), -1); - int comp_count {0}; + int comp_count{0}; - //dfs stack---a pair, node index and number of child to be processed next. + // dfs stack---a pair, node index and number of child to be processed next. // initially a for a node n we push (n,0)---first child to be visited next. // at final stage stack has (n, k) where k >= edges[n].size() indicating - // that all children have been processed---time to finish n. - vector> dfs_stack; + // that all children have been processed---time to finish n. + vector> dfs_stack; - //hold the return set of sccs + // hold the return set of sccs vector> all_sccs; - for(int nd = 0; static_cast(nd) < edges.size(); nd++) { - if(dfsnum[nd] != -1) - continue; //already explored + for (int nd = 0; static_cast(nd) < edges.size(); nd++) { + if (dfsnum[nd] != -1) continue; // already explored dfs_stack.push_back({nd, 0}); - - while(!dfs_stack.empty()) { + + while (!dfs_stack.empty()) { auto n_ci = dfs_stack.back(); auto node = n_ci.first; auto neg_node = toInt(~toLit(node)); - auto childi = n_ci.second; //index into neg_node's binary clauses...these are implicants of node + auto childi = n_ci.second; // index into neg_node's binary + // clauses...these are implicants of node - if(childi == 0) { - //0 we are first visiting node. Mark its visit number and put - //it in its own tentative scc. - dfsnum[node] = dfscount++; + if (childi == 0) { + // 0 we are first visiting node. Mark its visit number and put + // it in its own tentative scc. + dfsnum[node] = dfscount++; unfinished.push_back(node); roots.push_back(node); } - if(static_cast(childi) >= edges[neg_node].size()) { - //processed all children...finish the node. + if (static_cast(childi) >= edges[neg_node].size()) { + // processed all children...finish the node. dfs_stack.pop_back(); - if(node == roots.back()) { + if (node == roots.back()) { vector scc; - int w {}; + int w{}; do { w = unfinished.back(); unfinished.pop_back(); comp_num[w] = comp_count; - if(comp_num[neg_node] == -1) //dual scc not processed + if (comp_num[neg_node] == -1) // dual scc not processed scc.push_back(toLit(w)); } while (w != node); comp_count++; roots.pop_back(); - if(scc.size() > 1) - all_sccs.push_back(std::move(scc)); + if (scc.size() > 1) all_sccs.push_back(std::move(scc)); } - } - else { - //explore next child and update stack entry + } else { + // explore next child and update stack entry dfs_stack.back().second++; auto w = toInt(edges[neg_node][childi]); - if(dfsnum[w] == -1) //put on stack to explore. + if (dfsnum[w] == -1) // put on stack to explore. dfs_stack.push_back({w, 0}); - else if(comp_num[w] == -1) { - //merge scc - while(dfsnum[roots.back()] > dfsnum[w]) - roots.pop_back(); + else if (comp_num[w] == -1) { + // merge scc + while (dfsnum[roots.back()] > dfsnum[w]) roots.pop_back(); } } } } return all_sccs; } - -void Wcnf::rmUnits() { - if(unsat) return; - SatSolver_uniqp sat_solver {new miniSolver}; - sat_solver->eliminate(true); //no preprocessing - - for(size_t i = 0; i < nHards(); i++) - if(!sat_solver->addClause(getHard(i))) { - unsat = true; - return; - } - - auto ph = hard_cls.size(); - auto ph_lits = hard_cls.total_size(); - auto ps = soft_cls.size(); - auto ps_lits = soft_cls.total_size(); - - auto hard_units_found = sat_solver->getForced(0); - - if(hard_units_found.size() > 0) { - hard_cls = reduce_by_units(hard_cls, sat_solver, false); - soft_cls = reduce_by_units(soft_cls, sat_solver, true); - } - //add hard units to formula - for(auto l : hard_units_found) - _addHardClause(l); - - total_cls_wt = 0; - for(auto wt : soft_clswts) - total_cls_wt += wt; - - if(params.verbosity > 0) - cout << "c WCNF units: found " << hard_units_found.size() << " units\n" - << "c WCNF units: removed " - << ph - hard_cls.size() << " hard clauses with " - << ph_lits - hard_cls.total_size() << " lits\n" - << "c WCNF units: removed " - << ps - soft_cls.size() << " soft clauses with " - << ps_lits - soft_cls.total_size() << " lits\n"; -} - -Packed_vecs Wcnf::reduce_by_units(Packed_vecs& cls, SatSolver_uniqp& sat_solver, bool softs) { - //auxilary function for rmUnits - Packed_vecs tmp; - if(unsat) return tmp; - - size_t j = 0; - vector c; - for(size_t i = 0; i < cls.size(); i++) { - c.clear(); - bool isSat {false}; - for(auto l : cls[i]) - if(sat_solver->curVal(l) == l_Undef) - c.push_back(l); - else if(sat_solver->curVal(l) == l_True) { - isSat = true; - break; - } - if(isSat) - continue; - else if(c.empty()) { - if(!softs) - //empty hards should be caught when clauses added to sat solver. - cout << "c ERROR: Wcnf::reduce_by_units found empty hard clause\n"; - base_cost += soft_clswts[i]; - } - else if(softs || c.size() > 1) { - tmp.addVec(c); - if(softs) - soft_clswts[j++] = soft_clswts[i]; - } - } - if(softs) { - soft_clswts.resize(j); - soft_clswts.shrink_to_fit(); - } - return tmp; -} void Wcnf::remDupCls() { /*compute hash code for each clause. Use this to detect identical clauses hash code for units is the hash code of the var---so that -x and x get the same hash code and we can detect this clash. */ - if(noDups || unsat) - return; - noDups=true; + if (noDups || unsat) return; + noDups = true; vector cdata; initClsData(cdata); - auto byHash = [](const ClsData& a, const ClsData &b) - { return a.hash < b.hash; }; + auto byHash = [](const ClsData& a, const ClsData& b) { + return a.hash < b.hash; + }; sort(cdata.begin(), cdata.end(), byHash); Packed_vecs tmpH, tmpS; vector tmp_wts; - for(size_t i=0; i < cdata.size(); i++) { - if(cdata[i].w == 0) - continue; //w==0 indicates clause is deleted; - + for (size_t i = 0; i < cdata.size(); i++) { + if (cdata[i].w == 0) continue; // w==0 indicates clause is deleted; - uint32_t i_index { cdata[i].index }; - bool ihard { cdata[i].w < 0}; + uint32_t i_index{cdata[i].index}; + bool ihard{cdata[i].w < 0}; auto vi = ihard ? hard_cls[i_index] : soft_cls[i_index]; - for(size_t j=i+1; j < cdata.size() && cdata[i].hash==cdata[j].hash; j++) { - if(cdata[j].w == 0) - continue; + for (size_t j = i + 1; j < cdata.size() && cdata[i].hash == cdata[j].hash; + j++) { + if (cdata[j].w == 0) continue; - uint32_t j_index {cdata[j].index}; - bool jhard {cdata[j].w < 0}; + uint32_t j_index{cdata[j].index}; + bool jhard{cdata[j].w < 0}; auto vj = jhard ? hard_cls[j_index] : soft_cls[j_index]; - if(vi.size() == 1 && vj.size() == 1 && vi[0] == ~vj[0]) { //contradictory units - if(ihard && jhard) { + if (vi.size() == 1 && vj.size() == 1 && + vi[0] == ~vj[0]) { // contradictory units + if (ihard && jhard) { unsat = true; return; - } - else if(ihard || jhard) { //soft is falsified - if(jhard) vi[0] = vj[0]; + } else if (ihard || jhard) { // soft is falsified + if (jhard) vi[0] = vj[0]; auto cost = jhard ? getWt(i_index) : getWt(j_index); base_cost += cost; cdata[j].w = 0; cdata[i].w = -1; - } - else { //neither is hard. This is a resolution of unit softs. - Weight cost {}, residue {}; - if(cdata[i].w < cdata[j].w) { - vi[0] = vj[0]; //higher cost unit is preserved + } else { // neither is hard. This is a resolution of unit softs. + Weight cost{}, residue{}; + if (cdata[i].w < cdata[j].w) { + vi[0] = vj[0]; // higher cost unit is preserved cost = cdata[i].w; residue = cdata[j].w - cost; - } - else { //note that if costs are equal residue becomes 0 and both clauses vanish + } else { // note that if costs are equal residue becomes 0 and both + // clauses vanish cost = cdata[j].w; residue = cdata[i].w - cost; } @@ -676,37 +622,37 @@ void Wcnf::remDupCls() { } } - else if(eqVecs(cdata[i], cdata[j])) { //equal clauses are merged. - if(ihard || jhard) { //hard subsumes both soft and hard. - cdata[i].w = -1; + else if (eqVecs(cdata[i], cdata[j])) { // equal clauses are merged. + if (ihard || jhard) { // hard subsumes both soft and hard. + cdata[i].w = -1; cdata[j].w = 0; - } - else { - cdata[i].w += cdata[j].w; //join softs. + } else { + cdata[i].w += cdata[j].w; // join softs. cdata[j].w = 0; } } } - //finished processing all clauses that match with vi. + // finished processing all clauses that match with vi. } - auto byIndex = [](const ClsData& a, const ClsData &b) - { return a.index < b.index; }; + auto byIndex = [](const ClsData& a, const ClsData& b) { + return a.index < b.index; + }; sort(cdata.begin(), cdata.end(), byIndex); - for(size_t i=0; i < cdata.size(); i++) { + for (size_t i = 0; i < cdata.size(); i++) { auto i_index = cdata[i].index; auto ohard = cdata[i].origHard; auto vi = ohard ? hard_cls[i_index] : soft_cls[i_index]; - if(cdata[i].w < 0) { + if (cdata[i].w < 0) { tmpH.addVec(vi.getVec()); - //cout << "i = " << i << " Added hard " << vi.getVec() << "\n\n"; - } - else if(cdata[i].w > 0) { + // cout << "i = " << i << " Added hard " << vi.getVec() << "\n\n"; + } else if (cdata[i].w > 0) { tmpS.addVec(vi.getVec()); tmp_wts.push_back(cdata[i].w); - //cout << "i = " << i << " Added soft " << vi.getVec() << " weight = " << cdata[i].w << "\n\n"; + // cout << "i = " << i << " Added soft " << vi.getVec() << " weight = " << + // cdata[i].w << "\n\n"; } } @@ -717,167 +663,159 @@ void Wcnf::remDupCls() { soft_cls = std::move(tmpS); soft_clswts = std::move(tmp_wts); total_cls_wt = 0; - for(auto wt : soft_clswts) - total_cls_wt += wt; + for (auto wt : soft_clswts) total_cls_wt += wt; - if(params.verbosity > 0) - cout << "c WCNF found " << ph - hard_cls.size() - << " redundant hards and " << ps -soft_cls.size() - << " duplicate or subsumed softs\n"; + if (params.verbosity > 0) + cout << "c WCNF found " << ph - hard_cls.size() << " redundant hards and " + << ps - soft_cls.size() << " duplicate or subsumed softs\n"; } bool Wcnf::eqVecs(const ClsData& a, const ClsData& b) { - //auxilary function for rmDupCls - //relies on all clauses being sorted on input. + // auxilary function for rmDupCls + // relies on all clauses being sorted on input. size_t sa = (a.w < 0) ? hardSize(a.index) : softSize(a.index); size_t sb = (b.w < 0) ? hardSize(b.index) : softSize(b.index); - if(sa != sb) - return false; - + if (sa != sb) return false; + auto va = (a.w < 0) ? getHard(a.index) : getSoft(a.index); auto vb = (b.w < 0) ? getHard(b.index) : getSoft(b.index); - for(size_t i=0; i < va.size(); i++) - if(va[i] != vb[i]) - return false; + for (size_t i = 0; i < va.size(); i++) + if (va[i] != vb[i]) return false; return true; -} +} void Wcnf::initClsData(vector& cdata) { - //auxilary function for rmDupcls. - //units are hashed as variables not as lits. - for(size_t i = 0; i < nHards(); i++) - if(hardSize(i) == 1) { - vector unit { var(hard_cls[i][0]) }; - cdata.emplace_back(static_cast(i), + // auxilary function for rmDupcls. + // units are hashed as variables not as lits. + for (size_t i = 0; i < nHards(); i++) + if (hardSize(i) == 1) { + vector unit{var(hard_cls[i][0])}; + cdata.emplace_back(static_cast(i), hashCode(unit.begin(), unit.end()), -1, true); - } - else - cdata.emplace_back(static_cast(i), - hashCode(hard_cls[i].begin(), hard_cls[i].end()),-1, true); + } else + cdata.emplace_back(static_cast(i), + hashCode(hard_cls[i].begin(), hard_cls[i].end()), -1, + true); - for(size_t i = 0; i < nSofts(); i++) - if(softSize(i) == 1) { - vector unit { var(soft_cls[i][0]) }; + for (size_t i = 0; i < nSofts(); i++) + if (softSize(i) == 1) { + vector unit{var(soft_cls[i][0])}; cdata.emplace_back(static_cast(i), hashCode(unit.begin(), unit.end()), getWt(i), false); - } - else - cdata.emplace_back(static_cast(i), - hashCode(soft_cls[i].begin(), soft_cls[i].end()), getWt(i), false); + } else + cdata.emplace_back(static_cast(i), + hashCode(soft_cls[i].begin(), soft_cls[i].end()), + getWt(i), false); - if(params.verbosity > 2) { + if (params.verbosity > 2) { vector hcodes; - for(auto &item : cdata) - hcodes.push_back(item.hash); + for (auto& item : cdata) hcodes.push_back(item.hash); sort(hcodes.begin(), hcodes.end()); auto it = std::unique(hcodes.begin(), hcodes.end()); hcodes.resize(std::distance(hcodes.begin(), it)); - cout << "c Hashed " << cdata.size() << " clauses with " - << it-hcodes.begin() << " (" << hcodes.size() << ") different hash codes\n"; + cout << "c Hashed " << cdata.size() << " clauses with " + << it - hcodes.begin() << " (" << hcodes.size() + << ") different hash codes\n"; } } void Wcnf::simpleHarden() { - //Check if we can make some soft clauses hard before we do other - //preprocessing that might be aided by additional hard clauses. - //This hardening uses the transition weights if there are any. - //In particular W is a transition weight if the sum of the weights - //of the clauses with weight less than W is less than W: + // Check if we can make some soft clauses hard before we do other + // preprocessing that might be aided by additional hard clauses. + // This hardening uses the transition weights if there are any. + // In particular W is a transition weight if the sum of the weights + // of the clauses with weight less than W is less than W: // \sum_{soft clauses c s.t. wt(c) < W} wt(c) < W // - //This means that we would prefer to falsify all clauses of weight - //less than W rather than a single clause of weight >= W. We check - //to see if the set of soft clauses with weight >= W are satisfiable + // This means that we would prefer to falsify all clauses of weight + // less than W rather than a single clause of weight >= W. We check + // to see if the set of soft clauses with weight >= W are satisfiable //(in conjunction with the hard clauses), i.e., the set - //HIGHWT = {c| wt(c) >= W or c is hard}. + // HIGHWT = {c| wt(c) >= W or c is hard}. - //If these clauses HIGHWT are satisfiable then there is no need to - //ever falsify any clause in HIGHWT, and we can harden these - //clauses. + // If these clauses HIGHWT are satisfiable then there is no need to + // ever falsify any clause in HIGHWT, and we can harden these + // clauses. - if(unsat) return; + if (unsat) return; computeWtInfo(); - //cout << "BEFORE HARDENING\n"; - //printFormula(); + // cout << "BEFORE HARDENING\n"; + // printFormula(); - SatSolver_uniqp sat_solver {new miniSolver}; - sat_solver->eliminate(true); //no preprocessing due to incremental use of solver + SatSolver_uniqp sat_solver{new miniSolver}; + sat_solver->eliminate( + true); // no preprocessing due to incremental use of solver - for(size_t i = 0; i < nHards(); i++) - if(!sat_solver->addClause(getHard(i))) { + for (size_t i = 0; i < nHards(); i++) + if (!sat_solver->addClause(getHard(i))) { unsat = true; - if(params.verbosity > 0) - cout << "c WCNF hardened 0 soft clauses\n"; + if (params.verbosity > 0) cout << "c WCNF hardened 0 soft clauses\n"; return; } - //Debug + // Debug /*cout << "Diffwts = ["; for(size_t i=0; i< diffWts.size(); i++) cout << "(" << diffWts[i] << ", " << diffWtCounts[i] << "), "; cout << "]\n";*/ - //Debug + // Debug - Weight maxHardenWt = wt_max+1; - Weight maxWt = wt_max+1; + Weight maxHardenWt = wt_max + 1; + Weight maxWt = wt_max + 1; - if(params.verbosity > 0) + if (params.verbosity > 0) cout << "c transitionWts = " << transitionWts << "\n"; - for(int i = transitionWts.size()-1; i >= 0; i--) { - //DEBUG - //cout << "Processing wt " << transitionWts[i] << " maxWt = " << maxWt << "\n"; - int n {0}; + for (int i = transitionWts.size() - 1; i >= 0; i--) { + // DEBUG + // cout << "Processing wt " << transitionWts[i] << " maxWt = " << maxWt << + // "\n"; + int n{0}; - for(size_t c =0; c < nSofts(); c++) { - if(soft_clswts[c] >= transitionWts[i] && soft_clswts[c] < maxWt) { + for (size_t c = 0; c < nSofts(); c++) { + if (soft_clswts[c] >= transitionWts[i] && soft_clswts[c] < maxWt) { n++; - if(!sat_solver->addClause(getSoft(c))) - break; + if (!sat_solver->addClause(getSoft(c))) break; } } - //DEBUG - //cout << "Added " << n << " soft clauses to sat solver\n"; + // DEBUG + // cout << "Added " << n << " soft clauses to sat solver\n"; - if(!sat_solver->status()) - break; + if (!sat_solver->status()) break; maxWt = transitionWts[i]; - auto canHarden = sat_solver->solvePropBudget(5*1024*1024); - //DEBUG - //cout << "sat solver returns " << canHarden << "\n"; + auto canHarden = sat_solver->solvePropBudget(5 * 1024 * 1024); + // DEBUG + // cout << "sat solver returns " << canHarden << "\n"; - if(canHarden == l_True) { + if (canHarden == l_True) { maxHardenWt = transitionWts[i]; - //DEBUG - //cout << "Incrementing hardening wt to " << maxHardenWt << "\n"; - } - else + // DEBUG + // cout << "Incrementing hardening wt to " << maxHardenWt << "\n"; + } else break; } - //DEBUG - //cout << "maxhardenwt = " << maxHardenWt << " wt_max = " << wt_max << "\n"; + // DEBUG + // cout << "maxhardenwt = " << maxHardenWt << " wt_max = " << wt_max << "\n"; - if(maxHardenWt > wt_max) { - if(params.verbosity > 0) - cout << "c WCNF hardened 0 soft clauses\n"; + if (maxHardenWt > wt_max) { + if (params.verbosity > 0) cout << "c WCNF hardened 0 soft clauses\n"; return; } Packed_vecs tmp; vector tmpWts; - int nHardened {0}; - for(size_t i = 0; i < nSofts(); i++) - if(soft_clswts[i] >= maxHardenWt) { + int nHardened{0}; + for (size_t i = 0; i < nSofts(); i++) + if (soft_clswts[i] >= maxHardenWt) { nHardened++; auto sftcls = getSoft(i); _addHardClause(sftcls); - } - else { + } else { tmp.addVec(getSoft(i)); tmpWts.push_back(soft_clswts[i]); } @@ -885,146 +823,146 @@ void Wcnf::simpleHarden() { soft_clswts = std::move(tmpWts); total_cls_wt = 0; - for(auto wt : soft_clswts) - total_cls_wt += wt; - - if(params.verbosity > 0) - cout << "c WCNF hardened " << nHardened << " soft clauses. New total_cls wt = " << total_cls_wt << "\n"; + for (auto wt : soft_clswts) total_cls_wt += wt; + + if (params.verbosity > 0) + cout << "c WCNF hardened " << nHardened + << " soft clauses. New total_cls wt = " << total_cls_wt << "\n"; // cout << "AFTER HARDENING\n"; // printFormula(); } void Wcnf::mxBvars() { - //modify the WCNF by finding at most one constraints among the bvars - //and replacing all of these vars by one bvar. - Bvars bvars {this}; - if(params.verbosity > 4) { + // modify the WCNF by finding at most one constraints among the bvars + // and replacing all of these vars by one bvar. + Bvars bvars{this}; + if (params.verbosity > 4) { cout << "BEFORE mxbvars\n"; printFormula(bvars); } processMxs(mxFinder(bvars), bvars); - if(params.verbosity > 4) { - Bvars newbvars {this}; + if (params.verbosity > 4) { + Bvars newbvars{this}; cout << "AFTER mxbvars\n"; printFormula(newbvars); } } - + void Wcnf::processMxs(vector> mxs, Bvars& bvars) { - //mxs should be disjoint collection of mx sets. Each set should be a - //non empty set of blits all of which have the same weight. These - //blits have the property that at most one of them can be true + // mxs should be disjoint collection of mx sets. Each set should be a + // non empty set of blits all of which have the same weight. These + // blits have the property that at most one of them can be true //(given the hard clauses). - //If the blits are cores (making them true relaxes the soft clause), - //then at most one of the corresponding soft clauses can be - //falsified. If the blits are non-cores then at most one of the - //corresponding soft clauses can be true. + // If the blits are cores (making them true relaxes the soft clause), + // then at most one of the corresponding soft clauses can be + // falsified. If the blits are non-cores then at most one of the + // corresponding soft clauses can be true. - //Modify the WCNF to account for this mx. - //Also store the mxes so that users of Wcnf can access them. + // Modify the WCNF to account for this mx. + // Also store the mxes so that users of Wcnf can access them. - if(unsat) - return; + if (unsat) return; - //debug - //cout << "Before processmx\n"; - //printFormula(bvars); + // debug + // cout << "Before processmx\n"; + // printFormula(bvars); - vector delMarks (nSofts(), 0); + vector delMarks(nSofts(), 0); auto orig_nsofts = nSofts(); - Var newVar = maxOrigVar() + 1; - - for(auto& mx : mxs) { - //debug - /*cout << "Processing " << (bvars.isCore(mx[0]) ? "Core Mx: [ " : "NonCore Mx: [ "); - for(auto l : mx) - cout << l << "(ci = " << bvars.clsIndex(l) << "), "; - cout << "]\n"; - if(mx.size() < 2) - cout << "ERROR: Got mutual exclusion with less than 2 vars" << mx << "\n"; - bool dc = bvars.isCore(mx[0]); + Var newVar = nVars(); + vector blits{}; + for (auto& mx : mxs) { + // debug + /*cout << "Processing " << (bvars.isCore(mx[0]) ? "Core Mx: [ " : "NonCore + Mx: [ "); for(auto l : mx) cout << l << "(ci = " << bvars.clsIndex(l) << "), + "; cout << "]\n"; if(mx.size() < 2) cout << "ERROR: Got mutual exclusion + with less than 2 vars" << mx << "\n"; bool dc = bvars.isCore(mx[0]); for(auto l : mx) { - if(dc && !bvars.isCore(l) || !dc && bvars.isCore(l)) + if(dc && !bvars.isCore(l) || !dc && bvars.isCore(l)) cout << "ERROR mx with mixed core/non-cores" << mx << "\n"; if(bvars.wt(var(l)) != bvars.wt(var(mx[0]))) cout << "ERROR mx with different wts" << mx << "\n"; }*/ - //debug - - vector blits {}; + // debug + if (mx.empty()) { + cout << "c WARNING. Mx finder returned empty mx\n"; + continue; + } Weight unitWt = mx.size() > 0 ? bvars.wt(var(mx[0])) : 0.0; - bool coreMx = mx.size() > 0 ? bvars.isCore(mx[0]) : false; - bool transform_mx = params.mx_transform == 3 - || params.mx_transform == 1 && coreMx - || params.mx_transform == 2 && !coreMx; - - //1. compute a set of b-literals for the soft clauses in the mx. - //Making these literals true incurs corresponds to incurring the - //cost of the soft clause. - for(auto l :mx) { - auto ci = bvars.clsIndex(l); - auto sftcls = getSoft(ci); - - if(sftcls.size() == 0) { - cout << "c ERROR WCNF processMxs encountered zero length soft clause\n"; - continue; - } - if(sftcls.size() == 1) { - //For unit softs the b-variable is the negation of the lit in the clause. - blits.push_back(~sftcls[0]); - if(transform_mx) - delMarks[ci] = 1; //transforming this mx so delete original soft - } - else { - // non unit, create new blit to convert this soft into a unit soft. - auto blit = mkLit(newVar++); - blits.push_back(blit); - sftcls.push_back(blit); - delMarks[ci] = 1; - _addHardClause(sftcls); - if(!transform_mx) - //not transforming so keep orginal soft clause + bool core = mx.size() > 0 ? bvars.isCore(mx[0]) : false; + auto dvar = var_Undef; + auto dlit = lit_Undef; + blits.clear(); + // we transform mx-non-cores into one single new soft clause we + // keep all of soft clauses of an mx-core but define a new variable + // that is true iff one of the mx-core soft clauses if falsified. + if (core) { + for (auto l : mx) { + auto ci = bvars.clsIndex(l); + auto sftcls = getSoft(ci); + if (sftcls.size() == 0) { + cout << "c ERROR WCNF processMxs encountered zero length soft " + "clause\n"; + continue; + } else if (sftcls.size() == 1) + blits.push_back(~sftcls[0]); + else { + auto blit = mkLit(newVar++); + blits.push_back(blit); + sftcls.push_back(blit); + delMarks[ci] = 1; + _addHardClause(sftcls); _addSoftClause(~blit, unitWt); } } - - //2. Introduce a new variable if transforming. - auto dvar = var_Undef; - auto dlit = lit_Undef; - if(transform_mx) { + mutexes.emplace_back(blits, core, lit_Undef); + // dvar = newVar++; + // dlit = mkLit(dvar); + // for (auto l : mx) { + // auto ci = bvars.clsIndex(l); + // auto sftcls = getSoft(ci); + // if (sftcls.size() == 0) { + // cout << "c ERROR WCNF processMxs encountered zero length soft " + // "clause\n"; + // continue; + // } + // sftcls.push_back(dlit); + // delMarks[ci] = 1; + // _addHardClause(sftcls); + // } + // _addSoftClause(~dlit, unitWt); + } else { + for (auto l : mx) { + auto ci = bvars.clsIndex(l); + auto sftcls = getSoft(ci); + if (sftcls.size() == 0) { + cout << "c ERROR WCNF processMxs encountered zero length soft " + "clause\n"; + continue; + } + for (auto x : sftcls) blits.push_back(x); // union of softs + delMarks[ci] = 1; + } dvar = newVar++; dlit = mkLit(dvar); - if(coreMx) { - for(size_t i = 0; i < blits.size(); i++) - _addHardClause(dlit, ~blits[i]); - vector dvar_hard_clause {~dlit}; - for(size_t i = 0; i < blits.size(); i++) - dvar_hard_clause.push_back(blits[i]); - _addHardClause(dvar_hard_clause); - } - else { - vector dvar_hard_clause {dlit}; - for(size_t i = 0; i < blits.size(); i++) - dvar_hard_clause.push_back(~blits[i]); - _addHardClause(dvar_hard_clause); - for(size_t i = 0; i < blits.size(); i++) - _addHardClause(~dlit, blits[i]); - base_cost += unitWt*(mx.size()-1); - } + blits.push_back(dlit); + _addHardClause(blits); + base_cost += unitWt * (mx.size() - 1); _addSoftClause(~dlit, unitWt); + + // if (!orig_usoft.empty()) + // mutexes.emplace_back(std::move(orig_usoft), coreMx, lit_Undef); } - //3. Store mutex for further use by the solver - mutexes.emplace_back(std::move(blits), coreMx, dlit); } - //now rewite the softs + // now rewite the softs Packed_vecs tmp; - size_t j {0}; - for(size_t i = 0; i < nSofts(); i++) - if(i >= delMarks.size() || !delMarks[i]) { - //delmarks don't extend to newly added softs + size_t j{0}; + for (size_t i = 0; i < nSofts(); i++) + if (i >= delMarks.size() || !delMarks[i]) { + // delmarks don't extend to newly added softs tmp.addVec(getSoft(i)); soft_clswts[j++] = soft_clswts[i]; } @@ -1033,52 +971,51 @@ void Wcnf::processMxs(vector> mxs, Bvars& bvars) { soft_cls = std::move(tmp); total_cls_wt = 0; - for(auto wt : soft_clswts) - total_cls_wt += wt; + for (auto wt : soft_clswts) total_cls_wt += wt; computeWtInfo(); - if(params.verbosity) { - cout << "c WCNF mutexes: original #softs " << orig_nsofts << " #softs after mx-transforms " << nSofts() + if (params.verbosity) { + cout << "c WCNF mutexes: original #softs " << orig_nsofts + << " #softs after mx-transforms " << nSofts() << "\n"; + cout << "c WCNF mutexes: reduction in softs " << orig_nsofts - nSofts() << "\n"; - cout << "c WCNF mutexes: reduction in softs " << orig_nsofts - nSofts() << "\n"; } - - if(params.verbosity > 2) { + + if (params.verbosity > 2) { cout << "Process mx\n"; cout << "mutexes\n"; - for(auto& mx : mutexes) - cout << mx << "\n"; + for (auto& mx : mutexes) cout << mx << "\n"; } - //debug - //Bvars newbvars {this}; - //cout << "After processmx\n"; - //printFormula(newbvars); + // debug + // Bvars newbvars {this}; + // cout << "After processmx\n"; + // printFormula(newbvars); } class MXFinder { - //helper class for finding mutually exclusive bvars -public: - MXFinder(Wcnf* f, Bvars& b) : - nImpCalls {0}, - bvars {b}, - theWcnf {f}, - blitMarks (2*(bvars.maxvar()+1), 0), - totalMxMem {0} - {} + // helper class for finding mutually exclusive bvars + public: + MXFinder(Wcnf* f, Bvars& b) + : nImpCalls{0}, + bvars{b}, + theWcnf{f}, + blitMarks(2 * (bvars.maxvar() + 1), 0), + totalMxMem{0} {} ~MXFinder() { - for(auto p : blitMXes) - if(p) delete p; + for (auto p : blitMXes) + if (p) delete p; } bool findMxs(vector>&); int nImpCalls; -private: + + private: miniSolver solver; Bvars bvars; Wcnf* theWcnf; vector blitMarks; - const uint8_t inmx {1}; - const uint8_t in2s {2}; + const uint8_t inmx{1}; + const uint8_t in2s{2}; bool fbeq(); size_t getMXLitSize(Lit); const vector* getMXLits(Lit); @@ -1086,102 +1023,106 @@ class MXFinder { vector*> blitMXes; void getMXRecomputeSizes(const vector&); vector growMx(Lit start); - //debug + // debug void MXprintLit(Lit l) { - cout << l << " (mkr=" << (int) blitMarks[toInt(l)] << (bvars.isCore(l) ? " C " : " NC ") - << " wt = " << bvars.wt(var(l)) << ") "; + cout << l << " (mkr=" << (int)blitMarks[toInt(l)] + << (bvars.isCore(l) ? " C " : " NC ") << " wt = " << bvars.wt(var(l)) + << ") "; } }; vector> Wcnf::mxFinder(Bvars& bvars) { - //Return collection of mutally exclusive bvars. Where each set of - //Bvars are cores or are non-cores and each is associated with a - //soft clause of identical weight; finder does all the work: we use - //a function wrapper to ensure MXFinder is deallocated after it has - //done its work. - MXFinder finder {this, bvars}; + // Return collection of mutally exclusive bvars. Where each set of + // Bvars are cores or are non-cores and each is associated with a + // soft clause of identical weight; finder does all the work: we use + // a function wrapper to ensure MXFinder is deallocated after it has + // done its work. + MXFinder finder{this, bvars}; vector> mxs; - if(!finder.findMxs(mxs)) - unsat = true; - if(params.verbosity>0) - cout << "c WCNF mx finder used " << finder.nImpCalls << " calls to UP engine\n"; + if (!finder.findMxs(mxs)) unsat = true; + if (params.verbosity > 0) + cout << "c WCNF mx finder used " << finder.nImpCalls + << " calls to UP engine\n"; return mxs; } bool MXFinder::findMxs(vector>& mxs) { - //Top level findMxs method of MXFinder class. find mx collections - //of bvars and store in mxs. Return false if we found a - //contradiction. - - //TODO: currently no attempt is made to reclaim - //memory during the computation. In particular, once a blit is in - //an MX we shouldn't have to store its set of implicants. (BUT the - //marking code is tricky so this is not necessarily an easy mod, - //this needs a redesign.. + // Top level findMxs method of MXFinder class. find mx collections + // of bvars and store in mxs. Return false if we found a + // contradiction. + // TODO: currently no attempt is made to reclaim + // memory during the computation. In particular, once a blit is in + // an MX we shouldn't have to store its set of implicants. (BUT the + // marking code is tricky so this is not necessarily an easy mod, + // this needs a redesign.. - bool timedOut {false}; + bool timedOut{false}; double start_time = Minisat::cpuTime(); - - //1. Initialize solver with fbeq - if(!fbeq()) { - if(params.verbosity>0) + + // 1. Initialize solver with fbeq + if (!fbeq()) { + if (params.verbosity > 0) cout << "c WCNF detected input to be unsat during preprocessing\n"; return false; } - //Two stage. Note that absorbing a blit into a mutex blocks it and - //its negation from being in any other mutex. So try to grow big - //mutexes we delay the processing of mutexes of size 2. E.g., it - //could be that (b1,b2) are mutex but so is (b2, b3, b4, b5) so we - //don't want to absorbe b2 into (b1, b2). + // Two stage. Note that absorbing a blit into a mutex blocks it and + // its negation from being in any other mutex. So try to grow big + // mutexes we delay the processing of mutexes of size 2. E.g., it + // could be that (b1,b2) are mutex but so is (b2, b3, b4, b5) so we + // don't want to absorbe b2 into (b1, b2). vector toProcess; - vector twos; //blits that might generate 2s. Process later. + vector twos; // blits that might generate 2s. Process later. - //toProcess will be used as a stack. Non core mx bump the base cost - //so process those first (put at back of vector--top of stack) - bool find_cores = params.mx_find_mxes == 3 || params.mx_find_mxes == 1; + // toProcess will be used as a stack. Non core mx bump the base cost + // so process those first (put at back of vector--top of stack) + bool find_cores = params.mx_find_mxes == 3 || params.mx_find_mxes == 1; bool find_ncores = params.mx_find_mxes == 3 || params.mx_find_mxes == 2; - if(find_cores) - for(size_t i = 0; i < theWcnf->nSofts(); ++i) { + if (find_cores) + for (size_t i = 0; i < theWcnf->nSofts(); ++i) { toProcess.push_back(bvars.litOfCls(i)); } - if(find_ncores) - for(size_t i = 0; i < theWcnf->nSofts(); ++i) { + if (find_ncores) + for (size_t i = 0; i < theWcnf->nSofts(); ++i) { toProcess.push_back(~bvars.litOfCls(i)); } - - int loops {0}; - while(!toProcess.empty()) { - //in this loop toProcess.back() is either selected, or blits that - //are mx with it are processed. Thus the size of - //toProcess.back()'s mx lit set is decreased. So eventually toProcess - //will become empty. + + int loops{0}; + while (!toProcess.empty()) { + // in this loop toProcess.back() is either selected, or blits that + // are mx with it are processed. Thus the size of + // toProcess.back()'s mx lit set is decreased. So eventually toProcess + // will become empty. ++loops; - //check for time out and mem-limit. - if(totalMxMem >= static_cast(1024*1024)*params.mx_mem_limit || - (params.mx_cpu_lim > 0 && loops%500 == 0 - && (Minisat::cpuTime() - start_time) > params.mx_cpu_lim)) { - timedOut=true; - if(totalMxMem >= static_cast(1024*1024)*params.mx_mem_limit) + // check for time out and mem-limit. + if (totalMxMem >= + static_cast(1024 * 1024) * params.mx_mem_limit || + (params.mx_cpu_lim > 0 && loops % 500 == 0 && + (Minisat::cpuTime() - start_time) > params.mx_cpu_lim)) { + timedOut = true; + if (totalMxMem >= + static_cast(1024 * 1024) * params.mx_mem_limit) cout << "c WCNF mx finder hit its memory limit. " - << "Potentially more mxes could be found with -mx-mem-lim made largeer\n"; - if((Minisat::cpuTime() - start_time) > params.mx_cpu_lim) + << "Potentially more mxes could be found with -mx-mem-lim made " + "largeer\n"; + if ((Minisat::cpuTime() - start_time) > params.mx_cpu_lim) cout << "c WCNF mx finder hit its time limit. " - << "Potentially more mxes could be found with -mx-cpu-lim made larger\n"; + << "Potentially more mxes could be found with -mx-cpu-lim made " + "larger\n"; break; } - + Lit blit = toProcess.back(); - if(blitMarks[toInt(blit)]) { - //in an mx or in twos + if (blitMarks[toInt(blit)]) { + // in an mx or in twos toProcess.pop_back(); continue; } auto mx = getMXLits(blit); - - //Debug + + // Debug /*cout << "findmxs first loop unmarked processing "; MXprintLit(blit); cout << " mx = ["; @@ -1191,54 +1132,53 @@ bool MXFinder::findMxs(vector>& mxs) { } cout << "]\n";*/ - if(mx->size() <= 1) { - if(mx->size() == 1) { + if (mx->size() <= 1) { + if (mx->size() == 1) { blitMarks[toInt(blit)] = in2s; twos.push_back(blit); - //cout << "Pushed into twos\n"; + // cout << "Pushed into twos\n"; } - //cout << "Got rid of\n"; + // cout << "Got rid of\n"; toProcess.pop_back(); continue; } - - //Potential mx of size > 2 (but not guaranteed!) - //cout << "Growing from start "; + + // Potential mx of size > 2 (but not guaranteed!) + // cout << "Growing from start "; Lit start = blit; - size_t size {mx->size()}; - - //Find start. - for(auto l : *mx) { + size_t size{mx->size()}; + + // Find start. + for (auto l : *mx) { size_t sz = getMXLitSize(l); - if(sz > size) { + if (sz > size) { size = sz; start = l; } } - //cout << start << "\n"; - //grow from start + // cout << start << "\n"; + // grow from start auto tmp = growMx(start); - //tmp might be small and it might not contain blit - if(tmp.size() <= 2) { + // tmp might be small and it might not contain blit + if (tmp.size() <= 2) { + // cout << "too small\n"; - //cout << "too small\n"; - - //no easy way to get rid of start from toProcess - //so mark as being in twos + // no easy way to get rid of start from toProcess + // so mark as being in twos blitMarks[toInt(blit)] = in2s; - if(tmp.size() == 2) { - //but only put there if it has potential + if (tmp.size() == 2) { + // but only put there if it has potential twos.push_back(start); - //cout << "put in twos\n"; + // cout << "put in twos\n"; } } - else { //legit mx for this stage - for(auto b : tmp) { - //cout << "adding mx\n"; + else { // legit mx for this stage + for (auto b : tmp) { + // cout << "adding mx\n"; blitMarks[toInt(b)] = inmx; blitMarks[toInt(~b)] = inmx; } @@ -1246,97 +1186,93 @@ bool MXFinder::findMxs(vector>& mxs) { } } - if(!timedOut) - while(!twos.empty()) { + if (!timedOut) + while (!twos.empty()) { Lit blit = twos.back(); - //cout << "Processing twos " << blit << "\n"; + // cout << "Processing twos " << blit << "\n"; twos.pop_back(); - if(blitMarks[toInt(blit)] == inmx) { - //cout << "Already marked\n"; + if (blitMarks[toInt(blit)] == inmx) { + // cout << "Already marked\n"; continue; } auto tmp = growMx(blit); - if(tmp.size() > 1) { - if(tmp.size() > 2) - cout << "c WARNING. WCNF large mx got into twos\n"; - for(auto b : tmp) { + if (tmp.size() > 1) { + if (tmp.size() > 2) cout << "c WARNING. WCNF large mx got into twos\n"; + for (auto b : tmp) { blitMarks[toInt(b)] = inmx; blitMarks[toInt(~b)] = inmx; } mxs.push_back(std::move(tmp)); } } - - if(params.verbosity > 0) { + + if (params.verbosity > 0) { cout << "c WCNF mutexes: #mutexes found = " << mxs.size() << "\n"; - if(mxs.size() > 0) { - double core_total_size {0}; - double ncore_total_size {0}; - int cores {0}; - int ncores {0}; - for(auto& mx : mxs) { - if(bvars.isCore(mx[0])) { + if (mxs.size() > 0) { + double core_total_size{0}; + double ncore_total_size{0}; + int cores{0}; + int ncores{0}; + for (auto& mx : mxs) { + if (bvars.isCore(mx[0])) { cores++; core_total_size += mx.size(); - } - else { + } else { ncores++; ncore_total_size += mx.size(); } } cout << "c WCNF mutexes: #cores mutexes = " << cores; - if(cores) - cout << " ave. size = " << core_total_size/cores; + if (cores) cout << " ave. size = " << core_total_size / cores; cout << "\n"; cout << "c WCNF mutexes: #non-cores mutexes = " << ncores; - if(ncores) - cout << " ave. size = " << ncore_total_size/ncores; + if (ncores) cout << " ave. size = " << ncore_total_size / ncores; cout << "\n"; - cout << "c WCNF mutexes: time used = " << Minisat::cpuTime() - start_time << "\n"; + cout << "c WCNF mutexes: time used = " << Minisat::cpuTime() - start_time + << "\n"; } } return true; } vector MXFinder::growMx(Lit start) { - //Starting with blit start, grow a mx constraint (at most one) - //The algorithm is as follows. - //We have a set mx that is initially start, and a set of candidates - //initially the negations of the appropriate implications of start + // Starting with blit start, grow a mx constraint (at most one) + // The algorithm is as follows. + // We have a set mx that is initially start, and a set of candidates + // initially the negations of the appropriate implications of start //(returned by getMXLits) - //Appropriate means that these negations are cores if start is a core - //and they are non-cores if start is a non-core. Furthermore, none - //of them is marked (as used in another mx constraint), and they all - //correspond to soft clauses with identical weight as start's soft clause. - //These restrictions are maintained by the subroutine getMXLits + // Appropriate means that these negations are cores if start is a core + // and they are non-cores if start is a non-core. Furthermore, none + // of them is marked (as used in another mx constraint), and they all + // correspond to soft clauses with identical weight as start's soft clause. + // These restrictions are maintained by the subroutine getMXLits - //The algorithm's invariant is that + // The algorithm's invariant is that //(a) the lits in mx form an mx constraint //(b) every single lit in candidates is mx with each lit in mx // Thus mx can be grown by any member of candidates. - //1. All l in candidates are MX (mutually exclusive) with all c in + // 1. All l in candidates are MX (mutually exclusive) with all c in // mx. So when we move l from candidates to mx (i.e., we select l) // we must remove from candiates all l' that are not MX with l. // Here we sort candidates by |mxset(l)\cap candidates|. So that // if l is selected we can retain as many members of candidates as - // is possible. We only sort at the start. A greedy method would + // is possible. We only sort at the start. A greedy method would // always recompute the size of this intersection since candidates // is shrinking after every l is selected. But we don't do that // to avoid this cost. - vector origCandidates(*getMXLits(start)); //need copy + vector origCandidates(*getMXLits(start)); // need copy std::set candidates(origCandidates.begin(), origCandidates.end()); - //compute |mxset(l) \cap candidates| or all l\in candidates + // compute |mxset(l) \cap candidates| or all l\in candidates vector interSize; - for(auto l : origCandidates) { - int count {0}; - int ci {bvars.clsIndex(l)}; - for(auto l1 : *getMXLits(l)) - if(candidates.find(l1) != candidates.end()) - ++count; - if(static_cast(ci) >= interSize.size()) + for (auto l : origCandidates) { + int count{0}; + int ci{bvars.clsIndex(l)}; + for (auto l1 : *getMXLits(l)) + if (candidates.find(l1) != candidates.end()) ++count; + if (static_cast(ci) >= interSize.size()) interSize.resize(ci + 1, 0); interSize[ci] = count; } @@ -1344,9 +1280,9 @@ vector MXFinder::growMx(Lit start) { auto mxSize = [this, &interSize](Lit l1, Lit l2) { return interSize[bvars.clsIndex(l1)] > interSize[bvars.clsIndex(l2)]; }; - sort(origCandidates.begin(), origCandidates.end(), mxSize); + sort(origCandidates.begin(), origCandidates.end(), mxSize); - //debug***** + // debug***** /*cout << "growMX on " << start << "\n"; cout << start << " mx = " << *getMXLits(start) << "\n"; cout << "candidiates\n"; @@ -1356,55 +1292,54 @@ vector MXFinder::growMx(Lit start) { cout << l << " mx = " << *getMXLits(l) << " [" << interSize[ci] << "]\n"; }*/ - //debug***** - - vector mx {start}; - size_t cani {0}; - while(!candidates.empty()) { + // debug***** + vector mx{start}; + size_t cani{0}; + while (!candidates.empty()) { /*cout << "Loop #" << cani << " candidiates ["; for(auto l : candidates) cout << l << ", "; cout << "]\n";*/ - auto l = origCandidates[cani++]; //select according to computed order - //cout << " growing " << cani << " lit = " << l; + auto l = origCandidates[cani++]; // select according to computed order + // cout << " growing " << cani << " lit = " << l; auto it = candidates.find(l); - if(it == candidates.end()) { - //cout << " Not found\n"; + if (it == candidates.end()) { + // cout << " Not found\n"; continue; } - //cout << " Found\n"; + // cout << " Found\n"; mx.push_back(l); candidates.erase(it); - //remove from candidates all elements not mx with the newly selected l + // remove from candidates all elements not mx with the newly selected l auto l_mx = getMXLits(l); std::set newLitMx(l_mx->begin(), l_mx->end()); - for(auto p = candidates.begin(); p != candidates.end(); ) { - //cout << "checking active candiate " << *p; - if(newLitMx.find(*p) == newLitMx.end()) { - //this candidate not mx with newly added lit---not a candidate anymore; - //cout << " Pruning\n"; - candidates.erase(p++); //sets! Erase invalidates p, so pass copy and increment here. - } - else { + for (auto p = candidates.begin(); p != candidates.end();) { + // cout << "checking active candiate " << *p; + if (newLitMx.find(*p) == newLitMx.end()) { + // this candidate not mx with newly added lit---not a candidate anymore; + // cout << " Pruning\n"; + candidates.erase(p++); // sets! Erase invalidates p, so pass copy and + // increment here. + } else { ++p; - //cout << " Keeping\n"; + // cout << " Keeping\n"; } } } - //debug - //cout << "Grow found mx " << mx << "\n"; - /*for(size_t i=0; i MXFinder::growMx(Lit start) { } bool MXFinder::fbeq() { - //Add fbeq to solver. - if(!params.mx_sat_preprocess) - solver.eliminate(true); //no preprocessing + // Add fbeq to solver. + if (!params.mx_sat_preprocess) solver.eliminate(true); // no preprocessing - for(size_t i = 0; i < theWcnf->nHards(); i++) - if(!solver.addClause(theWcnf->getHard(i))) - return false; + for (size_t i = 0; i < theWcnf->nHards(); i++) + if (!solver.addClause(theWcnf->getHard(i))) return false; - for(size_t i = 0; i < theWcnf->nSofts(); i++) { + for (size_t i = 0; i < theWcnf->nSofts(); i++) { Lit blit = bvars.litOfCls(i); - if(theWcnf->softSize(i) > 1) { - vector sftCls {theWcnf->getSoft(i)}; + if (theWcnf->softSize(i) > 1) { + vector sftCls{theWcnf->getSoft(i)}; sftCls.push_back(blit); - if(!solver.addClause(sftCls)) - return false; - + if (!solver.addClause(sftCls)) return false; + vector eqcls(2, lit_Undef); eqcls[1] = ~blit; - for(auto l : theWcnf->softs()[i]) { + for (auto l : theWcnf->softs()[i]) { eqcls[0] = ~l; - if(!solver.addClause(eqcls)) - return false; + if (!solver.addClause(eqcls)) return false; } } - } + } - if(params.mx_sat_preprocess) { - for(size_t i = 0; i < theWcnf->nSofts(); i++) { - Var v {bvars.varOfCls(i)}; - if(solver.activeVar(v)) - solver.freezeVar(v); + if (params.mx_sat_preprocess) { + for (size_t i = 0; i < theWcnf->nSofts(); i++) { + Var v{bvars.varOfCls(i)}; + if (solver.activeVar(v)) solver.freezeVar(v); } - double start {cpuTime()}; + double start{cpuTime()}; solver.eliminate(true); - if(params.verbosity>0) - cout << "c WCNF minisat preprocess eliminated " << solver.nEliminated() << " variables. Took " - << cpuTime() - start << "sec.\n";} + if (params.verbosity > 0) + cout << "c WCNF minisat preprocess eliminated " << solver.nEliminated() + << " variables. Took " << cpuTime() - start << "sec.\n"; + } return true; } - + const vector* MXFinder::getMXLits(Lit l) { - //return *unmarked* literals of same type (same wt and same core status) that - //are mutually exclusive with l. - //This is accomplished by asking the sat solver to find the implications - //of l that have the same weight and the opposite core status. - //If l --> l1, then l and ~l1 are mutually exclusive. - //To make this more efficient we remember the compute sets in blitMXex. + // return *unmarked* literals of same type (same wt and same core status) that + // are mutually exclusive with l. + // This is accomplished by asking the sat solver to find the implications + // of l that have the same weight and the opposite core status. + // If l --> l1, then l and ~l1 are mutually exclusive. + // To make this more efficient we remember the compute sets in blitMXex. // - //Since literals are marked at various stages, we must prune these cached - //sets to remove newly marked literals (??do we need to do this??) - - Weight lWT {0}; //rather than set up a class of template, we capture this local var in our lambda. - auto coreMX = [this, &lWT] (Lit l1) { - return blitMarks[toInt(l1)]!=inmx && bvars.isNonCore(l1) && bvars.wt(var(l1)) == lWT; + // Since literals are marked at various stages, we must prune these cached + // sets to remove newly marked literals (??do we need to do this??) + + Weight lWT{0}; // rather than set up a class of template, we capture this + // local var in our lambda. + auto coreMX = [this, &lWT](Lit l1) { + return blitMarks[toInt(l1)] != inmx && bvars.isNonCore(l1) && + bvars.wt(var(l1)) == lWT; }; - auto nonCoreMX = [this, &lWT] (Lit l1) { - return blitMarks[toInt(l1)]!=inmx && bvars.isCore(l1) && bvars.wt(var(l1)) == lWT; + auto nonCoreMX = [this, &lWT](Lit l1) { + return blitMarks[toInt(l1)] != inmx && bvars.isCore(l1) && + bvars.wt(var(l1)) == lWT; }; - if(blitMXes.size() <= static_cast(toInt(l))) - blitMXes.resize(toInt(l)+1, nullptr); + if (blitMXes.size() <= static_cast(toInt(l))) + blitMXes.resize(toInt(l) + 1, nullptr); vector imps; - if(!blitMXes[toInt(l)]) { //not cached compute - if(totalMxMem >= static_cast(1024*1024)*params.mx_mem_limit) { - //no more space for storing implications (2GB)...pretend that there aren't any + if (!blitMXes[toInt(l)]) { // not cached compute + if (totalMxMem >= + static_cast(1024 * 1024) * params.mx_mem_limit) { + // no more space for storing implications (2GB)...pretend that there + // aren't any blitMXes[toInt(l)] = new vector(std::move(imps)); return blitMXes[toInt(l)]; } lWT = bvars.wt(var(l)); ++nImpCalls; - if(bvars.isCore(l)) + if (bvars.isCore(l)) solver.findImplicationsIf(l, imps, coreMX); else solver.findImplicationsIf(l, imps, nonCoreMX); - //debug + // debug /*vector t; t.push_back(l); vector o; @@ -1502,7 +1438,7 @@ const vector* MXFinder::getMXLits(Lit l) { for(size_t i=0; i < o.size(); ++i) { if(blitMarks[toInt(o[i])] != inmx && ((bvars.isCore(l) && bvars.isNonCore(o[i])) - || (bvars.isNonCore(l) && bvars.isCore(o[i]))) + || (bvars.isNonCore(l) && bvars.isCore(o[i]))) && (bvars.wt(var(l)) == bvars.wt(var(o[i])))) pruned.push_back(o[i]); } @@ -1511,18 +1447,20 @@ const vector* MXFinder::getMXLits(Lit l) { std::sort(imps.begin(), imps.end()); if(pruned.size() != imps.size()) { cout << "ERROR regular interface returned different set of imps for "; - cout << l << " (marks = " << (int) blitMarks[toInt(l)] << (bvars.isCore(l) ? " Core " : " nonCore "); - cout << " wt = " << bvars.wt(var(l)) << ")\n"; - + cout << l << " (marks = " << (int) blitMarks[toInt(l)] << (bvars.isCore(l) + ? " Core " : " nonCore "); cout << " wt = " << bvars.wt(var(l)) << ")\n"; + cout << " imps = [ "; - for(auto x : imps) - cout << x << " (marks = " << (int) blitMarks[toInt(x)] << (bvars.isCore(x) ? " Core " : " nonCore ") + for(auto x : imps) + cout << x << " (marks = " << (int) blitMarks[toInt(x)] << (bvars.isCore(x) + ? " Core " : " nonCore ") << " wt = " << bvars.wt(var(x)) << "), "; cout << "] (" << imps.size() << ")\n"; - cout << " pruned = [ "; - for(auto x : pruned) - cout << x << " (marks = " << (int) blitMarks[toInt(x)] << (bvars.isCore(x) ? " Core " : " nonCore ") + cout << " pruned = [ "; + for(auto x : pruned) + cout << x << " (marks = " << (int) blitMarks[toInt(x)] << (bvars.isCore(x) + ? " Core " : " nonCore ") << " wt = " << bvars.wt(var(x)) << "), "; cout << "] (" << pruned.size() << ")\n"; } @@ -1530,113 +1468,197 @@ const vector* MXFinder::getMXLits(Lit l) { else { for(size_t i =0; i < pruned.size(); i++) if(pruned[i] != imps[i]) { - cout << "ERROR regular interface returned different set of imps\n"; - - cout << l << " (marks = " << (int) blitMarks[toInt(l)] << (bvars.isCore(l) ? " Core " : " nonCore "); - cout << " wt = " << bvars.wt(var(l)) << ")\n"; - - cout << " imps = [ "; - for(auto x : imps) - cout << x << " (marks = " << (int) blitMarks[toInt(x)] << (bvars.isCore(x) ? " Core " : " nonCore ") + cout << "ERROR regular interface returned different set of imps\n"; + + cout << l << " (marks = " << (int) blitMarks[toInt(l)] << + (bvars.isCore(l) ? " Core " : " nonCore "); cout << " wt = " << + bvars.wt(var(l)) << ")\n"; + + cout << " imps = [ "; + for(auto x : imps) + cout << x << " (marks = " << (int) blitMarks[toInt(x)] << (bvars.isCore(x) + ? " Core " : " nonCore ") << " wt = " << bvars.wt(var(x)) << "), "; - cout << "] (" << imps.size() << ")\n"; - - cout << " pruned = [ "; - for(auto x : pruned) - cout << x << " (marks = " << (int) blitMarks[toInt(x)] << (bvars.isCore(x) ? " Core " : " nonCore ") + cout << "] (" << imps.size() << ")\n"; + + cout << " pruned = [ "; + for(auto x : pruned) + cout << x << " (marks = " << (int) blitMarks[toInt(x)] << (bvars.isCore(x) + ? " Core " : " nonCore ") << " wt = " << bvars.wt(var(x)) << "), "; - cout << "] (" << pruned.size() << ")\n"; + cout << "] (" << pruned.size() << ")\n"; - break; + break; } }*/ - //debug + // debug - for(size_t i=0; i < imps.size(); ++i) - imps[i] = ~imps[i]; //convert from implication to MX + for (size_t i = 0; i < imps.size(); ++i) + imps[i] = ~imps[i]; // convert from implication to MX - totalMxMem += sizeof(Lit)*imps.size(); + totalMxMem += sizeof(Lit) * imps.size(); blitMXes[toInt(l)] = new vector(std::move(imps)); return blitMXes[toInt(l)]; } - //otherwise prune for marks + // otherwise prune for marks auto v = blitMXes[toInt(l)]; - size_t j {0}; - for(size_t i=0; i < v->size(); ++i) - if(blitMarks[toInt((*v)[i])] != inmx) - (*v)[j++] = (*v)[i]; + size_t j{0}; + for (size_t i = 0; i < v->size(); ++i) + if (blitMarks[toInt((*v)[i])] != inmx) (*v)[j++] = (*v)[i]; v->resize(j); return v; } - + size_t MXFinder::getMXLitSize(Lit l) { - //potentially we could aproximate this by returning - //the size without pruning for marks and using getMXrecomputeSizes - //on each found mx to get most of the sizes right. + // potentially we could aproximate this by returning + // the size without pruning for marks and using getMXrecomputeSizes + // on each found mx to get most of the sizes right. return getMXLits(l)->size(); } void MXFinder::getMXRecomputeSizes(const vector& newlyMarked) { auto nic = nImpCalls; - for(auto l : newlyMarked) { //note we don't need to update vectors of marked lits - auto v = getMXLits(l); //this is automatic from getMXLits. v is set of lits that need update - for(auto x : *v) { + for (auto l : + newlyMarked) { // note we don't need to update vectors of marked lits + auto v = getMXLits(l); // this is automatic from getMXLits. v is set of + // lits that need update + for (auto x : *v) { auto vx = blitMXes[toInt(x)]; - if(!vx) continue; - size_t j {0}; - for(size_t i = 0; i < vx->size(); ++i) - if(blitMarks[toInt((*vx)[i])] == inmx) - (*vx)[j++] = (*vx)[i]; + if (!vx) continue; + size_t j{0}; + for (size_t i = 0; i < vx->size(); ++i) + if (blitMarks[toInt((*vx)[i])] == inmx) (*vx)[j++] = (*vx)[i]; vx->resize(j); } } - if(nImpCalls > nic && params.verbosity > 0) + if (nImpCalls > nic && params.verbosity > 0) cout << "c WARNING getMXRecomputeSizes used some implication calls!\n"; } -//INPUT PROBLEM/INTERNAL PROBLEM interface -//Take model found by solver and rewrite it into model of original formula -void Wcnf::rewriteModelToInput(vector& ubmodel) { - //for now all we need to do is to remove the extra added variables. - ubmodel.resize(nOrigVars(), l_Undef); +// Internal variable number to input file numbering. + +void Wcnf::remapVars() { + vector appears(nVars(), false); + for (const auto cls : hard_cls) + for (auto l : cls) appears[var(l)] = true; + + flipped_vars.resize(nVars(), 0); + for (const auto cls : soft_cls) { + for (auto l : cls) appears[var(l)] = true; + // convert formula so that unit softs are of the form (-x) instead + // of (x)---so making the 'blit' x true incurs the cost + if (cls.size() == 1 && !sign(cls[0])) flipped_vars[var(cls[0])] = true; + } + + Var nxtvar{0}; + ex2in.resize(nVars(), var_Undef); + in2ex.resize(nVars(), var_Undef); + for (Var v = 0; v != nVars(); v++) + if (appears[v]) { + in2ex[nxtvar] = v; + ex2in[v] = nxtvar; + ++nxtvar; + } + maxvar = nxtvar - 1; + + Packed_vecs tmp; + vector c; + for (auto cls : hard_cls) { + c.clear(); + for (auto l : cls) { + assert(ex2in[var(l)] != var_Undef); + c.push_back( + mkLit(ex2in[var(l)], flipped_vars[var(l)] ? !sign(l) : sign(l))); + } + tmp.addVec(c); + } + hard_cls = std::move(tmp); + tmp.clear(); + + for (auto cls : soft_cls) { + c.clear(); + for (auto l : cls) { + assert(ex2in[var(l)] != var_Undef); + c.push_back( + mkLit(ex2in[var(l)], flipped_vars[var(l)] ? !sign(l) : sign(l))); + } + tmp.addVec(c); + } + soft_cls = std::move(tmp); + + for (auto& mx : mutexes) { + for (auto& l : mx.soft_clause_lits_mod()) + l = mkLit(ex2in[var(l)], flipped_vars[var(l)] ? !sign(l) : sign(l)); + auto& el = mx.encoding_lit_mod(); + if (el != lit_Undef) + el = mkLit(ex2in[var(el)], flipped_vars[var(el)] ? !sign(el) : sign(el)); + } } -//verify model against original formula. -Weight Wcnf::checkModel(vector& ubmodel, int& nfalseSofts, bool final) { - if(final) { +// INPUT PROBLEM/INTERNAL PROBLEM interface +// Take model found by solver and rewrite it into model of original formula +vector Wcnf::rewriteModelToInput(const vector& ubmodel) { + // all original internal vars are preserved by solver. But + // more vars might be added after. + vector ex_model(nOrigVars(), l_True); + for (size_t i = 0; i < in2ex.size() && i < ubmodel.size(); ++i) { + if (in2ex[i] != var_Undef && in2ex[i] < static_cast(nOrigVars())) + ex_model[in2ex[i]] = + flipped_vars[in2ex[i]] ? ubmodel[i].neg() : ubmodel[i]; + } + for (auto l : hard_units) { + ex_model[var(l)] = sign(l) ? l_False : l_True; + } + for (const auto& scc : all_scc) { + for (size_t i = 0; i < scc.size(); ++i) { + ex_model[var(scc[i])] = sign(scc[i]) == sign(scc[0]) + ? ex_model[var(scc[0])] + : ex_model[var(scc[0])].neg(); + } + } + return ex_model; +} + +// verify model against original formula. +Weight Wcnf::checkModel(const vector& ubmodel, int& nfalseSofts, + bool final) { + if (final) { hard_cls.clear(); soft_cls.clear(); } nfalseSofts = 0; Wcnf newCopy; - newCopy.inputDimacs(fileName(), true); //don't process input problem - for(auto hc : newCopy.hards()) { + newCopy.inputDimacs(fileName(), true); // don't process input problem + vector ex_model = rewriteModelToInput(ubmodel); + for (auto hc : newCopy.hards()) { bool isSat = false; - for(auto lt : hc) - if(lbool(!sign(lt)) == ubmodel[var(lt)]) { //ubmodel[var(lt)] = l_True or l_False, not = l_Undef + for (auto lt : hc) + if ((sign(lt) && ex_model[var(lt)] == l_False) || + (!sign(lt) && ex_model[var(lt)] == l_True)) { isSat = true; break; } - if(!isSat) { - cout << "c ERROR WCNF. Model does not satisfy the hards\nc violated hard = ["; - for(auto l : hc) - cout << l << ", "; + if (!isSat) { + cout << "c ERROR WCNF. Model does not satisfy the hards\n" + << "c violated hard = ["; + for (auto l : hc) cout << l << ", "; cout << "]\n"; return -1; } } - Weight w {0}; - int i=0; - for(auto sc : newCopy.softs()) { + Weight w{0}; + int i = 0; + for (auto sc : newCopy.softs()) { bool isSat = false; - for(auto lt : sc) - if(lbool(!sign(lt)) == ubmodel[var(lt)]) { + for (auto lt : sc) + if ((sign(lt) && ex_model[var(lt)] == l_False) || + (!sign(lt) && ex_model[var(lt)] == l_True)) { isSat = true; break; } - if(!isSat) { + if (!isSat) { w += newCopy.getWt(i); ++nfalseSofts; } @@ -1645,22 +1667,21 @@ Weight Wcnf::checkModel(vector& ubmodel, int& nfalseSofts, bool final) { return w; } -//Stats and output +// Stats and output void Wcnf::computeWtInfo() { - diffWts.clear(); - diffWtCounts.clear(); + vector diffWts; + vector diffWtCounts; transitionWts.clear(); - if(soft_clswts.size() == 0) { + if (soft_clswts.size() == 0) { wt_min = wt_max = wt_mean = wt_var = 0; - if(hard_cls.size() > 0) { - if(baseCost() > 0) + if (hard_cls.size() > 0) { + if (baseCost() > 0) ms_type = MStype::wpms; else ms_type = MStype::pms; - } - else { - if(baseCost() > 0) + } else { + if (baseCost() > 0) ms_type = MStype::wms; else ms_type = MStype::ms; @@ -1676,41 +1697,39 @@ void Wcnf::computeWtInfo() { wt_max = wts.back(); wt_mean = wt_var = 0; - for(auto x : wts) - wt_mean += x; + for (auto x : wts) wt_mean += x; wt_mean /= wts.size(); - for(auto x : wts) - wt_var += (x-wt_mean)*(x-wt_mean); - wt_var /= wts.size()-1; + for (auto x : wts) wt_var += (x - wt_mean) * (x - wt_mean); + wt_var /= wts.size() - 1; - size_t j {0}; + size_t j{0}; diffWts.push_back(wts[0]); diffWtCounts.push_back(0); - for(size_t i = 0; i < wts.size(); i++) - if(wts[j] == wts[i]) + for (size_t i = 0; i < wts.size(); i++) + if (wts[j] == wts[i]) diffWtCounts.back()++; else { - j=i; + j = i; diffWts.push_back(wts[i]); diffWtCounts.push_back(1); } double wtSoFar = diffWts[0] * diffWtCounts[0]; - for(size_t i = 1; i < diffWts.size(); i++) { - if(diffWts[i] > wtSoFar) - transitionWts.push_back(diffWts[i]); + for (size_t i = 1; i < diffWts.size(); i++) { + if (diffWts[i] > wtSoFar) transitionWts.push_back(diffWts[i]); wtSoFar += diffWts[i] * diffWtCounts[i]; } - if(hard_cls.size() > 0) { - if(diffWts.size() > 1 || baseCost() > 0) + ndiffWts = diffWts.size(); + + if (hard_cls.size() > 0) { + if (diffWts.size() > 1 || baseCost() > 0) ms_type = MStype::wpms; else ms_type = MStype::pms; - } - else { - if(diffWts.size() > 1 || baseCost() > 0) + } else { + if (diffWts.size() > 1 || baseCost() > 0) ms_type = MStype::wms; else ms_type = MStype::ms; @@ -1718,102 +1737,137 @@ void Wcnf::computeWtInfo() { } void Wcnf::printFormulaStats() { + auto n_units = hard_units.size(); cout << "c Instance: " << instance_file_name << "\n"; cout << "c Dimacs Vars: " << dimacs_nvars << "\n"; cout << "c Dimacs Clauses: " << dimacs_nclauses << "\n"; - cout << "c HARD: #Clauses = " << hard_cls.size() - << ", Total Lits = " << hard_cls.total_size() - << ", Ave Len = " << ((hard_cls.size() > 0) ? (1.0*hard_cls.total_size())/hard_cls.size() : 0.0) << "\n"; + cout << "c HARD: #Clauses = " << hard_cls.size() + n_units + << ", Total Lits = " << hard_cls.total_size() + n_units << ", Ave Len = " + << ((hard_cls.size() + n_units > 0) + ? (1.0 * hard_cls.total_size() + n_units) / + (hard_cls.size() + n_units) + : 0.0) + << " #units = " << n_units << "\n"; cout << "c SOFT: #Clauses = " << soft_cls.size() - << ", Total Lits = " << soft_cls.total_size() - << ", Ave Len = " << (1.0*soft_cls.total_size())/soft_cls.size() << "\n"; + << ", Total Lits = " << soft_cls.total_size() + << ", Ave Len = " << (1.0 * soft_cls.total_size()) / soft_cls.size() + << "\n"; cout << "c Total Soft Clause Weight (+ basecost): " << totalClsWt() << " (+ " << baseCost() << "), Dimacs Top = " << dimacs_top << "\n"; - cout << "c SOFT%: " << (100.0*soft_cls.size())/(soft_cls.size()+hard_cls.size()) << "%\n"; + cout << "c SOFT%: " + << (100.0 * soft_cls.size()) / + (soft_cls.size() + hard_cls.size() + n_units) + << "%\n"; cout << "c #distinct weights: " << nDiffWts() << ", mean = " << aveSftWt() - << ", std. dev = " << std::sqrt(varSftWt()) << ", min = " << minSftWt() << ", max = " << maxSftWt() << "\n"; - cout << "c Total Clauses: " << hard_cls.size() + soft_cls.size() << "\n"; + << ", std. dev = " << std::sqrt(varSftWt()) << ", min = " << minSftWt() + << ", max = " << maxSftWt() << "\n"; + cout << "c Total Clauses: " << hard_cls.size() + n_units + soft_cls.size() + << "\n"; cout << "c Parse time: " << parsing_time << "\n"; - cout << "c Wcnf Space Required: " << ((hard_cls.total_size()+soft_cls.total_size())*sizeof(Lit) + soft_clswts.size()*sizeof(Weight))/(1024*1024) << "MB\n"; - if(unsat) - cout << "c Wcnf is UNSAT (hards are contradictory)\n"; - cout << "c ================================" << "\n"; + cout << "c Wcnf Space Required: " + << ((hard_cls.total_size() + soft_cls.total_size()) * sizeof(Lit) + + soft_clswts.size() * sizeof(Weight)) / + (1024 * 1024) + << "MB\n"; + if (unsat) cout << "c Wcnf is UNSAT (hards are contradictory)\n"; + cout << "c ================================" + << "\n"; } void Wcnf::printSimpStats() { cout << "c After WCNF Simplification\n"; cout << "c HARD: #Clauses = " << hard_cls.size() - << ", Total Lits = " << hard_cls.total_size() - << ", Ave Len = " << ((hard_cls.size() > 0) ? (1.0*hard_cls.total_size())/hard_cls.size() : 0.0) << "\n"; + << ", Total Lits = " << hard_cls.total_size() << ", Ave Len = " + << ((hard_cls.size() > 0) + ? (1.0 * hard_cls.total_size()) / hard_cls.size() + : 0.0) + << "\n"; cout << "c SOFT: #Clauses = " << soft_cls.size() - << ", Total Lits = " << soft_cls.total_size() - << ", Ave Len = " << (1.0*soft_cls.total_size())/soft_cls.size() << "\n"; + << ", Total Lits = " << soft_cls.total_size() + << ", Ave Len = " << (1.0 * soft_cls.total_size()) / soft_cls.size() + << "\n"; cout << "c Total Soft Clause Weight (+ basecost): " << totalClsWt() << " (+ " << baseCost() << "), Dimacs Top = " << dimacs_top << "\n"; cout << "c #distinct weights: " << nDiffWts() << ", mean = " << aveSftWt() - << ", std. dev = " << std::sqrt(varSftWt()) << ", min = " << minSftWt() << ", max = " << maxSftWt() << "\n"; + << ", std. dev = " << std::sqrt(varSftWt()) << ", min = " << minSftWt() + << ", max = " << maxSftWt() << "\n"; cout << "c Total Clauses: " << hard_cls.size() + soft_cls.size() << "\n"; - cout << "c Wcnf Space Required: " << ((hard_cls.total_size()+soft_cls.total_size())*sizeof(Lit) + soft_clswts.size()*sizeof(Weight))/1000000.0 << "MB\n"; - if(unsat) - cout << "c Wcnf is UNSAT (hards are contradictory)\n"; - cout << "c ================================" << "\n"; + cout << "c Wcnf Space Required: " + << ((hard_cls.total_size() + soft_cls.total_size()) * sizeof(Lit) + + soft_clswts.size() * sizeof(Weight)) / + 1000000.0 + << "MB\n"; + if (unsat) cout << "c Wcnf is UNSAT (hards are contradictory)\n"; + cout << "c ================================" + << "\n"; } void Wcnf::printFormula(std::ostream& out) const { - //TODO modify to optionally output new DIMACS file. + // TODO modify to optionally output new DIMACS file. out << "c Wcnf---Print Formula\n"; - out << "c Dimacs (Vars, Clauses, TOP) = (" << dimacs_nvars - << " ," << dimacs_nclauses - << " ," << dimacs_top << ")"; - out << " maxvar = " << maxvar+1 << "\n"; - if(unsat) - out << " formula is UNSAT\n"; - out << "c Hard Clauses # = " - << hard_cls.size() << "\n"; + out << "c Dimacs (Vars, Clauses, TOP) = (" << dimacs_nvars << " ," + << dimacs_nclauses << " ," << dimacs_top << ")"; + out << " maxvar = " << nVars() << "\n"; + if (unsat) out << " formula is UNSAT\n"; + out << "c Hard Clauses # = " << hard_cls.size() + hard_units.size() << "\n"; out << "c Soft Clauses, # = " << soft_cls.size() << "\n"; out << "c Base cost = " << base_cost << "\n"; + out << "c HARD Units\n"; + out << hard_units << "\n"; + out << "c HARD SCCs\n"; + out << all_scc << "\n"; out << "c HARDS\n"; out << hard_cls; + out << "c SOFTS\n"; - for(size_t i = 0; i < soft_cls.size(); i++) { + for (size_t i = 0; i < soft_cls.size(); i++) { out << soft_clswts[i] << " "; - for(auto& item : soft_cls[i]) - out << item << " "; + for (auto& item : soft_cls[i]) out << item << " "; out << "0 \n"; } } void Wcnf::printFormula(Bvars& bvars, std::ostream& out) const { - //TODO modify to optionally output new DIMACS file. + // TODO modify to optionally output new DIMACS file. out << "c Wcnf---Print Formula\n"; - out << "c Dimacs (Vars, Clauses, TOP) = (" << dimacs_nvars - << " ," << dimacs_nclauses - << " ," << dimacs_top << ")"; - out << " maxvar = " << maxvar+1 << "\n"; + out << "c Dimacs (Vars, Clauses, TOP) = (" << dimacs_nvars << " ," + << dimacs_nclauses << " ," << dimacs_top << ")"; + out << " maxvar = " << nVars() << "\n"; out << "c totalClsWt = " << totalClsWt() << "\n"; - if(unsat) - out << " formula is UNSAT\n"; - out << "c Hard Clauses # = " << hard_cls.size()+nhard_units << "\n"; - int nh {0}; - for(size_t i = 0; i < nHards(); i++) + if (unsat) out << " formula is UNSAT\n"; + out << "c Hard Clauses # = " << hard_cls.size() << "\n"; + out << "c Hard Units # = " << hard_units.size() << "\n"; + out << "c Hard SCC # = " << all_scc.size() << "\n"; + + int nh{0}; + for (size_t i = 0; i < all_scc.size(); i++) + out << "scc#" << nh++ << ": " << all_scc[i] << "\n"; + nh = 0; + + for (size_t i = 0; i < hard_units.size(); i++) + out << "h#" << nh++ << ": " << hard_units[i] << "\n"; + + for (size_t i = 0; i < nHards(); i++) out << "h#" << nh++ << ": " << getHard(i) << "\n"; out << "c Soft Clauses, # = " << soft_cls.size() << "\n"; out << "c Base cost = " << base_cost << "\n"; - int ns {0}; - for(size_t i = 0; i < nSofts(); i++) - out << "c#" << ns++ << " blit = " << bvars.litOfCls(i) + int ns{0}; + for (size_t i = 0; i < nSofts(); i++) + out << "c#" << ns++ << " blit = " << bvars.litOfCls(i) << " wt = " << getWt(i) << " : " << getSoft(i) << "\n"; } void Wcnf::printDimacs(std::ostream& out) const { + // Broken---fix or remove on next update + return; auto flags = out.flags(); out << std::fixed << std::setprecision(0); out << "c maxhs-simplify max original var: " << maxOrigVar() + 1 << "\n"; out << "c maxhs-simplify original file name: " << instance_file_name << "\n"; - if(unsat) { + if (unsat) { out << "c HARDS are UNSAT\n"; out << "p cnf 1 2\n"; out << "-1 0\n"; @@ -1821,52 +1875,54 @@ void Wcnf::printDimacs(std::ostream& out) const { return; } auto top = totalWt() + 1; - int nvars = nVars(); - int ncls = nSofts() + nHards(); - - if(baseCost() > 0) { // Encode base cost with extra pair - ncls += 2; // or contradictory softs clauses - if(nvars == 0) // Ensure we have a variable to work with. + int nvars = nOrigVars(); + int scc_cls_size{0}; + for (const auto& scc : all_scc) scc_cls_size += (scc.size() - 1) * 2; + int ncls = nSofts() + nHards() + hard_units.size() + scc_cls_size; + + if (baseCost() > 0) { // Encode base cost with extra pair + ncls += 2; // or contradictory softs clauses + if (nvars == 0) // Ensure we have a variable to work with. nvars++; } - switch(mstype()) { - case MStype::ms: - out << "p cnf " << nvars << " " << ncls << "\n"; - break; - case MStype::wms: - out << "p wcnf " << nvars << " " << ncls << "\n"; - break; - case MStype::pms: - out << "p wcnf " << nvars << " " << ncls << " " << top << "\n"; - break; - case MStype::wpms: - out << "p wcnf " << nVars() << " " << ncls << " " << top << "\n"; - break; - case MStype::undef: - out << "c ERROR problem finding out mstype\n"; - out << "p wcnf " << nVars() << " " << ncls << " " << top << "\n"; + switch (mstype()) { + case MStype::ms: + out << "p cnf " << nvars << " " << ncls << "\n"; + break; + case MStype::wms: + out << "p wcnf " << nvars << " " << ncls << "\n"; + break; + case MStype::pms: + out << "p wcnf " << nvars << " " << ncls << " " << top << "\n"; + break; + case MStype::wpms: + out << "p wcnf " << nVars() << " " << ncls << " " << top << "\n"; + break; + case MStype::undef: + out << "c ERROR problem finding out mstype\n"; + out << "p wcnf " << nVars() << " " << ncls << " " << top << "\n"; } - if(baseCost() > 0) { - //Above we ensured that nvars >= 1. Use 1 to encode the base costs. + if (baseCost() > 0) { + // Above we ensured that nvars >= 1. Use 1 to encode the base costs. out << baseCost() << " " << 1 << " 0\n"; out << baseCost() << " " << -1 << " 0\n"; } - for(size_t i = 0; i < nSofts(); i++) { - if(mstype() != MStype::ms) - out << soft_clswts[i] << " "; - for(auto& l : soft_cls[i]) - out << l << " "; + for (size_t i = 0; i < nSofts(); i++) { + if (mstype() != MStype::ms) out << soft_clswts[i] << " "; + for (auto l : soft_cls[i]) out << map_in2ex(l) << " "; out << "0\n"; } - for(size_t i = 0; i < nHards(); i++) { - //if we have hards wcnf dimacs format includes weights before clauses + if (!hard_units.empty()) + for (auto l : hard_units) out << l << " 0\n"; + + for (size_t i = 0; i < nHards(); i++) { + // if we have hards wcnf dimacs format includes weights before clauses out << top << " "; - for(auto& l : hard_cls[i]) - out << l << " "; + for (auto l : hard_cls[i]) out << map_in2ex(l) << " "; out << "0\n"; } out.flags(flags); diff --git a/maxhs/core/Wcnf.h b/maxhs/core/Wcnf.h old mode 100755 new mode 100644 index f0f6a4b..2fc4448 --- a/maxhs/core/Wcnf.h +++ b/maxhs/core/Wcnf.h @@ -24,10 +24,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef WCNF_H #define WCNF_H +#include +#include #include #include -#include -#include #ifdef GLUCOSE #include "glucose/core/SolverTypes.h" @@ -35,8 +35,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "minisat/core/SolverTypes.h" #endif -#include "maxhs/ds/Packed.h" #include "maxhs/core/MaxSolverTypes.h" +#include "maxhs/ds/Packed.h" #include "maxhs/ifaces/miniSatSolver.h" using std::cout; @@ -45,18 +45,17 @@ using std::cout; namespace Minisat = Glucose; #endif - +using MaxHS_Iface::SatSolver_uniqp; +using Minisat::l_Undef; +using Minisat::lbool; using Minisat::Lit; using Minisat::toInt; using Minisat::var; -using Minisat::l_Undef; -using Minisat::lbool; -using MaxHS_Iface::SatSolver_uniqp; /* Store a weighted CNF formula */ enum class MStype { undef, ms, pms, wms, wpms }; -class Bvars; //Bvars.h loads this header. +class Bvars; // Bvars.h loads this header. class SC_mx { /* The blits are such that if they are made @@ -64,30 +63,30 @@ class SC_mx { relaxed. Thus we incur the cost of the soft clause. */ -public: - SC_mx(vector blits, bool is_core, Lit dlit) - : _blits { blits}, - _dlit { dlit }, - _is_core { is_core } - {} - //if is_core then - // at most one of the blits can be true (at most one of the - // corresponding soft clauses can be falsified) - // and if dlit is true one of the blits is true. - - //if !is_core then - // at most one of the blits can be false (at most one of the - // corresponding soft clauses can be satisfied) - // and if dlit is false then one of the blits is false. - - const vector& soft_clause_lits() const { return _blits; } - bool is_core() const { return _is_core; } - Lit encoding_lit() const { return _dlit; } - -private: - vector _blits; - Lit _dlit; - bool _is_core; + public: + SC_mx(vector blits, bool is_core, Lit dlit) + : _blits{blits}, _dlit{dlit}, _is_core{is_core} {} + // if is_core then + // at most one of the blits can be true (at most one of the + // corresponding soft clauses can be falsified) + // and if dlit is true one of the blits is true. + + // if !is_core then + // at most one of the blits can be false (at most one of the + // corresponding soft clauses can be satisfied) + // and if dlit is false then one of the blits is false. + + const vector& soft_clause_lits() const { return _blits; } + bool is_core() const { return _is_core; } + Lit encoding_lit() const { return _dlit; } + // modifying versions + vector& soft_clause_lits_mod() { return _blits; } + Lit& encoding_lit_mod() { return _dlit; } + + private: + vector _blits; + Lit _dlit; + bool _is_core; }; inline ostream& operator<<(ostream& os, const SC_mx& mx) { @@ -97,57 +96,75 @@ inline ostream& operator<<(ostream& os, const SC_mx& mx) { return os; } -class Wcnf -{ -public: - Wcnf(); +class Wcnf { + public: bool inputDimacs(std::string filename) { return inputDimacs(filename, false); } - //input clauses - void addDimacsClause(vector &lits, Weight w); //Changes input vector lits - void set_dimacs_params(int nvars, int nclauses, Weight top = std::numeric_limits::max()); + // input clauses + void addDimacsClause(vector& lits, + Weight w); // Changes input vector lits + void set_dimacs_params(int nvars, int nclauses, + Weight top = std::numeric_limits::max()); double parseTime() const { return parsing_time; } Weight dimacsTop() const { return dimacs_top; } int dimacsNvars() const { return dimacs_nvars; } - //api-support for adding hard or soft clauses - void addHardClause(vector &lits); - void addSoftClause(vector &lits, Weight w); - void addHardClause(Lit p) { vector tmp {p}; addHardClause(tmp); } - void addHardClause(Lit p, Lit q) { vector tmp {p, q}; addHardClause(tmp); } - void addSoftClause(Lit p, Weight w) { vector tmp {p}; addSoftClause(tmp, w); } - void addSoftClause(Lit p, Lit q, Weight w) { vector tmp {p, q}; addSoftClause(tmp, w); } - - //modify Wcnf. This might add new variables. But all variables - //in the range 0--dimacs_nvars-1 are preserved. New variables - //only exist above that range. - //TODO: for incremental solving we need a map from external to internal + // api-support for adding hard or soft clauses + void addHardClause(vector& lits); + void addSoftClause(vector& lits, Weight w); + void addHardClause(Lit p) { + vector tmp{p}; + addHardClause(tmp); + } + void addHardClause(Lit p, Lit q) { + vector tmp{p, q}; + addHardClause(tmp); + } + void addSoftClause(Lit p, Weight w) { + vector tmp{p}; + addSoftClause(tmp, w); + } + void addSoftClause(Lit p, Lit q, Weight w) { + vector tmp{p, q}; + addSoftClause(tmp, w); + } + + void addSumConst(); + // modify Wcnf. This might add new variables. But all variables + // in the range 0--dimacs_nvars-1 are preserved. New variables + // only exist above that range. + // TODO: for incremental solving we need a map from external to internal // variables so that new clauses with new variables can be // added on top of any added new variables arising from transformations - + void simplify(); + bool orig_all_lits_are_softs() const { return orig_all_lits_soft; } - //print info + // print info void printFormulaStats(); void printSimpStats(); void printFormula(std::ostream& out = std::cout) const; void printFormula(Bvars&, std::ostream& out = std::cout) const; void printDimacs(std::ostream& out = std::cout) const; - //data setters and getters mainly for solver - void rewriteModelToInput(vector& ubmodel); //convert model to model of input formula + // data setters and getters mainly for solver + vector rewriteModelToInput( + const vector& ubmodel); // convert model to model of input formula // check model against input formula. Use fresh copy of input formula! - // checkmodelfinal delete current data structures to get space for input formula. - // This makes the WCNF unusable so only use if the program is about to exit + // checkmodelfinal delete current data structures to get space for input + // formula. This makes the WCNF unusable so only use if the program is about + // to exit Weight checkModel(vector& ubmodel, int& nfalseSofts) { - return checkModel(ubmodel, nfalseSofts, false); } - Weight checkModelFinal(vector& ubmodel, int& nfalseSofts) { - return checkModel(ubmodel, nfalseSofts, true); } - Weight checkModel(vector&, int&, bool); - + return checkModel(ubmodel, nfalseSofts, false); + } + Weight checkModelFinal(const vector& ubmodel, int& nfalseSofts) { + return checkModel(ubmodel, nfalseSofts, true); + } + Weight checkModel(const vector&, int&, bool); + const Packed_vecs& hards() const { return hard_cls; } const Packed_vecs& softs() const { return soft_cls; } const vector& softWts() const { return soft_clswts; } @@ -162,17 +179,19 @@ class Wcnf Weight totalClsWt() const { return total_cls_wt; } Weight baseCost() const { return base_cost; } - //info about wcnf + // info about wcnf size_t nHards() const { return hard_cls.size(); } size_t nSofts() const { return soft_cls.size(); } - int nVars() const { return maxvar+1; } //including extra variables added via transformations - Var maxVar() const { return maxvar; } //Users should regard this as being the number of vars + // including extra variables added via transformations + int nVars() const { return maxvar + 1; } + + Var maxVar() const { + return maxvar; + } // Users should regard this as being the number of vars Weight minSftWt() const { return wt_min; } Weight maxSftWt() const { return wt_max; } - int nDiffWts() const { return diffWts.size(); } - const vector& getDiffWts() { return diffWts; } - const vector& getDiffWtCounts() { return diffWtCounts; } + int nDiffWts() const { return ndiffWts; } const vector& getTransitionWts() { return transitionWts; } MStype mstype() const { return ms_type; } @@ -183,74 +202,103 @@ class Wcnf bool integerWts() { return intWts; } const std::string& fileName() const { return instance_file_name; } - //mutexes + // mutexes const vector& get_SCMxs() const { return mutexes; } int n_mxes() const { return mutexes.size(); } - const SC_mx& get_ith_mx(int i) const {return mutexes[i]; } - int ith_mx_size(int i) const {return mutexes[i].soft_clause_lits().size(); } + const SC_mx& get_ith_mx(int i) const { return mutexes[i]; } + int ith_mx_size(int i) const { return mutexes[i].soft_clause_lits().size(); } -private: - bool inputDimacs(std::string filename, bool verify); + private: + bool inputDimacs(std::string filename, bool verify); void update_maxorigvar(vector& lits); - bool prepareClause(vector & lits); + bool prepareClause(vector& lits); void _addHardClause(vector& lits); - void _addHardClause(Lit p) { vector tmp {p}; _addHardClause(tmp); } - void _addHardClause(Lit p, Lit q) { vector tmp {p, q}; _addHardClause(tmp); } + void _addHardClause(Lit p) { + vector tmp{p}; + _addHardClause(tmp); + } + void _addHardClause(Lit p, Lit q) { + vector tmp{p, q}; + _addHardClause(tmp); + } void _addSoftClause(vector& lits, Weight w); - void _addSoftClause(Lit p, Weight w) { vector tmp {p}; _addSoftClause(tmp, w); } - void _addSoftClause(Lit p, Lit q, Weight w) { vector tmp {p, q}; _addSoftClause(tmp, w); } + void _addSoftClause(Lit p, Weight w) { + vector tmp{p}; + _addSoftClause(tmp, w); + } + void _addSoftClause(Lit p, Lit q, Weight w) { + vector tmp{p, q}; + _addSoftClause(tmp, w); + } void computeWtInfo(); - //preprocessing routines - bool subEqs(); + void _addSumConst(); + // preprocessing routines + void subEqsAndUnits(); vector get_binaries(SatSolver_uniqp& sat_solver); vector get_units(); - Packed_vecs reduce_by_eqs_and_units(Packed_vecs& cls, bool softs, - vector>& sccs, vector units); + Packed_vecs reduce_by_eqs_and_units(const Packed_vecs& cls, + bool softs); + vector> binary_scc(vector>& edges); void remDupCls(); + bool test_all_lits_are_softs(); void simpleHarden(); - void rmUnits(); void mxBvars(); vector> mxFinder(Bvars&); void processMxs(vector>, Bvars&); - Var maxOrigVar() const { return maxorigvar; } //input variables - size_t nOrigVars() const { return maxorigvar+1; } //are for private use. - - Packed_vecs reduce_by_units(Packed_vecs& cls, SatSolver_uniqp& slv, bool softs); - int maxorigvar, maxvar; - int dimacs_nvars; - int dimacs_nclauses; - MStype ms_type; - double parsing_time; - Weight total_cls_wt; //Weight of soft clauses after simplifications. - Weight base_cost; - Weight dimacs_top; //weight of a hard clause...typically sum of soft clause weights + 1 - Weight wt_var, wt_mean, wt_min, wt_max; + void remapVars(); + Var maxOrigVar() const { return maxorigvar; } // input variables + size_t nOrigVars() const { return maxorigvar + 1; } // are for private use. + + Packed_vecs reduce_by_units(Packed_vecs& cls, SatSolver_uniqp& slv, + bool softs); + int maxorigvar{}, maxvar{}; + int dimacs_nvars{}; + int dimacs_nclauses{}; + MStype ms_type{MStype::undef}; + double parsing_time{}; + Weight total_cls_wt{}; // Weight of soft clauses after simplifications. + Weight base_cost{}; + // weight of a hardclause...typically sum of soft clause weights + 1 + Weight dimacs_top{std::numeric_limits::max()}; + Weight wt_var{}, wt_mean{}, wt_min{}, wt_max{}; std::string instance_file_name; - bool unsat; - bool noDups; - bool intWts; - int nhard_units; - vector diffWts; - vector diffWtCounts; - vector transitionWts; //weights w s.t. sum of soft clauses with weight less that w is less than w + bool unsat{false}; + bool noDups{true}; + bool intWts{true}; + bool orig_all_lits_soft{false}; + int ndiffWts; + vector orig_unit_soft; + vector transitionWts; // weights w s.t. sum of soft clauses with + // weight less that w is less than w Packed_vecs hard_cls; Packed_vecs soft_cls; vector soft_clswts; + // store preprocessing computation for remaping. + vector hard_units; // in external ordering + vector> all_scc; // in external ordering + vector flipped_vars; // convert unit softs to contain positive lit. + // must remove dups first + vector ex2in, in2ex; + Lit map_in2ex(Lit l) const { + assert(var(l) < in2ex.size() && in2ex[var(l)] != var_Undef); + return Minisat::mkLit(in2ex[var(l)], sign(l)); + } struct ClsData { uint32_t index; uint32_t hash; - Weight w; //weight < 1 ==> hard. weight == 0 redundant + Weight w; // weight < 1 ==> hard. weight == 0 redundant bool origHard; - ClsData(uint32_t i, uint32_t h, Weight wt, bool oh) : index {i}, hash {h}, w {wt}, origHard{oh} {} + ClsData(uint32_t i, uint32_t h, Weight wt, bool oh) + : index{i}, hash{h}, w{wt}, origHard{oh} {} }; - + void initClsData(vector& cdata); bool eqVecs(const ClsData& a, const ClsData& b); - //mutexes + // mutexes vector mutexes; }; diff --git a/maxhs/core/file b/maxhs/core/file new file mode 100644 index 0000000..4ffa9ee --- /dev/null +++ b/maxhs/core/file @@ -0,0 +1,3 @@ +alskjda +lajdflsjkd +alsjd diff --git a/maxhs/ds/Packed.h b/maxhs/ds/Packed.h old mode 100755 new mode 100644 index 923a458..5392233 --- a/maxhs/ds/Packed.h +++ b/maxhs/ds/Packed.h @@ -21,63 +21,63 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ***********/ - #ifndef PACKED_VEC_H #define PACKED_VEC_H -#include +#include #include #include -#include +#include -using std::vector; using std::cout; +using std::vector; /* pack a set of vectors into a single vector. This implemenation only works for concrete types of classes. There is no support for erasing elements (either packed vectors or elements of the packed vector) */ -template +template class Packed_vecs { -public: - class ivec; //Accessor for an internal vector + public: + class ivec; // Accessor for an internal vector class const_ivec; - class iterator; //iterator over internal vectors + class iterator; // iterator over internal vectors class const_iterator; - Packed_vecs() {}; + Packed_vecs(){}; Packed_vecs(vector> init_vecs); - Packed_vecs(std::initializer_list> ilist); + Packed_vecs(std::initializer_list> ilist); - //keep default move constructors and assignments. - Packed_vecs(Packed_vecs &&) = default; - Packed_vecs& operator=(Packed_vecs &&) = default; - - size_t size() const; //# of internal vecs (including zero sized vecs) + // keep default move constructors and assignments. + Packed_vecs(Packed_vecs&&) = default; + Packed_vecs& operator=(Packed_vecs&&) = default; + + size_t size() const; //# of internal vecs (including zero sized vecs) bool empty() const { return !size(); } - const ivec operator[](size_t); //accessor for i'th internal vec - const const_ivec operator[](size_t) const; - iterator begin(); //iterator over internal vecs + const ivec operator[](size_t); // accessor for i'th internal vec + const const_ivec operator[](size_t) const; + iterator begin(); // iterator over internal vecs iterator end(); const_iterator begin() const; const_iterator end() const; /* internal vectors <--> standard vectors */ - void addVec(const vector &); //will add and get zero sized vecs. - vector getVec (size_t i) const; - size_t ithSize(size_t i) const; //Return size of i-th internal vector - - void compress(); //remove spaces; Retain zero length vectors - void rm_empty_vecs(); //remove zero length vectors...don't do this - //to save space as they don't occupy much - //space - void clear() { //release allVec's memory + void addVec(const vector&); // will add and get zero sized vecs. + vector getVec(size_t i) const; + size_t ithSize(size_t i) const; // Return size of i-th internal vector + + void compress(); // remove spaces; Retain zero length vectors + void rm_empty_vecs(); // remove zero length vectors...don't do this + // to save space as they don't occupy much + // space + void clear() { // release allVec's memory vector tmp; std::swap(allVecs, tmp); offsets.clear(); - sizes.clear(); } - + sizes.clear(); + } + size_t capacity() const { return allVecs.capacity(); } void reserve(size_t n) { allVecs.reserve(n); } size_t total_size() const { return allVecs.size(); } @@ -88,155 +88,170 @@ class Packed_vecs { begin, end are standard vector iterators so various (e.g., sort) can be run on the internal vector (a subsequence of a std::vector) */ - public: - ivec(Packed_vecs * pv, size_t index): i(index), p(pv) {} + public: + ivec(Packed_vecs* pv, size_t index) : i(index), p(pv) {} size_t size() const { return p->sizes[i]; } bool empty() const { return !size(); } - typename vector::iterator begin() const { return p->allVecs.begin()+p->offsets[i]; } - typename vector::iterator end() const { return begin()+p->sizes[i]; } - T& operator[](size_t j) const { return p->allVecs[p->offsets[i]+j]; } + typename vector::iterator begin() const { + return p->allVecs.begin() + p->offsets[i]; + } + typename vector::iterator end() const { return begin() + p->sizes[i]; } + T& operator[](size_t j) const { return p->allVecs[p->offsets[i] + j]; } void shrink(size_t n) const { p->sizes[i] -= n; } vector getVec() const { return p->getVec(i); } - private: + + private: size_t i; - Packed_vecs *p; + Packed_vecs* p; }; - + class const_ivec { - public: - const_ivec(const Packed_vecs *pv, size_t index): i (index), p (pv) {} - const_ivec(ivec iv): p (iv.p), i (iv.i) {} + public: + const_ivec(const Packed_vecs* pv, size_t index) : i(index), p(pv) {} + const_ivec(ivec iv) : p(iv.p), i(iv.i) {} size_t size() const { return p->sizes[i]; } bool empty() const { return !size(); } typename vector::const_iterator begin() const { - return p->allVecs.cbegin()+p->offsets[i]; } + return p->allVecs.cbegin() + p->offsets[i]; + } typename vector::const_iterator end() const { - return begin()+p->sizes[i]; } - const T& operator[](size_t j) const { return p->allVecs[p->offsets[i]+j]; } + return begin() + p->sizes[i]; + } + const T& operator[](size_t j) const { + return p->allVecs[p->offsets[i] + j]; + } const vector getVec() const { return p->getVec(i); } - private: + + private: size_t i; - const Packed_vecs * p; + const Packed_vecs* p; }; class iterator { - public: - iterator(Packed_vecs *pv, size_t pos) : _pos(pos), _pv(pv) {} - bool operator!=(iterator &other) const { - return (_pv != other._pv || _pos != other._pos); + public: + iterator(Packed_vecs* pv, size_t pos) : _pos(pos), _pv(pv) {} + bool operator!=(iterator& other) const { + return (_pv != other._pv || _pos != other._pos); + } + iterator& operator++() { + _pos++; + return *this; } - iterator& operator++() { _pos++; return *this; } const ivec operator*() { return ivec(_pv, _pos); } - private: + + private: size_t _pos; - Packed_vecs * _pv; + Packed_vecs* _pv; }; class const_iterator { - public: - const_iterator(const Packed_vecs *pv, size_t pos) : _pos(pos), _pv(pv) {} + public: + const_iterator(const Packed_vecs* pv, size_t pos) : _pos(pos), _pv(pv) {} const_iterator(iterator i) : _pos(i._pos), _pv(i._pv) {} - bool operator!=(const_iterator &other) const { - return (_pv != other._pv || _pos != other._pos); + bool operator!=(const_iterator& other) const { + return (_pv != other._pv || _pos != other._pos); + } + const_iterator& operator++() { + _pos++; + return *this; } - const_iterator& operator++() { _pos++; return *this; } const const_ivec operator*() { return const_ivec(_pv, _pos); } - private: + + private: size_t _pos; - const Packed_vecs * _pv; + const Packed_vecs* _pv; }; -private: + private: vector offsets; vector allVecs; vector sizes; }; template -inline size_t Packed_vecs::size() const { return offsets.size(); } +inline size_t Packed_vecs::size() const { + return offsets.size(); +} template -inline const typename Packed_vecs::ivec Packed_vecs::operator[](size_t i) -{ +inline const typename Packed_vecs::ivec Packed_vecs::operator[]( + size_t i) { return ivec(this, i); } template -inline const typename Packed_vecs::const_ivec Packed_vecs::operator[](size_t i) const -{ - return const_ivec(this, i); +inline const typename Packed_vecs::const_ivec Packed_vecs::operator[]( + size_t i) const { + return const_ivec(this, i); } template -inline typename Packed_vecs::iterator Packed_vecs::begin() { return iterator(this, 0); } +inline typename Packed_vecs::iterator Packed_vecs::begin() { + return iterator(this, 0); +} template inline typename Packed_vecs::iterator Packed_vecs::end() { - return iterator(this, offsets.size()); + return iterator(this, offsets.size()); } template inline typename Packed_vecs::const_iterator Packed_vecs::begin() const { - return const_iterator(this, 0); + return const_iterator(this, 0); } template -inline typename Packed_vecs::const_iterator Packed_vecs::end() const { - return const_iterator(this, offsets.size()); +inline typename Packed_vecs::const_iterator Packed_vecs::end() const { + return const_iterator(this, offsets.size()); } template -inline void Packed_vecs::addVec(const vector &v) -{ +inline void Packed_vecs::addVec(const vector& v) { offsets.push_back(allVecs.size()); sizes.push_back(v.size()); - for(size_t i = 0; i < v.size(); i++) + for (size_t i = 0; i < v.size(); i++) { allVecs.push_back(v[i]); + } } template -inline vector Packed_vecs::getVec(size_t i) const -{ +inline vector Packed_vecs::getVec(size_t i) const { vector v; - for(size_t j = offsets[i]; j < offsets[i] + sizes[i]; j++) + for (size_t j = offsets[i]; j < offsets[i] + sizes[i]; j++) v.push_back(allVecs[j]); return v; } template -inline size_t Packed_vecs::ithSize(size_t i) const -{ +inline size_t Packed_vecs::ithSize(size_t i) const { return sizes[i]; } template -inline void Packed_vecs::compress() -{ +inline void Packed_vecs::compress() { size_t to = 0; size_t cur_vec; /* skip compressed prefix */ - for(cur_vec = 0; cur_vec < offsets.size(); cur_vec++) { - if (offsets[cur_vec] != to) - break; + for (cur_vec = 0; cur_vec < offsets.size(); cur_vec++) { + if (offsets[cur_vec] != to) break; to += sizes[cur_vec]; } - for( ; cur_vec < offsets.size(); cur_vec++) { + for (; cur_vec < offsets.size(); cur_vec++) { size_t start = offsets[cur_vec]; offsets[cur_vec] = to; - for(size_t j = 0; j < sizes[cur_vec]; j++) - allVecs[to++] = allVecs[start+j]; + for (size_t j = 0; j < sizes[cur_vec]; j++) + allVecs[to++] = allVecs[start + j]; } allVecs.resize(offsets.back() + sizes.back()); } template -inline void Packed_vecs::rm_empty_vecs() -{ +inline void Packed_vecs::rm_empty_vecs() { size_t from; size_t to; - for(to = from = 0; from < offsets.size(); from++) - if(sizes[from] > 0) { + for (to = from = 0; from < offsets.size(); from++) + if (sizes[from] > 0) { offsets[to] = offsets[from]; sizes[to] = sizes[from]; to++; @@ -246,26 +261,21 @@ inline void Packed_vecs::rm_empty_vecs() } template -inline Packed_vecs::Packed_vecs(vector> init_vecs) -{ - for(auto& item : init_vecs) - addVec(item); +inline Packed_vecs::Packed_vecs(vector> init_vecs) { + for (auto& item : init_vecs) addVec(item); } template -inline Packed_vecs::Packed_vecs(std::initializer_list> ilist) -{ - for(auto& item: ilist) - addVec(item); +inline Packed_vecs::Packed_vecs(std::initializer_list> ilist) { + for (auto& item : ilist) addVec(item); } - -/* -//Example Usage. +/* +//Example Usage. #include #include -int main (int argc, char * const argv[]) +int main (int argc, char * const argv[]) { Packed_vecs pv; vector v1 {1, 2, 3, 4}; @@ -282,7 +292,7 @@ int main (int argc, char * const argv[]) //Following should work for pv, pv1, or pv2. pv1[0][0] = 999; - + auto v = pv1[0]; cout << "pv1[0].size() = " << v.size() << "\n"; //Flagged as error by compiler: pv1[0] = pv1[1]; @@ -313,7 +323,7 @@ int main (int argc, char * const argv[]) cout << "T4 range-for Packed_vecs access\n"; for(auto v : pv2) { cout << "Vec:" << "\n" << "["; - for(auto& item : v) + for(auto& item : v) cout << item << ", "; cout << "]\n"; } diff --git a/maxhs/ifaces/Cplex.cc b/maxhs/ifaces/Cplex.cc index 56ab7b5..a7fc1d8 100644 --- a/maxhs/ifaces/Cplex.cc +++ b/maxhs/ifaces/Cplex.cc @@ -22,12 +22,12 @@ ***********/ -#include #include #include -#include -#include #include +#include +#include +#include #ifdef GLUCOSE #include "glucose/utils/System.h" @@ -36,9 +36,8 @@ #endif #include "maxhs/ifaces/Cplex.h" -#include "maxhs/utils/io.h" #include "maxhs/utils/Params.h" - +#include "maxhs/utils/io.h" //#include @@ -47,353 +46,614 @@ using std::cout; using std::endl; using std::min; -Cplex::Cplex(Bvars& b, vector& ubmodelsofts, vector& ubmodel, bool integerWts) : - bvars (b), - ubModelSofts (ubmodelsofts), - ubModel (ubmodel), - env {nullptr}, - mip {nullptr}, - trial_mip {nullptr}, - solver_valid {true}, - intWts {integerWts}, - LB {0}, //can't have incumbent with less than zero cost - absGap {params.tolerance}, - exUnits {}, - numSolves {0}, - totalTime {0}, - stime {-1}, - prevTotalTime {0}, - numLPSolves {0}, - totalLPTime {0}, - numConstraints {0}, - numNonCoreConstraints {0}, - totalConstraintSize {0}, - totalNonCoreSize {0}, - in2ex_map {}, - ex2in_map {} -{ - if(integerWts) - absGap = 0.75; +Cplex::Cplex(Bvars& b, TotalizerManager* t, vector& ubmodelsofts, + vector& ubmodel, bool integerWts) + : bvars(b), + totalizers(t), + ubModelSofts(ubmodelsofts), + ubModel(ubmodel), + solver_valid{true}, + intWts{integerWts}, + absGap{params.tolerance} { + if (integerWts) absGap = 0.75; int status; env = CPXXopenCPLEX(&status); - if(env == nullptr) + if (env == nullptr) processError(status, true, "Could not open CPLEX environment"); - if(params.cplex_output && - (status = CPXXsetintparam(env, CPX_PARAM_SCRIND, CPX_ON))) - cout << "c WARNING. failure to turn on CPLEX screen indicator, error " << status << "\n"; + if (params.cplex_output && + (status = CPXXsetintparam(env, CPX_PARAM_SCRIND, CPX_ON))) + cout << "c WARNING. failure to turn on CPLEX screen indicator, error " + << status << "\n"; cout << "c Using IBM CPLEX version " << CPXXversion(env) << " under IBM's Academic Initiative licencing program\n"; - /* if(!params.cplex_output) { - env.setOut(env.getNullStream()); - env.setWarning(env.getNullStream()); - env.setError(env.getNullStream()); - }*/ - - //model = IloModel(env); - //bool_variables = IloBoolVarArray(env); - //cplex_obj = IloMinimize(env); - //model.add(bool_variables); - //model.add(cplex_obj); - //cplex = IloCplex(model); - - if(!(mip = CPXXcreateprob(env, &status, "cplex_prob"))) + if (!(mip = CPXXcreateprob(env, &status, "cplex_prob"))) processError(status, true, "Could not create CPLEX problem"); - if(!(trial_mip = CPXXcreateprob(env, &status, "trial_cplex_prob"))) + if (!(linp = CPXXcreateprob(env, &status, "lp_cplex_prob"))) processError(status, true, "Could not create CPLEX problem"); - if(status = CPXXchgprobtype(env, mip, CPXPROB_MILP)) + if (status = CPXXchgprobtype(env, mip, CPXPROB_MILP)) processError(status, false, "Could not change CPLEX problem to MIP"); - if(status = CPXXchgprobtype(env, trial_mip, CPXPROB_MILP)) - processError(status, false, "Could not change trail CPLEX problem to MIP"); - if(status = CPXXsetdblparam(env, CPX_PARAM_EPAGAP, absGap)) + if (status = CPXXsetdblparam(env, CPX_PARAM_EPAGAP, absGap)) processError(status, false, "Could not set CPLEX absolute gap"); - if(status = CPXXsetdblparam(env, CPX_PARAM_EPGAP, 0.0)) + if (status = CPXXsetdblparam(env, CPX_PARAM_EPGAP, 0.0)) processError(status, false, "Could not set CPLEX relative gap"); - if(status = CPXXsetintparam(env, CPX_PARAM_CLOCKTYPE, 1)) + if (status = CPXXsetintparam(env, CPX_PARAM_CLOCKTYPE, 1)) processError(status, false, "Could not set CPLEX CLOCKTYPE"); - if(status = CPXXsetintparam(env, CPX_PARAM_THREADS, params.cplex_threads)) + if (status = CPXXsetintparam(env, CPX_PARAM_THREADS, params.cplex_threads)) processError(status, false, "Could not set CPLEX global threads"); - if(status = CPXXsetintparam(env, CPX_PARAM_DATACHECK, params.cplex_data_chk)) + if (status = CPXXsetintparam(env, CPX_PARAM_DATACHECK, params.cplex_data_chk)) processError(status, false, "Could not set CPLEX Data Check"); - if(status = CPXXsetintparam(env, CPX_PARAM_MIPEMPHASIS, CPX_MIPEMPHASIS_OPTIMALITY)) + if (status = CPXXsetintparam(env, CPX_PARAM_MIPEMPHASIS, + CPX_MIPEMPHASIS_OPTIMALITY)) processError(status, false, "Could not set CPLEX Optimality Emphasis"); - if(status = CPXXsetintparam(env, CPX_PARAM_POPULATELIM, params.cplex_pop_nsoln)) + if (status = + CPXXsetintparam(env, CPX_PARAM_POPULATELIM, params.cplex_pop_nsoln)) processError(status, false, "Could not set CPLEX Population limit"); - if(status = CPXXsetintparam(env, CPX_PARAM_SOLNPOOLCAPACITY, params.cplex_pop_nsoln)) + if (status = CPXXsetintparam(env, CPX_PARAM_SOLNPOOLCAPACITY, + params.cplex_pop_nsoln)) processError(status, false, "Could not set CPLEX Solution Pool limit"); - if(status = CPXXsetintparam(env, CPX_PARAM_SOLNPOOLINTENSITY, 2)) + if (status = CPXXsetintparam(env, CPX_PARAM_SOLNPOOLINTENSITY, 2)) processError(status, false, "Could not set CPLEX Solution Pool limit"); - if(params.cplex_tune) { + if (params.cplex_tune) { cout << "c Using cplex tune parameters\n"; - if(status = CPXXsetintparam(env, CPX_PARAM_MIPEMPHASIS, CPX_MIPEMPHASIS_BALANCED)) + if (status = CPXXsetintparam(env, CPX_PARAM_MIPEMPHASIS, + CPX_MIPEMPHASIS_BALANCED)) processError(status, false, "Could not set CPLEX Optimality Emphasis"); - if(status = CPXXsetintparam(env, CPX_PARAM_HEURFREQ, -1)) + if (status = CPXXsetintparam(env, CPX_PARAM_HEURFREQ, -1)) processError(status, false, "Could not set CPLEX heuristic frequency"); - if(status = CPXXsetintparam(env, CPX_PARAM_MIRCUTS, 1)) + if (status = CPXXsetintparam(env, CPX_PARAM_MIRCUTS, 1)) processError(status, false, "Could not set CPLEX mir cuts"); - if(status = CPXXsetintparam(env, CPX_PARAM_FLOWCOVERS, 1)) + if (status = CPXXsetintparam(env, CPX_PARAM_FLOWCOVERS, 1)) processError(status, false, "Could not set CPLEX mir cuts"); - if(status = CPXXsetdblparam(env, CPX_PARAM_BTTOL, 0.75)) + if (status = CPXXsetdblparam(env, CPX_PARAM_BTTOL, 0.75)) processError(status, false, "Could not set CPLEX backtrack tolerance"); - if(status = CPXXsetdblparam(env, CPX_PARAM_SOLNPOOLGAP, 0.0)) + if (status = CPXXsetdblparam(env, CPX_PARAM_SOLNPOOLGAP, 0.0)) processError(status, false, "Could not set CPLEX pool relative gap"); } } Cplex::~Cplex() { int status; - if(mip && - (status = CPXXfreeprob(env, &mip))) + if (mip && (status = CPXXfreeprob(env, &mip))) processError(status, false, "Could not free the cplex mip model"); - if(trial_mip && - (status = CPXXfreeprob(env, &trial_mip))) + if (linp && (status = CPXXfreeprob(env, &linp))) processError(status, false, "Could not free the trial cplex mip model"); - if(env && - (status = CPXXcloseCPLEX(&env))) + if (env && (status = CPXXcloseCPLEX(&env))) processError(status, false, "Could not close the cplex environment"); } -void Cplex::processError(int status, bool terminal, const char *msg) { +void Cplex::processError(int status, bool terminal, const char* msg) { char errmsg[CPXMESSAGEBUFSIZE]; auto errstr = CPXXgeterrorstring(env, status, errmsg); cout << "c WARNING. " << msg << "\n"; - if(errstr) + if (errstr) cout << "c WARNING. " << errmsg << "\n"; else cout << "c WARNING. error code = " << status << "\n"; - if(terminal) - solver_valid = false; + if (terminal) solver_valid = false; } void Cplex::ensure_mapping(const Var ex) { - //Create new cplex bool variable if one does not already exist - if (ex >= (int) ex2in_map.size()) - ex2in_map.resize(ex+1,var_Undef); - - if(ex2in_map[ex] == var_Undef) { + // Create new cplex bool variable if one does not already exist + if (ex >= (int)ex2in_map.size()) ex2in_map.resize(ex + 1, var_Undef); + if (ex2in_map[ex] == var_Undef) { int newCplexVar = CPXXgetnumcols(env, mip); ex2in_map[ex] = newCplexVar; - if (newCplexVar >= (int) in2ex_map.size()) - in2ex_map.resize(newCplexVar+1, var_Undef); + if (newCplexVar >= (int)in2ex_map.size()) + in2ex_map.resize(newCplexVar + 1, var_Undef); in2ex_map[newCplexVar] = ex; addNewVar(ex); } } void Cplex::addNewVar(Var ex) { - //add bvar "ex" to Cplex as a new column with its weight being - //in the objective fn. - double lb {0}; - double ub {1}; - char type {'B'}; + // add bvar "ex" to Cplex as a new column with its weight being + // in the objective fn. + double lb{0}; + double ub{1}; + char type{'B'}; double intPart; - double clsWt; - clsWt = bvars.isBvar(ex) ? bvars.wt(ex) : 0; + double varWt; + varWt = bvars.isBvar(ex) ? bvars.wt(ex) : 0; - if(modf(clsWt, &intPart) > 0) { //reset abs gap to zero if dealing with non-int weights + if (modf(varWt, &intPart) > + 0) { // reset abs gap to zero if dealing with non-int weights cout << "c Non-int weights detected Resetting cplex absolute gap to zero\n"; - if(int status = CPXXsetdblparam(env, CPX_PARAM_EPAGAP, 0)) + if (int status = CPXXsetdblparam(env, CPX_PARAM_EPAGAP, 0)) processError(status, false, "Could not set CPLEX absolute gap"); } - if(int status = CPXXnewcols(env, mip, 1, &clsWt, &lb, &ub, &type, nullptr)) { + if (int status = CPXXnewcols(env, mip, 1, &varWt, &lb, &ub, &type, nullptr)) { processError(status, false, "Could not create new CPLEX variable"); - cout << "c WARNING. var = " << ex << " wt = " << clsWt << "\n"; + cout << "c WARNING. var = " << ex << " wt = " << varWt << "\n"; } - if(int status = CPXXnewcols(env, trial_mip, 1, &clsWt, &lb, &ub, &type, nullptr)) { - processError(status, false, "Could not create new CPLEX variable in trial mip"); - cout << "c WARNING. var = " << ex << " wt = " << clsWt << "\n"; + + if (int status = + CPXXnewcols(env, linp, 1, &varWt, &lb, &ub, nullptr, nullptr)) { + processError(status, false, + "Could not create new CPLEX variable in trial mip"); + cout << "c WARNING. var = " << ex << " wt = " << varWt << "\n"; } } void Cplex::setExUnits(Lit l) { - //mark external lit as being true in exUnits. + // mark external lit as being true in exUnits. size_t i = var(l); - if(i >= exUnits.size()) - exUnits.resize(i+1, l_Undef); + if (i >= exUnits.size()) exUnits.resize(i + 1, l_Undef); auto val = sign(l) ? l_False : l_True; - if(exUnits[i] != l_Undef) { - cout << "c WARNING double add of unit to CPLEX lit = " << l << "\n"; - if(exUnits[i] != val) - cout << "c ERROR: positive and negative units of same variable added to Cplex\n"; + if (exUnits[i] != l_Undef) { + if (!totalizers->isToutput(l)) + cout << "c WARNING double add of unit to CPLEX lit = " << l << "\n"; + if (exUnits[i] != val) + cout << "c ERROR: positive and negative units of same variable added to " + "Cplex\n"; } exUnits[i] = val; /* - cout << "setExUnits(" << l << ") exUnits[" << i << "] = " << exUnits[i] << "\n"; + cout << "setExUnits(" << l << ") exUnits[" << i << "] = " << exUnits[i] << + "\n"; */ } lbool Cplex::getExUnits(Lit l) { - //return value of lit l if its variable was previously added as a unit + // return value of lit l if its variable was previously added as a unit size_t i = var(l); - -/* cout << "getExUnits(" << l << ") exUnits[" << i << "]\n"; - cout << "exUnits.size() = " << exUnits.size() << "\n"; - if(exUnits.size() > i) - cout << "exUnits[" << i << "] = " << exUnits[i] << "\n";*/ - - if(i >= exUnits.size()) + /* cout << "getExUnits(" << l << ") exUnits[" << i << "]\n"; + cout << "exUnits.size() = " << exUnits.size() << "\n"; + if(exUnits.size() > i) + cout << "exUnits[" << i << "] = " << exUnits[i] << "\n";*/ + if (i >= exUnits.size()) return l_Undef; - else if(exUnits[i] == l_True) + else if (exUnits[i] == l_True) return sign(l) ? l_False : l_True; - else if(exUnits[i] == l_False) + else if (exUnits[i] == l_False) return sign(l) ? l_True : l_False; else return l_Undef; } -bool Cplex::add_clausal_constraint(vector& theCon) { - //TODO: the return value is being used for two purposes, error and to indicate - //a non core...this should be fixed. +bool Cplex::filter_by_units(vector& theCon) { + size_t cur_size, examine; + for (cur_size = examine = 0; examine < theCon.size(); examine++) { + lbool v = getExUnits(theCon[examine]); + if (v == l_True) { + return true; // tautological constraint + } else if (v == l_False) + continue; // can remove this lit + else + theCon[cur_size++] = theCon[examine]; + } + theCon.resize(cur_size); + return false; +} - //cout << "Cplex adding clause " << theCon; +void Cplex::add_clausal_constraint(vector& theCon) { + /*cout << "Cplex adding clause ["; + for(auto l : theCon) { + cout << l << "[" << getExUnits(l) << ","; + if(totalizers->isToutput(l)) { + cout << "T:" << totalizers->get_olit_index(l) << "[" + << totalizers->get_olit_idx(l) << "]"; + } + cout << "], "; + } + cout << "]\n";*/ - if(!solver_valid) - return false; + if (!solver_valid) return; + if (filter_by_units(theCon)) return; + if (theCon.empty()) { + cout << "c ERROR cplex got contradictory clausal constraint\n"; + return; + } - if(theCon.size() > 1) { - //simplify non-unit based on already added units - size_t cur_size, examine; - for(cur_size = examine = 0; examine < theCon.size(); examine++) { - /*if(!bvars.isBvar(theCon[examine])) { - cout << "c ERROR: Cplex passed constraint with non-b-variable\n" - << theCon << "\n"; - return false; - }*/ - lbool v = getExUnits(theCon[examine]); - if(v == l_True) - return true; //tautological constraint - else if(v == l_False) - continue; //can remove this lit - else - theCon[cur_size++] = theCon[examine]; + if (theCon.size() == 1) { + setExUnits(theCon[0]); + if (totalizers->isToutput(theCon[0])) { + add_tot_unit(theCon[0]); + return; + } else if (ex2in(theCon[0]) == var_Undef && bvars.isNonCore(theCon[0])) { + // Non-core forced bvars never seen before need not be added + // to CPLEX model. Instead we put these into exunits + // and use them to reduce future passed constraints. + // In particular, CPLEX will automatically set a b-var to its + // non-core value, making the nonCore lit true---as this + // adds zero cost to the hitting set. + return; } - theCon.resize(cur_size); + // otherwise process unit core bvar as ordinary clausal constraint } - if(theCon.size() == 1) { - /*if(!bvars.isBvar(theCon[0])) { - cout << "c ERROR: Cplex passed constraint with non-b-variable\n" - << theCon << "\n"; - return false; - }*/ - setExUnits(theCon[0]); - //If the bvar has never been seen before and is not a core bvar - //then don't add it into CPLEX. Putting it into exunits will serve - //to filter future passed constraints. But the value returned by - //cplex from "getSolution" is unaffected by non-bvars and for - //b-vars the default setting is to return the non-core value for all - //b-vars not in the cplex model. - if(ex2in(theCon[0]) == var_Undef && !bvars.isCore(theCon[0])) - return true; //Don't add constraint. + for (auto lt : theCon) { + // must do ensure_mapping after add_tot_output_defn + if (totalizers->isToutput(lt)) add_tot_output_defn(lt); + ensure_mapping(lt); } + add_processed_clause(theCon); +} +void Cplex::add_processed_clause(const vector& theCon) { numConstraints++; totalConstraintSize += theCon.size(); - bool nonCore {false}; - vector cplex_vars {}; - vector cplex_coeff {}; - int numNeg {0}; - char sense {'G'}; + bool nonCore{false}; + vector cplex_vars{}; + vector cplex_coeff{}; + int numNeg{0}; + char sense{'G'}; double rhs; - CPXNNZ beg {0}; + CPXNNZ beg{0}; - for(auto lt: theCon) { - ensure_mapping(lt); + for (auto lt : theCon) { cplex_vars.push_back(ex2in(lt)); - if(bvars.isCore(lt) || (!bvars.isBvar(lt) && !sign(lt))) + if (bvars.isCore(lt) || totalizers->isPositiveToutput(lt) || + (!bvars.isBvar(lt) && !totalizers->isToutput(lt) && !sign(lt))) cplex_coeff.push_back(1.0); else { cplex_coeff.push_back(-1.0); + // cout << "nonCore: " << lt<< "\n"; nonCore = true; numNeg++; } } - if(nonCore) { + if (nonCore) { numNonCoreConstraints++; totalNonCoreSize += theCon.size(); } rhs = 1.0 - numNeg; - if(int status = CPXXaddrows(env, mip, 0, 1, cplex_vars.size(), &rhs, &sense, - &beg, cplex_vars.data(), cplex_coeff.data(), - nullptr, nullptr)) - processError(status, false, "Could not create CPLEX clausal constraint"); - if(int status = CPXXaddrows(env, trial_mip, 0, 1, cplex_vars.size(), &rhs, &sense, - &beg, cplex_vars.data(), cplex_coeff.data(), - nullptr, nullptr)) - processError(status, false, "Could not create trial CPLEX clausal constraint"); - return (nonCore); + // cout << "rhs: " << rhs << ", numNeg: " << numNeg << "\n"; + if (int status = + CPXXaddrows(env, mip, 0, 1, cplex_vars.size(), &rhs, &sense, &beg, + cplex_vars.data(), cplex_coeff.data(), nullptr, nullptr)) + processError( + status, false, + "add_processed_clause could not create CPLEX clausal constraint"); + if (int status = + CPXXaddrows(env, linp, 0, 1, cplex_vars.size(), &rhs, &sense, &beg, + cplex_vars.data(), cplex_coeff.data(), nullptr, nullptr)) + processError( + status, false, + "add_processed_clause could not create trial CPLEX clausal constraint"); + return; +} + +void Cplex::add_tot_unit(Lit tout) { + assert(totalizers->isToutput(tout)); + assert(getExUnits(tout) == l_True); + vector forced_tot_outs; + + if (totalizers->isPositiveToutput(tout)) + for (auto idx = totalizers->get_olit_index(tout) - 1; idx >= 0; --idx) { + auto t = totalizers->get_olits_from_olit(tout)[idx]; + auto val = getExUnits(t); + if (val == l_True) continue; + if (val == l_False) { + cout + << "c ERROR: forced totalizer out already set to opposite value in " + "Cplex\n"; + return; + } + setExUnits(t); + if (ex2in(t) != var_Undef) forced_tot_outs.push_back(t); + } + else if (totalizers->isNegativeToutput(tout)) + for (auto idx = totalizers->get_olit_index(tout) + 1; + static_cast(idx) < + totalizers->get_olits_from_olit(tout).size(); + ++idx) { + auto t = ~totalizers->get_olits_from_olit(tout)[idx]; + auto val = getExUnits(t); + if (val == l_True) continue; + if (val == l_False) { + cout + << "c ERROR: forced totalizer out already set to opposite value in " + "Cplex\n"; + return; + } + setExUnits(t); + if (ex2in(t) != var_Undef) forced_tot_outs.push_back(t); + } + + for (auto t_forced : forced_tot_outs) add_processed_clause({t_forced}); + + if (ex2in(tout) == var_Undef) + // for unseen touts only add the implied summation constraint + add_tot_output_constraint(tout); + // add_tot_output_defn(tout); + + else + add_processed_clause({tout}); +} + +void Cplex::add_tot_output_constraint(Lit tout) { + // add only touts constraint on its input bvars. + assert(totalizers->isToutput(tout)); + assert(ex2in(tout) == var_Undef); + assert(getExUnits(tout) == l_True); + + // add constraint in terms of positive totalizer + if (totalizers->isNegativeToutput(tout)) tout = ~tout; + // Construct cplex constraints + vector cplex_vars{}; + vector cplex_coeff{}; + char sense; + double rhs; + CPXNNZ beg{0}; + int nt{0}, nf{0}; + int tout_idx{totalizers->get_olit_index(tout)}; + int tout_coeff{tout_idx + 1}; + int tmax{static_cast(totalizers->get_olits_from_olit(tout).size())}; + + // add unvalued inputs to cplex constraint. + for (auto l : totalizers->get_ilits_from_olit(tout)) { + assert(bvars.isCore(l)); + auto val = getExUnits(l); + if (val == l_True) { + ++nt; + continue; + } + if (val == l_False) { + ++nf; + continue; + } + ensure_mapping(l); + cplex_vars.push_back(ex2in(l)); + cplex_coeff.push_back(1.0); + } + if (cplex_vars.empty()) { + assert(getExUnits(tout) != l_True || tout_coeff <= nt); + assert(getExUnits(tout) != l_False || tout_coeff > nt); + return; + } + // 2. Add cplex constraints. + if (getExUnits(tout) == l_True && nt < tout_coeff) { + sense = 'G'; + rhs = tout_coeff - nt; + if (cplex_vars.size() < rhs) { + cout << "c ERROR in add_tout_constraint. Can't have " << rhs + << " tins true since " << nf << " out of " << tmax + << " are already false\n"; + return; + } + if (int status = CPXXaddrows(env, mip, 0, 1, cplex_vars.size(), &rhs, + &sense, &beg, cplex_vars.data(), + cplex_coeff.data(), nullptr, nullptr)) { + processError(status, false, + "add_tout_constraint could not create CPLEX (A) constraint"); + } + if (int status = CPXXaddrows(env, linp, 0, 1, cplex_vars.size(), &rhs, + &sense, &beg, cplex_vars.data(), + cplex_coeff.data(), nullptr, nullptr)) + processError(status, false, + "add_tout_constraint could not create trial CPLEX (A) " + "constraint"); + } else if (getExUnits(tout) == l_False && + static_cast(cplex_vars.size()) >= (tout_coeff - nt)) { + // constraint is trivial if less than tout_coeff-nt tins are left + rhs = tout_coeff - nt - 1; //'L' is <= not < + sense = 'L'; + if (int status = CPXXaddrows(env, mip, 0, 1, cplex_vars.size(), &rhs, + &sense, &beg, cplex_vars.data(), + cplex_coeff.data(), nullptr, nullptr)) { + processError(status, false, + "add_tout_constraint could not create CPLEX (B) constraint"); + } + if (int status = CPXXaddrows(env, linp, 0, 1, cplex_vars.size(), &rhs, + &sense, &beg, cplex_vars.data(), + cplex_coeff.data(), nullptr, nullptr)) + processError( + status, false, + "add_tout_constraint could not create trial CPLEX (B) constraint"); + } + return; +} + +void Cplex::add_tot_output_defn(Lit tout) { + assert(totalizers->isToutput(tout)); + if (ex2in(tout) != var_Undef) // defn already in place. + return; + // add defn in terms of positive totalizer + if (totalizers->isNegativeToutput(tout)) tout = ~tout; + + // add cplex constraints capturing the equivalence between + // a totalizer output and its inputs + // 1. check if the defn would be subsumed by a superseeding tout + + vector cplex_vars{}; + vector cplex_coeff{}; + char sense; + double rhs; + CPXNNZ beg{0}; + int nt{0}, nf{0}; + int tout_idx{totalizers->get_olit_index(tout)}; + int tout_coeff{tout_idx + 1}; + int tmax{static_cast(totalizers->get_olits_from_olit(tout).size())}; + + // add \sum unvalued inputs to cplex constraint. + for (auto l : totalizers->get_ilits_from_olit(tout)) { + assert(bvars.isCore(l) || totalizers->isPositiveToutput(l)); + auto val = getExUnits(l); + if (val == l_True) { + ++nt; + continue; + } + if (val == l_False) { + ++nf; + continue; + } + ensure_mapping(l); + cplex_vars.push_back(ex2in(l)); + cplex_coeff.push_back(1.0); + } + if (cplex_vars.empty()) { + assert(getExUnits(tout) != l_True || tout_coeff <= nt); + assert(getExUnits(tout) != l_False || tout_coeff > nt); + return; + } + + // 2. Add cplex constraints. + // Tout <==> \sum in >= tout_coeff + // + // A) Tout ==> \sum in >= tout_coeff + // CPLEX: \sum_in - tout_coeff * Tout >= 0 + // Tout = 1 ==> \sum in >= tout_coeff; Tout = 0 ==> trivial + // constraint + // We sum over only unvalued inputs so we can subtract number true (nt) + // from both sizes FINAL CPLEX constraint: \sum unvalued_in - coeff*Tout + // >= -nt (A) is trivial if nt >= tout_coeff + // if Tout is TRUE add only \sum in >= tout_coeff + + if (nt < tout_coeff && + getExUnits(tout) != l_False) { // non-trivial constraint + sense = 'G'; + rhs = -nt; + bool tout_added{false}; + if (getExUnits(tout) == l_Undef) { + ensure_mapping(var(tout)); + cplex_vars.push_back(ex2in(tout)); + cplex_coeff.push_back(-tout_coeff); + tout_added = true; + } else { // tout is TRUE + assert(getExUnits(tout) == l_True); + rhs += tout_coeff; + } + + /*cout << "Adding totalizer defn (A)\n" + << tout << "[" << tout_coeff << ":" << getExUnits(tout) << ":" + << totalizers->get_olit_idx(tout) << "]: "; + for (size_t i = 0; i < cplex_vars.size(); ++i) + cout << cplex_coeff[i] << "*" << in2ex(cplex_vars[i]) << " + "; + cout << sense << " " << rhs << "\n";*/ + + if (int status = CPXXaddrows(env, mip, 0, 1, cplex_vars.size(), &rhs, + &sense, &beg, cplex_vars.data(), + cplex_coeff.data(), nullptr, nullptr)) { + processError(status, false, + "add_tot_output_defn could not create CPLEX (A) constraint"); + } + if (int status = CPXXaddrows(env, linp, 0, 1, cplex_vars.size(), &rhs, + &sense, &beg, cplex_vars.data(), + cplex_coeff.data(), nullptr, nullptr)) + processError(status, false, + "add_tot_output_defn could not create trial CPLEX (A) " + "constraint"); + if (tout_added) { + cplex_vars.pop_back(); + cplex_coeff.pop_back(); + } + } + //(B) \sum_in >= tout_coeff ==> TOut + // == TOut = 0 ==> \sum_in < tout_coeff + // == TOut = 0 ==> \sum_in <= tout_idx + // + // CPLEX: \sum in - (max - tout_idx)*Tout <= tout_idx + // == \sum in -max*Tout + tout_idx*Tout <= tout_idx + // Tout = 1: sum in -max <= 0 -- trivial + // Tout = 0: \sum_in <= tout_idx <==> \sum_in < tout_coeff + // As in (A) subtract nt from each side: + // FINAL CPLEX: \sum unvalued_in - (max-tout_idx)*TOut <= tout_idx - nt + // We know that sum_in <= max - nf + // so TOut = 0 ==> \sum_in < tout_coeff becomes trival if we already + // have \sum_in < tout_coeff so max-nf < tout_coeff means constraint + // would be trivial and max-nf >= tout_coeff is when constraint is + // non-trivial + if ((tmax - nf) >= tout_coeff && getExUnits(tout) != l_True) { + rhs = tout_idx - nt; + sense = 'L'; + if (getExUnits(tout) == l_Undef) { + ensure_mapping(var(tout)); + cplex_vars.push_back(ex2in(tout)); + cplex_coeff.push_back(-(tmax - tout_idx)); + } else { + assert(getExUnits(tout) == l_False); + } + + /*cout << "Adding totalizer defn (B)\n" + << tout << "[" << tout_coeff << ":" << getExUnits(tout) << ":" + << totalizers->get_olit_idx(tout) << "]: "; + for (size_t i = 0; i < cplex_vars.size(); ++i) + cout << cplex_coeff[i] << "*" << in2ex(cplex_vars[i]) << " + "; + cout << sense << " " << rhs << "\n";*/ + + if (int status = CPXXaddrows(env, mip, 0, 1, cplex_vars.size(), &rhs, + &sense, &beg, cplex_vars.data(), + cplex_coeff.data(), nullptr, nullptr)) { + processError(status, false, + "add_tot_output_defn could not create CPLEX (B) constraint"); + } + if (int status = CPXXaddrows(env, linp, 0, 1, cplex_vars.size(), &rhs, + &sense, &beg, cplex_vars.data(), + cplex_coeff.data(), nullptr, nullptr)) + processError( + status, false, + "add_tot_output_defn could not create trial CPLEX (B) constraint"); + } + return; } bool Cplex::add_mutex_constraint(const SC_mx& mx) { - if(!solver_valid) - return false; + if (!solver_valid) return false; - //the ~encoding lit + all of the soft clause lits must sum to 1 - //If the encoding lit does not exist the soft clause lits must sum to <= 1 - //If a non-core, then the sum condition applies to the negated lits. + // the ~encoding lit + all of the soft clause lits must sum to 1 + // If the encoding lit does not exist the soft clause lits must sum to <= 1 + // If a non-core, then the sum condition applies to the negated lits. - auto mx_lits {mx.soft_clause_lits()}; + auto mx_lits{mx.soft_clause_lits()}; bool have_encoding_lit = (mx.encoding_lit() != lit_Undef); - if(have_encoding_lit) - mx_lits.push_back(~mx.encoding_lit()); + if (have_encoding_lit) mx_lits.push_back(~mx.encoding_lit()); - if(!mx.is_core()) - for(size_t i = 0; i < mx_lits.size(); i++) - mx_lits[i] = ~mx_lits[i]; + if (!mx.is_core()) + for (size_t i = 0; i < mx_lits.size(); i++) mx_lits[i] = ~mx_lits[i]; - //mx_lits must sum to <= 1. simplify if any are one set others to zero - //if any are zero remove from mx_lits. + // mx_lits must sum to <= 1. simplify if any are one set others to zero + // if any are zero remove from mx_lits. - bool oneTrue {false}; + bool oneTrue{false}; size_t cur_size, examine; - for(cur_size = examine = 0; examine < mx_lits.size(); examine++) { + for (cur_size = examine = 0; examine < mx_lits.size(); examine++) { lbool val = getExUnits(mx_lits[examine]); - if(val == l_True) { - if(oneTrue) { - cout << "c ERROR: Cplex passed mutex with more than one lit forced to be true\n" + if (val == l_True) { + if (oneTrue) { + cout << "c ERROR: Cplex passed mutex with more than one lit forced to " + "be true\n" << "c " << mx << "\n"; return false; } - oneTrue=true; - } - else if(val == l_False) + oneTrue = true; + } else if (val == l_False) continue; else - //keep the lit. + // keep the lit. mx_lits[cur_size++] = mx_lits[examine]; } mx_lits.resize(cur_size); - if(mx_lits.size() == 0) - return true; + if (mx_lits.size() == 0) return true; - if(oneTrue) { //force remaining to false - vector forced (1); - for(size_t i = 0; i < mx_lits.size(); i++) { + if (oneTrue) { // force remaining to false + vector forced(1); + for (size_t i = 0; i < mx_lits.size(); i++) { forced[0] = ~mx_lits[i]; add_clausal_constraint(forced); } return true; } - //Otherwise add the constraint that the lits sum to 1 or to <= 1 + // Otherwise add the constraint that the lits sum to 1 or to <= 1 - vector cplex_vars {}; - vector cplex_coeff {}; - int numNeg {0}; - char sense {(have_encoding_lit ? 'E' : 'L')}; - double rhs {1.0}; - CPXNNZ beg {0}; + vector cplex_vars{}; + vector cplex_coeff{}; + int numNeg{0}; + char sense{(have_encoding_lit ? 'E' : 'L')}; + double rhs{1.0}; + CPXNNZ beg{0}; - //add cplex constraint lt1 + lt2 ... ltk = (<=) 1 - for(auto lt: mx_lits) { + // add cplex constraint lt1 + lt2 ... ltk = (<=) 1 + for (auto lt : mx_lits) { ensure_mapping(lt); cplex_vars.push_back(ex2in(lt)); - if(bvars.isCore(lt) || (!bvars.isBvar(lt) && !sign(lt))) + if (bvars.isCore(lt) || (!bvars.isBvar(lt) && !sign(lt))) cplex_coeff.push_back(1.0); else { cplex_coeff.push_back(-1.0); @@ -402,45 +662,140 @@ bool Cplex::add_mutex_constraint(const SC_mx& mx) { } rhs -= numNeg; - if(int status = CPXXaddrows(env, mip, 0, 1, cplex_vars.size(), &rhs, &sense, - &beg, cplex_vars.data(), cplex_coeff.data(), - nullptr, nullptr)) + if (int status = + CPXXaddrows(env, mip, 0, 1, cplex_vars.size(), &rhs, &sense, &beg, + cplex_vars.data(), cplex_coeff.data(), nullptr, nullptr)) processError(status, false, "Could not create CPLEX mutex constraint"); - if(int status = CPXXaddrows(env, trial_mip, 0, 1, cplex_vars.size(), &rhs, &sense, - &beg, cplex_vars.data(), cplex_coeff.data(), - nullptr, nullptr)) + if (int status = + CPXXaddrows(env, linp, 0, 1, cplex_vars.size(), &rhs, &sense, &beg, + cplex_vars.data(), cplex_coeff.data(), nullptr, nullptr)) processError(status, false, "Could not create CPLEX mutex constraint"); return true; } -/*void Cplex::add_impl_constraint(Lit blit, const vector& theCon){ - IloExpr expr(env); - numConstraints++; - totalConstraintSize += theCon.size() + 1; - int k = -theCon.size(); - ensure_mapping(blit); - IloBoolVar& blitvar = bool_variables[ex2in(blit)]; - if(bvars.wt(blit) > 0) - expr += k * blitvar; - else - expr += k * (1 - blitvar); - for(auto lt: theCon) { - ensure_mapping(lt); - IloBoolVar &boolvar = bool_variables[ex2in(lt)]; - if(bvars.wt(lt) > 0) - expr += boolvar; - else - expr += (1 - boolvar); +bool Cplex::add_sum_constraint(vector& sc_lits, int64_t k) { + if (!solver_valid) return false; + + // all of the soft clause lits must sum to k, if is_exact = 'e' + int sk = sc_lits.size() - k; + cout << "sc_lits size: " << sc_lits.size() << " k: " << k << "\n"; + char exact = 'G'; // sc.is_exact(); + // sc_lits must sum to <= k. simplify if any are one set decrease k + // if any are zero remove from sc_lits. + // Otherwise add the constraint that the lits sum to k or to <= k + bool k_True{false}; + bool sk_False{false}; + size_t cur_size, examine; + + // check to see which vars are already set + for (cur_size = examine = 0; examine < sc_lits.size(); examine++) { + lbool val = getExUnits( + sc_lits[examine]); // if previously defined as a unit set to true, + if (val == l_True) { + if (k_True) { + cout << "c ERROR: Cplex passed sumcons with more than k lits forced to " + "be true\n"; + return false; + } + k--; // decrease size of k + // check for exact cases + if (k == 0 && (exact == 'E') || (exact == 'L')) { + k_True = true; + } + } else if (val == l_False) { + sk--; + if (sk == 0) { + sk_False = true; + } + continue; + } else + // keep the lit. + sc_lits[cur_size++] = sc_lits[examine]; + } + sc_lits.resize(cur_size); + + if (sc_lits.size() == 0) + return true; // no sum constraints to be added all vars assigned true or + // false + cout << "sclits.size: " << sc_lits.size() << "\n"; + + // Only if equal to K + /* + if(k_True) { //force remaining to false + vector forced (1); + for(size_t i = 0; i < sc_lits.size(); i++) { + forced[0] = ~sc_lits[i]; + add_clausal_constraint(forced); + } + return true; + } + */ + if (sk_False) { // force remaining to true + printf("A| sk is True, force remaining to true\n"); + vector forced(1); + for (size_t i = 0; i < sc_lits.size(); i++) { + forced[0] = sc_lits[i]; + add_clausal_constraint(forced); + } + return true; + } + + // Otherwise add the constraint that the lits sum to >=k + + vector cplex_vars{}; + vector cplex_coeff{}; + int numNeg{0}; + // char sense{'G'}; + char sense{exact}; + double rhs{static_cast(k)}; // rhs = k? + CPXNNZ beg{0}; + // std::cout << "a sc_lits:\n"; + // add cplex constraint lt1 + lt2 ... ltk = (<=) 1 + for (auto lt : sc_lits) { + // std::cout << lt << " "; + ensure_mapping(lt); + cplex_vars.push_back(ex2in(lt)); + if (bvars.isCore(lt) || (!bvars.isBvar(lt) && !sign(lt))) + cplex_coeff.push_back(1.0); + else { + // cout<< "neg coeff: " << lt << "\n"; + cplex_coeff.push_back(-1.0); + numNeg++; + } } - model.add(0 <= expr); - }*/ + // std::cout<< "\n"; + rhs -= numNeg; + + /*std::cout << "cplex_vars: "; + for (int l:cplex_vars){ + std::cout << "[" <::max()}; - - CBInfo* info {static_cast(cbhandle)}; - - if(status = CPXXgetcallbackinfo(env, cbdata, wherefrom, CPX_CALLBACK_INFO_MIP_FEAS, &hasincumbent)) - info->cplexObject->processError(status, false, "Could not get MIP_FEAS in CPLEX call back"); - - if (hasincumbent) { - if(status = CPXXgetcallbackinfo(env, cbdata, wherefrom, CPX_CALLBACK_INFO_BEST_INTEGER, &objval)) - info->cplexObject->processError(status, false, "Could not get incumbent value in CPLEX call back"); - - //_LB should be a true lower bound on the cost of this model. - //if we found a solution within our tolerance, we can stop declaring this solution - //to be "optimal" (within tolerance). - //If we have integer weights will also - if ( fabs(objval - info->LB) <= info->absGap) { - //printf("c CPLEX Terminating early on found incumbent achieving optimum\n"); - info->found_opt = true; - return(1); - } +static int CPXPUBLIC info_callback(CPXCENVptr env, void* cbdata, int wherefrom, + void* cbhandle) { + // Use info callback to all use of dynamic search. + // Abort early if we found incumbent with cost < UB. + // or if we found incmbent with cost == LB (then incumbent is optimal). + int status{0}; + int hasincumbent{0}; + double objval{std::numeric_limits::max()}; + + CBInfo* info{static_cast(cbhandle)}; + + if (status = CPXXgetcallbackinfo(env, cbdata, wherefrom, + CPX_CALLBACK_INFO_MIP_FEAS, &hasincumbent)) + info->cplexObject->processError( + status, false, "Could not get MIP_FEAS in CPLEX call back"); + + if (hasincumbent) { + if (status = CPXXgetcallbackinfo(env, cbdata, wherefrom, + CPX_CALLBACK_INFO_BEST_INTEGER, &objval)) + info->cplexObject->processError( + status, false, "Could not get incumbent value in CPLEX call back"); + + //_LB should be a true lower bound on the cost of this model. + // if we found a solution within our tolerance, we can stop declaring this + // solution to be "optimal" (within tolerance). If we have integer weights + // will also + if (fabs(objval - info->LB) <= info->absGap) { + // printf("c CPLEX Terminating early on found incumbent achieving + // optimum\n"); + info->found_opt = true; + return (1); + } - double cplex_current_ticks {}; - CPXXgettime(env, &cplex_current_ticks); - - /*cout << "c CPLEX start ticks = " << info->cplex_start_ticks - << " current ticks = " << cplex_current_ticks - << " min tick limit = " << params.cplex_min_ticks - << " objective value gap = " << info->UB - objval - << " (absgap = " << info->absGap - << ")\n";*/ - - if ((cplex_current_ticks - info->cplex_start_ticks) >= params.cplex_min_ticks - && info->UB - objval > info->absGap) { - printf("c Cplex found better incumbent than UB (%f < %f)\n", objval, info->UB); - info->found_better_soln = true; - return(1); - } + double cplex_current_ticks{}; + CPXXgettime(env, &cplex_current_ticks); + + /*cout << "c CPLEX start ticks = " << info->cplex_start_ticks + << " current ticks = " << cplex_current_ticks + << " min tick limit = " << params.cplex_min_ticks + << " objective value gap = " << info->UB - objval + << " (absgap = " << info->absGap + << ")\n";*/ + + if ((cplex_current_ticks - info->cplex_start_ticks) >= + params.cplex_min_ticks && + info->UB - objval > info->absGap) { + printf("c Cplex found better incumbent than UB (%f < %f)\n", objval, + info->UB); + info->found_better_soln = true; + return (1); } - return(0); } + return (0); +} } -Weight Cplex::solve_(vector &solution, double UB, double timeLimit) { - //return a setting of all bvariables and lower bound on cplex model - //as function value. - //1. Return -1 as lower bound if error. - //2. Will try to return solution of cost < UB. - //4. If solution cost >= UB, solution will be optimal - //5. If cost of solution == returned LB, solve_ found an optimal model +Weight Cplex::solve_(vector& solution, double UB, double timeLimit) { + // return a setting of all bvariables and lower bound on cplex model + // as function value. + // 1. Return -1 as lower bound if error. + // 2. Will try to return solution of cost < UB. + // 4. If solution cost >= UB, solution will be optimal + // 5. If cost of solution == returned LB, solve_ found an optimal model // of the cplex model. int status; - //DEBUG - //cout << "c Cplex solve passed UB = " << UB << " current LB = " << LB << "\n"; + // DEBUG + // cout << "c Cplex solve passed UB = " << UB << " current LB = " << LB << + // "\n"; timeLimit = (timeLimit < 0) ? 1e+75 : timeLimit; - if(status = CPXXsetdblparam(env, CPX_PARAM_TILIM, timeLimit)) + if (status = CPXXsetdblparam(env, CPX_PARAM_TILIM, timeLimit)) processError(status, false, "Could not set CPLEX time limit"); solution.clear(); - if(params.cplex_write_model) - writeCplexModel(); + if (params.cplex_write_model) writeCplexModel(); - if(params.bestmodel_mipstart) - useBestModelStart(mip); + if (params.bestmodel_mipstart) useBestModelStart(mip); - double cplex_start_ticks {}; - if(status = CPXXgettime(env, &cplex_start_ticks)) - processError(status, false, "Failed to get the Cplex deterministic start time"); + double cplex_start_ticks{}; + if (status = CPXXgettime(env, &cplex_start_ticks)) + processError(status, false, + "Failed to get the Cplex deterministic start time"); - CBInfo myloginfo {this, LB, UB, absGap, cplex_start_ticks, false, false}; - if(status = CPXXsetinfocallbackfunc(env, info_callback, &myloginfo)) + CBInfo myloginfo{this, LB, UB, absGap, cplex_start_ticks, false, false}; + if (status = CPXXsetinfocallbackfunc(env, info_callback, &myloginfo)) processError(status, false, "Failed to set logging callback function"); status = CPXXmipopt(env, mip); - //DEBUG - //cout << "Cplex after solve call back found_opt = " << myloginfo.found_opt + // DEBUG + // cout << "Cplex after solve call back found_opt = " << myloginfo.found_opt // << " call back better_soln = " << myloginfo.found_better_soln // << "\n"; - //Check Call backs. - if(myloginfo.found_opt) { - if(params.verbosity > 0) + // Check Call backs. + if (myloginfo.found_opt) { + if (params.verbosity > 0) cout << "c found incumbent of obj cost = " << LB << "\n"; return getSolution(solution, true); } - if(myloginfo.found_better_soln) { - if(params.verbosity > 0) + if (myloginfo.found_better_soln) { + if (params.verbosity > 0) cout << "c found incumbent of cost better than UB (= " << UB << ")\n"; return getSolution(solution, false); } - //Else mipopt returned via normal means. - if(status) - processError(status, false, "CPLEX Failed to optmize MIP"); + // Else mipopt returned via normal means. + if (status) processError(status, false, "CPLEX Failed to optmize MIP"); status = CPXXgetstat(env, mip); - if(params.verbosity > 2) - cout << "c Solution Pool size " << CPXXgetsolnpoolnumsolns(env, mip) << "\n"; - if(params.verbosity > 2) { - printf("c Solution Pool replaced %d\n", CPXXgetsolnpoolnumreplaced(env, mip)); - for(int i=0; i < CPXXgetsolnpoolnumsolns(env, mip); i++) { - double objval {0.0}; + if (params.verbosity > 2) + cout << "c Solution Pool size " << CPXXgetsolnpoolnumsolns(env, mip) + << "\n"; + if (params.verbosity > 2) { + printf("c Solution Pool replaced %d\n", + CPXXgetsolnpoolnumreplaced(env, mip)); + for (int i = 0; i < CPXXgetsolnpoolnumsolns(env, mip); i++) { + double objval{0.0}; CPXXgetsolnpoolobjval(env, mip, i, &objval); printf("c Solution %d: objval = %.0f\n", i, objval); } } - if(status == CPXMIP_OPTIMAL || status == CPXMIP_OPTIMAL_TOL) { - //cout << "Cplex found standard solution " << + if (status == CPXMIP_OPTIMAL || status == CPXMIP_OPTIMAL_TOL) { + // cout << "Cplex found standard solution " << //(status == CPXMIP_OPTIMAL ? "OPTIMAL\n" : " WITHIN TOLERANCE\n"); return getSolution(solution, true); - } - - else { + } else { + if (status == CPXMIP_TIME_LIM_FEAS || CPXMIP_TIME_LIM_INFEAS) return -1; char buf[CPXMESSAGEBUFSIZE]; char* p = CPXXgetstatstring(env, status, buf); - if(p) + if (p) cout << "c WARNING. Cplex status = " << status << " " << buf << "\n"; else cout << "c WARNING. Cplex status = " << status << "\n"; @@ -588,302 +950,145 @@ Weight Cplex::solve_(vector &solution, double UB, double timeLimit) { } struct ebInfo { - //callback data for testing if is forced + // callback data for testing if is forced Cplex* cplexObject; double UB; double LB; }; -extern "C" { - static int CPXPUBLIC - eb_callback (CPXCENVptr env, void *cbdata, int wherefrom, void *cbhandle) { - //Use info callback for testing if forced. - //we can abort early if best bound becomes >= UB.n - int status {0}; - double bstobjval {0}; - - ebInfo* info {static_cast(cbhandle)}; - - if(status = CPXXgetcallbackinfo(env, cbdata, wherefrom, CPX_CALLBACK_INFO_BEST_REMAINING, &bstobjval)) - info->cplexObject->processError(status, false, "Could not get best objective in CPLEX test forced call back"); - - //debug - //cout << "eb_callback bstobjval = " << bstobjval << "\n"; - //cout << "eb_callback LB = " << info->LB << " UB = " << info->UB << "\n"; - - if( bstobjval >= info->UB ) { - //printf("c CPLEX test force Terminating early on found LB >= UB\n"); - info->LB = bstobjval; - return(1); - } - return(0); - } -} - -bool Cplex::exceeds_bounds(vector& lits, Weight upper_bound, double cpu_limit) { - //test if making the literals in lits true forces the CPLEX model to - //have cost greater than or equal to the upper_bound (so we can - //force it to be false). Limit time to do test to cpu_limit. - Weight increment {0}; - size_t cur_size, examine; - lbool val; - for(cur_size = examine = 0; examine < lits.size(); examine++) { - auto l = lits[examine]; - //1. If l not in model---will increment the cost by its weight. - if(ex2in(var(l)) == var_Undef) - increment += (bvars.isBvar(l) ? bvars.wt(l) : 0); - //2. Is already forced to true in cplex model---no change to weight. - else if((val = getExUnits(l)) == l_True) - continue; - //3. If already forced to be false will cause infinite cost model - // (infeasible) - else if(val == l_False) - return true; - //4. add to final testing constraint - else - lits[cur_size++] = lits[examine]; - } - lits.resize(cur_size); +Weight Cplex::solve_lp_relaxation_(vector& solution, + vector& reduced_costs, + vector& cplex_vars) { + /* Unlike other solvers lp_relaxation returns the lp optimal value + of all cplex variables---not just the bvars */ - //debug - /*cout << "======Exceeds_bounds: increment = " << increment - << " lits = " << lits << "\n";*/ + int status{0}; + double objval{-1.0}; + int nvars = CPXXgetnumcols(env, linp); + if (nvars <= 0) return -1.0; - if(LB + increment > upper_bound) - return true; - if(lits.empty()) - return false; - - bool retval = false; - //Now test with cplex. - vector cplex_vars {}; - vector cplex_coeff {}; - int numNeg {0}; - char sense {'G'}; - double rhs; - CPXNNZ beg {0}; - int status {0}; + solution.clear(); + reduced_costs.clear(); + solution.resize(nvars, 0.0); + reduced_costs.resize(nvars, 0.0); - for(auto lt: lits) { - cplex_vars.push_back(ex2in(lt)); - if(bvars.isCore(lt) || (!bvars.isBvar(lt) && !sign(lt))) - cplex_coeff.push_back(1.0); - else { - cplex_coeff.push_back(-1.0); - numNeg++; - } + if (status = CPXXlpopt(env, linp)) { + processError(status, false, "CPLEX Failed to optmize lp relaxation"); + return -1.0; } - rhs = lits.size() - numNeg; - if(status = CPXXaddrows(env, trial_mip, 0, 1, cplex_vars.size(), &rhs, &sense, - &beg, cplex_vars.data(), cplex_coeff.data(), - nullptr, nullptr)) - processError(status, false, "Could not create CPLEX test forced constraint"); - if(status = CPXXsetdblparam(env, CPX_PARAM_TILIM, cpu_limit)) - processError(status, false, "Could not set CPLEX time limit in test forced constraint"); - ebInfo myebinfo {this, upper_bound, -1.0}; - if(status = CPXXsetinfocallbackfunc (env, eb_callback, &myebinfo)) - processError(status, false, "Failed to set callback function in test forced constraint"); - - if(params.bestmodel_mipstart) - useBestModelStart(trial_mip); - - status = CPXXmipopt(env, trial_mip); - if(myebinfo.LB > 0) { - //debug - //cout << "exceeds_bounds call back found exceeds lb\n"; - retval = true; - } - else if(status) { + + status = CPXXgetstat(env, linp); + if (status != CPX_STAT_OPTIMAL) { char buf[CPXMESSAGEBUFSIZE]; char* p = CPXXgetstatstring(env, status, buf); - if(p) - cout << "c exceed_bounds. Cplex status = " << status << " " << buf << "\n"; + if (p) + cout << "c WARNING. Cplex status = " << status << " " << buf << "\n"; else - cout << "c WARNING. exceeds_bounds. Cplex status = " << status << "\n"; - retval = false; - } - else { - double bstobjval {}; - if(status = CPXXgetbestobjval(env, trial_mip, &bstobjval)) { - processError(status, false, "Problem getting mip best objective value"); - retval = false; - } - else { - //debug - //cout << "exceed_bounds found bstobjval = " << bstobjval << "\n"; - retval = bstobjval >= upper_bound; - } - } - int nrows = CPXXgetnumrows(env, trial_mip); - if(int status = CPXXdelrows(env, trial_mip, nrows-1, nrows-1)) - processError(status, false, "Could not delete added CPLEX test forced constraint"); - return retval; -} - -Weight Cplex::solve_lp_relaxation_(vector& solution, vector& reduced_costs) { - /* Solve lp relaxation. Return objective value and set the solution values - and reduced costs---indexed by soft clause index (as in getsolution) */ - int status {0}; - bool extract {true}; - double objval {-1.0}; - int nvars = CPXXgetnumcols(env, trial_mip); - solution.clear(); - reduced_costs.clear(); - if(nvars <= 0) - return -1.0; - - vector solnvals(nvars, 0.0); - vector redcosts(nvars, 0.0); - - if(status = CPXXchgprobtype(env, trial_mip, CPXPROB_LP)) { - processError(status, false, "Could not change mip to lp in solve_lp_relaxation"); - extract = false; - } - if(extract) { - if(status = CPXXlpopt(env, trial_mip)) { - processError(status, false, "CPLEX Failed to optmize lp relaxation"); - extract = false; - } - } - if(extract) { - status = CPXXgetstat(env, trial_mip); - if(status != CPX_STAT_OPTIMAL) { - char buf[CPXMESSAGEBUFSIZE]; - char* p = CPXXgetstatstring(env, status, buf); - if(p) - cout << "c WARNING. Cplex status = " << status << " " << buf << "\n"; - else - cout << "c WARNING. Cplex status = " << status << "\n"; - } - else { - if(status = CPXXgetobjval(env, trial_mip, &objval)) - processError(status, false, "Problem getting objective value of lp-relaxation"); - if(status = CPXXgetx(env, trial_mip, solnvals.data(), 0, nvars-1)) - processError(status, false, "Problem getting lp-relaxation variable assignments"); - if(status = CPXXgetdj(env, trial_mip, redcosts.data(), 0, nvars-1)) - processError(status, false, "Problem getting lp-relaxation variable assignments"); - - solution.resize(bvars.n_bvars(), 0.0); - reduced_costs.resize(bvars.n_bvars(), 0.0); - - //set output vectors. Note that if a soft clause variable does not exist in the - //cplex model then its value is zero and its reduced cost is equal to its weight. - for(size_t sftCls=0; sftCls < bvars.n_bvars(); sftCls++) { - auto ci = ex2in(bvars.varOfCls(sftCls)); - if(ci != var_Undef) { - solution[sftCls] = solnvals[ci]; - reduced_costs[sftCls] = redcosts[ci]; - } - else { - solution[sftCls] = 0.0; - reduced_costs[sftCls] = bvars.wtNcls(sftCls); - } - } - } + cout << "c WARNING. Cplex status = " << status << "\n"; + } else { + if (status = CPXXgetobjval(env, linp, &objval)) + processError(status, false, + "Problem getting objective value of lp-relaxation"); + if (status = CPXXgetx(env, linp, solution.data(), 0, nvars - 1)) + processError(status, false, + "Problem getting lp-relaxation variable assignments"); + if (status = CPXXgetdj(env, linp, reduced_costs.data(), 0, nvars - 1)) + processError(status, false, + "Problem getting lp-relaxation variable assignments"); + + cplex_vars.clear(); + for (int i = 0; i < nvars; i++) cplex_vars.push_back(in2ex(i)); } - if(status = CPXXchgprobtype(env, trial_mip, CPXPROB_MILP)) - processError(status, true, "Could not change lp back into MIP in solve_lp_relaxation"); - vector columns(nvars); - vector types(nvars, 'B'); - vector u(nvars, 'U'); - vector l(nvars, 'L'); - vector ones(nvars, 1.0); - vector zeros(nvars, 0.0); - for(size_t i=0; i < columns.size(); i++) - columns[i] = i; - - if(status = CPXXchgctype(env, trial_mip, columns.size(), columns.data(), types.data())) - processError(status, true, "Could not change variables back to binary in solve_lp_relaxation"); - if(status = CPXXchgbds(env, trial_mip, columns.size(), columns.data(), u.data(), ones.data())) - processError(status, true, "Could not re-enforce upper bound of 1 on binary variables in solve_lp_relaxation"); - if(status = CPXXchgbds(env, trial_mip, columns.size(), columns.data(), l.data(), zeros.data())) - processError(status, true, "Could not re-enforce lower bound of 0 on binary variables in solve_lp_relaxation"); - return objval; } void Cplex::writeCplexModel() { - //write out the cplex models for debugging + // write out the cplex models for debugging auto fn_start = params.instance_file_name.find_last_of('/'); - std::string fname = "mip_" - + params.instance_file_name.substr(fn_start+1) - + std::to_string(numSolves) + ".mps"; - if(int status = CPXXwriteprob(env, mip, fname.c_str(), nullptr)) + std::string fname = "mip_" + params.instance_file_name.substr(fn_start + 1) + + std::to_string(numSolves) + ".mps"; + if (int status = CPXXwriteprob(env, mip, fname.c_str(), nullptr)) processError(status, false, "Could not write MIPS model"); } void Cplex::useBestModelStart(CPXLPptr this_mip) { - //use the current upper bound model as a mip start in the passed problem + // use the current upper bound model as a mip start in the passed problem - //clear old starts - /*if(CPXXgetnummipstarts(env, this_mip) > 0) { - if(int status = CPXXdelmipstarts(env, this_mip, 0, CPXXgetnummipstarts(env, this_mip)-1)) - processError(status, false, "CPLEX Failed to Delete MIP Starts"); + // clear old starts + /*if (CPXXgetnummipstarts(env, this_mip) > 0) { + if (int status = CPXXdelmipstarts(env, this_mip, 0, + CPXXgetnummipstarts(env, this_mip) - 1)) + processError(status, false, "CPLEX Failed to Delete MIP Starts"); }*/ - vector cplex_vars {}; - vector cplex_vals {}; - CPXNNZ beg {0}; - int effort {1}; - for(size_t sftCls = 0; sftCls < bvars.n_bvars(); sftCls++) { + vector cplex_vars{}; + vector cplex_vals{}; + CPXNNZ beg{0}; + int effort{1}; + + for (size_t sftCls = 0; sftCls < bvars.n_bvars(); sftCls++) { auto ci = ex2in(bvars.varOfCls(sftCls)); - if(ci != var_Undef) { + if (ci != var_Undef) { cplex_vars.push_back(ci); - double val = (ubModelSofts[sftCls]==l_True) ? 0.0 : 1.0; + double val = (ubModelSofts[sftCls] == l_True) ? 0.0 : 1.0; cplex_vals.push_back(val); } } - for(size_t v = 0; v < bvars.n_vars(); v++) - if(!bvars.isBvar(v)) { - auto ci = ex2in(v); - if(ci != var_Undef) { - cplex_vars.push_back(ci); - double val = (ubModel[v]==l_True) ? 1.0 : 0.0; - cplex_vals.push_back(val); - } + for (size_t v = 0; v < bvars.n_ovars(); v++) + if (!bvars.isBvar(v)) { + auto ci = ex2in(v); + if (ci != var_Undef) { + cplex_vars.push_back(ci); + double val = (ubModel[v] == l_True) ? 1.0 : 0.0; + cplex_vals.push_back(val); } + } - if(!cplex_vars.empty()) { - if(int status = CPXXaddmipstarts(env, this_mip, 1, cplex_vars.size(), &beg, cplex_vars.data(), cplex_vals.data(), &effort, nullptr)) - processError(status, false, "CPLEX Failed to add best model as MIP start"); + if (!cplex_vars.empty()) { + if (int status = CPXXaddmipstarts(env, this_mip, 1, cplex_vars.size(), &beg, + cplex_vars.data(), cplex_vals.data(), + &effort, nullptr)) + processError(status, false, + "CPLEX Failed to add best model as MIP start"); } } Weight Cplex::getSolution(vector& solution, bool optimal) { - //Return incumbent in solution and lower bound on objective value as function value - //If passed bool "optimal" is true then cplex found optimal solution to its current model. + // Return incumbent in solution and lower bound on objective value as + // function value If passed bool "optimal" is true then cplex found optimal + // solution to its current model. - double objval {}; + double objval{}; int status; - if(status = CPXXgetobjval(env, mip, &objval)) - processError(status, false, "Problem getting mip objective value of incumbent"); - - if(optimal) { - if(objval > LB) - LB = objval; - } - else { - //set LB to current lower bound. Int weights ==> can round up. - double bstobjval {}; - if(status = CPXXgetbestobjval(env, mip, &bstobjval)) + if (status = CPXXgetobjval(env, mip, &objval)) + processError(status, false, + "Problem getting mip objective value of incumbent"); + + if (optimal) { + if (objval > LB) LB = objval; + } else { + // set LB to current lower bound. Int weights ==> can round up. + double bstobjval{}; + if (status = CPXXgetbestobjval(env, mip, &bstobjval)) processError(status, false, "Problem getting mip best objective value"); - //DEBUG - //cout << "Cplex getSolution objval = " << objval << " bstobjval = " << bstobjval - // << " current LB = " << LB << " integer wts = " << intWts << "\n"; + // DEBUG + // cout << "Cplex getSolution objval = " << objval << " bstobjval = " << + // bstobjval + // << " current LB = " << LB << " integer wts = " << intWts << + //"\n"; - if(bstobjval > LB) - LB = bstobjval; + if (bstobjval > LB) LB = bstobjval; } - //cout << "Updated LB = " << LB << "\n"; + // cout << "Updated LB = " << LB << "\n"; int nvars = CPXXgetnumcols(env, mip); - vector vals(nvars,0.0); + vector vals(nvars, 0.0); - if(nvars>0) { - status = CPXXgetx(env, mip, vals.data(), 0, nvars-1); - if(status) + if (nvars > 0) { + status = CPXXgetx(env, mip, vals.data(), 0, nvars - 1); + if (status) processError(status, false, "Problem getting mip variable assignments"); } @@ -892,17 +1097,17 @@ Weight Cplex::getSolution(vector& solution, bool optimal) { printf ("%d=%g, ", j+1, vals[j]); printf("]\n");*/ - for(size_t sftCls=0; sftCls < bvars.n_bvars(); sftCls++) { + for (size_t sftCls = 0; sftCls < bvars.n_bvars(); sftCls++) { auto ci = ex2in(bvars.varOfCls(sftCls)); - if(ci == var_Undef) + if (ci == var_Undef) solution.push_back(~bvars.litOfCls(sftCls)); else { auto val = vals[ci]; - if(val > 0.99) + if (val > 0.99) solution.push_back(bvars.litOfCls(sftCls)); else if (val < 0.01) solution.push_back(~bvars.litOfCls(sftCls)); - else { //found unset value + else { // found unset value solution.clear(); return -1; } @@ -911,23 +1116,46 @@ Weight Cplex::getSolution(vector& solution, bool optimal) { return LB; } +Weight Cplex::greedySolution(vector& solution, int denom) { + /* Round LP solution to greedy solution */ + vector lp_sol; + vector red_costs_dummy; + vector cplex_vars_dummy; + Weight greedyCost = + solve_lp_relaxation(lp_sol, red_costs_dummy, cplex_vars_dummy); + double bound = 1.0 / (double)denom; + solution.clear(); + + for (size_t sftCls = 0; sftCls < bvars.n_bvars(); sftCls++) { + auto blit = bvars.litOfCls(sftCls); + auto val = lp_sol[sftCls]; + // cout << " v: " << val << " " << endl; + if (val > bound) + solution.push_back(blit); + else + solution.push_back(~blit); + } + return greedyCost; +} + int Cplex::populate(double timeLimit, double gap) { int status; timeLimit = (timeLimit < 0) ? 1e+75 : timeLimit; - if(status = CPXXsetdblparam(env, CPX_PARAM_TILIM, timeLimit)) + if (status = CPXXsetdblparam(env, CPX_PARAM_TILIM, timeLimit)) processError(status, false, "Could not set CPLEX time limit for populate"); - if(status = CPXXsetdblparam(env, CPX_PARAM_SOLNPOOLAGAP, gap)) - processError(status, false, "Could not set CPLEX pool absolute gap in populate"); + if (status = CPXXsetdblparam(env, CPX_PARAM_SOLNPOOLAGAP, gap)) + processError(status, false, + "Could not set CPLEX pool absolute gap in populate"); - if(status = CPXXsetinfocallbackfunc (env, NULL, NULL)) - processError(status, false, "Failed to unset logging callback function in populate"); + if (status = CPXXsetinfocallbackfunc(env, NULL, NULL)) + processError(status, false, + "Failed to unset logging callback function in populate"); status = CPXXpopulate(env, mip); - if(status) - processError(status, false, "CPLEX Failed to Populate"); + if (status) processError(status, false, "CPLEX Failed to Populate"); int nsolns = CPXXgetsolnpoolnumsolns(env, mip); - if(nsolns == 0) + if (nsolns == 0) cout << "c Tried populate but cplex n Pool found no solutions\n"; return nsolns; } @@ -935,27 +1163,28 @@ int Cplex::populate(double timeLimit, double gap) { void Cplex::getPopulatedSolution(int i, vector& solution) { int nvars = CPXXgetnumcols(env, mip); int nsolns = CPXXgetsolnpoolnumsolns(env, mip); - vector vals(nvars,0.0); + vector vals(nvars, 0.0); solution.clear(); - if(nvars>0 && nsolns > i) { - auto status = CPXXgetsolnpoolx(env, mip, i, vals.data(), 0, nvars-1); - if(status) { - processError(status, false, "Problem getting solution from solution pool"); + if (nvars > 0 && nsolns > i) { + auto status = CPXXgetsolnpoolx(env, mip, i, vals.data(), 0, nvars - 1); + if (status) { + processError(status, false, + "Problem getting solution from solution pool"); return; } - for(size_t sftCls=0; sftCls < bvars.n_bvars(); sftCls++) { + for (size_t sftCls = 0; sftCls < bvars.n_bvars(); sftCls++) { auto ci = ex2in(bvars.varOfCls(sftCls)); - if(ci == var_Undef) + if (ci == var_Undef) solution.push_back(~bvars.litOfCls(sftCls)); else { auto val = vals[ci]; - if(val > 0.99) + if (val > 0.99) solution.push_back(bvars.litOfCls(sftCls)); else if (val < 0.01) solution.push_back(~bvars.litOfCls(sftCls)); - else { //found unset value + else { // found unset value solution.clear(); return; } diff --git a/maxhs/ifaces/Cplex.h b/maxhs/ifaces/Cplex.h old mode 100755 new mode 100644 index df6642f..dd4c42d --- a/maxhs/ifaces/Cplex.h +++ b/maxhs/ifaces/Cplex.h @@ -25,7 +25,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef CPLEX_H #define CPLEX_H -#include +#include #include #include @@ -37,168 +37,168 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "minisat/utils/System.h" #endif -#include "maxhs/core/MaxSolverTypes.h" #include "maxhs/core/Bvars.h" +#include "maxhs/core/MaxSolverTypes.h" +#include "maxhs/core/TotalizerManager.h" #include "maxhs/core/Wcnf.h" #ifdef GLUCOSE namespace Minisat = Glucose; #endif -using namespace Minisat; using std::vector; namespace MaxHS_Iface { - class Cplex { - public: - Cplex(Bvars& b, vector& ubModelSofts, - vector& ubModel, bool integerWts); - ~Cplex(); - - Weight solveBudget(vector& solution, double UB, double timeLimit) { - //return a setting of all bvars in solution. If the var is not - //mentioned in the cplex problem, return the literal that has - //zero weight in the solution. Also return the best lower bound found by Cplex. - //If the cost of "solution" == lower bound returned, this was an optimal cplex solution - stime = cpuTime(); - prevTotalTime = totalTime; - auto val = solve_(solution, UB, timeLimit); - totalTime += cpuTime() - stime; - stime = -1; - numSolves++; - return val; - } - - Weight solve(vector& solution, double UB) { - return solveBudget(solution, UB, -1); - } - - //try to populate cplex solutions using a time limit...don't return - //them return the number of solutions. - int populate(double timeLimit, double gap); - int populate(double gap) { return populate(-1, gap); } - void getPopulatedSolution(int, vector&); - //Other special purpose solvers - // Does the conjunction of lits cause the problem to exceed UB? - bool exceeds_bounds(vector& lits, Weight UB, double timeLimit); - bool exceeds_bounds(Lit l, Weight UB, double timeLimit) - { vector lits {l}; - return exceeds_bounds(lits, UB, timeLimit); } - - //solve the lp relaxation...return objective value of LP relaxation - //vector of solution weights and reduced costs indexed by soft clause index - //e.g., solution[i] = weight of blit of i-th soft clause. - Weight solve_lp_relaxation(vector& solution, vector& reduced_costs) { - auto startTime = cpuTime(); - auto val = solve_lp_relaxation_(solution, reduced_costs); - totalLPTime += cpuTime() - startTime; - numLPSolves++; - return val; - } - - bool is_valid() { return solver_valid; } - bool add_clausal_constraint(vector& con); - bool add_mutex_constraint(const SC_mx& mx); - bool var_in_cplex(Var v) { return ex2in(v) != var_Undef; } - bool lit_in_cplex(Lit l) { return var_in_cplex(var(l)); } - - //stats - int nCnstr() { return numConstraints; } - uint64_t totalCnstrSize() { return totalConstraintSize; } - int nNonCores() { return numNonCoreConstraints; } - uint64_t totalNonCore() { return totalNonCoreSize; } - - double solveTime() { return totalTime-prevTotalTime; } - double total_time() { - if (stime >= 0) - totalTime += cpuTime() - stime; - return totalTime; - } - - double total_lp_time() { return totalLPTime; } - int nSolves() { return numSolves; } - int nLPSolves() { return numLPSolves; } - - //public for call back access --- friend should work? - void processError(int status, bool terminal, const char *msg); - - protected: - Bvars& bvars; - vector& ubModelSofts; - vector& ubModel; - CPXENVptr env; - CPXLPptr mip; - CPXLPptr trial_mip; //use this mip to do lp-relaxation and trial hardening - bool solver_valid; - bool intWts; - double LB; - double absGap; - - //forced units (in external ordering) - vector exUnits; - void setExUnits(Lit l); - lbool getExUnits(Lit l); - - //main processing code - void addNewVar(Var ex); - Weight getSolution(vector &solution, bool optimal); - Weight solve_(vector& solution, double UB, double timeLimit); - Weight solve_lp_relaxation_(vector& solution, vector& reduced_costs); - - //internal cplex routines - void writeCplexModel(); - void useBestModelStart(CPXLPptr); - - //Stats - int numSolves; - double totalTime, stime, prevTotalTime; - int numLPSolves; - double totalLPTime; - - int numConstraints, numNonCoreConstraints; - uint64_t totalConstraintSize, totalNonCoreSize; - - - //External to Internal Mapping - vectorin2ex_map; - vectorex2in_map; - - void ensure_mapping(const Var ex); - void ensure_mapping(const Lit lt) { - ensure_mapping(var(lt)); - } - - Var in2ex(int v) const { - if(v >= (int) in2ex_map.size()) - return var_Undef; - else - return in2ex_map[v]; - } - - //In most applications every internal variable of the Cplex Solver - //is associated with an external literal on creation. - //So this array function is safe...i.e., won't add var_Undef to output - //vector. An array version of ex2in is typically not safe in this way. - //so is not provided. - void in2ex(const vec &from, vector &to) const { - to.clear(); - for(int i = 0; i < from.size(); i++) - to.push_back(in2ex(from[i])); - } - - int ex2in(Var v) const { - if(v >= static_cast(ex2in_map.size())) - return var_Undef; - else - return ex2in_map[v]; - } - - int ex2in(Lit lt) const { - return ex2in(var(lt)); - } - - }; -} //namespace +class Cplex { + public: + Cplex(Bvars& b, TotalizerManager* t, vector& ubModelSofts, + vector& ubModel, bool integerWts); + ~Cplex(); + + Weight solveBudget(vector& solution, double UB, double timeLimit) { + // return a setting of all bvars in solution. If the var is not + // mentioned in the cplex problem, return the literal that has + // zero weight in the solution. Also return the best lower bound found by + // Cplex. If the cost of "solution" == lower bound returned, this was an + // optimal cplex solution + auto stime = cpuTime(); + prevTotalTime = totalTime; + auto val = solve_(solution, UB, timeLimit); + totalTime += cpuTime() - stime; + numSolves++; + return val; + } + + Weight solve(vector& solution, double UB) { + return solveBudget(solution, UB, -1); + } + + // try to populate cplex solutions using a time limit...don't return + // them return the number of solutions. + int populate(double timeLimit, double gap); + int populate(double gap) { return populate(-1, gap); } + void getPopulatedSolution(int, vector&); + + // solve the lp relaxation...return objective value of LP relaxation + // vector of solution weights and reduced costs indexed by soft clause index + // e.g., solution[i] = weight of blit of i-th soft clause. + Weight solve_lp_relaxation(vector& solution, + vector& reduced_costs, + vector& cplex_vars) { + auto startTime = cpuTime(); + auto val = solve_lp_relaxation_(solution, reduced_costs, cplex_vars); + totalLPTime += cpuTime() - startTime; + numLPSolves++; + return val; + } + + bool is_valid() { return solver_valid; } + bool filter_by_units(vector& theCon); + void add_clausal_constraint(vector& con); + void add_processed_clause(const vector& theCon); + + bool add_mutex_constraint(const SC_mx& mx); + bool add_sum_constraint(vector&, int64_t); + bool var_in_cplex(Var v) { return ex2in(v) != var_Undef; } + bool lit_in_cplex(Lit l) { return var_in_cplex(var(l)); } + + // stats + int nCnstr() { return numConstraints; } + uint64_t totalCnstrSize() { return totalConstraintSize; } + int nNonCores() { return numNonCoreConstraints; } + uint64_t totalNonCore() { return totalNonCoreSize; } + + double solveTime() { return totalTime - prevTotalTime; } + double total_time() { return totalTime; } + + double total_lp_time() { return totalLPTime; } + int nSolves() { return numSolves; } + int nLPSolves() { return numLPSolves; } + + // public for call back access --- friend should work? + void processError(int status, bool terminal, const char* msg); + Weight greedySolution(vector& solution, int bound); + + protected: + Bvars& bvars; + TotalizerManager* totalizers; + + vector& ubModelSofts; + vector& ubModel; + + CPXENVptr env{nullptr}; + CPXLPptr mip{nullptr}; + CPXLPptr linp{ + nullptr}; // use this mip to do lp-relaxation and trial hardening + bool solver_valid; + bool intWts; + double LB{}; + double absGap; + + void add_tot_unit(Lit tout); + void add_tot_output_defn(Lit lt); + void add_tot_output_constraint(Lit lt); + // bool add_tot_atmost(Lit lt); + + // forced units (in external ordering) + vector exUnits; + void setExUnits(Lit l); + lbool getExUnits(Lit l); + + // main processing code + void addNewVar(Var ex); + Weight getSolution(vector& solution, bool optimal); + + Weight solve_(vector& solution, double UB, double timeLimit); + Weight solve_lp_relaxation_(vector& solution, + vector& reduced_costs, + vector& cplex_vars); + + // internal cplex routines + void writeCplexModel(); + void useBestModelStart(CPXLPptr); + + // Stats + int numSolves{}; + double totalTime{}, prevTotalTime{}; + int numLPSolves{}; + double totalLPTime{}; + + int numConstraints{}, numNonCoreConstraints{}; + uint64_t totalConstraintSize{0}, totalNonCoreSize{0}; + + // External to Internal Mapping + vector in2ex_map; + vector ex2in_map; + + void ensure_mapping(const Var ex); + void ensure_mapping(const Lit lt) { ensure_mapping(var(lt)); } + + Var in2ex(int v) const { + if (v >= (int)in2ex_map.size()) + return var_Undef; + else + return in2ex_map[v]; + } + + // In most applications every internal variable of the Cplex Solver + // is associated with an external literal on creation. + // So this array function is safe...i.e., won't add var_Undef to output + // vector. An array version of ex2in is typically not safe in this way. + // so is not provided. + void in2ex(const vec& from, vector& to) const { + to.clear(); + for (int i = 0; i < from.size(); i++) to.push_back(in2ex(from[i])); + } + + int ex2in(Var v) const { + if (v >= static_cast(ex2in_map.size())) + return var_Undef; + else + return ex2in_map[v]; + } + + int ex2in(Lit lt) const { return ex2in(var(lt)); } +}; +} // namespace MaxHS_Iface #endif - - diff --git a/maxhs/ifaces/GreedySolver.cc b/maxhs/ifaces/GreedySolver.cc old mode 100755 new mode 100644 index 617ba88..0f07275 --- a/maxhs/ifaces/GreedySolver.cc +++ b/maxhs/ifaces/GreedySolver.cc @@ -1,4 +1,4 @@ -/***********[greedySatSolver.cc] +/***********[greedysatsolver.cc] Copyright (c) 2012-2013 Jessica Davies, Fahiem Bacchus Permission is hereby granted, free of charge, to any person obtaining a @@ -25,25 +25,24 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* Interface to minisat. */ -#include #include #include +#include #ifdef GLUCOSE #include "glucose/core/SolverTypes.h" -#include "glucose/utils/System.h" #include "glucose/mtl/Heap.h" +#include "glucose/utils/System.h" #else #include "minisat/core/SolverTypes.h" -#include "minisat/utils/System.h" #include "minisat/mtl/Heap.h" +#include "minisat/utils/System.h" #endif - #include "maxhs/ifaces/GreedySolver.h" +#include "maxhs/utils/Params.h" #include "maxhs/utils/io.h" - using namespace MaxHS_Iface; #ifdef GLUCOSE @@ -52,71 +51,66 @@ namespace Minisat = Glucose; using namespace Minisat; -GreedySolver::GreedySolver(Bvars& b) : - bvars (b), - nSatCores {0}, - dyn_nSatCores {0}, - heap_lt {bvars, dyn_satCount}, - sftcls_heap {heap_lt}, - n_core_mxes {0}, - solves {0}, - totalTime {0}, - prevTotalTime {0}, - stime{-1} - {} - -GreedySolver::~GreedySolver() { } +GreedySolver::GreedySolver(Bvars& b, TotalizerManager* t) + : bvars(b), + totalizers(t), + nSatCores{0}, + dyn_nSatCores{0}, + heap_lt{bvars, dyn_satCount}, + sftcls_heap{heap_lt}, + tins_heap{heap_lt}, + n_core_mxes{0}, + solves{0}, + totalTime{0}, + prevTotalTime{0}, + stime{-1} {} void GreedySolver::ensureSC(int clsi) { - //make sure all data structures are initialized for soft clause clsi - if(clsi >= static_cast(clsVal.size())) - clsVal.resize(clsi+1, l_Undef); - if(clsi >= static_cast(init_sc.size())) - init_sc.resize(clsi+1, 0); - if(clsi >= static_cast(occurList.size())) - occurList.resize(clsi+1,{}); - if(clsi >= static_cast(core_mx_num.size())) - core_mx_num.resize(clsi+1, -1); + // make sure all data structures are initialized for soft clause clsi + if (clsi >= static_cast(clsVal.size())) clsVal.resize(clsi + 1, l_Undef); + if (clsi >= static_cast(init_sc.size())) init_sc.resize(clsi + 1, 0); + if (clsi >= static_cast(occurList.size())) + occurList.resize(clsi + 1, {}); + if (clsi >= static_cast(core_mx_num.size())) + core_mx_num.resize(clsi + 1, -1); } void GreedySolver::ensureCore(int corei) { - //make sure all data structures are initialized for core corei - if(corei >= static_cast(cores.size())) - cores.resize(corei+1); - if(corei >= static_cast(coreIsSat.size())) - coreIsSat.resize(corei+1, 0); + // make sure all data structures are initialized for core corei + if (corei >= static_cast(cores.size())) cores.resize(corei + 1); + if (corei >= static_cast(coreIsSat.size())) + coreIsSat.resize(corei + 1, 0); } bool GreedySolver::addClause(const vector& lts) { - /* Internally the greedy solver stores cores and does it processing with soft clause indicies - This is a more compact range than the b-vars */ - //cout << "Adding " << lts << " to greedy solver\n"; - for(auto l: lts) - if(!bvars.isBvar(l) - || (lts.size() > 1 && !bvars.isCore(l))) { - cout << "c ERROR added non-unit non-core or unit non-bvar constraint to greedy solver: " + /* Internally the greedy solver stores cores and does it processing with soft + clause indicies This is a more compact range than the b-vars */ + for (auto l : lts) { + if (!bvars.isBvar(l) || (lts.size() > 1 && !bvars.isCore(l))) { + cout << "c ERROR added non-unit non-core or unit non-bvar constraint to " + "greedy solver: " << lts << "\n"; return false; } - if(lts.size() == 1) - return inputUnit(lts[0]); - + } + if (lts.size() == 1) return inputUnit(lts[0]); vector c; - for(auto l: lts) { + for (auto l : lts) { auto clsi = bvars.clsIndex(l); ensureSC(clsi); - if(clsVal[clsi] == l_True) { - //remove warning---can often happen as new b-vars are forced inbetween computing - //the greedy clause and adding it to the greedy solver. - //cout << "c WARNING added already satisfied constraint to greedy solver\n"; - //cout << lts << " soft cls " << l << "\n"; - return true; //ignore this core. + if (clsVal[clsi] == l_True) { + // remove warning---can often happen as new b-vars are forced inbetween + // computing the greedy clause and adding it to the greedy solver. cout << + // "c WARNING added already satisfied constraint to greedy solver\n"; cout + // << lts << " soft cls " << l << "\n"; + return true; // ignore this core. } - if(clsVal[clsi] != l_False) //if soft clauses is hard it cannot be used to satisfy this core. + if (clsVal[clsi] != l_False) // if soft clauses is hard it cannot be used + // to satisfy this core. c.push_back(clsi); } - if(c.size() == 0) { + if (c.size() == 0) { cout << "c ERROR added unsatisfiable core to greedy solver\n"; cout << lts << "\n"; return false; @@ -124,7 +118,7 @@ bool GreedySolver::addClause(const vector& lts) { auto corei = cores.size(); ensureCore(corei); - for(auto clsi : c) { + for (auto clsi : c) { occurList[clsi].push_back(corei); init_sc[clsi] += 1; } @@ -139,69 +133,55 @@ lbool GreedySolver::blit_curval(Lit b) { } bool GreedySolver::addMutexConstraint(const SC_mx& mx) { - /* constrain the greedy solver to respect the mutex mx. Note - the mx is only passed to the greedy if it has no encoding lit. - This means that all soft-clause-lits in the mx correspond to b-lits. - */ - if(mx.encoding_lit() != lit_Undef) { - cout << "c ERROR greedysolver passed mx that has already been transformed:\n" - << mx << "\n"; - return false; - } - - if(mx.soft_clause_lits().size() <= 1) { + /* constrain the greedy solver to respect the mutex mx. */ + if (mx.soft_clause_lits().size() <= 1) { cout << "c ERROR greedysolver passed mx that is too short (<= 1) in size:\n" << mx << "\n"; return false; } - //preprocess mutex - auto mx_lits {mx.soft_clause_lits()}; //at most one of these lits can be true - if(!mx.is_core()) - for(size_t i = 0; i < mx_lits.size(); i++) - mx_lits[i] = ~mx_lits[i]; + // preprocess mutex + auto mx_lits{mx.soft_clause_lits()}; // at most one of these lits can be true + if (!mx.is_core()) + for (size_t i = 0; i < mx_lits.size(); i++) mx_lits[i] = ~mx_lits[i]; - bool oneTrue {false}; + bool oneTrue{false}; size_t cur_size, examine; - for(cur_size = examine = 0; examine < mx_lits.size(); examine++) { + for (cur_size = examine = 0; examine < mx_lits.size(); examine++) { lbool val = blit_curval(mx_lits[examine]); - if(val == l_True) { - if(oneTrue) { - cout << "c ERROR: Greedy passed mutex with more than one lit forced to be true\n" + if (val == l_True) { + if (oneTrue) { + cout << "c ERROR: Greedy passed mutex with more than one lit forced to " + "be true\n" << "c " << mx << "\n"; return false; } - oneTrue=true; - } - else if(val == l_False) + oneTrue = true; + } else if (val == l_False) continue; else - //keep the lit only if it is unvalued + // keep the lit only if it is unvalued mx_lits[cur_size++] = mx_lits[examine]; } mx_lits.resize(cur_size); - if(mx_lits.size() == 0) - return true; + if (mx_lits.size() == 0) return true; - if(oneTrue) { //force remaining to false - for(auto l : mx_lits) - inputUnit(~l); + if (oneTrue) { // force remaining to false + for (auto l : mx_lits) inputUnit(~l); return true; } - //Otherwise add the constraint - if(mx.is_core()) { - for(auto l : mx_lits) - core_mx_num[bvars.clsIndex(l)] = n_core_mxes; + // Otherwise add the constraint + if (mx.is_core()) { + for (auto l : mx_lits) core_mx_num[bvars.clsIndex(l)] = n_core_mxes; n_core_mxes++; - } - else { + } else { ncore_mxes.push_back(std::move(mx_lits)); } return true; } -bool GreedySolver::inputUnit(Lit lt) { +bool GreedySolver::inputUnit(Lit lt) { /* Special function to deal with input of unit bvars that have been discovered to have a forced value by the user of GreedySolver. @@ -223,15 +203,15 @@ bool GreedySolver::inputUnit(Lit lt) { int clsi = bvars.clsIndex(lt); ensureSC(clsi); lbool clsival = bvars.isCore(lt) ? l_True : l_False; - if(clsVal[clsi] == clsival) - return true; //redundant unit; - if(clsVal[clsi] != l_Undef) { - cout << "c ERROR adding unit whose opposite has already been added to or inferred by greedy solver \n"; + if (clsVal[clsi] == clsival) return true; // redundant unit; + if (clsVal[clsi] != l_Undef) { + cout << "c ERROR adding unit whose opposite has already been added to or " + "inferred by greedy solver \n"; cout << lt << "\n"; return false; } clsVal[clsi] = clsival; - if(clsival == l_True) + if (clsival == l_True) processTrueB(clsi); else processFalseB(clsi); @@ -239,55 +219,53 @@ bool GreedySolver::inputUnit(Lit lt) { } void GreedySolver::processTrueB(int clsi) { - //soft clause clsi has become relaxed. This satisfies some cores. - for(auto corei : occurList[clsi]) { - if(coreIsSat[corei]) - continue; - for(auto ci : cores[corei]) - if(ci != clsi) { - init_sc[ci] -= 1; //decrement sat count + // soft clause clsi has become relaxed. This satisfies some cores. + for (auto corei : occurList[clsi]) { + if (coreIsSat[corei]) continue; + for (auto ci : cores[corei]) + if (ci != clsi) { + init_sc[ci] -= 1; // decrement sat count auto& olist = occurList[ci]; - size_t j {0}; - for(size_t i=0; i < olist.size(); i++) - if(olist[i] != corei) - olist[j++] = olist[i]; + size_t j{0}; + for (size_t i = 0; i < olist.size(); i++) + if (olist[i] != corei) olist[j++] = olist[i]; olist.resize(j); } coreIsSat[corei] = 1; ++nSatCores; } vector tmp; - std::swap(occurList[clsi], tmp); //soft clause clsi now has a sat count of zero. + std::swap(occurList[clsi], + tmp); // soft clause clsi now has a sat count of zero. init_sc[clsi] = 0; } void GreedySolver::processFalseB(int clsi) { - //soft clause clsi has become hard. This reduces some cores. + // soft clause clsi has become hard. This reduces some cores. vector newUnits; - for(auto corei: occurList[clsi]) { - if(coreIsSat[corei]) - continue; + for (auto corei : occurList[clsi]) { + if (coreIsSat[corei]) continue; auto& core = cores[corei]; - size_t j {0}; - for(size_t i = 0; i < core.size(); i++) - if(core[i] != clsi) - core[j++] = core[i]; + size_t j{0}; + for (size_t i = 0; i < core.size(); i++) + if (core[i] != clsi) core[j++] = core[i]; core.resize(j); - if(core.size() == 1) {//new positive unit. - //cout << "Found new unit core " << bvars.litOfCls(core[0]) << "\n"; + if (core.size() == 1) { // new positive unit. + // cout << "Found new unit core " << bvars.litOfCls(core[0]) << "\n"; newUnits.push_back(core[0]); } } vector tmp; - std::swap(occurList[clsi], tmp); //soft clause clsi now has a sat count of zero. + std::swap(occurList[clsi], + tmp); // soft clause clsi now has a sat count of zero. init_sc[clsi] = 0; - for(auto sci: newUnits) - inputUnit(bvars.litOfCls(sci)); //This cannot generate any new units + for (auto sci : newUnits) + inputUnit(bvars.litOfCls(sci)); // This cannot generate any new units } vector GreedySolver::solve_() { - //Compute greedy solution. + // Compute greedy solution. solution.clear(); dyn_satCount = init_sc; dyn_coreIsSat = coreIsSat; @@ -297,11 +275,11 @@ vector GreedySolver::solve_() { core_mx_in_solution.resize(n_core_mxes, false); solution_update_ncore_mxes(); + //solution_update_totalizers(); sftcls_heap.clear(); - for(size_t i = 0; i < dyn_satCount.size(); i++) - if(dyn_satCount[i] > 0) - sftcls_heap.insert(i); + for (size_t i = 0; i < dyn_satCount.size(); i++) + if (dyn_satCount[i] > 0) sftcls_heap.insert(i); /*//DEBUG if(!sftcls_heap.check()) { @@ -320,31 +298,34 @@ vector GreedySolver::solve_() { } //DEBUG*/ - while(dyn_nSatCores < static_cast(cores.size()) && !sftcls_heap.empty()) { + while (dyn_nSatCores < static_cast(cores.size()) && + !sftcls_heap.empty()) { auto next = sftcls_heap.removeMin(); /*//DEBUG - cout << "Heap item " << heap_n << ". clsi = " << next << " dyn_satCount = " << dyn_satCount[next] - << " wt = " << bvars.wtNcls(next) << " dyn_nSatCores = " << dyn_nSatCores + cout << "Heap item " << heap_n << ". clsi = " << next << " dyn_satCount = " + << dyn_satCount[next] + << " wt = " << bvars.wtNcls(next) << " dyn_nSatCores = " << + dyn_nSatCores << " total number of cores " << cores.size() << "\n"; heap_n++; //DEBUG*/ - if(core_mx_num[next] >= 0 && core_mx_in_solution[core_mx_num[next]]) - continue; //only one soft clause in a core mx can be in the solution. + if (core_mx_num[next] >= 0 && core_mx_in_solution[core_mx_num[next]]) + continue; // only one soft clause in a core mx can be in the solution. /*//DEBUG if(clsVal[next] != l_Undef) cout << "Heap item #" << heap_n - << " Greedy value next soft clause " << bvars.litOfCls(next) << " from heap not unvalued\n" + << " Greedy value next soft clause " << bvars.litOfCls(next) << " + from heap not unvalued\n" << " value = " << clsVal[next] << " dyn_satCount = " << dyn_satCount[next] << " dyn_nSatCores = " << dyn_nSatCores << "\n"; //DEBUG*/ - - //cout << "Processing blit " << bvars.litOfCls(next) << "\n"; - //cout << "dyn_nSatCores = " << dyn_nSatCores << "\n"; + // cout << "Processing blit " << bvars.litOfCls(next) << "\n"; + // cout << "dyn_nSatCores = " << dyn_nSatCores << "\n"; add_sc_to_soln(next); } @@ -354,46 +335,47 @@ vector GreedySolver::solve_() { cout << "]\n";*/ vector tmp; - for(size_t i = 0; i < static_cast(bvars.n_bvars()); i++) - if(i < clsVal.size() && clsVal[i] == l_True) + for (size_t i = 0; i < static_cast(bvars.n_bvars()); i++) + if (i < clsVal.size() && clsVal[i] == l_True) tmp.push_back(bvars.litOfCls(i)); else - tmp.push_back(~bvars.litOfCls(i)); //harden by default + tmp.push_back(~bvars.litOfCls(i)); // harden by default - for(auto clsi : solution) - tmp[clsi] = bvars.litOfCls(clsi); + for (auto clsi : solution) tmp[clsi] = bvars.litOfCls(clsi); return tmp; } void GreedySolver::add_sc_to_soln(int clsi) { - //add soft clause index to solution and update dynamic data - //for greedy solver. Return vector of soft clauses whose - //satCount's modified. + // add soft clause index to solution and update dynamic data + // for greedy solver. Return vector of soft clauses whose + // satCount's modified. solution.push_back(clsi); /*//DEBUG if(clsVal[clsi] != l_Undef) - cout << "Added valued soft clause to solution " << bvars.litOfCls(clsi) << " value " << clsVal[clsi] << "\n"; + cout << "Added valued soft clause to solution " << bvars.litOfCls(clsi) << " +value " << clsVal[clsi] << "\n"; //DEBUG*/ auto mx_num = core_mx_num[clsi]; - if(mx_num >= 0) { - if(core_mx_in_solution[mx_num]) - cout << "c ERROR greedy solver tried to add more than one soft clause of a core mx to solution\n"; + if (mx_num >= 0) { + if (core_mx_in_solution[mx_num]) + cout << "c ERROR greedy solver tried to add more than one soft clause of " + "a core mx to solution\n"; core_mx_in_solution[mx_num] = true; } - for(auto corei : occurList[clsi]) - if(dyn_coreIsSat[corei]==0) { + for (auto corei : occurList[clsi]) + if (dyn_coreIsSat[corei] == 0) { dyn_coreIsSat[corei] = 1; ++dyn_nSatCores; auto core = cores[corei]; - for(auto ci: core) { //update dyn_satCount of soft clauses in core. + for (auto ci : core) { // update dyn_satCount of soft clauses in core. dyn_satCount[ci] -= 1; - if(sftcls_heap.inHeap(ci)) { + if (sftcls_heap.inHeap(ci)) { sftcls_heap.increase(ci); } /*//DEBUG @@ -421,22 +403,22 @@ void GreedySolver::add_sc_to_soln(int clsi) { } void GreedySolver::solution_update_ncore_mxes() { - //Process non-cores. All of the soft clauses except 1 must be set to l_True. + // Process non-cores. All of the soft clauses except 1 must be set to l_True. /*//DEBUG int n_added_to_soln {0}; //DEBUG*/ - for(auto ncore_mx : ncore_mxes) { - int trueCount {0}; - bool has_unvalued {false}; - int minSatCount {-1}; - int minSatClsIndex {0}; + for (auto ncore_mx : ncore_mxes) { + int trueCount{0}; + bool has_unvalued{false}; + int minSatCount{-1}; + int minSatClsIndex{0}; - for(auto b : ncore_mx) { - if(blit_curval(b) == l_True) + for (auto b : ncore_mx) { + if (blit_curval(b) == l_True) trueCount++; - else if(blit_curval(b) == l_Undef) { + else if (blit_curval(b) == l_Undef) { has_unvalued = true; /*//DEBUG @@ -446,54 +428,85 @@ void GreedySolver::solution_update_ncore_mxes() { << "\n"; //DEBUG*/ - if(minSatCount < 0 || minSatCount > dyn_satCount[bvars.clsIndex(b)]) { + if (minSatCount < 0 || minSatCount > dyn_satCount[bvars.clsIndex(b)]) { minSatCount = dyn_satCount[bvars.clsIndex(b)]; minSatClsIndex = bvars.clsIndex(b); } } } - if(trueCount > 1) - cout << "c ERROR: Greedy encountered mutex with more than one lit true during solving\n" + if (trueCount > 1) + cout << "c ERROR: Greedy encountered mutex with more than one lit true " + "during solving\n" << "c " << ncore_mx << "\n"; - else if(trueCount && has_unvalued) { - //add unvalued soft clauses to solution (a non-core lit is true, so - //all other non-cores must be false). non-core = false ==> corresponding - //core blit is true and part of greedy solution. - for(auto b : ncore_mx) - if(blit_curval(b) == l_Undef) { + else if (trueCount && has_unvalued) { + // add unvalued soft clauses to solution (a non-core lit is true, so + // all other non-cores must be false). non-core = false ==> corresponding + // core blit is true and part of greedy solution. + for (auto b : ncore_mx) + if (blit_curval(b) == l_Undef) { add_sc_to_soln(bvars.clsIndex(b)); - //n_added_to_soln++; + // n_added_to_soln++; } - } - else if(has_unvalued) { - //add all unvalued softs except the first soft achieving minSatCount to solution - for(auto b : ncore_mx) - if(blit_curval(b) == l_Undef) { + } else if (has_unvalued) { + // add all unvalued softs except the first soft achieving minSatCount to + // solution + for (auto b : ncore_mx) + if (blit_curval(b) == l_Undef) { auto clsi = bvars.clsIndex(b); - if(clsi != minSatClsIndex) { + if (clsi != minSatClsIndex) { add_sc_to_soln(clsi); - //n_added_to_soln++; + // n_added_to_soln++; } } } } } +void GreedySolver::solution_update_totalizers() { + // Process true totalizer outputs + // For each toplevel totalizer get is input blits. + // count true outputs and #true inputs known to greedy solver + // If k is the difference then select k inputs of the + // totalizer and add them to the solution. + vector tots{totalizers->get_top_level_tidxes()}; + int forced_tins{0}; + + for (auto tidx : tots) { + int out_true = totalizers->get_n_true_outs(tidx); + vector tins{totalizers->get_ilits_from_tidx(tidx)}; + int in_true = 0; + for (auto b : tins) + if (blit_curval(b) == l_True) ++in_true; + if (in_true >= out_true) continue; + + tins_heap.clear(); + for (auto b : tins) + if (blit_curval(b) == l_Undef) tins_heap.insert(bvars.clsIndex(b)); + + while (!tins_heap.empty() && in_true < out_true) { + auto clsi = tins_heap.removeMin(); + add_sc_to_soln(clsi), ++forced_tins; + ++in_true; + } + } + + if (forced_tins && params.verbosity > 0) + cout << "c added " << forced_tins << " forced tins to greedy soln\n"; +} + void GreedySolver::printDS() { cout << "GreedySolver Data Structures:\n"; - for(size_t i=0; i < cores.size(); ++i) { + for (size_t i = 0; i < cores.size(); ++i) { cout << "Core #" << i << ": ["; - for(auto sc: cores[i]) - cout << bvars.litOfCls(sc) << ", "; + for (auto sc : cores[i]) cout << bvars.litOfCls(sc) << ", "; cout << "] (" << cores[i].size() << ") "; cout << " core is sat = " << ((coreIsSat[i] == 1) ? 1 : 0) << "\n"; } cout << "Number of satisfied cores = " << nSatCores << "\n"; - for(size_t i=0; i < occurList.size(); ++i) { + for (size_t i = 0; i < occurList.size(); ++i) { cout << "Lit " << bvars.litOfCls(i) << ": ["; - for(auto ci: occurList[i]) - cout << ci << ", "; + for (auto ci : occurList[i]) cout << ci << ", "; cout << "] (" << occurList[i].size() << ") "; cout << " initial sat count = " << init_sc[i]; cout << " initial value = " << clsVal[i]; diff --git a/maxhs/ifaces/GreedySolver.h b/maxhs/ifaces/GreedySolver.h old mode 100755 new mode 100644 index 515f06d..69507fc --- a/maxhs/ifaces/GreedySolver.h +++ b/maxhs/ifaces/GreedySolver.h @@ -35,93 +35,97 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef GREEDYSOLVER_H #define GREEDYSOLVER_H -#include "maxhs/core/MaxSolverTypes.h" #include "maxhs/core/Bvars.h" +#include "maxhs/core/MaxSolverTypes.h" +#include "maxhs/core/TotalizerManager.h" #include "maxhs/core/Wcnf.h" namespace MaxHS_Iface { - class GreedySolver { - public: - GreedySolver(Bvars& b); - ~GreedySolver(); - //external interface is in terms of b-literals - bool addClause(const vector& lts); //should be core - bool addMutexConstraint(const SC_mx& mx); - - vector solve() { - stime = cpuTime(); - prevTotalTime = totalTime; - auto tmp = solve_(); - totalTime += cpuTime() - stime; - solves++; - return tmp; +class GreedySolver { + public: + GreedySolver(Bvars& b, TotalizerManager* t); + // external interface is in terms of b-literals + bool addClause(const vector& lts); // should be core + bool addMutexConstraint(const SC_mx& mx); + + vector solve() { + stime = cpuTime(); + prevTotalTime = totalTime; + auto tmp = solve_(); + totalTime += cpuTime() - stime; + solves++; + return tmp; + } + + double solveTime() { return totalTime - prevTotalTime; } + double total_time() { return totalTime; } + int nSolves() { return (int)solves; } + void printDS(); // debugging routine + + protected: + Bvars& bvars; + TotalizerManager* totalizers; + + void ensureSC(int); + void ensureCore(int); + bool inputUnit(Lit); // units can be non-cores (but must be blits) + void processTrueB(int); + void processFalseB(int); + vector solve_(); + + Weight clsWt(int i) { return bvars.wtNcls(i); } + vector init_sc; // init_sc[ithSoftclause] = number of cores satisfied by + // ith soft clause + vector clsVal; // clsVal[soft_clause_index] == l_True if soft clause + // has been falsfied (and thus some cores satisfied) + vector> + cores; // input cores; stored as sets of soft clause indices + vector> + occurList; // map from soft clause indices to input cores they appear in. + vector coreIsSat; // mark satisifed clauses + int nSatCores; // count how many satisfied cores via units. + + // Dynamic data used during greedy solver + vector solution; + vector dyn_satCount; // dynamic count of cores satisfied by soft clause + vector dyn_coreIsSat; // dynamic mark of satisfied clauses + int dyn_nSatCores; // dynamic count of satisfied cores + + struct ClsOrderLt { + // minisat heap is a min-heap. Want the "minimum" soft clause to + // satisfies the most cores for the least wt. I.e., + // x < y if satcount(x)/wt(x) > satcount(y)/wt(y) + // assume that weights are non-zero. + bool operator()(int x, int y) const { + double x_score = sc[x] * 1.0 / bvars.wtNcls(x); + double y_score = sc[y] * 1.0 / bvars.wtNcls(y); + if (x_score >= y_score) return true; + return false; } - - double solveTime() { return totalTime-prevTotalTime; } - double total_time() { return totalTime; } - int nSolves() { return (int) solves; } - void printDS(); //debugging routine - - - protected: - void ensureSC(int); - void ensureCore(int); - bool inputUnit(Lit); //units can be non-cores (but must be blits) - void processTrueB(int); - void processFalseB(int); - vector solve_(); - - Weight clsWt(int i) { return bvars.wtNcls(i); } Bvars& bvars; - vector init_sc; //init_sc[ithSoftclause] = number of cores satisfied by ith soft clause - vector clsVal; //clsVal[soft_clause_index] == l_True if soft clause has been - //falsfied (and thus some cores satisfied) - vector> cores; //input cores; stored as sets of soft clause indices - vector> occurList; //map from soft clause indices to input cores they appear in. - vector coreIsSat; //mark satisifed clauses - int nSatCores; //count how many satisfied cores via units. - - //Dynamic data used during greedy solver - vector solution; - vector dyn_satCount; //dynamic count of cores satisfied by soft clause - vector dyn_coreIsSat; //dynamic mark of satisfied clauses - int dyn_nSatCores; //dynamic count of satisfied cores - - struct ClsOrderLt { - //minisat heap is a min-heap. Want the "minimum" soft clause to - //satisfies the most cores for the least wt. I.e., - //x < y if satcount(x)/wt(x) > satcount(y)/wt(y) - //assume that weights are non-zero. - bool operator() (int x, int y) const { - double x_score = sc[x]*1.0/bvars.wtNcls(x); - double y_score = sc[y]*1.0/bvars.wtNcls(y); - if(x_score >= y_score) - return true; - return false; - } - Bvars& bvars; - vector& sc; - ClsOrderLt(Bvars& b, vector& c) : bvars (b), sc (c) {} - }; - ClsOrderLt heap_lt; - Heap sftcls_heap; - void add_sc_to_soln(int soft_clause_index); - - //Mutex processing - vector> ncore_mxes; //store the non-core mutexes - int n_core_mxes; //number of core mutexes - vector core_mx_num; //map from soft clause index to core-mx the soft clause - //is in. -1 if not in a core-mx; - vector core_mx_in_solution; - lbool blit_curval(Lit b); - void solution_update_ncore_mxes(); - - //stats - int solves; - double totalTime, prevTotalTime, stime; + vector& sc; + ClsOrderLt(Bvars& b, vector& c) : bvars(b), sc(c) {} }; - - -} //end namespace - -#endif + ClsOrderLt heap_lt; + Heap sftcls_heap; + Heap tins_heap; + void add_sc_to_soln(int soft_clause_index); + + // Mutex processing + vector> ncore_mxes; // store the non-core mutexes + int n_core_mxes; // number of core mutexes + vector core_mx_num; // map from soft clause index to core-mx the soft + // clause is in. -1 if not in a core-mx; + vector core_mx_in_solution; + lbool blit_curval(Lit b); + void solution_update_ncore_mxes(); + void solution_update_totalizers(); + + // stats + int solves; + double totalTime, prevTotalTime, stime; +}; + +} // namespace MaxHS_Iface + +#endif diff --git a/maxhs/ifaces/SatSolver.cc b/maxhs/ifaces/SatSolver.cc index 0e954b0..41b0748 100644 --- a/maxhs/ifaces/SatSolver.cc +++ b/maxhs/ifaces/SatSolver.cc @@ -32,83 +32,95 @@ so that the external theory can be fed to the sat solver in parts with generating huge gaps in the variable numbering. - This also means however, that not all external variables will be given a value - by a found sat model. + This also means however, that not all external variables will be given a + value by a found sat model. */ #include "maxhs/ifaces/SatSolver.h" namespace MaxHS_Iface { - lbool SatSolver::solveBudget(const vector& assumps, vector& conflict, - int64_t confBudget, int64_t propBudget) { - /******************************************************************* - Solve with assumptions. Track statistics. - can set conflict and propagation budgets (-1 = no budget) - Return l_true/l_false/l_Undef formula is sat/unsat/budget-exceeded - - If unsat put conflict clause into conflict (mapped to external numbering) - if sat, the "modelValue" function allow access to satisfying assignment - *******************************************************************/ - stime = cpuTime(); - prevTotalTime = totalTime; - lbool val = solve_(assumps, conflict, confBudget, propBudget); - totalTime += cpuTime() - stime; - stime = -1; - solves++; - return val; - } +lbool SatSolver::solveBudget(const vector& assumps, vector& conflict, + int64_t confBudget, int64_t propBudget) { + /******************************************************************* +Solve with assumptions. Track statistics. +can set conflict and propagation budgets (-1 = no budget) +Return l_true/l_false/l_Undef formula is sat/unsat/budget-exceeded - lbool SatSolver::solveBudget(const vector& assumps, vector& conflict, double timeLimit) { - //minisat's runtime is not very predictable. So if no complex solves have been done before - //odds are the time taken will far from the timeLimit. - //The accuracy of the timeLimit gets better as more solves are executed. - int64_t propBudget, props; - props = getProps(); //returns total number of props done by solver - bool didTrial {false}; - - if(solves > 0 && props > 0) { - propBudget = props/totalTime * timeLimit; - } - else { - propBudget = 1024*1024*10; - didTrial = true; - } - stime = cpuTime(); - prevTotalTime = totalTime; - lbool val = solve_(assumps, conflict, -1, propBudget); - solves++; - double solvetime1 = cpuTime() - stime; - if(didTrial && val == l_Undef && solvetime1 < timeLimit*.60) { - auto moreProps = int64_t((getProps()-props)/solvetime1 * timeLimit - propBudget); - if (moreProps > propBudget*0.5) { - val = solve_(assumps, conflict, -1, moreProps); - } - solves++; - } - totalTime += cpuTime() - stime; - stime = -1; - return val; - } +If unsat put conflict clause into conflict (mapped to external numbering) +if sat, the "modelValue" function allow access to satisfying assignment + *******************************************************************/ + stime = cpuTime(); + prevTotalTime = totalTime; + + // cout << "SatSolver confBudget = " << confBudget << " propBudget " << + // propBudget << "\n"; + + lbool val = solve_(assumps, conflict, confBudget, propBudget); + totalTime += cpuTime() - stime; + stime = -1; + solves++; + return val; +} - lbool SatSolver::relaxSolve(const vector& assumps, const vector& branchLits, double timeLimit) { - //Do relaxed solve (where sat solver must initially branch on branchLits - int64_t propBudget, props; - if(timeLimit <= 0) - propBudget = -1; - else { - props = getProps(); //returns total number of props done by solver - if(solves > 0 && props > 0) - propBudget = props/totalTime * timeLimit; - else - propBudget = 1024*1024*10; - } - stime = cpuTime(); - prevTotalTime = totalTime; - auto val = relaxSolve_(assumps, branchLits, propBudget); - solves++; - totalTime += cpuTime() - stime; - stime = -1; - return val; +lbool SatSolver::solveBudget(const vector& assumps, vector& conflict, + double timeLimit) { + // minisat's runtime is not very predictable. So if no complex solves have + // been done before odds are the time taken will far from the timeLimit. The + // accuracy of the timeLimit gets better as more solves are executed. + int64_t propBudget, props; + props = getProps(); // returns total number of props done by solver + bool didTrial{false}; + + if (solves > 0 && props > 0) { + propBudget = props / totalTime * timeLimit; + } else { + propBudget = 1024 * 1024 * 10; + didTrial = true; + } + stime = cpuTime(); + prevTotalTime = totalTime; + + // cout << "SatSolver 1. timelimit = " << timeLimit << " confBudget = -1 " << + // propBudget << "\n"; + + lbool val = solve_(assumps, conflict, -1, propBudget); + solves++; + double solvetime1 = cpuTime() - stime; + if (didTrial && val == l_Undef && solvetime1 < timeLimit * .60) { + auto moreProps = + int64_t((getProps() - props) / solvetime1 * timeLimit - propBudget); + if (moreProps > propBudget * 0.5) { + val = solve_(assumps, conflict, -1, moreProps); + // cout << "SatSolver 2. timelimit = " << timeLimit << " confBudget = -1" + // << " propBudget " << moreProps << "\n"; } -} //namespace + solves++; + } + totalTime += cpuTime() - stime; + stime = -1; + return val; +} + +lbool SatSolver::relaxSolve(const vector& assumps, + const vector& branchLits, double timeLimit) { + // Do relaxed solve (where sat solver must initially branch on branchLits + int64_t propBudget, props; + if (timeLimit <= 0) + propBudget = -1; + else { + props = getProps(); // returns total number of props done by solver + if (solves > 0 && props > 0) + propBudget = props / totalTime * timeLimit; + else + propBudget = 1024 * 1024 * 10; + } + stime = cpuTime(); + prevTotalTime = totalTime; + auto val = relaxSolve_(assumps, branchLits, propBudget); + solves++; + totalTime += cpuTime() - stime; + stime = -1; + return val; +} +} // namespace MaxHS_Iface diff --git a/maxhs/ifaces/SatSolver.h b/maxhs/ifaces/SatSolver.h old mode 100755 new mode 100644 index 5e4d893..4f982d4 --- a/maxhs/ifaces/SatSolver.h +++ b/maxhs/ifaces/SatSolver.h @@ -1,6 +1,6 @@ /***********[SatSolver.h] Copyright (c) 2012-2013 Jessica Davies, Fahiem Bacchus - + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including @@ -8,10 +8,10 @@ distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -19,38 +19,38 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -***********/ + + ***********/ /* Generic interface for maxhs to use a satsolver. Access to a particular sat solver is implemented by defining a derived class. - + The interface maintains a mapping between interally numbered variables and externally numbered variables. This is to ensure that the internal variables are consecutively ordered, so that the external theory can be fed to the sat solver in parts with generating huge gaps in the variable numbering. - - This also means however, that not all external variables will be given a value - by a found sat model. - -*/ + + This also means however, that not all external variables will be given a + value by a found sat model. + + */ #ifndef IFACESAT_H #define IFACESAT_H +#include #include -#include #include #ifdef GLUCOSE -#include "glucose/core/SolverTypes.h" #include "glucose/core/Solver.h" +#include "glucose/core/SolverTypes.h" #include "glucose/utils/System.h" #else -#include "minisat/core/SolverTypes.h" #include "minisat/core/Solver.h" +#include "minisat/core/SolverTypes.h" #include "minisat/utils/System.h" #endif @@ -60,193 +60,233 @@ namespace Minisat = Glucose; #endif -using Minisat::Lit; +using Minisat::cpuTime; +using Minisat::l_False; +using Minisat::l_True; +using Minisat::l_Undef; using Minisat::lbool; +using Minisat::Lit; using Minisat::Var; -using Minisat::l_Undef; -using Minisat::l_True; -using Minisat::l_False; -using Minisat::cpuTime; -using std::vector; using std::cout; +using std::vector; namespace MaxHS { - class MaxSolver; +class MaxSolver; } namespace MaxHS_Iface { - class SatSolver; - typedef std::unique_ptr SatSolver_uniqp; - - class SatSolver { - public: - SatSolver(): timer {0}, totalTime {0}, prevTotalTime {0}, stime{-1}, solves {0}, prevSolves{0} {} - virtual ~SatSolver() {} - - virtual bool status() const = 0; - //Return false if unsat already detected (solver is in inconsistent state) - - lbool solveBudget(const vector& assumps, vector& conflict, - int64_t confBudget, int64_t propBudget); - // Solve with assumptions. Track statistics. - // can set conflict and propagation budgets (-1 = no budget) - // Return l_true/l_false/l_Undef formula is sat/unsat/budget-exceeded - - // If unsat put conflict clause into conflict (mapped to external numbering) - // if sat, the "modelValue" function allow access to satisfying assignment - - lbool solveBudget(const vector& assumps, vector& conflict, double timeLimit); - //minisat's runtime is not very predictable. So if no complex solves have been done before - //odds are the time taken will far from the timeLimit. - //The accuracy of the timeLimit gets better as more solves are executed. - - lbool solve(const vector& assumps, vector& conflict) - { return solveBudget(assumps, conflict, -1, -1); } - - lbool solve() - { vector tc; return solve(vector{}, tc); } - - lbool solveBudget(double timeLimit) - { vector tc; return solveBudget(vector{}, tc, timeLimit); } - - lbool solvePropBudget(int64_t propBudget) - { vector tc; return solveBudget(vector{}, tc, -1, propBudget); } - - lbool solve(const vector & assumps) - { vector tc; return solve(assumps, tc); } - - lbool solve(Lit l) - { vector tc; return solve(vector {l}, tc); } - - lbool relaxSolve(const vector& assumps, const vector& branchLits, double timeLimit); - //Do relaxed solve (where sat solver must initially branch on branchLits - - virtual lbool relaxSolve_(const vector& assumps, const vector& branchLits, - int64_t propBudget) = 0; - - - //preprocessing - virtual void freezeVar(Var v) = 0; - virtual bool eliminate(bool turn_off_elim) = 0; - virtual int nEliminated() = 0; - - //simplify clauses according to detected forced literals. - //return false if unsat already detected - virtual bool simplify() = 0; - - //use unit prop to find literals implied by a conjunction of literals "assumps: - //return false if assumps causes contradiction ---> ~assumps is forced - - virtual bool findImplications(const vector &assumps, vector& imps) = 0; - virtual bool findImplications(const Lit p, vector& imps) { - vector tmp {p}; return findImplications(tmp, imps); } - - virtual vector getForced(int index) = 0; - - //data from the solver - virtual bool inSolver(Lit lt) const { return inSolver(var(lt)); } //the solver knows about these variables - virtual bool inSolver(Var v) const = 0; - virtual int nAssigns() const = 0; //# assigned variables - virtual int nClauses() const = 0; //# original clauses - virtual int nInVars() const = 0; //# internal variables - virtual size_t nVars() const = 0; //# number of external variables known to the solver. - virtual bool activeVar(Var v) const = 0; //Is this external variable active in the solver? - //inactive if never added, eliminated by preprocessing - //or forced. - - - //get upper bound on number of clauses in solver DB (some might be deleted or satisfied) - int getNClauses(bool learnts) { return learnts ? get_learnts_size() : get_clauses_size(); } - virtual int get_clauses_size() const = 0; - virtual int get_learnts_size() const = 0; - //get the ith clause from solvers DB (converted to external variables) - //if the empty vector is returned then there is no ith clause (was deleted or satisfied) - //or i is to large. - vector getIthClause(int ith, bool learnts) { - return learnts ? getIth_learnts(ith) :getIth_clauses(ith); } - virtual vector getIth_clauses(int ith) const = 0; - virtual vector getIth_learnts(int ith) const = 0; - - //Reduce a sequence of lits by values forced at decision level 0. - // ==> true if sequence has true or complimentary lits - // ==> false otherwise, but also reset lts to remove any falsified lits. - // - virtual bool reduceClause(vector& lts) const = 0; - - //add clause to theory, return theory status - virtual bool addClause(const vector& lts) = 0; - bool addClause(Lit p) { vector tmp {p}; return(addClause(tmp)); } - bool addClause(Lit p, Lit q) { vector tmp {p,q}; return(addClause(tmp)); } - - //set polarity of variable (lbool b = l_True if we give it a sign---set to false) - virtual void setPolarity(Var v, lbool b) = 0; - - //set Var as decision (variable should already be known to the solver, e.g., in added clause) - virtual void setDecisionVar(Var v, bool b) = 0; - - //install variable b as a new control variables. b must not have been previously added - //if decisionVar is false the solver won't branch on b, if polarity is set when the solver - //does branch on b it will set its value to polarity (l_Undef lets the solver pick) - virtual void newControlVar(Var b, bool decisionVar=true, lbool polarity=l_Undef) = 0; - - //free variable by setting l to TRUE, simplifying, and freeing var(l) - //to be available for future use. - virtual void freeVar(Lit l) = 0; - - //get info from clause - virtual void printLit(Lit l) const = 0; - - //Reverse heuristic ordering of variables - virtual void invertVarActivities() = 0; - - //prune learnts - virtual void pruneLearnts() = 0; - - //obtain truth assignments of last model found by SAT solver. - //The SAT solver will only set those variables it has been given, so - //values for external variables the SAT solver does not know about - //will be l_Undef. - virtual lbool modelValue(Lit p) const = 0; - virtual lbool modelValue(Var x) const = 0; - - //get current truth values (only set if variable is forced) - virtual lbool curVal(const Var x) const = 0; - lbool curVal(const Lit p) const { return curVal(var(p)) ^ sign(p); } - - //stats - //Use startTimer and elapTime to accumulate time over a set of SAT solver calls. - //nSolves() counts number of calls since startTimer - //solveTime for time of most recent call. - void startTimer() { timer = totalTime; prevSolves = solves; } - double elapTime() { return totalTime-timer; } - double solveTime() { return totalTime-prevTotalTime; } - double total_time() { - if (stime >=0) { - totalTime += cpuTime() - stime; - stime = -1; - } - return totalTime; +class SatSolver { + public: + SatSolver() + : timer{0}, + totalTime{0}, + prevTotalTime{0}, + stime{-1}, + solves{0}, + prevSolves{0} {} + virtual ~SatSolver() {} + + virtual bool status() const = 0; + // Return false if unsat already detected (solver is in inconsistent state) + + lbool solveBudget(const vector& assumps, vector& conflict, + int64_t confBudget, int64_t propBudget); + // Solve with assumptions. Track statistics. + // can set conflict and propagation budgets (-1 = no budget) + // Return l_true/l_false/l_Undef formula is sat/unsat/budget-exceeded + + // If unsat put conflict clause into conflict (mapped to external numbering) + // if sat, the "modelValue" function allow access to satisfying assignment + + lbool solveBudget(const vector& assumps, vector& conflict, + double timeLimit); + // minisat's runtime is not very predictable. So if no complex solves have + // been done before odds are the time taken will far from the timeLimit. The + // accuracy of the timeLimit gets better as more solves are executed. + + lbool solve(const vector& assumps, vector& conflict) { + return solveBudget(assumps, conflict, -1, -1); + } + + lbool solve() { + vector tc; + return solve(vector{}, tc); + } + + lbool solveBudget(double timeLimit) { + vector tc; + return solveBudget(vector{}, tc, timeLimit); + } + + lbool solvePropBudget(int64_t propBudget) { + vector tc; + return solveBudget(vector{}, tc, -1, propBudget); + } + + lbool solve(const vector& assumps) { + vector tc; + return solve(assumps, tc); + } + + lbool solve(Lit l) { + vector tc; + return solve(vector{l}, tc); + } + + lbool relaxSolve(const vector& assumps, const vector& branchLits, + double timeLimit); + // Do relaxed solve (where sat solver must initially branch on branchLits + + virtual lbool relaxSolve_(const vector& assumps, + const vector& branchLits, + int64_t propBudget) = 0; + + // preprocessing + virtual void freezeVar(Var v) = 0; + virtual bool eliminate(bool turn_off_elim) = 0; + virtual int nEliminated() = 0; + + // simplify clauses according to detected forced literals. + // return false if unsat already detected + virtual bool simplify() = 0; + + // use unit prop to find literals implied by a conjunction of literals + // "assumps: return false if assumps causes contradiction ---> ~assumps is + // forced + + virtual bool findImplications(const vector& assumps, + vector& imps) = 0; + virtual bool findImplications(const Lit p, vector& imps) { + vector tmp{p}; + return findImplications(tmp, imps); + } + + virtual vector getForced(int index) = 0; + + // data from the solver + virtual bool inSolver(Lit lt) const { + return inSolver(var(lt)); + } // the solver knows about these variables + virtual bool inSolver(Var v) const = 0; + virtual int nAssigns() const = 0; //# assigned variables + virtual int nClauses() const = 0; //# original clauses + virtual int nInVars() const = 0; //# internal variables + virtual size_t nVars() + const = 0; //# number of external variables known to the solver. + virtual bool activeVar( + Var v) const = 0; // Is this external variable active in the solver? + // inactive if never added, eliminated by + // preprocessing or forced. + + // get upper bound on number of clauses in solver DB (some might be deleted or + // satisfied) + int getNClauses(bool learnts) { + return learnts ? get_learnts_size() : get_clauses_size(); + } + virtual int get_clauses_size() const = 0; + virtual int get_learnts_size() const = 0; + // get the ith clause from solvers DB (converted to external variables) + // if the empty vector is returned then there is no ith clause (was deleted or + // satisfied) or i is to large. + vector getIthClause(int ith, bool learnts) { + return learnts ? getIth_learnts(ith) : getIth_clauses(ith); + } + virtual vector getIth_clauses(int ith) const = 0; + virtual vector getIth_learnts(int ith) const = 0; + + // Reduce a sequence of lits by values forced at decision level 0. + // ==> true if sequence has true or complimentary lits + // ==> false otherwise, but also reset lts to remove any falsified lits. + // + virtual bool reduceClause(vector& lts) const = 0; + + // add clause to theory, return theory status + virtual bool addClause(const vector& lts) = 0; + bool addClause(Lit p) { + vector tmp{p}; + return (addClause(tmp)); + } + bool addClause(Lit p, Lit q) { + vector tmp{p, q}; + return (addClause(tmp)); + } + + // set polarity of variable (lbool b = l_True if we give it a sign---set to + // false) + virtual void setPolarity(Var v, lbool b) = 0; + + // set Var as decision (variable should already be known to the solver, e.g., + // in added clause) + virtual void setDecisionVar(Var v, bool b) = 0; + + // install variable b as a new control variables. b must not have been + // previously added if decisionVar is false the solver won't branch on b, if + // polarity is set when the solver does branch on b it will set its value to + // polarity (l_Undef lets the solver pick) + virtual void newControlVar(Var b, bool decisionVar = true, + lbool polarity = l_Undef) = 0; + + // free variable by setting l to TRUE, simplifying, and freeing var(l) + // to be available for future use. + virtual void freeVar(Lit l) = 0; + + // get info from clause + virtual void printLit(Lit l) const = 0; + + // Reverse heuristic ordering of variables + virtual void invertVarActivities() = 0; + + // prune learnts + virtual void pruneLearnts() = 0; + + // obtain truth assignments of last model found by SAT solver. + // The SAT solver will only set those variables it has been given, so + // values for external variables the SAT solver does not know about + // will be l_Undef. + virtual lbool modelValue(Lit p) const = 0; + virtual lbool modelValue(Var x) const = 0; + + // get current truth values (only set if variable is forced) + virtual lbool curVal(const Var x) const = 0; + lbool curVal(const Lit p) const { return curVal(var(p)) ^ sign(p); } + + // stats + // Use startTimer and elapTime to accumulate time over a set of SAT solver + // calls. nSolves() counts number of calls since startTimer solveTime for time + // of most recent call. + void startTimer() { + timer = totalTime; + prevSolves = solves; + } + double elapTime() { return totalTime - timer; } + double solveTime() { return totalTime - prevTotalTime; } + double total_time() { + if (stime >= 0) { + totalTime += cpuTime() - stime; + stime = -1; } - int nSolvesSinceTimer() { return (int) solves-prevSolves; } - int nSolves() { return (int) solves; } - - virtual uint64_t getProps() const = 0; - virtual uint64_t getConfs() const = 0; - - protected: - //interface to solver - virtual lbool solve_(const vector& assumps, vector& conflict, - int64_t confBudget, int64_t propBudget) = 0; - - //stats - double timer, totalTime, prevTotalTime, stime; - int solves; - int prevSolves; - - }; //Class SatSolver - - -} //namespace + return totalTime; + } + int nSolvesSinceTimer() { return (int)solves - prevSolves; } + int nSolves() { return (int)solves; } + + virtual uint64_t getProps() const = 0; + virtual uint64_t getConfs() const = 0; + + protected: + // interface to solver + virtual lbool solve_(const vector& assumps, vector& conflict, + int64_t confBudget, int64_t propBudget) = 0; + + // stats + double timer, totalTime, prevTotalTime, stime; + int solves; + int prevSolves; +}; // Class SatSolver + +typedef std::unique_ptr SatSolver_uniqp; + +} // namespace MaxHS_Iface #endif diff --git a/maxhs/ifaces/greedySatSolver.cc b/maxhs/ifaces/greedySatSolver.cc old mode 100755 new mode 100644 index b88238e..afbd96e --- a/maxhs/ifaces/greedySatSolver.cc +++ b/maxhs/ifaces/greedySatSolver.cc @@ -23,11 +23,11 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ***********/ /* Interface to minisat. -*/ + */ -#include #include #include +#include #ifdef GLUCOSE #include "glucose/core/SolverTypes.h" @@ -37,8 +37,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "minisat/mtl/Sort.h" #endif -#include "maxhs/ifaces/greedySatSolver.h" #include "maxhs/core/MaxSolver.h" +#include "maxhs/ifaces/greedySatSolver.h" +#include "maxhs/core/Bvars.h" +#include "maxhs/utils/Params.h" using namespace MaxHS_Iface; @@ -48,62 +50,57 @@ namespace Minisat = Glucose; using namespace Minisat; -GreedySatSolver::GreedySatSolver(Bvars& b) : - bvars (b), - nsatClauses{0}, - sftcls_heap { ClsOrderLt(wt, satCount) } - { } +GreedySatSolver::GreedySatSolver(Bvars& b) + : bvars(b), nsatClauses{0}, sftcls_heap{ClsOrderLt(wt, satCount)} {} -GreedySatSolver::~GreedySatSolver() { } +GreedySatSolver::~GreedySatSolver() {} void GreedySatSolver::ensureLit(Lit l) { - //l is internal lit...ensure pos and neg + // l is internal lit...ensure pos and neg size_t li = toInt(l) > toInt(~l) ? toInt(l) : toInt(~l); - if(li >= satCount.size()) - satCount.resize(li+1,0); - if(li >= wt.size()) { - wt.resize(li+1, 0); - wt[toInt(l)] = bvars.wt(in2ex(l)); //set once so dvar wts can be adjusted + if (li >= satCount.size()) satCount.resize(li + 1, 0); + if (li >= wt.size()) { + wt.resize(li + 1, 0); + wt[toInt(l)] = bvars.wt(in2ex(l)); // set once so dvar wts can be adjusted wt[toInt(~l)] = bvars.wt(in2ex(~l)); } - if(li >= occurList.size()) - occurList.resize(li+1); + if (li >= occurList.size()) occurList.resize(li + 1); } void GreedySatSolver::ensureMxMap(Lit p) { size_t pi = toInt(p) > toInt(~p) ? toInt(p) : toInt(~p); - if(pi >= mx_map.size()) - mx_map.resize(pi+1, noMutex); + if (pi >= mx_map.size()) mx_map.resize(pi + 1, noMutex); } bool GreedySatSolver::addClauseGreedy(const vector& lts, bool count) { - //add clause to solver and update occurance list. - //adding clause might cause call to unchecked enqueue which - //will adjust the sat counts. So set up the sat count structures - //before adding clause to solver - //cout << "addClauseGreedy " << lts << "\n"; + // add clause to solver and update occurance list. + // adding clause might cause call to unchecked enqueue which + // will adjust the sat counts. So set up the sat count structures + // before adding clause to solver + // cout << "addClauseGreedy " << lts << "\n"; vec ps; - for(auto lt: lts) { + for (auto lt : lts) { ensure_mapping(var(lt)); ensureLit(ex2in(lt)); ps.push(ex2in(lt)); } - - if(count) { //count towards greedy heuristic - //first prepare ps---unchecked enqueue won't occur for already - //valued lits---so don't want clause to count towards greedy - //selection if it will already be true. + + if (count) { // count towards greedy heuristic + // first prepare ps---unchecked enqueue won't occur for already + // valued lits---so don't want clause to count towards greedy + // selection if it will already be true. Minisat::sort(ps); - Lit p; int i, j; + Lit p; + int i, j; for (i = j = 0, p = lit_Undef; i < ps.size(); i++) if (value(ps[i]) == l_True || ps[i] == ~p) - return true; + return true; else if (value(ps[i]) != l_False && ps[i] != p) - ps[j++] = p = ps[i]; + ps[j++] = p = ps[i]; ps.shrink(i - j); - + vector inClause; - for(int i = 0; i < ps.size(); i++) { + for (int i = 0; i < ps.size(); i++) { inClause.push_back(ps[i]); int index = toInt(ps[i]); occurList[index].push_back(inputClauses.size()); @@ -112,60 +109,60 @@ bool GreedySatSolver::addClauseGreedy(const vector& lts, bool count) { inputClauses.push_back(std::move(inClause)); clsIsSat.push_back(lit_Undef); } - + return Minisat::Solver::addClause_(ps); } bool GreedySatSolver::addMutex(const vector& mx, Var dvar) { - //add an encoding of a mutual exclusion between the lits in mx. - //use simple quadratic for now - if(mx.size() <= 1) - return true; - - vector bin (2); - for(size_t i = 0; i < mx.size(); i++) - for(size_t j = i+1; j < mx.size(); j++) { + // add an encoding of a mutual exclusion between the lits in mx. + // use simple quadratic for now + if (mx.size() <= 1) return true; + + vector bin(2); + for (size_t i = 0; i < mx.size(); i++) + for (size_t j = i + 1; j < mx.size(); j++) { bin[0] = ~mx[i]; bin[1] = ~mx[j]; - if(!addClauseGreedy(bin,false)) //don't use this clause in greedy - return false; + if (!addClauseGreedy(bin, false)) // don't use this clause in greedy + return false; } ensure_mapping(dvar); auto inDvar = ex2in(dvar); ensureLit(mkLit(inDvar)); - wt[toInt(mkLit(inDvar))] = bvars.isCore(mx[0]) ? bvars.wt(mx[0]) : bvars.wt(~mx[0]); + wt[toInt(mkLit(inDvar))] = + bvars.isCore(mx[0]) ? bvars.wt(mx[0]) : bvars.wt(~mx[0]); vector imx; - for(auto l : mx) { + for (auto l : mx) { ensure_mapping(l); imx.push_back(ex2in(l)); } - //Adjust sat counts - //Core d == (b1, ..., bk), and at most one bi can be true. So add satcount of d - //to each bi. One will be picked---all others falsified and d is made true. - //So total #clauses satisfied = satCount[bi] + satCount[d] (ASSUMING - //no input core has both a d and a bi. + // Adjust sat counts + // Core d == (b1, ..., bk), and at most one bi can be true. So add satcount of + // d to each bi. One will be picked---all others falsified and d is made true. + // So total #clauses satisfied = satCount[bi] + satCount[d] (ASSUMING + // no input core has both a d and a bi. - //Non core -d == (-b1, .., -bk) at at least k-1 bi true. These are processed - //at the start to first value all but one of the bi to true. This leaves -d == -bj - //where bj is the only one not made true initially. Makeing bj true also makes d true - //and again the #clauses satisfied = satcount[bj] + satcount[d]. - //We can again add satcount[d] to each b---since only one will remain during - //greedy heuristic selection phase. + // Non core -d == (-b1, .., -bk) at at least k-1 bi true. These are processed + // at the start to first value all but one of the bi to true. This leaves -d + // == -bj where bj is the only one not made true initially. Makeing bj true + // also makes d true and again the #clauses satisfied = satcount[bj] + + // satcount[d]. We can again add satcount[d] to each b---since only one will + // remain during greedy heuristic selection phase. auto dlitPos = mkLit(inDvar); - for(size_t j = 0; j < imx.size(); j++) - if(bvars.isCore(mx[0])) + for (size_t j = 0; j < imx.size(); j++) + if (bvars.isCore(mx[0])) satCount[toInt(imx[j])] += satCount[toInt(dlitPos)]; else satCount[toInt(~imx[j])] += satCount[toInt(dlitPos)]; - + int mxi = mutexes.size(); mutexes.push_back(Mutex(inDvar, imx)); ensureMxMap(mkLit(inDvar)); - mx_map[toInt(mkLit(inDvar))] = mx_map[toInt(mkLit(inDvar,true))] = mxi; - for(auto il : imx) { + mx_map[toInt(mkLit(inDvar))] = mx_map[toInt(mkLit(inDvar, true))] = mxi; + for (auto il : imx) { ensureMxMap(il); mx_map[toInt(il)] = mx_map[toInt(~il)] = mxi; } @@ -173,24 +170,25 @@ bool GreedySatSolver::addMutex(const vector& mx, Var dvar) { } Lit GreedySatSolver::pickBranchLit() { - //cout << "GREEDY PICKBRANCHLIT\n"; - //cout << " nonCoreMx size = " << nonCoreMxToProcess.size() << "\n"; - //cout << " sftcls_heap is " << (sftcls_heap.empty() ? "empty" : "not empty") << "\n"; + // cout << "GREEDY PICKBRANCHLIT\n"; + // cout << " nonCoreMx size = " << nonCoreMxToProcess.size() << "\n"; + // cout << " sftcls_heap is " << (sftcls_heap.empty() ? "empty" : "not + // empty") << "\n"; Lit next = lit_Undef; - while(next == lit_Undef && !nonCoreMxToProcess.empty()) { - if(value(nonCoreMxToProcess.back()) != l_Undef) + while (next == lit_Undef && !nonCoreMxToProcess.empty()) { + if (value(nonCoreMxToProcess.back()) != l_Undef) nonCoreMxToProcess.pop_back(); else { next = nonCoreMxToProcess.back(); nonCoreMxToProcess.pop_back(); - //cout << "Noncore forced " << in2ex(next) << " satcount " + // cout << "Noncore forced " << in2ex(next) << " satcount " // << satCount[toInt(next)] << " wt = " << wt[toInt(next)] << "\n"; } } - while(next == lit_Undef && !sftcls_heap.empty()) { + while (next == lit_Undef && !sftcls_heap.empty()) { auto lt = sftcls_heap.removeMin(); - if(value(lt) == l_Undef) { - //cout << "sftcls unvalued min = " << lt << " satcount " + if (value(lt) == l_Undef) { + // cout << "sftcls unvalued min = " << lt << " satcount " // << satCount[toInt(lt)] << " wt = " << wt[toInt(lt)] << "\n"; next = lt; } @@ -201,51 +199,49 @@ Lit GreedySatSolver::pickBranchLit() { // satCount[toInt(next)] << " wt = " << wt[toInt(next)] << "\n"; }*/ - if(next != lit_Undef && - static_cast(nsatClauses) == inputClauses.size() && - wt[toInt(next)] > 0) + if (next != lit_Undef && + static_cast(nsatClauses) == inputClauses.size() && + wt[toInt(next)] > 0) next = ~next; - //cout << "PICKED " << next << " satcount " << + // cout << "PICKED " << next << " satcount " << // satCount[toInt(next)] << " wt = " << wt[toInt(next)] << "\n"; - - //debug - /*if(static_cast(nsatClauses) == inputClauses.size() && bvars.isCore(in2ex(next))) - cout << "c Greedy solver WARNING...all clauses sat and picking core (costly) blit\n"; - if(static_cast(toInt(next)) < mx_map.size() && mx_map[toInt(next)] != noMutex) { - auto mxi = mx_map[toInt(next)]; - if(var(next) == mutexes[mxi].dVar) - cout << "c greedy solver WARNING...selected dvar!\n"; + + // debug + /*if(static_cast(nsatClauses) == inputClauses.size() && + bvars.isCore(in2ex(next))) cout << "c Greedy solver WARNING...all clauses + sat and picking core (costly) blit\n"; if(static_cast(toInt(next)) < + mx_map.size() && mx_map[toInt(next)] != noMutex) { auto mxi = + mx_map[toInt(next)]; if(var(next) == mutexes[mxi].dVar) cout << "c greedy + solver WARNING...selected dvar!\n"; }*/ return next; } void GreedySatSolver::cancelUntil(int level) { - //cout << "greedy backtracking\n"; - + // cout << "greedy backtracking\n"; + if (decisionLevel() > level) { - for (int c = trail.size()-1; c >= trail_lim[level]; c--) { + for (int c = trail.size() - 1; c >= trail_lim[level]; c--) { Lit l = trail[c]; - Var x = var(l); - assigns [x] = l_Undef; + Var x = var(l); + assigns[x] = l_Undef; if (phase_saving > 1 || (phase_saving == 1 && c > trail_lim.last())) - polarity[x] = sign(trail[c]); + polarity[x] = sign(trail[c]); insertVarOrder(x); - //undo sat counts. + // undo sat counts. int index = toInt(l); - for(auto cls : occurList[index]) - if(clsIsSat[cls] == l) { - clsIsSat[cls] = lit_Undef; - --nsatClauses; - for(auto li : inputClauses[cls]) { - satCount[toInt(li)] += 1; - if(sftcls_heap.inHeap(li)) - sftcls_heap.decrease(li); - } - } - if(!sftcls_heap.inHeap(l)) - sftcls_heap.insert(l); + for (auto cls : occurList[index]) + if (clsIsSat[cls] == l) { + clsIsSat[cls] = lit_Undef; + --nsatClauses; + for (auto li : inputClauses[cls]) { + satCount[toInt(li)] += 1; + if (sftcls_heap.inHeap(li)) sftcls_heap.decrease(li); + } + } + if (!sftcls_heap.inHeap(l)) sftcls_heap.insert(l); } qhead = trail_lim[level]; trail.shrink(trail.size() - trail_lim[level]); @@ -255,53 +251,56 @@ void GreedySatSolver::cancelUntil(int level) { void GreedySatSolver::uncheckedEnqueue(Lit p, CRef from) { Solver::uncheckedEnqueue(p, from); - //update sat counts + // update sat counts int index = toInt(p); - for(auto cls : occurList[index]) - if(clsIsSat[cls] == lit_Undef) { + for (auto cls : occurList[index]) + if (clsIsSat[cls] == lit_Undef) { clsIsSat[cls] = p; ++nsatClauses; - for(auto l : inputClauses[cls]) { - satCount[toInt(l)] -= 1; - if(sftcls_heap.inHeap(l)) - sftcls_heap.increase(l); + for (auto l : inputClauses[cls]) { + satCount[toInt(l)] -= 1; + if (sftcls_heap.inHeap(l)) sftcls_heap.increase(l); } } } lbool GreedySatSolver::solve_(const vector& assumps, vector& conflict, - int64_t confBudget, int64_t propBudget) { - if(params.verbosity > 1) + int64_t confBudget, int64_t propBudget) { + if (params.verbosity > 1) cout << "c Greedy has " << nClauses() << " clauses\n"; - //find non-cores to force initially. - auto scount = [this](Lit x, Lit y) { return satCount[toInt(~x)] > satCount[toInt(~y)]; }; + // find non-cores to force initially. + auto scount = [this](Lit x, Lit y) { + return satCount[toInt(~x)] > satCount[toInt(~y)]; + }; auto isFalse = [this](Lit x) { return value(x) == l_False; }; - for(size_t i = 0; i < mutexes.size(); i++) - if(bvars.isNonCore(in2ex(mutexes[i].mx[0]))) { - vector mutex { mutexes[i].mx }; //copy + for (size_t i = 0; i < mutexes.size(); i++) + if (bvars.isNonCore(in2ex(mutexes[i].mx[0]))) { + vector mutex{mutexes[i].mx}; // copy auto newEnd = std::remove_if(mutex.begin(), mutex.end(), isFalse); mutex.resize(std::distance(mutex.begin(), newEnd)); std::sort(mutex.begin(), mutex.end(), scount); - for(size_t j = 0; j < mutex.size()-1; j++) //force all but one - nonCoreMxToProcess.push_back(~mutex[j]); + for (size_t j = 0; j < mutex.size() - 1; j++) // force all but one + nonCoreMxToProcess.push_back(~mutex[j]); } - - //Now reinit soft clause heap to account for any newly added clauses - //and the new satcount scores. + + // Now reinit soft clause heap to account for any newly added clauses + // and the new satcount scores. /*for(int i = 0; i < nInVars(); i++) { Lit pos = mkLit(i); Lit neg = mkLit(i, true); - cout << in2ex(pos) << " In = " << pos << " satcount = " << satCount[toInt(pos)] << " wt = " << wt[toInt(pos)] << "\n"; - cout << in2ex(neg) << " In = " << neg << " satcount = " << satCount[toInt(neg)] << " wt = " << wt[toInt(neg)] << "\n"; + cout << in2ex(pos) << " In = " << pos << " satcount = " << + satCount[toInt(pos)] << " wt = " << wt[toInt(pos)] << "\n"; cout << + in2ex(neg) << " In = " << neg << " satcount = " << satCount[toInt(neg)] << " + wt = " << wt[toInt(neg)] << "\n"; }*/ sftcls_heap.clear(); - for(int i = 0; i < nInVars(); i++) { - sftcls_heap.insert(mkLit(i)); - sftcls_heap.insert(mkLit(i,true)); + for (int i = 0; i < nInVars(); i++) { + sftcls_heap.insert(mkLit(i)); + sftcls_heap.insert(mkLit(i, true)); } return miniSolver::solve_(assumps, conflict, confBudget, propBudget); diff --git a/maxhs/ifaces/greedySatSolver.h b/maxhs/ifaces/greedySatSolver.h old mode 100755 new mode 100644 index 3c8549e..db56434 --- a/maxhs/ifaces/greedySatSolver.h +++ b/maxhs/ifaces/greedySatSolver.h @@ -39,82 +39,84 @@ namespace Minisat = Glucose; #endif #include "maxhs/core/MaxSolverTypes.h" -#include "maxhs/core/Bvars.h" #include "maxhs/ifaces/miniSatSolver.h" +class Bvars; + namespace MaxHS_Iface { - class GreedySatSolver : public miniSolver { - public: - GreedySatSolver(Bvars& b); - virtual ~GreedySatSolver(); - bool addClauseGreedy(const vector& lts, bool count); - bool addMutex(const vector& mx, Var dvar); - lbool solve() { - return SatSolver::solve(); +class GreedySatSolver : public miniSolver { + public: + GreedySatSolver(Bvars& b); + virtual ~GreedySatSolver(); + bool addClauseGreedy(const vector& lts, bool count); + bool addMutex(const vector& mx, Var dvar); + lbool solve() { return SatSolver::solve(); } + + protected: + Bvars& bvars; + Lit pickBranchLit(); + void ensureLit(Lit l); + void ensureMxMap(Lit p); + void cancelUntil(int level); // override cancelUntil + void uncheckedEnqueue(Lit, Minisat::CRef); + vector isDvar; // l_Undef == not dvar, l_True == core Dvar l_False == + // noncore dvar + + // static + vector satCount; + vector wt; + vector> inputClauses; + vector> occurList; + vector clsIsSat; + int nsatClauses; + + const int noMutex{-1}; + vector mx_map; + struct Mutex { + Var dVar; + vector mx; + Mutex(Var d, vector m) : dVar{d}, mx{m} {} + }; + vector mutexes; + vector nonCoreMxToProcess; + + struct ClsOrderLt { + // minisat heap is a min-heap. Want the "minimum" lit to be the lit + // that satisfies the most clauses for the least wt. I.e., + // x < y if satcount(x)/wt(x) > satcount(x)/wt(y). Note lits have + // been converted to indicies before putting into the heap. + bool operator()(Lit x, Lit y) const { + // cout << "ClsOrderLt(" << x << "," << y << ") sc(" << sc[toInt(x)] << + // "," << sc[toInt(y)] << ") " + // << " wt(" << wts[toInt(x)] << "," << wts[toInt(y)] << ")\n"; + + if (sc[toInt(x)] == sc[toInt(y)] && wts[toInt(x)] == wts[toInt(y)]) + return x < y; + return better(sc[toInt(x)], wts[toInt(x)], sc[toInt(y)], wts[toInt(y)]); } - protected: - Bvars& bvars; - Lit pickBranchLit(); - void ensureLit(Lit l); - void ensureMxMap(Lit p); - void cancelUntil(int level); //override cancelUntil - void uncheckedEnqueue(Lit, Minisat::CRef); - vector isDvar; //l_Undef == not dvar, l_True == core Dvar l_False == noncore dvar - - //static - vector satCount; - vector wt; - vector> inputClauses; - vector> occurList; - vector clsIsSat; - int nsatClauses; - - const int noMutex {-1}; - vector mx_map; - struct Mutex { - Var dVar; - vector mx; - Mutex(Var d, vector m) : dVar {d}, mx {m} {} - }; - vector mutexes; - vector nonCoreMxToProcess; - - struct ClsOrderLt { - //minisat heap is a min-heap. Want the "minimum" lit to be the lit - //that satisfies the most clauses for the least wt. I.e., - //x < y if satcount(x)/wt(x) > satcount(x)/wt(y). Note lits have - //been converted to indicies before putting into the heap. - bool operator() (Lit x, Lit y) const { - //cout << "ClsOrderLt(" << x << "," << y << ") sc(" << sc[toInt(x)] << "," << sc[toInt(y)] << ") " - // << " wt(" << wts[toInt(x)] << "," << wts[toInt(y)] << ")\n"; - - if(sc[toInt(x)] == sc[toInt(y)] && wts[toInt(x)] == wts[toInt(y)]) - return x < y; - return better(sc[toInt(x)], wts[toInt(x)], sc[toInt(y)], wts[toInt(y)]); - } - bool better(int sc1, Weight wt1, int sc2, Weight wt2) const { - //return true if the metric (sc1, w1) (satcount, litwt) - //represents a better choice than (sc2, w2). - if(wt1 == 0 && wt2 == 0) - return sc1 > sc2; - else if(wt1 == 0 && wt2 > 0) - return (sc1 > 0 || sc2 == 0); - else if(wt1 > 0 && wt2 == 0) - return (sc1 > 0 && sc2 == 0); - else //(wt1 > 0 && wt2 > 0) - return sc1/wt1 > sc2/wt2; - } - vector& wts; - vector& sc; - ClsOrderLt(vector& w, vector& c) : wts (w), sc (c) {} - }; - - Heap sftcls_heap; - - lbool solve_(const vector& assumps, vector& conflict, - int64_t confBudget, int64_t propBudget); + bool better(int sc1, Weight wt1, int sc2, Weight wt2) const { + // return true if the metric (sc1, w1) (satcount, litwt) + // represents a better choice than (sc2, w2). + if (wt1 == 0 && wt2 == 0) + return sc1 > sc2; + else if (wt1 == 0 && wt2 > 0) + return (sc1 > 0 || sc2 == 0); + else if (wt1 > 0 && wt2 == 0) + return (sc1 > 0 && sc2 == 0); + else //(wt1 > 0 && wt2 > 0) + return sc1 / wt1 > sc2 / wt2; + } + vector& wts; + vector& sc; + ClsOrderLt(vector& w, vector& c) : wts(w), sc(c) {} }; - -} //end namespace -#endif + Heap sftcls_heap; + + lbool solve_(const vector& assumps, vector& conflict, + int64_t confBudget, int64_t propBudget); +}; + +} // namespace MaxHS_Iface + +#endif diff --git a/maxhs/ifaces/miniSatSolver.cc b/maxhs/ifaces/miniSatSolver.cc old mode 100755 new mode 100644 index 03a91fc..58c1e6f --- a/maxhs/ifaces/miniSatSolver.cc +++ b/maxhs/ifaces/miniSatSolver.cc @@ -23,12 +23,12 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ***********/ /* Interface to minisat. -*/ + */ -#include +#include "maxhs/ifaces/miniSatSolver.h" #include #include -#include "maxhs/ifaces/miniSatSolver.h" +#include #include "maxhs/core/MaxSolver.h" #include "maxhs/utils/io.h" @@ -40,121 +40,114 @@ namespace Minisat = Glucose; using namespace Minisat; -miniSolver::miniSolver() : - relaxOrder_heap (RelaxVarOrderLt(relaxVarRank)), - doingRelaxSearch {false} -{} +miniSolver::miniSolver() + : relaxOrder_heap(RelaxVarOrderLt(relaxVarRank)), doingRelaxSearch{false} {} miniSolver::~miniSolver() {} - /*************************************************/ -//Internal to external variable ordering mappings. +// Internal to external variable ordering mappings. +void miniSolver::ensure_mapping(const Var ex) { + if (ex >= (int)ex2in_map.size()) ex2in_map.resize(ex + 1, var_Undef); -void miniSolver::ensure_mapping(const Var ex) -{ - if (ex >= (int) ex2in_map.size()) - ex2in_map.resize(ex+1,var_Undef); - - if(ex2in_map[ex] == var_Undef) { - Var in {newVar()}; + if (ex2in_map[ex] == var_Undef) { + Var in{newVar()}; ex2in_map[ex] = in; - if (in >= (int) in2ex_map.size()) - in2ex_map.resize(in+1, var_Undef); + if (in >= (int)in2ex_map.size()) in2ex_map.resize(in + 1, var_Undef); in2ex_map[in] = ex; } } - /*************************************************/ -//Solver interface routines +// Solver interface routines -bool miniSolver::status() const { - return Minisat::SimpSolver::okay(); -} +bool miniSolver::status() const { return Minisat::SimpSolver::okay(); } lbool miniSolver::solve_(const vector& assumps, vector& conflict, - int64_t confBudget, int64_t propBudget) { + int64_t confBudget, int64_t propBudget) { conflict.clear(); + + // cout << "miniSolver confBudget = " << confBudget << " propBudget " << + // propBudget << "\n"; + Minisat::SimpSolver::assumptions.clear(); - for(auto lt : assumps) { + for (auto lt : assumps) { ensure_mapping(lt); Minisat::SimpSolver::assumptions.push(ex2in(lt)); } - if(confBudget < 0) + if (confBudget < 0) conflict_budget = -1; else conflict_budget = conflicts + confBudget; - - if(propBudget < 0) + + if (propBudget < 0) propagation_budget = -1; else propagation_budget = propagations + propBudget; - lbool val = Minisat::SimpSolver::solve_(false); //don't do simplification incrementally. + lbool val = Minisat::SimpSolver::solve_(false); in2ex(Minisat::SimpSolver::conflict.toVec(), conflict); return val; } -lbool miniSolver::relaxSolve_(const vector& assumps, const vector& branchLits, - int64_t propBudget) { +lbool miniSolver::relaxSolve_(const vector& assumps, + const vector& branchLits, + int64_t propBudget) { Minisat::SimpSolver::assumptions.clear(); - for(auto lt : assumps) { + for (auto lt : assumps) { ensure_mapping(lt); Minisat::SimpSolver::assumptions.push(ex2in(lt)); } relaxVarRank.clear(); relaxOrder_heap.clear(); - for(int i = 0; i < static_cast(branchLits.size()); i++) { + for (int i = 0; i < static_cast(branchLits.size()); i++) { auto lt = branchLits[i]; ensure_mapping(lt); auto inLt = ex2in(lt); - //If var is not a relax var, then either !relaxVarRank.has() or relaxVarRank = max() + // If var is not a relax var, then either !relaxVarRank.has() or + // relaxVarRank = max() relaxVarRank.insert(var(inLt), i, std::numeric_limits::max()); relaxVarSign.insert(var(inLt), sign(inLt)); - //note all elements already in heap are already in relaxVarRank, - //so new element can be inserted in correct place. - relaxOrder_heap.insert(var(inLt)); + // note all elements already in heap are already in relaxVarRank, + // so new element can be inserted in correct place. + relaxOrder_heap.insert(var(inLt)); } - if(propBudget < 0) + if (propBudget < 0) propagation_budget = -1; else propagation_budget = propagations + propBudget; savedModel.clear(); - for(int i = 0; i < Minisat::SimpSolver::model.size(); i++) + for (int i = 0; i < Minisat::SimpSolver::model.size(); i++) savedModel.push_back(Minisat::SimpSolver::model[i]); doingRelaxSearch = true; auto val = Minisat::SimpSolver::solve_(false); doingRelaxSearch = false; - if(val != l_True) { + if (val != l_True) { Minisat::SimpSolver::model.clear(); Minisat::SimpSolver::model.growTo(savedModel.size()); - for(int i = 0; i < static_cast(savedModel.size()); i++) + for (int i = 0; i < static_cast(savedModel.size()); i++) Minisat::SimpSolver::model[i] = savedModel[i]; } return val; } - Lit miniSolver::pickBranchLit() { - if(!doingRelaxSearch) - return Minisat::SimpSolver::pickBranchLit(); + if (!doingRelaxSearch) return Minisat::SimpSolver::pickBranchLit(); Var next = var_Undef; - while(next == var_Undef || value(next) != l_Undef) - if(relaxOrder_heap.empty()) { + while (next == var_Undef || value(next) != l_Undef) + if (relaxOrder_heap.empty()) { next = var_Undef; break; - } - else + } else next = relaxOrder_heap.removeMin(); - if(next == var_Undef) + if (next == var_Undef) return Minisat::SimpSolver::pickBranchLit(); else return mkLit(next, relaxVarSign[next]); @@ -162,14 +155,14 @@ Lit miniSolver::pickBranchLit() { void miniSolver::cancelUntil(int level) { if (decisionLevel() > level) { - for (int c = trail.size()-1; c >= trail_lim[level]; c--) { - Var x = var(trail[c]); - assigns [x] = l_Undef; + for (int c = trail.size() - 1; c >= trail_lim[level]; c--) { + Var x = var(trail[c]); + assigns[x] = l_Undef; if (phase_saving > 1 || (phase_saving == 1 && c > trail_lim.last())) - polarity[x] = sign(trail[c]); - if(doingRelaxSearch && isRelaxVar(x)) - insertRelaxOrder(x); - insertVarOrder(x); //also insert into var order rank to restore for next non-relax search. + polarity[x] = sign(trail[c]); + if (doingRelaxSearch && isRelaxVar(x)) insertRelaxOrder(x); + insertVarOrder(x); // also insert into var order rank to restore for next + // non-relax search. } qhead = trail_lim[level]; trail.shrink(trail.size() - trail_lim[level]); @@ -177,29 +170,24 @@ void miniSolver::cancelUntil(int level) { } } -void miniSolver::freezeVar(Var v) -{ +void miniSolver::freezeVar(Var v) { Var in = ex2in(v); if (in != var_Undef) { - //if(!frozen[in]) cout << "mini freezevar " << in << " external " << v << "\n"; + // if(!frozen[in]) cout << "mini freezevar " << in << " external " << v << + // "\n"; Minisat::SimpSolver::freezeVar(in); - } - else - cout << "c ERROR. Tried to freeze variable " << v << " that is not yet in solver\n."; + } else + cout << "c ERROR. Tried to freeze variable " << v + << " that is not yet in solver\n."; } -bool miniSolver::eliminate(bool turn_off_elim) -{ +bool miniSolver::eliminate(bool turn_off_elim) { return Minisat::SimpSolver::eliminate(turn_off_elim); } -bool miniSolver::simplify() -{ - return Minisat::SimpSolver::simplify(); -} - +bool miniSolver::simplify() { return Minisat::SimpSolver::simplify(); } -//REMOVE FOR NOW +// REMOVE FOR NOW #if 0 void miniSolver::analyzeFinal(Lit p, LSet& out_conflict) { @@ -268,10 +256,10 @@ void miniSolver::analyzeFinal(Lit p, LSet& out_conflict) } #endif -bool miniSolver::findImplications(const vector &assumps, vector& imps) -{ +bool miniSolver::findImplications(const vector& assumps, + vector& imps) { vec a; - for(auto lt : assumps) { + for (auto lt : assumps) { ensure_mapping(lt); a.push(ex2in(lt)); } @@ -279,90 +267,85 @@ bool miniSolver::findImplications(const vector &assumps, vector& imps) vec r; bool val = Minisat::SimpSolver::implies(a, r); - if(!val) return val; + if (!val) return val; - for(int i=0; i < r.size(); i++) - imps.push_back(in2ex(r[i])); + for (int i = 0; i < r.size(); i++) imps.push_back(in2ex(r[i])); return true; } vector miniSolver::getForced(int index) { - //DEBUG - //cout << "miniSolver::getForced: Forced =" << forced << "\n"; + // DEBUG + // cout << "miniSolver::getForced: Forced =" << forced << "\n"; updateForced(forced); - //DEBUG - //cout << "after update forced =" << forced << "\n"; + // DEBUG + // cout << "after update forced =" << forced << "\n"; vector tmp; - for(size_t i = index; i < forced.size(); i++) + for (size_t i = index; i < forced.size(); i++) tmp.push_back(in2ex(forced[i])); return tmp; } void miniSolver::updateForced(vector& frc) { - int limit = trail_lim.size() > 0 ? - trail_lim[0] : trail.size(); - int i {0}; + int limit = trail_lim.size() > 0 ? trail_lim[0] : trail.size(); + int i{0}; - //DEBUG + // DEBUG /*cout << "MINISolver update Forced. TRAIL = \n["; for(int jj = 0; jj < limit; jj++) - cout << "[" << jj << ". " << Solver::trail[jj] << "/" << in2ex(Solver::trail[jj]) << "] "; - cout << "]\n";*/ - - if(frc.size() > 0) { + cout << "[" << jj << ". " << Solver::trail[jj] << "/" << + in2ex(Solver::trail[jj]) << "] "; cout << "]\n";*/ + + if (frc.size() > 0) { i = frc.size() - 1; - while(i < limit && trail[i++] != frc.back()); + while (i < limit && trail[i++] != frc.back()) + ; } - for( ; i < limit; i++) - if(in2ex(trail[i]) != lit_Undef) - frc.push_back(trail[i]); + for (; i < limit; i++) + if (in2ex(trail[i]) != lit_Undef) frc.push_back(trail[i]); } -bool miniSolver::reduceClause(vector& lts) const -{ +bool miniSolver::reduceClause(vector& lts) const { // remove false/duplicate literals return false if true lits is present assert(decisionLevel() == 0); std::sort(lts.begin(), lts.end()); - //In this loop i is the (index of) next literal to examine. j is the index of - //the last literal to keep. j=-1 means nothing has yet been kept. - int j {-1}; - for(size_t i =0; i< lts.size(); i++) { + // In this loop i is the (index of) next literal to examine. j is the index of + // the last literal to keep. j=-1 means nothing has yet been kept. + int j{-1}; + for (size_t i = 0; i < lts.size(); i++) { Lit in = ex2in(lts[i]); - lbool val {(in == lit_Undef) ? l_Undef : value(in)}; - if(val == l_True || (j > 0 && lts[i] == ~lts[j])) { - return true; + lbool val{(in == lit_Undef) ? l_Undef : value(in)}; + if (val == l_True || (j > 0 && lts[i] == ~lts[j])) { + return true; } - if(val == l_False || (j > 0 && lts[i] == lts[j])) - continue; + if (val == l_False || (j > 0 && lts[i] == lts[j])) continue; lts[++j] = lts[i]; } - lts.resize(j+1); + lts.resize(j + 1); - return false; //clause not true + return false; // clause not true } -bool miniSolver::addClause(const vector& lts) -{ +bool miniSolver::addClause(const vector& lts) { vec ps; - for(auto lt: lts) { + for (auto lt : lts) { ensure_mapping(lt); - if(isEliminated(var(ex2in(lt)))) { + if (isEliminated(var(ex2in(lt)))) { ps.clear(); break; } ps.push(ex2in(lt)); } - //DEBUG - //cout << "mini addCls: ext=" << lts << " int=" << ps << "\n"; + // DEBUG + // cout << "mini addCls: ext=" << lts << " int=" << ps << "\n"; - if(ps.size()>0) + if (ps.size() > 0) return Minisat::SimpSolver::addClause_(ps); else return true; @@ -371,55 +354,51 @@ bool miniSolver::addClause(const vector& lts) void miniSolver::setPolarity(Var v, lbool pol) { Var in = ex2in(v); assert(in != var_Undef); - if(in != var_Undef) - Minisat::SimpSolver::setPolarity(in, pol); + if (in != var_Undef) Minisat::SimpSolver::setPolarity(in, pol); } void miniSolver::setDecisionVar(Var v, bool dvar) { Var in = ex2in(v); assert(in != var_Undef); - if(in != var_Undef) - Minisat::SimpSolver::setDecisionVar(in, dvar); + if (in != var_Undef) Minisat::SimpSolver::setDecisionVar(in, dvar); } void miniSolver::newControlVar(Var b, bool dvar, lbool pol) { - if (b >= static_cast(ex2in_map.size())) - ex2in_map.resize(b+1, var_Undef); + ex2in_map.resize(b + 1, var_Undef); if (ex2in_map[b] != var_Undef) - cout << "c ERROR: new control variable for variable that already exists (" << b << ")"; + cout << "c ERROR: new control variable for variable that already exists (" + << b << ")"; else { - Var c {newVar(pol, dvar)}; + Var c{newVar(pol, dvar)}; ex2in_map[b] = c; - if(c >= static_cast(in2ex_map.size())) - in2ex_map.resize(c+1, var_Undef); + if (c >= static_cast(in2ex_map.size())) + in2ex_map.resize(c + 1, var_Undef); in2ex_map[c] = b; } } void miniSolver::freeVar(Lit l) { - //remove mapping and assert l to sat solver. + // remove mapping and assert l to sat solver. Var in = ex2in(var(l)); - if(in != var_Undef) { + if (in != var_Undef) { ex2in_map[var(l)] = var_Undef; in2ex_map[in] = var_Undef; releaseVar(mkLit(in, sign(l))); } } -void miniSolver::printLit(const Lit l) const -{ - cout << (sign(l) ? "-" : "") << var(l)+1 << ":"; +void miniSolver::printLit(const Lit l) const { + cout << (sign(l) ? "-" : "") << var(l) + 1 << ":"; Lit in = ex2in(l); - if(in == lit_Undef || value(in) == l_Undef) + if (in == lit_Undef || value(in) == l_Undef) cout << 'U'; - else + else cout << (value(in) == l_True ? 'T' : 'F'); } -void miniSolver::invertVarActivities() -{ - double minAct = -1; // min non-zero activity +void miniSolver::invertVarActivities() { + double minAct = -1; // min non-zero activity for (size_t i = 0; i < nVars(); i++) { if (activity[i] > 0 && (minAct < 0 || activity[i] < minAct)) { minAct = activity[i]; @@ -427,24 +406,22 @@ void miniSolver::invertVarActivities() } // If no variables have any activity, then there is no need to invert if (minAct < 0) return; - + for (size_t i = 0; i < nVars(); i++) { if (activity[i] > 0) { - activity[i] = (1/activity[i])/(1/minAct); + activity[i] = (1 / activity[i]) / (1 / minAct); } else { activity[i] = 1; - } + } } rebuildOrderHeap(); } - -void miniSolver::pruneLearnts() -{ +void miniSolver::pruneLearnts() { int i, j; - for (i = j = 0; i < learnts.size(); i++){ + for (i = j = 0; i < learnts.size(); i++) { Clause& c = ca[learnts[i]]; - if (!locked(c) && c.size() > 6) + if (!locked(c) && c.size() > 6) removeClause(learnts[i]); else learnts[j++] = learnts[i]; @@ -453,8 +430,7 @@ void miniSolver::pruneLearnts() checkGarbage(); } -lbool miniSolver::modelValue(const Lit p) const -{ +lbool miniSolver::modelValue(const Lit p) const { Lit in = ex2in(p); if (in == lit_Undef) return l_Undef; @@ -462,8 +438,7 @@ lbool miniSolver::modelValue(const Lit p) const return Minisat::SimpSolver::modelValue(in); } -lbool miniSolver::modelValue(const Var x) const -{ +lbool miniSolver::modelValue(const Var x) const { Var in = ex2in(x); if (in == var_Undef) return l_Undef; @@ -473,43 +448,36 @@ lbool miniSolver::modelValue(const Var x) const lbool miniSolver::curVal(const Var x) const { Var in = ex2in(x); - if(in == var_Undef) + if (in == var_Undef) return l_Undef; - else + else return Minisat::SimpSolver::value(in); } lbool miniSolver::curVal(const Lit x) const { Lit in = ex2in(x); - if(in == lit_Undef) + if (in == lit_Undef) return l_Undef; - else + else return Minisat::SimpSolver::value(in); } vector miniSolver::getIth_clauses(int ith) const { - vector cl {}; - if(ith >= clauses.size() || isRemoved(clauses[ith])) - return cl; - auto &c = ca[clauses[ith]]; - if(satisfied(c)) - return cl; - for(int i=0; i< c.size(); i++) - if(value(c[i]) != l_False) - cl.push_back(in2ex(c[i])); + vector cl{}; + if (ith >= clauses.size() || isRemoved(clauses[ith])) return cl; + auto& c = ca[clauses[ith]]; + if (satisfied(c)) return cl; + for (int i = 0; i < c.size(); i++) + if (value(c[i]) != l_False) cl.push_back(in2ex(c[i])); return cl; } vector miniSolver::getIth_learnts(int ith) const { - vector cl {}; - if(ith >= learnts.size() || isRemoved(learnts[ith])) - return cl; - auto &c = ca[learnts[ith]]; - if(satisfied(c)) - return cl; - for(int i=0; i< c.size(); i++) - if(value(c[i]) != l_False) - cl.push_back(in2ex(c[i])); + vector cl{}; + if (ith >= learnts.size() || isRemoved(learnts[ith])) return cl; + auto& c = ca[learnts[ith]]; + if (satisfied(c)) return cl; + for (int i = 0; i < c.size(); i++) + if (value(c[i]) != l_False) cl.push_back(in2ex(c[i])); return cl; } - diff --git a/maxhs/ifaces/miniSatSolver.h b/maxhs/ifaces/miniSatSolver.h old mode 100755 new mode 100644 index dcd45fa..e0b9099 --- a/maxhs/ifaces/miniSatSolver.h +++ b/maxhs/ifaces/miniSatSolver.h @@ -28,20 +28,20 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. This is to ensure that the internal variables are consecutively ordered, so that the external theory can be fed to the sat solver in parts with generating huge gaps in the variable numbering. - - This also means however, that not all external variables will be given a value - by a found sat model. + + This also means however, that not all external variables will be given a + value by a found sat model. */ #ifndef MINISATSOLVER_H #define MINISATSOLVER_H #ifdef GLUCOSE -#include "glucose/simp/SimpSolver.h" #include "glucose/core/SolverTypes.h" +#include "glucose/simp/SimpSolver.h" #else -#include "minisat/simp/SimpSolver.h" #include "minisat/core/SolverTypes.h" +#include "minisat/simp/SimpSolver.h" #endif #include "maxhs/ifaces/SatSolver.h" @@ -50,53 +50,63 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. namespace Minisat = Glucose; #endif -using Minisat::vec; -using Minisat::var_Undef; +using Minisat::Heap; using Minisat::lit_Undef; using Minisat::mkLit; +using Minisat::var_Undef; +using Minisat::vec; using Minisat::VMap; -using Minisat::Heap; + namespace MaxHS_Iface { -//Minisat Solver +// Minisat Solver class miniSolver : public SatSolver, protected Minisat::SimpSolver { -public: + public: miniSolver(); virtual ~miniSolver(); bool status() const; - bool findImplications(const vector &assumps, vector& unitImps); + bool findImplications(const vector& assumps, vector& unitImps); bool findImplications(Lit p, vector& unitImps) { - vector tmp {p}; return findImplications(tmp, unitImps); + vector tmp{p}; + return findImplications(tmp, unitImps); } bool findImplications(Lit p, Lit q, vector& unitImps) { - vector tmp {p,q}; return findImplications(tmp, unitImps); + vector tmp{p, q}; + return findImplications(tmp, unitImps); } bool isMX(Lit p, Lit q) { - vector tmp {p,q}; vector tmp2; return !findImplications(tmp, tmp2); + vector tmp{p, q}; + vector tmp2; + return !findImplications(tmp, tmp2); } template - bool findImplicationsIf(Lit p, vector& imps, S pred); + bool findImplicationsIf(Lit p, vector& imps, S pred); vector getForced(int index); - int nForced() const { return trail_lim.size() > 0 ? trail_lim[0] : trail.size(); } + int nForced() const { + return trail_lim.size() > 0 ? trail_lim[0] : trail.size(); + } int nEliminated() const { return eliminated_vars; } int nAssigns() const { return Minisat::SimpSolver::nAssigns(); } int nClauses() const { return Minisat::SimpSolver::nClauses(); } - int nInVars() const { return Minisat::SimpSolver::nVars(); } - size_t nVars() const { return ex2in_map.size(); } //not all these externals exist + int nInVars() const { return Minisat::SimpSolver::nVars(); } + size_t nVars() const { + return ex2in_map.size(); + } // not all these externals exist bool inSolver(Var v) const { return ex2in(v) != var_Undef; } bool activeVar(Var v) const { - return inSolver(v) && !isEliminated(ex2in(v)) && curVal(v) == l_Undef; } + return inSolver(v) && !isEliminated(ex2in(v)) && curVal(v) == l_Undef; + } - //get clauses currently in solver (e.g., after preprocessing) + // get clauses currently in solver (e.g., after preprocessing) //_size counts clauses possibly deleted or already satisfied int get_clauses_size() const { return clauses.size(); } int get_learnts_size() const { return learnts.size(); } - //getIth_clause filters deleted and satisfied clauses returning an empty vector - //in those cases. + // getIth_clause filters deleted and satisfied clauses returning an empty + // vector in those cases. vector getIth_clauses(int ith) const; vector getIth_learnts(int ith) const; @@ -116,124 +126,125 @@ class miniSolver : public SatSolver, protected Minisat::SimpSolver { uint64_t getProps() const { return propagations; } uint64_t getConfs() const { return conflicts; } - //preprocessing + // preprocessing void freezeVar(Lit l) { freezeVar(var(l)); } void freezeVar(Var v); bool eliminate(bool turn_off_elim); bool simplify(); - int nEliminated() { return eliminated_vars; } + int nEliminated() { return eliminated_vars; } -protected: + protected: lbool solve_(const vector& assumps, vector& conflict, - int64_t confBudget, int64_t propBudget); - lbool relaxSolve_(const vector& assumps, const vector& branchLits, - int64_t propBudget); - //void analyzeFinal(Lit p, Minisat::LSet& out_conflict); + int64_t confBudget, int64_t propBudget); + lbool relaxSolve_(const vector& assumps, const vector& branchLits, + int64_t propBudget); + // void analyzeFinal(Lit p, Minisat::LSet& out_conflict); Lit pickBranchLit(); void cancelUntil(int level); void ensure_mapping(const Var ex); - void ensure_mapping(const Lit lt) { - ensure_mapping(var(lt)); - } + void ensure_mapping(const Lit lt) { ensure_mapping(var(lt)); } void updateForced(vector& frc); vector forced; - //For relaxation search - VMap relaxVarRank; //During relaxation search branch on lower ranked vars first - VMap relaxVarSign; //If true use signed literal for decisions, otherwise use unsigned literal. - struct RelaxVarOrderLt { //Operator < for heap. - const VMap& relaxVarRank; - bool operator () (Var x, Var y) const { return relaxVarRank[x] < relaxVarRank[y]; } - RelaxVarOrderLt(const VMap& rank) : relaxVarRank (rank) { } + // For relaxation search + VMap relaxVarRank; // During relaxation search branch on lower ranked + // vars first + VMap relaxVarSign; // If true use signed literal for decisions, + // otherwise use unsigned literal. + struct RelaxVarOrderLt { // Operator < for heap. + const VMap& relaxVarRank; + bool operator()(Var x, Var y) const { + return relaxVarRank[x] < relaxVarRank[y]; + } + RelaxVarOrderLt(const VMap& rank) : relaxVarRank(rank) {} }; - Heap relaxOrder_heap; //Used to select next relaxation literal to branch on (according to rank) - vector savedModel; //Used to save the current model (if relaxation search times - //out we restore the saved model) + Heap + relaxOrder_heap; // Used to select next relaxation literal to branch on + // (according to rank) + vector savedModel; // Used to save the current model (if relaxation + // search times out we restore the saved model) bool doingRelaxSearch; - bool isRelaxVar(Var v) const { return relaxVarRank.has(v) && relaxVarRank[v] < std::numeric_limits::max(); } - void insertRelaxOrder(Var x) { if (!relaxOrder_heap.inHeap(x)) relaxOrder_heap.insert(x); } + bool isRelaxVar(Var v) const { + return relaxVarRank.has(v) && + relaxVarRank[v] < std::numeric_limits::max(); + } + void insertRelaxOrder(Var x) { + if (!relaxOrder_heap.inHeap(x)) relaxOrder_heap.insert(x); + } - //External to internal mapping. - vectorin2ex_map; - vectorex2in_map; + // External to internal mapping. + vector in2ex_map; + vector ex2in_map; Lit in2ex(Lit lt) const { - if(var(lt) >= (int) in2ex_map.size() || - in2ex_map[var(lt)] == var_Undef) + if (var(lt) >= (int)in2ex_map.size() || in2ex_map[var(lt)] == var_Undef) return lit_Undef; else return mkLit(in2ex_map[var(lt)], sign(lt)); } - + Var in2ex(Var v) const { - if(v >= (int) in2ex_map.size()) + if (v >= (int)in2ex_map.size()) return var_Undef; else return in2ex_map[v]; } - - //In most applications every internal literal of the SatSolver - //is associated with an external literal on creation. - //So this array function is safe...i.e., won't add var_Undef to output - //vector. An array version of ex2in is typically not safe in this way. - //so is not provided. + // In most applications every internal literal of the SatSolver + // is associated with an external literal on creation. + // So this array function is safe...i.e., won't add var_Undef to output + // vector. An array version of ex2in is typically not safe in this way. + // so is not provided. - - void in2ex(const vec &from, vector &to) const { + void in2ex(const vec& from, vector& to) const { to.clear(); - for(int i = 0; i < from.size(); i++) - to.push_back(in2ex(from[i])); + for (int i = 0; i < from.size(); i++) to.push_back(in2ex(from[i])); } - - void in2ex(const vec &from, vector &to) const { + + void in2ex(const vec& from, vector& to) const { to.clear(); - for(int i = 0; i < from.size(); i++) - to.push_back(in2ex(from[i])); + for (int i = 0; i < from.size(); i++) to.push_back(in2ex(from[i])); } - + Var ex2in(Var v) const { - if(v >= static_cast(ex2in_map.size())) + if (v >= static_cast(ex2in_map.size())) return var_Undef; else - return ex2in_map[v]; + return ex2in_map[v]; } Lit ex2in(Lit lt) const { - if(var(lt) >= static_cast(ex2in_map.size()) || - ex2in_map[var(lt)] == var_Undef) + if (var(lt) >= static_cast(ex2in_map.size()) || + ex2in_map[var(lt)] == var_Undef) return lit_Undef; else return mkLit(ex2in_map[var(lt)], sign(lt)); } - }; template bool miniSolver::findImplicationsIf(Lit p, vector& imps, S pred) { imps.clear(); - if(!activeVar(var(p))) - return false; + if (!activeVar(var(p))) return false; Lit a = ex2in(p); trail_lim.push(trail.size()); uncheckedEnqueue(a); - + unsigned trail_before = trail.size(); - bool ret = true; + bool ret = true; if (propagate() == Minisat::CRef_Undef) { - for (int j = trail_before; j < trail.size(); j++) - if( in2ex(trail[j]) != lit_Undef && (pred(in2ex(trail[j]))) ) - imps.push_back(in2ex(trail[j])); - } - else + for (int j = trail_before; j < trail.size(); j++) + if (in2ex(trail[j]) != lit_Undef && (pred(in2ex(trail[j])))) + imps.push_back(in2ex(trail[j])); + } else ret = false; cancelUntil(0); return ret; } - -} //end namespace -#endif +} // namespace MaxHS_Iface + +#endif diff --git a/maxhs/ifaces/muser.cc b/maxhs/ifaces/muser.cc index c43903c..6a74202 100644 --- a/maxhs/ifaces/muser.cc +++ b/maxhs/ifaces/muser.cc @@ -22,11 +22,11 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ***********/ -#include -#include #include "maxhs/ifaces/muser.h" -#include "maxhs/utils/io.h" +#include +#include #include "maxhs/utils/Params.h" +#include "maxhs/utils/io.h" #ifdef GLUCOSE #include "glucose/utils/Options.h" @@ -42,287 +42,285 @@ namespace Minisat = Glucose; using namespace Minisat; -Muser::Muser(const Wcnf* f, Bvars& b): - theWcnf{f}, bvars (b), timer {0}, totalTime {0}, - prevTotalTime {0}, stime{-1}, solves {0}, succ_solves {0}, prevSolves{0}, - satSolves {0}, prevSatSolves{0} -{ +Muser::Muser(const Wcnf* f, Bvars& b) + : theWcnf{f}, + bvars(b), + timer{0}, + totalTime{0}, + prevTotalTime{0}, + stime{-1}, + solves{0}, + succ_solves{0}, + prevSolves{0}, + satSolves{0}, + prevSatSolves{0} { bool doPre = doPreprocessing(); - if(!doPre) - eliminate(true); + if (!doPre) eliminate(true); - //Initialize MUS underlying sat solver with hard clauses of Wcnf. + // Initialize MUS underlying sat solver with hard clauses of Wcnf. vec ps; - int nHards {0}; + int nHards{0}; - for(auto hc : theWcnf->hards()) { + for (auto hc : theWcnf->hards()) { ps.clear(); nHards++; - for(auto lt: hc) { + for (auto lt : hc) { ensure_mapping(var(lt)); ps.push(ex2in(lt)); } - //cout << "Muser: adding hard clause " << ps << "\n"; + // cout << "Muser: adding hard clause " << ps << "\n"; - if(!addClause_(ps)) + if (!addClause_(ps)) cout << "c WARNING Adding Hard clauses to muser caused unsat.\n"; } - if(params.mverbosity > 0) + if (params.mverbosity > 0) cout << "c Muser added " << nHards << " hard clauses\n"; - if(doPre) { - for(auto sc: theWcnf->softs()) - for(auto lt: sc) { //the muser must freeze all variables appearing in soft clauses - Var v = var(ex2in(lt)); - if(v != var_Undef) { //if var not in hards the initial eliminate will not affect - //cout << " MUSER freezing " << v << "\n"; - freezeVar(v); - } + if (doPre) { + for (auto& sc : theWcnf->softs()) + for (auto lt : sc) { // the muser must freeze all variables appearing in + // soft clauses + Var v = var(ex2in(lt)); + if (v != var_Undef) { // if var not in hards the initial eliminate will + // not affect + // cout << " MUSER freezing " << v << "\n"; + freezeVar(v); + } } + // and must freeze all mutex encoding lits + for (auto& mx : theWcnf->get_SCMxs()) + if (mx.encoding_lit() != lit_Undef) { + ensure_mapping(var(mx.encoding_lit())); + freezeVar(var(ex2in(mx.encoding_lit()))); + } + auto start = cpuTime(); eliminate(true); - if(params.verbosity > 0) - cout << "c Muser Preprocess eliminated " << eliminated_vars << " variables. took " << cpuTime()-start << " sec.\n"; + if (params.verbosity > 0) + cout << "c Muser Preprocess eliminated " << eliminated_vars + << " variables. took " << cpuTime() - start << " sec.\n"; } } bool Muser::doPreprocessing() const { - //test if we should do preprocessing. - //We have to freeze all vars appearing in the softs. So if this spans all variables we won't - //be able to eliminate any variables in preprocessing. - if(!params.preprocess) - return false; - - const int dne {0}, nfrz {1}; //markers dne==does not exist, nfrz==not frozen. - int toBeFrozen {0}, totalVars {0}; // - vector varStatus(theWcnf->nVars(), dne); - - for(auto hc: theWcnf->hards()) - for(auto lt: hc) - if(varStatus[var(lt)] == dne) { - ++totalVars; - varStatus[var(lt)] = nfrz; + // test if we should do preprocessing. + // We have to freeze all vars appearing in the softs. So if this spans all + // variables we won't be able to eliminate any variables in preprocessing. + if (!params.preprocess) return false; + + const int dne{0}, nfrz{1}; // markers dne==does not exist, nfrz==not frozen. + int toBeFrozen{0}, totalVars{0}; // + vector varStatus(theWcnf->nVars(), dne); + + for (auto hc : theWcnf->hards()) + for (auto lt : hc) + if (varStatus[var(lt)] == dne) { + ++totalVars; + varStatus[var(lt)] = nfrz; } - for(auto sc: theWcnf->softs()) - for(auto lt: sc) - if(varStatus[var(lt)] == nfrz) { - ++toBeFrozen; - varStatus[var(lt)] = dne; + for (auto sc : theWcnf->softs()) + for (auto lt : sc) + if (varStatus[var(lt)] == nfrz) { + ++toBeFrozen; + varStatus[var(lt)] = dne; } - if(params.verbosity > 0) - cout << "c Muser: Vars of hards = " << totalVars << " vars to be frozen = " << toBeFrozen << "\n"; + if (params.verbosity > 0) + cout << "c Muser: Vars of hards = " << totalVars + << " vars to be frozen = " << toBeFrozen << "\n"; return toBeFrozen < totalVars; } - -inline Lit Muser::in2ex(Lit lt) const -{ - if(var(lt) >= static_cast(in2ex_map.size()) || - in2ex_map[var(lt)] == var_Undef) +inline Lit Muser::in2ex(Lit lt) const { + if (var(lt) >= static_cast(in2ex_map.size()) || + in2ex_map[var(lt)] == var_Undef) return lit_Undef; else return mkLit(in2ex_map[var(lt)], sign(lt)); } -inline Var Muser::in2ex(Var v) const -{ - if(v >= static_cast(in2ex_map.size())) +inline Var Muser::in2ex(Var v) const { + if (v >= static_cast(in2ex_map.size())) return var_Undef; else return in2ex_map[v]; } - -inline void Muser::in2ex(const vec &from, vector &to) const -{ + +inline void Muser::in2ex(const vec& from, vector& to) const { to.clear(); - for(int i = 0; i < from.size(); i++) - to.push_back(in2ex(from[i])); + for (int i = 0; i < from.size(); i++) to.push_back(in2ex(from[i])); } - -inline void Muser::in2ex(const vec &from, vector &to) const -{ + +inline void Muser::in2ex(const vec& from, vector& to) const { to.clear(); - for(int i = 0; i < from.size(); i++) - to.push_back(in2ex(from[i])); + for (int i = 0; i < from.size(); i++) to.push_back(in2ex(from[i])); } - -inline Var Muser::ex2in(Var v) const -{ - if(v >= static_cast(ex2in_map.size())) - return var_Undef; - else - return ex2in_map[v]; + +inline Var Muser::ex2in(Var v) const { + if (v >= static_cast(ex2in_map.size())) + return var_Undef; + else + return ex2in_map[v]; } - -inline Lit Muser::ex2in(Lit lt) const -{ - if(var(lt) >= static_cast(ex2in_map.size()) || - ex2in_map[var(lt)] == var_Undef) + +inline Lit Muser::ex2in(Lit lt) const { + if (var(lt) >= static_cast(ex2in_map.size()) || + ex2in_map[var(lt)] == var_Undef) return lit_Undef; else - return mkLit(ex2in_map[var(lt)], sign(lt)); + return mkLit(ex2in_map[var(lt)], sign(lt)); } -inline bool Muser::inSolver(Lit lt) const -{ - return inSolver(var(lt)); -} +inline bool Muser::inSolver(Lit lt) const { return inSolver(var(lt)); } -inline bool Muser::inSolver(Var v) const -{ - return ex2in(v) != var_Undef; -} +inline bool Muser::inSolver(Var v) const { return ex2in(v) != var_Undef; } -void Muser::ensure_mapping(const Var ex) -{ +void Muser::ensure_mapping(const Var ex) { if (ex >= static_cast(ex2in_map.size())) - ex2in_map.resize(ex+1,var_Undef); - - if(ex2in_map[ex] == var_Undef) { - Var in {newVar()}; + ex2in_map.resize(ex + 1, var_Undef); + + if (ex2in_map[ex] == var_Undef) { + Var in{newVar()}; ex2in_map[ex] = in; if (in >= static_cast(in2ex_map.size())) - in2ex_map.resize(in+1, var_Undef); + in2ex_map.resize(in + 1, var_Undef); in2ex_map[in] = ex; } } vector Muser::getForced(int index) { - //if(params.mverbosity > 3) + // if(params.mverbosity > 3) // cout << "getForced: forced.size() = " << forced.size() << "\n"; updateForced(forced); - //if(params.mverbosity > 3) + // if(params.mverbosity > 3) // cout << "getForced new forced.size() = " << forced.size() << "\n" // << " forced = " << forced << "\n"; vector tmp; - for(size_t i = index; i < forced.size(); i++) + for (size_t i = index; i < forced.size(); i++) tmp.push_back(in2ex(forced[i])); return tmp; } void Muser::updateForced(vector& frc) { - int limit = trail_lim.size() > 0 ? - trail_lim[0] : trail.size(); - int i {0}; + int limit = trail_lim.size() > 0 ? trail_lim[0] : trail.size(); + int i{0}; - //if(params.mverbosity>3) - //cout << "updateForced: trail = " << trail << "\n" + // if(params.mverbosity>3) + // cout << "updateForced: trail = " << trail << "\n" // << " frc.size() = " << frc.size() << "\n"; - - if(frc.size() > 0) { + + if (frc.size() > 0) { i = frc.size() - 1; - //if(params.mverbosity>3) - //cout << " frc.back() = " << frc.back() << "\n"; - while(i < limit && trail[i++] != frc.back()); + // if(params.mverbosity>3) + // cout << " frc.back() = " << frc.back() << "\n"; + while (i < limit && trail[i++] != frc.back()) + ; } - //if(params.mverbosity>3) - //cout << " set i to " << i << "\n"; + // if(params.mverbosity>3) + // cout << " set i to " << i << "\n"; - for( ; i < limit; i++) - if(in2ex(trail[i]) != lit_Undef) - frc.push_back(trail[i]); + for (; i < limit; i++) + if (in2ex(trail[i]) != lit_Undef) frc.push_back(trail[i]); } -bool Muser::addClause(const vector& lts) -{ - //if a b-var is refereed to make sure we add the equivalent softcls. +bool Muser::addClause(const vector& lts) { + // if a b-var is refereed to make sure we add the equivalent softcls. vec ps; - for(auto lt: lts) { - if(bvars.isBvar(lt)) + for (auto lt : lts) { + if (bvars.isBvar(lt)) ensureSoftCls(lt); else ensure_mapping(lt); ps.push(ex2in(lt)); } - //cout << "muser addCls: ext=" << lts << " int=" << ps << "\n"; - return addClause_(ps); + // cout << "muser addCls: ext=" << lts << " int=" << ps << "\n"; + return addClause_(ps); } lbool Muser::curVal(Var x) const { Var in = ex2in(x); - if(in == var_Undef) + if (in == var_Undef) return l_Undef; - else + else return value(in); } lbool Muser::curVal(Lit x) const { Lit in = ex2in(x); - if(in == lit_Undef) + if (in == lit_Undef) return l_Undef; - else + else return value(in); } void Muser::ensureSoftCls(vector& conflict) { - //Ensure that all soft clauses referenced by conflict are in the solver. - //This means that we incrementally add only the soft clauses of the theory - //that are actually involved in minimizations. - for(auto blit : conflict) - ensureSoftCls(blit); + // Ensure that all soft clauses referenced by conflict are in the solver. + // This means that we incrementally add only the soft clauses of the theory + // that are actually involved in minimizations. + for (auto blit : conflict) ensureSoftCls(blit); } void Muser::ensureSoftCls(Lit blit) { - //Ensure that all soft clauses referenced by conflict are in the solver. - //This means that we incrementally add only the soft clauses of the theory - //that are actually involved in minimizations. + // Ensure that all soft clauses referenced by conflict are in the solver. + // This means that we incrementally add only the soft clauses of the theory + // that are actually involved in minimizations. vec ps; vec eqCls; - if(inSolver(blit)) - return; - + if (inSolver(blit)) return; + + /* if(!bvars.isBvar(blit)) - cout << "c ERROR conflict does not contain only bvars\n"; - + cout << "c ERROR conflict does not contain only bvars: " << blit<< "\n"; + */ auto clsI = bvars.clsIndex(blit); - //if(params.mverbosity > 4) - //cout << "ensureSoftCls adding sft clause" + // if(params.mverbosity > 4) + // cout << "ensureSoftCls adding sft clause" // << theWcnf->getSoft(clsI) << "\n"; - - Lit b = bvars.litOfCls(clsI); //note that blit might be ~bvars.litOfCls(clsI) + + Lit b = bvars.litOfCls(clsI); // note that blit might be + // ~bvars.litOfCls(clsI) ensure_mapping(b); Lit inbPos = ex2in(b); ps.clear(); - ps.push(inbPos); - for(auto lt: theWcnf->softs()[clsI]) { + ps.push(inbPos); + for (auto lt : theWcnf->softs()[clsI]) { ensure_mapping(lt); Lit inLt = ex2in(lt); ps.push(inLt); eqCls.clear(); - eqCls.push(~inbPos); eqCls.push(~inLt); - //if(params.mverbosity > 4) - //cout << "eq cls: [ " << eqCls[0] << "/" << value(eqCls[0]) + eqCls.push(~inbPos); + eqCls.push(~inLt); + // if(params.mverbosity > 4) + // cout << "eq cls: [ " << eqCls[0] << "/" << value(eqCls[0]) // << " " << eqCls[1] << "/" << value(eqCls[1]) << " ]\n"; - if(!addClause_(eqCls)) + if (!addClause_(eqCls)) cout << "c ERROR minimize equality clause caused UNSAT\n"; } - //if(params.mverbosity > 4) { + // if(params.mverbosity > 4) { // cout << "Adding sft cls: ["; - // for(int i = 0; i < ps.size(); i++) + // for(int i = 0; i < ps.size(); i++) // cout << ps[i] << "/" << value(ps[i]) << " "; // cout << "]\n"; // } - - if(!addClause_(ps)) + if (!addClause_(ps)) cout << "c ERROR Adding soft clauses of conflict to muser caused unsat.\n"; } void Muser::preProcessConflict(vector& conflict) { - //Convert conflict to internal variables. Remove any false variables. - //If member of conflict is true in MUSER, that literal is a minimal - //conflict. + // Convert conflict to internal variables. Remove any false variables. + // If member of conflict is true in MUSER, that literal is a minimal + // conflict. - size_t j {0}; - for(auto lt : conflict) { + size_t j{0}; + for (auto lt : conflict) { auto val = value(ex2in(lt)); - if(val == l_Undef) + if (val == l_Undef) conflict[j++] = ex2in(lt); - else if(val == l_True) { - conflict = vector { ex2in(lt) }; + else if (val == l_True) { + conflict = vector{ex2in(lt)}; return; } } @@ -330,175 +328,171 @@ void Muser::preProcessConflict(vector& conflict) { } bool Muser::mus_(vector& conflict, int64_t propBudget) { - //reduce conflict to a MUS, return true if we succeeded. - //don't use more than probBudget props to do so. - bool isMus {true}; + // reduce conflict to a MUS, return true if we succeeded. + // don't use more than probBudget props to do so. + bool isMus{true}; ensureSoftCls(conflict); preProcessConflict(conflict); - if(conflict.size() <= 1) { - if(conflict.size() == 0) - cout << "c ERROR: MUSER found false conflict\n"; - if(conflict.size() == 1) { + if (conflict.size() <= 1) { + if (conflict.size() == 0) cout << "c ERROR: MUSER found false conflict\n"; + if (conflict.size() == 1) { cout << "c MUSER found unit conflict on input\n"; conflict[0] = in2ex(conflict[0]); } return isMus; } - auto notInCon = [this](Lit l){ return !SimpSolver::conflict.has(l); }; + auto notInCon = [this](Lit l) { return !SimpSolver::conflict.has(l); }; bool haveBudget = propBudget > 0; - int64_t perTestBudget = propBudget/16; + int64_t perTestBudget = propBudget / 16; uint64_t initialProps = propagations; assumptions.clear(); - while(conflict.size() > 0) { - if(haveBudget && (propagations - initialProps >= static_cast(propBudget))) { //timed out - for(size_t i = 0; i < conflict.size(); i++) - //act like all remaining conflict lits are critical and end loop - assumptions.push(~conflict[i]); + while (conflict.size() > 0) { + if (haveBudget && (propagations - initialProps >= + static_cast(propBudget))) { // timed out + for (size_t i = 0; i < conflict.size(); i++) + // act like all remaining conflict lits are critical and end loop + assumptions.push(~conflict[i]); conflict.clear(); isMus = false; - } - else { - //test next conflict literal for criticality + } else { + // test next conflict literal for criticality Lit test = conflict.back(); conflict.pop_back(); - //lits in assumptions are already known to be critical - int ncrits = assumptions.size(); - for(size_t i = 0; i < conflict.size(); i++) - assumptions.push(~conflict[i]); + // lits in assumptions are already known to be critical + int ncrits = assumptions.size(); + for (size_t i = 0; i < conflict.size(); i++) + assumptions.push(~conflict[i]); setPropBudget(haveBudget, perTestBudget); auto val = solve_(); satSolves++; - if(params.mverbosity > 3) { - cout << "Conflict = " << conflict << "\n"; + if (params.mverbosity > 3) { + cout << "Conflict = " << conflict << "\n"; cout << "Assumptions = " << assumptions << "\n"; - cout << "Prop budget for test " << perTestBudget << "\n"; + cout << "Prop budget for test " << perTestBudget << "\n"; cout << "test = " << test << " val = " << val << "\n"; } - - assumptions.shrink(assumptions.size() - ncrits); //restore assumptions to crits only - if(val == l_Undef) { //this test timed out. - assumptions.push(~test); //assume test is critical. - isMus = false; //don't know if we have a mus any more. - } - else if(val == l_False) { //redundant - auto p = std::remove_if(conflict.begin(), conflict.end(), notInCon); - conflict.erase(p, conflict.end()); - } - else { // l_True: - assumptions.push(~test); - addMoreCrits(conflict, haveBudget ? perTestBudget : -1); + + assumptions.shrink(assumptions.size() - + ncrits); // restore assumptions to crits only + if (val == l_Undef) { // this test timed out. + assumptions.push(~test); // assume test is critical. + isMus = false; // don't know if we have a mus any more. + } else if (val == l_False) { // redundant + auto p = std::remove_if(conflict.begin(), conflict.end(), notInCon); + conflict.erase(p, conflict.end()); + // if (bvars.isTvar(test) && bvars.nextOutput(test) != lit_Undef) + // conflict.push_back(bvars.nextOutput(test)); + } else { // l_True: + assumptions.push(~test); + addMoreCrits(conflict, haveBudget ? perTestBudget : -1); } } } - + assert(conflict.size() == 0); - //convert assumptions back into a conflict. - for(int i = 0; i < assumptions.size(); i++) + // convert assumptions back into a conflict. + for (int i = 0; i < assumptions.size(); i++) conflict.push_back(in2ex(~assumptions[i])); - - //if(params.mverbosity > 1) + + // if(params.mverbosity > 1) // cout << "mus_ returns conflict: " << conflict << "\n"; - + return isMus; } -void Muser::addMoreCrits(vector& conflict, int64_t propBudget) -{ - if(conflict.size() <= 1) - return; +void Muser::addMoreCrits(vector& conflict, int64_t propBudget) { + if (conflict.size() <= 1) return; - int critsFnd {0}; + int critsFnd{0}; - //Insert removable most one constraint over conflicts to more criticals + // Insert removable most one constraint over conflicts to more criticals int aInitSize = assumptions.size(); vector clits = addAmoUnk(conflict); lbool critVal; vector isCrit(conflict.size(), false); - - assumptions.push(~clits[0]); //activate at-most-one + + assumptions.push(~clits[0]); // activate at-most-one setPropBudget(propBudget > 0, propBudget); - while((critVal = solve_()) == l_True) { + while ((critVal = solve_()) == l_True) { satSolves++; - for(size_t j = 0; j < conflict.size(); j++) { - if(modelValue(conflict[j]) == l_True) { - isCrit[j] = true; - assumptions.push(~conflict[j]); - break; + for (size_t j = 0; j < conflict.size(); j++) { + if (modelValue(conflict[j]) == l_True) { + isCrit[j] = true; + assumptions.push(~conflict[j]); + break; } } } satSolves++; - //Note that clits don't go through the ex-to-in interface. They are internal variables only. - for(auto lt : clits) - releaseVar(lt); + // Note that clits don't go through the ex-to-in interface. They are internal + // variables only. + for (auto lt : clits) releaseVar(lt); assumptions.shrink(assumptions.size() - aInitSize); - size_t j {0}; //move lits detected to be critical into assumptions - for(size_t i = 0; i < conflict.size(); i++) { - if(isCrit[i]) { + size_t j{0}; // move lits detected to be critical into assumptions + for (size_t i = 0; i < conflict.size(); i++) { + if (isCrit[i]) { critsFnd++; assumptions.push(~conflict[i]); - } - else + } else conflict[j++] = conflict[i]; } conflict.resize(j); - if(critVal == l_False && conflict.size() > 0) - //If we detected unsat even when any one of the remaining conflict lits - //was allowed to be relaxed, then we can remove any one of these lits. - conflict.pop_back(); + if (critVal == l_False && conflict.size() > 0) + // If we detected unsat even when any one of the remaining conflict lits + // was allowed to be relaxed, then we can remove any one of these lits. + conflict.pop_back(); - if(params.mverbosity>1) - cout << "addMoreCrits added " << critsFnd << "\n"; + if (params.mverbosity > 1) cout << "addMoreCrits added " << critsFnd << "\n"; } vector Muser::addAmoUnk(vector& unknowns) { - //Add an at-most-one constraint to the solver. + // Add an at-most-one constraint to the solver. // at most one unknown lit can be TRUE. - //In: unknowns.size() > 1 - //Out: clits[0] is the control lit that deletes all clauses + // In: unknowns.size() > 1 + // Out: clits[0] is the control lit that deletes all clauses // clits[1]...are the other auxiliary variables // added to encode the constraint. - + vector clits; - for(size_t i = 0; i < unknowns.size(); i++) { + for (size_t i = 0; i < unknowns.size(); i++) { Var c = newVar(l_Undef, false); clits.push_back(mkLit(c, false)); - if(c >= static_cast(in2ex_map.size())) - in2ex_map.resize(c+1, var_Undef); //space reserved in in2ex_map for these internal only vars + if (c >= static_cast(in2ex_map.size())) + in2ex_map.resize(c + 1, var_Undef); // space reserved in in2ex_map for + // these internal only vars } - + vec cls(3); - for(size_t i = 0; i < unknowns.size()-1 ; i++) { - cls[0] = ~unknowns[i]; //unk_i --> clit_i+1 - cls[1] = clits[i+1]; //~clit_i+1 >> ~unk_i + for (size_t i = 0; i < unknowns.size() - 1; i++) { + cls[0] = ~unknowns[i]; // unk_i --> clit_i+1 + cls[1] = clits[i + 1]; //~clit_i+1 >> ~unk_i cls[2] = clits[0]; addClause_(cls); } - - for(size_t i = 1; i < clits.size()-1 ; i++) { - cls[0] = ~clits[i]; //clit_i --> clit_i+1 - cls[1] = clits[i+1]; //~clit_i+1 --> ~clit_i + + for (size_t i = 1; i < clits.size() - 1; i++) { + cls[0] = ~clits[i]; // clit_i --> clit_i+1 + cls[1] = clits[i + 1]; //~clit_i+1 --> ~clit_i cls[2] = clits[0]; addClause_(cls); } - - for(size_t i = 1; i < clits.size(); i++) { - cls[0] = ~clits[i]; //clit_i --> ~unk_i - cls[1] = ~unknowns[i]; //unk_i --> ~clit_i - cls[2] = clits[0]; + + for (size_t i = 1; i < clits.size(); i++) { + cls[0] = ~clits[i]; // clit_i --> ~unk_i + cls[1] = ~unknowns[i]; // unk_i --> ~clit_i + cls[2] = clits[0]; addClause_(cls); } return clits; } - -//REMOVE FOR NOW +// REMOVE FOR NOW #if 0 void Muser::analyzeFinal(Lit p, LSet& out_conflict) { @@ -539,6 +533,3 @@ void Muser::analyzeFinal(Lit p, LSet& out_conflict) seen[var(p)] = 0; } #endif - - - diff --git a/maxhs/ifaces/muser.h b/maxhs/ifaces/muser.h index 25ce03f..420de13 100644 --- a/maxhs/ifaces/muser.h +++ b/maxhs/ifaces/muser.h @@ -25,9 +25,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* muser---compute a mus for maxhs. TODO?: The timer/budget interface is idential to Satsolver The internal to external mapping is identical to minisatsolver - Don't want to inherit from these classes as the functionality - for muser is quite different. Potentially these functions could - be abstracted so that they can be shared. + Don't want to inherit from these classes as the functionality + for muser is quite different. Potentially these functions could + be abstracted so that they can be shared. */ #ifndef MUSER_H @@ -36,26 +36,26 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #ifdef GLUCOSE -#include "glucose/simp/SimpSolver.h" #include "glucose/core/SolverTypes.h" +#include "glucose/simp/SimpSolver.h" #else -#include "minisat/simp/SimpSolver.h" #include "minisat/core/SolverTypes.h" +#include "minisat/simp/SimpSolver.h" #endif -#include "maxhs/core/Wcnf.h" #include "maxhs/core/Bvars.h" +#include "maxhs/core/Wcnf.h" #ifdef GLUCOSE namespace Minisat = Glucose; #endif -using Minisat::Lit; +using Minisat::cpuTime; +using Minisat::l_False; +using Minisat::l_True; +using Minisat::l_Undef; using Minisat::lbool; +using Minisat::Lit; using Minisat::Var; -using Minisat::l_Undef; -using Minisat::l_True; -using Minisat::l_False; -using Minisat::cpuTime; using Minisat::vec; using std::vector; @@ -63,16 +63,16 @@ using std::vector; namespace MaxHS_Iface { class Muser : protected Minisat::SimpSolver { -public: + public: Muser(const Wcnf* f, Bvars& b); virtual ~Muser() {} - + bool musBudget(vector& conflict, int64_t propBudget) { /******************************************************************* Reduce a conflict (a set of blits) to a MUS. Try to remove elements of conflict in the order given (try removing conflict[0] before conflict[1] etc.). So if there is a priority for - reductions pass a sorted conflict vector. + reductions pass a sorted conflict vector. Set total propagation budget for computing the MUS. (-1 = no budget). @@ -85,49 +85,49 @@ class Muser : protected Minisat::SimpSolver { totalTime += cpuTime() - stime; stime = -1; solves++; - if(val) - succ_solves++; - return val; + if (val) succ_solves++; + return val; } - - bool musBudget(vector& conflict, double timeLimit) { + + bool musBudget(vector& conflict, double timeLimit) { int64_t propBudget, props; - props = getProps(); //returns total number of props done by solver - bool didTrial {false}; + props = getProps(); // returns total number of props done by solver + bool didTrial{false}; - if(props > 0 && totalTime > 0) - propBudget = props/totalTime * timeLimit; + if (props > 0 && totalTime > 0) + propBudget = props / totalTime * timeLimit; else { - propBudget = 1024*1024*10; + propBudget = 1024 * 1024 * 10; didTrial = true; } stime = cpuTime(); - prevTotalTime = totalTime; + prevTotalTime = totalTime; auto val = mus_(conflict, propBudget); solves++; - if(val) + if (val) succ_solves++; else { double solvetime1 = cpuTime() - stime; - if(didTrial && !val && solvetime1 < timeLimit*.60) { - auto moreProps = int64_t((getProps()-props)/solvetime1 * timeLimit - propBudget); - if (moreProps > propBudget*0.5) { - //try again - val = mus_(conflict, moreProps); - solves++; - if(val) - succ_solves++; - } + if (didTrial && !val && solvetime1 < timeLimit * .60) { + auto moreProps = + int64_t((getProps() - props) / solvetime1 * timeLimit - propBudget); + if (moreProps > propBudget * 0.5) { + // try again + val = mus_(conflict, moreProps); + solves++; + if (val) succ_solves++; + } } } totalTime += cpuTime() - stime; stime = -1; - return val; + return val; + } + + bool mus(vector& conflict) { + return musBudget(conflict, static_cast(-1)); } - - bool mus(vector& conflict) - { return musBudget(conflict, static_cast(-1)); } vector getForced(int index); void updateForced(vector& frc); @@ -135,62 +135,63 @@ class Muser : protected Minisat::SimpSolver { lbool curVal(Var x) const; lbool curVal(Lit x) const; - - //stats - //Use startTimer and elapTime to accumulate time over a set of muser calls. - //nSolves() counts number of calls since startTimer - //solveTime for time of most recent call. - void startTimer() { timer = totalTime; prevSolves = solves; prevSatSolves = satSolves; } - double elapTime() { return totalTime-timer; } - double solveTime() { return totalTime-prevTotalTime; } + // stats + // Use startTimer and elapTime to accumulate time over a set of muser calls. + // nSolves() counts number of calls since startTimer + // solveTime for time of most recent call. + void startTimer() { + timer = totalTime; + prevSolves = solves; + prevSatSolves = satSolves; + } + double elapTime() { return totalTime - timer; } + double solveTime() { return totalTime - prevTotalTime; } double total_time() { - if (stime >=0) { + if (stime >= 0) { totalTime += cpuTime() - stime; stime = -1; } return totalTime; } - int nSolvesSinceTimer() { return (int) solves-prevSolves; } - int nSolves() { return (int) solves; } - int nSucc_Solves() { return (int) succ_solves; } - int nSatSolvesSinceTimer() { return (int) satSolves - prevSatSolves; } - int nSatSolves() { return (int) satSolves; } - + int nSolvesSinceTimer() { return (int)solves - prevSolves; } + int nSolves() { return (int)solves; } + int nSucc_Solves() { return (int)succ_solves; } + int nSatSolvesSinceTimer() { return (int)satSolves - prevSatSolves; } + int nSatSolves() { return (int)satSolves; } + protected: const Wcnf* theWcnf; Bvars& bvars; vector forced; - //Stats. + // Stats. double timer, totalTime, prevTotalTime, stime; double m_timer, m_stime, m_prevTotalTime, m_totalTime; - + int solves, succ_solves, prevSolves, satSolves, prevSatSolves; - //External to internal variable mapping. - vectorin2ex_map; - vectorex2in_map; + // External to internal variable mapping. + vector in2ex_map; + vector ex2in_map; void ensure_mapping(Var ex); - void ensure_mapping(Lit lt) { - ensure_mapping(var(lt)); - } + void ensure_mapping(Lit lt) { ensure_mapping(var(lt)); } Lit in2ex(Lit lt) const; Var in2ex(Var v) const; - - //In most applications every internal literal of the SatSolver - //is associated with an external literal on creation. - //So this array function is safe...i.e., won't add var_Undef to output - //vector. An array version of ex2in is typically not safe in this way - //so is not provided. - - void in2ex(const vec &from, vector &to) const; - void in2ex(const vec &from, vector &to) const; + + // In most applications every internal literal of the SatSolver + // is associated with an external literal on creation. + // So this array function is safe...i.e., won't add var_Undef to output + // vector. An array version of ex2in is typically not safe in this way + // so is not provided. + + void in2ex(const vec& from, vector& to) const; + void in2ex(const vec& from, vector& to) const; Var ex2in(Var v) const; Lit ex2in(Lit lt) const; - //computation routines + // computation routines bool doPreprocessing() const; bool inSolver(Lit lt) const; bool inSolver(Var v) const; @@ -201,11 +202,11 @@ class Muser : protected Minisat::SimpSolver { vector addAmoUnk(vector& unknowns); void addMoreCrits(vector& conflict, int64_t propBudget); void preProcessConflict(vector& conflict); - //void analyzeFinal(Lit p, Minisat::LSet& out_conflict); + // void analyzeFinal(Lit p, Minisat::LSet& out_conflict); void setPropBudget(bool haveBudget, int64_t propBudget) { propagation_budget = haveBudget ? propBudget + propagations : -1; } -}; //Class Muser - -} //namesspace -#endif +}; // Class Muser + +} // namespace MaxHS_Iface +#endif diff --git a/maxhs/utils/Params.cc b/maxhs/utils/Params.cc index f258138..1a7adb7 100644 --- a/maxhs/utils/Params.cc +++ b/maxhs/utils/Params.cc @@ -1,4 +1,4 @@ -/***********[Params.cc] +/*****[Params.cc] Copyright (c) 2012-2013 Jessica Davies, Fahiem Bacchus Permission is hereby granted, free of charge, to any person obtaining a @@ -28,8 +28,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #endif #include "maxhs/utils/Params.h" -#include + #include +#include using std::cout; #ifdef GLUCOSE @@ -38,170 +39,351 @@ namespace Minisat = Glucose; using namespace Minisat; static const char* maxhs = "A: General MaxHS"; -static const char* disjoint = "B: Disjoint Phase"; -static const char* seed = "C: Seeding"; -static const char* seqOfSat = "D: Sequence of Sat"; -static const char* muser = "E: Core Minimization"; -static const char* cplex = "F: CPLEX"; -static const char* pop = "G: CPLEX Solution Pool and Populate"; -static const char* pre = "H: Preprocessing"; -static const char* debug = "I: Debugging"; - - -//General Controls -static IntOption opt_verb(maxhs, "verb", "Verbosity level (0=silent, 1=some, 2=more, " - "3=debugging output, 4=more debugging output).", 1, IntRange(0, 5)); -static BoolOption opt_bvardecisions(maxhs, "bvardecisions", "FB: make bvars decision variables.", false); -static BoolOption opt_fbeq(maxhs, "fbeq", "FB: Use FbEq theory. Independent of \"coretype\"", false); -static BoolOption opt_printOptions(maxhs, "printOptions", "Print paramater settings", true); -static BoolOption opt_printBstSoln(maxhs, "printBstSoln", "Print best solution found", false); -static BoolOption opt_printSoln(maxhs, "printSoln", "Print solution", true); -static DoubleOption opt_tolerance(maxhs, "tolerance", "For floating point weights only: return solution when when |soln-cost - lower bound| <= tolerance\n", - 1e-6, DoubleRange(0.0, true, std::numeric_limits::max(), true)); - -//Muser Controls -static IntOption opt_mintype(muser, "mintype", "JD: 0 = no minimization of " - "constraints/cores, 1 = Use Muser", 1, IntRange(0, 1)); -static DoubleOption opt_mus_cpu_lim(muser, "mus-cpu-lim", - "FB: CPU time limit for minimizing each core (-1 == no limit).", - 2.5, DoubleRange(-1.0, true, std::numeric_limits::max(), true)); -static DoubleOption opt_mus_min_red(muser, "mus-min-red", - "FB: Run muser only if on average it can remove at least this " - "fraction of a core (-1 == no limit). (eventually the muser is turned off)", - 0.10, DoubleRange(-1.0, true, 1.0, true)); -static IntOption muser_verb(muser, "mverb", "Muser verbosity level (0=silent, 1=some, 2=more,3=debugging output, 4=more debugging output).", 0, IntRange(0, 4)); - - - - - -//Disjoint Phase Controls -static BoolOption opt_dsjnt(disjoint, "dsjnt", "JD: Find disjoint cores in a first phase.", true); - -static DoubleOption opt_dsjnt_cpu_per_core(disjoint, "dsjnt-cpu-lim", - "FB: CPU time limit for finding each disjoint cores (-1 == no limit).", - 30.0, DoubleRange(-1.0, true, std::numeric_limits::max(), true)); -static DoubleOption opt_dsjnt_mus_cpu_lim(disjoint, "dsjnt-mus-cpu-lim", - "FB: CPU time limit for minimizing each *disjoint* core (-1 == no limit).", - 10.0, DoubleRange(-1.0, true, std::numeric_limits::max(), true)); +static const char* abstract = "B: MaxHS with abstractions"; +static const char* disjoint = "C: Disjoint Phase"; +static const char* seed = "D: Seeding"; +static const char* seqOfSat = "E: Sequence of Sat"; +static const char* muser = "F: Core Minimization"; +static const char* cplex = "G: CPLEX"; +static const char* pop = "H: CPLEX Solution Pool and Populate"; +static const char* pre = "I: Preprocessing"; +static const char* debug = "J: Debugging"; + +// General Controls +static IntOption opt_verb(maxhs, "verb", + "Verbosity level (0=silent, 1=some, 2=more, " + "3=debugging output, 4=more debugging output).", + 1, IntRange(0, 5)); +static BoolOption opt_bvardecisions(maxhs, "bvardecisions", + "FB: make bvars decision variables.", + false); +static BoolOption opt_fbeq(maxhs, "fbeq", + "FB: Use FbEq theory. Independent of \"coretype\"", + false); +static BoolOption opt_printOptions(maxhs, "printOptions", + "Print paramater settings", true); +static BoolOption opt_printBstSoln(maxhs, "printBstSoln", + "Print best solution found", false); +static BoolOption opt_printSoln(maxhs, "printSoln", "Print solution", false); +static BoolOption opt_printNewFormat(maxhs, "printSoln-new-format", + "Print solution in new format", true); + +static DoubleOption opt_tolerance( + maxhs, "tolerance", + "For floating point weights only: return solution when when |soln-cost - " + "lower bound| <= tolerance\n", + 1e-6, DoubleRange(0.0, true, std::numeric_limits::max(), true)); + +// Muser Controls +static IntOption opt_mintype(muser, "mintype", + "JD: 0 = no minimization of " + "constraints/cores, 1 = Use Muser", + 1, IntRange(0, 1)); +static DoubleOption opt_mus_cpu_lim( + muser, "mus-cpu-lim", + "FB: CPU time limit for minimizing each core (-1 == no limit).", 2.5, + DoubleRange(-1.0, true, std::numeric_limits::max(), true)); +static DoubleOption opt_mus_min_red( + muser, "mus-min-red", + "FB: Run muser only if on average it can remove at least this " + "fraction of a core (-1 == no limit). (eventually the muser is turned off)", + 0.10, DoubleRange(-1.0, true, 1.0, true)); +static IntOption muser_verb( + muser, "mverb", + "Muser verbosity level (0=silent, 1=some, 2=more,3=debugging output, " + "4=more debugging output).", + 0, IntRange(0, 4)); + +static BoolOption opt_abstract(abstract, "abstract", "JB: abstract cores", + true); +static BoolOption opt_abstract_cores2greedy( + maxhs, "abstract-cores2greedy", "JB: convert cores to abstract cores", + false); + +static DoubleOption opt_abstract_max_ave_size( + abstract, "abstract-max-ave-size", + "Don't do abstractions if average core size is greater than this limit " + "(-1==no limit)", + 100.0, DoubleRange(-1.0, true, std::numeric_limits::max(), true)); + +static IntOption opt_abstract_cplex_cores( + abstract, "abstract-cplex_cores", + "Generate cores from abstracted cplex solution (0=cores from non-abstract " + "soln only, 1=cores " + "from abstract soln only, 2=cores from both abstract and non-abstract soln", + 2, IntRange(0, 2)); +static IntOption opt_abstract_greedy_cores( + abstract, "abstract-greedy_cores", + "Generate cores from abstracted greedy solution (0=cores from non-abstract " + "soln only, 1=cores " + "from abstract soln only, 2=cores from both abstract and non-abstract soln", + 2, IntRange(0, 2)); +static IntOption opt_cplexgreedy( + abstract, "cplex-greedy", + "JB: do greedy solving with cplex. (0=not at all, 1=always, 2=only after " + "adding totalizers)", + 0, IntRange(0, 2)); +static IntOption opt_abstract_min_size( + abstract, "abstract-minsize", + "JB: minimum-size of totalizer before adding them", 2, + IntRange(1, std::numeric_limits::max())); +static IntOption opt_abstract_max_core_size( + abstract, "abstract-max_core_size", + "JB: max-size of core to consider when abstracting", 1000, + IntRange(1, std::numeric_limits::max())); +static IntOption opt_abstract_min_cores( + abstract, "abstract-min-cores", + "Only allow softs to be clustered into abstractions when they appear in " + "this minimum number of cores", + 2, IntRange(0, std::numeric_limits::max())); +static DoubleOption opt_cpu_per_exhaust( + abstract, "exhaust-cpu-lim", + "JB: CPU time limit for exhausting totalizers (-1 == no limit).", 60.0, + DoubleRange(-1.0, true, std::numeric_limits::max(), true)); +static DoubleOption opt_cpu_per_exhaust_all_clauses( + abstract, "exhaust-cpu-lim-all-clauses", + "JB: CPU time limit for exhausting totalizers (-1 == no limit) when all " + "clauses seeded.", + 15.0, DoubleRange(-1.0, true, std::numeric_limits::max(), true)); +static DoubleOption opt_abstract_gap( + abstract, "abstract-gap", + "If CPLEX does not improve the gap between UB and LB by this amount we try " + "abstracting", + 5.0, DoubleRange(0.0, true, std::numeric_limits::max(), true)); + +// Disjoint Phase Controls +static BoolOption opt_dsjnt(disjoint, "dsjnt", + "JD: Find disjoint cores in a first phase.", true); + +static DoubleOption opt_dsjnt_cpu_per_core( + disjoint, "dsjnt-cpu-lim", + "FB: CPU time limit for finding each disjoint cores (-1 == no limit).", + 30.0, DoubleRange(-1.0, true, std::numeric_limits::max(), true)); + +static DoubleOption opt_dsjnt_mus_cpu_lim( + disjoint, "dsjnt-mus-cpu-lim", + "FB: CPU time limit for minimizing each *disjoint* core (-1 == no limit).", + 10.0, DoubleRange(-1.0, true, std::numeric_limits::max(), true)); // Noncore and Seeding Options -static BoolOption opt_seed_learnts(seed, "seed-learnts", "FB: seed any learnts available when seeding is performed.", true); - -static IntOption opt_coretype(maxhs, "coretype", "JD: Type of constraints to learn and" - " feed to CPLEX (0 = core constraints only) (1 = mixed constraints).", 0, IntRange(0,1)); -static IntOption opt_seedtype(seed, "seedtype", "FB: Type of seeded constraints allowed, 0 = no seeding, 1 = cores only, 2 = also allow non-cores, 3 = also allow mixed constraints", 3, - IntRange(0,3)); -static IntOption opt_maxseeds(seed, "seed-max", "FB: maximum number of seeded constraints", 1024*512, - IntRange(0, std::numeric_limits::max())); - -static IntOption opt_seed_all_limit(seed, "seed-all-limit", "If the total number of variables is <= this limit then seed all clauses CPLEX (subject to \"seed-max\" limit...CPLEX will do most of the solving but SAT might assist in finding feasible solutions", 256*3, IntRange(0, std::numeric_limits::max())); - -//Populate and Soln Pool +static BoolOption opt_seed_learnts( + seed, "seed-learnts", + "FB: seed any learnts available when seeding is performed.", true); + +static IntOption opt_coretype( + maxhs, "coretype", + "JD: Type of constraints to learn and" + " feed to CPLEX (0 = core constraints only) (1 = mixed constraints).", + 0, IntRange(0, 1)); +static IntOption opt_seedtype( + seed, "seedtype", + "FB: Type of seeded constraints allowed, 0 = no seeding, 1 = cores only, 2 " + "= also allow non-cores, 3 = also allow mixed constraints", + 3, IntRange(0, 3)); +static IntOption opt_maxseeds(seed, "seed-max", + "FB: maximum number of seeded constraints", + 1024 * 512, + IntRange(0, std::numeric_limits::max())); + +static IntOption opt_seed_all_limit( + seed, "seed-all-limit", + "If the total number of variables is <= this limit then seed all clauses " + "CPLEX (subject to \"seed-max\" limit...CPLEX will do most of the solving " + "but SAT might assist in finding feasible solutions", + 256 * 2, IntRange(0, std::numeric_limits::max())); + +static DoubleOption opt_seed_all_cpu_before_cplex( + seed, "seed_cpu_before_cplex", + "CPU time limit before calling cplex when all clauses seeded", 200.0, + DoubleRange(-1.0, true, std::numeric_limits::max(), true)); + +// Populate and Soln Pool // limit -static IntOption opt_cplex_solnpool_cap(pop, "cplex-solnpool-cap", "Set the capacity of cplex solution pool", 256, IntRange(0,2100000000)); - -static IntOption opt_cplex_pop_nsoln(pop, "cplex-pop-nsoln", "Set the size of cplex population pool", 512/2, IntRange(0,std::numeric_limits::max())); - -static DoubleOption opt_cplex_pop_cpu_lim(pop, "cplextime-pop-cpu-lim", "CPU time limit on cplex populate (-1 == no limit)", 7.5, DoubleRange(-1.0, true, std::numeric_limits::max(), true)); - -static IntOption opt_trypop(pop, "cplex-populate", "Use cplex populate to obtain more solutions (0=never) (1=when potentially useful) (2=always)", - 1, IntRange(0,2)); -static IntOption opt_conflicts_from_ub(pop, "ub-conflicts", "FB: Generate conflicts from upper bound (0=neve) (1=when potentially useful) (2=always)", 1, IntRange(0,2)); +static IntOption opt_cplex_solnpool_cap( + pop, "cplex-solnpool-cap", "Set the capacity of cplex solution pool", 256, + IntRange(0, 2100000000)); + +static IntOption opt_cplex_pop_nsoln( + pop, "cplex-pop-nsoln", "Set the size of cplex population pool", 512 / 2, + IntRange(0, std::numeric_limits::max())); + +static DoubleOption opt_cplex_pop_cpu_lim( + pop, "cplextime-pop-cpu-lim", + "CPU time limit on cplex populate (-1 == no limit)", 7.5, + DoubleRange(-1.0, true, std::numeric_limits::max(), true)); + +static IntOption opt_trypop(pop, "cplex-populate", + "Use cplex populate to obtain more solutions " + "(0=never) (1=when potentially useful) (2=always)", + 1, IntRange(0, 2)); +static IntOption opt_conflicts_from_ub( + pop, "ub-conflicts", + "FB: Generate conflicts from upper bound (0=neve) (1=when potentially " + "useful) (2=always)", + 1, IntRange(0, 2)); // Sequence of Sat Options -static DoubleOption opt_optcores_cpu_per(seqOfSat, "optcores-cpu-lim", - "FB: CPU time limit for finding each additional core (-1 == no limit).", - 10, DoubleRange(-1.0, true, std::numeric_limits::max(), true)); -static IntOption opt_nonopt(seqOfSat, "nonopt", "JD: Method for relaxing clauses of current " - "core (0 = pick a random clause, 1 = pick clause appearing in most cores" - ", 2 = relax a fraction of each core (set fraction with \"relaxfrac\" parameter), 3 = remove all clauses in core making next core disjoint.", - 3, IntRange(0,3)); - -static DoubleOption opt_relaxfrac(seqOfSat, "relaxfrac", "FB: After accumulating frac-rampup-end clauses " - "relax this fraction of current core, picking clauses most frequently " - "occuring in cores (must have \"nonopt=2\").", 0.3, DoubleRange(0, false, 1.0, true)); -static IntOption opt_frac_rampup_start(seqOfSat, "frac-rampup-start", "FB: When nonopt = 2 (relax a fraction) relax only" - " one clause until this many cores accumulated", 128, IntRange(0,std::numeric_limits::max())); -static IntOption opt_frac_rampup_end(seqOfSat, "frac-rampup-end", "FB: When nonopt = 2 (relax a fraction) increase " - "fract of core relaxed linearly to reach final \"relaxfrac\" after this many cores accumulated", - 512, IntRange(0,std::numeric_limits::max())); -static IntOption opt_max_cores_before_cplex(seqOfSat, "max-cores-before-cplex", - "FB: Force a call to Cplex after this many constraints", - 1024+512, IntRange(0,std::numeric_limits::max())); -static DoubleOption opt_max_cpu_before_cplex(seqOfSat, "max-cpu-before-cplex", - "FB: Force a call to Cplex after this many CPU seconds (-1 == no limit)", - 300.0, DoubleRange(-1.0, true, std::numeric_limits::max(), true)); - -static BoolOption opt_b_m_s(seqOfSat, "use-ub-mipstart", - "FB: Use current Sat solver upper bound model as cplex start. This entails deleting all other starts", - true); +static DoubleOption opt_optcores_cpu_per( + seqOfSat, "optcores-cpu-lim", + "FB: CPU time limit for finding each additional core (-1 == no limit).", 10, + DoubleRange(-1.0, true, std::numeric_limits::max(), true)); + +static IntOption opt_nonopt( + seqOfSat, "nonopt", + "JD: Method for relaxing clauses of current " + "core (0 = pick a random clause, 1 = pick clause appearing in most cores" + ", 2 = relax a fraction of each core (set fraction with \"relaxfrac\" " + "parameter), 3 = remove all clauses in core making next core disjoint.", + 3, IntRange(0, 3)); + +static IntOption opt_abstract_assumps( + seqOfSat, "abstract-assumps", + "Method for relaxing abstract assumptions (0 = remove totalizers outputs " + "like ordinary b-vars, 1 = relax totalizer to be next output, 2 = relax " + "only one totalizer output at a time", + 1, IntRange(0, 2)); + +static DoubleOption opt_relaxfrac( + seqOfSat, "relaxfrac", + "FB: After accumulating frac-rampup-end clauses " + "relax this fraction of current core, picking clauses most frequently " + "occuring in cores (must have \"nonopt=2\").", + 0.3, DoubleRange(0, false, 1.0, true)); +static IntOption opt_frac_rampup_start( + seqOfSat, "frac-rampup-start", + "FB: When nonopt = 2 (relax a fraction) relax only" + " one clause until this many cores accumulated", + 128, IntRange(0, std::numeric_limits::max())); +static IntOption opt_frac_rampup_end( + seqOfSat, "frac-rampup-end", + "FB: When nonopt = 2 (relax a fraction) increase " + "fract of core relaxed linearly to reach final \"relaxfrac\" after this " + "many cores accumulated", + 512, IntRange(0, std::numeric_limits::max())); +static IntOption opt_max_cores_before_cplex( + seqOfSat, "max-cores-before-cplex", + "FB: Force a call to Cplex after this many constraints", 300, + IntRange(0, std::numeric_limits::max())); +static DoubleOption opt_max_cpu_before_cplex( + seqOfSat, "max-cpu-before-cplex", + "FB: Force a call to Cplex after this many CPU seconds (-1 == no limit)", + 200.0, DoubleRange(-1.0, true, std::numeric_limits::max(), true)); +// static IntOption opt_max_cplex_calls_before_optimum( +// seqOfSat, "max-cplex-calls-before-optimum", +// "Maxmum number of cplex calls before we require cplex to find an +// optimum", 10, IntRange(1, std::numeric_limits::max())); + +static BoolOption opt_b_m_s( + seqOfSat, "use-ub-mipstart", + "FB: Use current Sat solver upper bound model as cplex start. This entails " + "deleting all other starts", + true); static IntOption opt_sort_assumps(seqOfSat, "sort-assumps", - "FB: (0=don't sort, 1=place best softs to relax at top of trail, 2 reverse of 1)", - 0, IntRange(0,2)); + "FB: (0=don't sort, 1=place best softs to " + "relax at top of trail, 2 reverse of 1)", + 0, IntRange(0, 2)); static BoolOption opt_improve_model(seqOfSat, "improve-model", - "FB: When we find a Satisfying model try to improve its cost via relaxation search", - false); -static BoolOption opt_find_forced(seqOfSat, "find-forced", - "Check for forced variables by UP or by the upper bound", - false); - -static IntOption opt_max_size_improve_model(seqOfSat, "max-size-improve-model", - "FB: Don't try to improve model if the number of falsified softs" - " is greater than this parameter (-1 == always try)", - -1, IntRange(-1, std::numeric_limits::max())); - -static DoubleOption opt_max_cpu_improve_model(seqOfSat, "max-cpu-improve-model", - "FB: CPU time limit on improve SAT model phase (-1 == no limit)", - 10, DoubleRange(-1.0, true, std::numeric_limits::max(), true)); - -static BoolOption opt_lp_harden(seqOfSat, "lp-harden", "Use LP version of CPLEX model to force soft clauses", true); + "FB: When we find a Satisfying model try " + "to improve its cost via relaxation search", + false); +static BoolOption opt_find_forced( + seqOfSat, "find-forced", + "Check for forced variables by UP or by the upper bound", false); + +static IntOption opt_max_size_improve_model( + seqOfSat, "max-size-improve-model", + "FB: Don't try to improve model if the number of falsified softs" + " is greater than this parameter (-1 == always try)", + -1, IntRange(-1, std::numeric_limits::max())); + +static DoubleOption opt_max_cpu_improve_model( + seqOfSat, "max-cpu-improve-model", + "FB: CPU time limit on improve SAT model phase (-1 == no limit)", 10, + DoubleRange(-1.0, true, std::numeric_limits::max(), true)); + +static BoolOption opt_lp_harden( + seqOfSat, "lp-harden", + "Use LP version of CPLEX model to force soft clauses", true); // Other Controls -//CPLEX Solver Options -static IntOption opt_cplex_threads(cplex, "cplex-threads", "Allow cplex to use this many threads (1 = sequential processing)", 1, IntRange(1,124)); -static BoolOption opt_cplex_tune(cplex, "cplex-tune", "Use cplex parameter setting recommended by cplex-tune", false); -static DoubleOption opt_cplex_min_ticks(cplex, "cplex-min-ticks", "Run CPLEX for at least this 1000's of its deterministic ticks can allow CPLEX to find better feasible (non-optimal) solutions", 4.0, - DoubleRange(1.0, true, std::numeric_limits::max(), false)); - +// CPLEX Solver Options +static IntOption opt_cplex_threads( + cplex, "cplex-threads", + "Allow cplex to use this many threads (1 = sequential processing)", 1, + IntRange(1, 124)); +static BoolOption opt_cplex_tune( + cplex, "cplex-tune", + "Use cplex parameter setting recommended by cplex-tune", false); +static DoubleOption opt_cplex_min_ticks( + cplex, "cplex-min-ticks", + "Run CPLEX for at least this 1000's of its deterministic ticks can allow " + "CPLEX to find better feasible (non-optimal) solutions", + 4.0, DoubleRange(1.0, true, std::numeric_limits::max(), false)); // //Preprocessing -static BoolOption opt_preprocess(pre, "preprocess", "Use minisat preprocessor", true); -static BoolOption opt_prepro_wcnf_eqs(pre, "wcnf-eqs", "Find and reduce equalities in wcnf", false); -static BoolOption opt_prepro_wcnf_units(pre, "wcnf-units", "Reduce wcnf by hard units", true); -static BoolOption opt_prepro_wcnf_harden(pre, "wcnf-harden", "Try to harden soft clauses by satisfiability tests", true); +static BoolOption opt_preprocess(pre, "preprocess", "Use minisat preprocessor", + true); +static BoolOption opt_prepro_wcnf_eqs(pre, "wcnf-eqs", + "Find and reduce equalities in wcnf", + true); +static BoolOption opt_prepro_wcnf_units(pre, "wcnf-units", + "Reduce wcnf by hard units", true); +static BoolOption opt_prepro_wcnf_harden( + pre, "wcnf-harden", "Try to harden soft clauses by satisfiability tests", + true); // //Mutexes -static IntOption opt_prepro_mx_find_mxes(pre, "mx-find-mxes", "Detect mutually exclusive soft clauses in the input formula (0=don't, 1= find at most one false (core-mxes), 2= find at most one true (non-core-mxes), 3=1&2)", 3, IntRange(0,3)); - -static IntOption opt_prepro_mx_mem_lim(pre, "mx-mem-lim", "Limit on memory usage in megabytes of the mutex finder", 512*3, IntRange(0, INT32_MAX)); - -static IntOption opt_prepro_mx_transform(pre, "mx-transform-mxes", "Rewrite the WCNF so that soft clause mutexes are re-encoded with single relaxation variable (0=don't, 1 = re-encode core mxes, 2 = re-encode non-core muxes, 3 = 1&2)", 2, IntRange(0,3)); - -static BoolOption opt_prepro_simplify_and_exit(pre, "simplify-only", "Write simplified WCNF file with new suffix then exit. If mx-exit-if-no-mutexes we exit before writing if no mutexes found", false); - -static BoolOption opt_prepro_mx_seed_originals(pre, "mx-seed-mxes", "Allow original softs clauses in mutexes to be seeded to CPLEX when formula is transformed", true); - -static BoolOption opt_prepro_mx_constrain_hs(pre, "mx-constrain-hs", "Ensure that computed hitting sets satisfy the discovered soft clause mutexes", true); - -static IntOption opt_prepro_mx_hs_use_abstraction(pre, "mx-hs-use-abs", "When using untransformed core mxes use mx abstracted hitting sets to generate cores (0=don't, 1=use abstract hs, 2=use abstracted and unabstracted hs", 2, IntRange(0, 2)); - -static BoolOption opt_prepro_mx_sat_preprocess(pre, "mx-sat-prepro", "Use minisat preprocessor before detecting mx softs", false); - -static DoubleOption opt_prepro_mx_max_cpu(pre, "mx-cpu-lim", - "Max time to spend on mx detection (-1 == no limit)", - 15.0, DoubleRange(-1, true, std::numeric_limits::max(), true)); +static IntOption opt_prepro_mx_find_mxes( + pre, "mx-find-mxes", + "Detect mutually exclusive soft clauses in the input formula (0=don't, 1= " + "find at most one false (core-mxes), 2= find at most one true " + "(non-core-mxes), 3=1&2)", + 2, IntRange(0, 3)); // 3 + +static IntOption opt_prepro_mx_mem_lim( + pre, "mx-mem-lim", "Limit on memory usage in megabytes of the mutex finder", + 512 * 3, IntRange(0, INT32_MAX)); + +static BoolOption opt_prepro_simplify_and_exit( + pre, "simplify-only", + "Write simplified WCNF file with new suffix then exit. If " + "mx-exit-if-no-mutexes we exit before writing if no mutexes found", + false); + +static BoolOption opt_prepro_mx_seed_originals( + pre, "mx-seed-mxes", + "Allow original softs clauses in mutexes to be seeded to CPLEX when " + "formula is transformed", + true); // true + +static BoolOption opt_prepro_mx_constrain_hs( + pre, "mx-constrain-hs", + "Ensure that computed hitting sets satisfy the discovered soft clause " + "mutexes", + true); // true + +static BoolOption opt_prepro_mx_sat_preprocess( + pre, "mx-sat-prepro", "Use minisat preprocessor before detecting mx softs", + false); + +static DoubleOption opt_prepro_mx_max_cpu( + pre, "mx-cpu-lim", "Max time to spend on mx detection (-1 == no limit)", + 15.0, DoubleRange(-1, true, std::numeric_limits::max(), true)); // //Debugging -static BoolOption opt_cplex_data_chk(debug, "cplex-data-chk", "Run cplex data checker on its input", false); -static BoolOption opt_cplex_write_model(debug, "cplex-wrt-model", "Make cplex write out each of its models", false); -static BoolOption opt_cplex_output(debug, "cplex-output", "Turn on cplex output", false); -static BoolOption opt_prepro_output(debug, "dump-prepro", "Output the preprocessed formula", false); - - -Params::Params() : noLimit {-1.0} {} +static BoolOption opt_cplex_data_chk(debug, "cplex-data-chk", + "Run cplex data checker on its input", + true); +static BoolOption opt_cplex_write_model( + debug, "cplex-wrt-model", "Make cplex write out each of its models", false); +static BoolOption opt_cplex_output(debug, "cplex-output", + "Turn on cplex output", false); +static BoolOption opt_prepro_output(debug, "dump-prepro", + "Output the preprocessed formula", false); + +Params::Params() : noLimit{-1.0} {} void Params::readOptions() { verbosity = opt_verb; @@ -210,23 +392,29 @@ void Params::readOptions() { printOptions = opt_printOptions; printBstSoln = opt_printBstSoln; + printNewFormat = opt_printNewFormat; printSoln = opt_printSoln; + printNewFormat = opt_printNewFormat; tolerance = opt_tolerance; min_type = opt_mintype; mus_cpu_lim = (opt_mus_cpu_lim > 0) ? opt_mus_cpu_lim : noLimit; mus_min_red = (opt_mus_min_red > 0) ? opt_mus_min_red : noLimit; dsjnt_phase = opt_dsjnt; - dsjnt_cpu_per_core = (opt_dsjnt_cpu_per_core > 0) ? opt_dsjnt_cpu_per_core : noLimit; - dsjnt_mus_cpu_lim = (opt_dsjnt_mus_cpu_lim > 0) ? opt_dsjnt_mus_cpu_lim : noLimit; - optcores_cpu_per = (opt_optcores_cpu_per > 0) ? opt_optcores_cpu_per : noLimit; + dsjnt_cpu_per_core = + (opt_dsjnt_cpu_per_core > 0) ? opt_dsjnt_cpu_per_core : noLimit; + dsjnt_mus_cpu_lim = + (opt_dsjnt_mus_cpu_lim > 0) ? opt_dsjnt_mus_cpu_lim : noLimit; + optcores_cpu_per = + (opt_optcores_cpu_per > 0) ? opt_optcores_cpu_per : noLimit; improve_model = opt_improve_model; - if(opt_max_size_improve_model > 0) + if (opt_max_size_improve_model > 0) improve_model_max_size = opt_max_size_improve_model; - else if(opt_max_size_improve_model == 0) + else if (opt_max_size_improve_model == 0) improve_model = false; else improve_model_max_size = -1; - improve_model_cpu_lim = (opt_max_cpu_improve_model > 0) ? opt_max_cpu_improve_model : noLimit; + improve_model_cpu_lim = + (opt_max_cpu_improve_model > 0) ? opt_max_cpu_improve_model : noLimit; find_forced = opt_find_forced; @@ -234,11 +422,13 @@ void Params::readOptions() { seed_max = opt_maxseeds; seed_learnts = opt_seed_learnts; seed_all_limit = opt_seed_all_limit; + seed_all_cpu = opt_seed_all_cpu_before_cplex; frac_to_relax = opt_relaxfrac; frac_rampup_start = opt_frac_rampup_start; frac_rampup_end = opt_frac_rampup_end; max_cores_before_cplex = opt_max_cores_before_cplex; max_cpu_before_cplex = opt_max_cpu_before_cplex; + // max_cplex_calls_before_opt = opt_max_cplex_calls_before_optimum; lp_harden = opt_lp_harden; sort_assumps = opt_sort_assumps; @@ -246,37 +436,38 @@ void Params::readOptions() { bvarDecisions = opt_bvardecisions; fbeq = opt_fbeq; fb = !fbeq; - + + abstract_assumps = opt_abstract_assumps; int no = opt_nonopt; - switch(no) { - case 0: - coreRelaxFn = CoreRelaxFn::rand; - break; - case 1: - coreRelaxFn = CoreRelaxFn::maxoccur; - break; - case 2: - coreRelaxFn = CoreRelaxFn::frac; - break; - case 3: - coreRelaxFn = CoreRelaxFn::dsjn; - break; + switch (no) { + case 0: + coreRelaxFn = CoreRelaxFn::rand; + break; + case 1: + coreRelaxFn = CoreRelaxFn::maxoccur; + break; + case 2: + coreRelaxFn = CoreRelaxFn::frac; + break; + case 3: + coreRelaxFn = CoreRelaxFn::dsjn; + break; } int ct = opt_coretype; - switch(ct) { - case 0: - coreType = CoreType::cores; - break; - case 1: - coreType = CoreType::mixed; - break; - default: - coreType = CoreType::cores; - break; + switch (ct) { + case 0: + coreType = CoreType::cores; + break; + case 1: + coreType = CoreType::mixed; + break; + default: + coreType = CoreType::cores; + break; } - //cplex limits + // cplex limits cplex_threads = opt_cplex_threads; cplex_tune = opt_cplex_tune; cplex_min_ticks = opt_cplex_min_ticks; @@ -284,34 +475,46 @@ void Params::readOptions() { cplex_write_model = opt_cplex_write_model; cplex_output = opt_cplex_output; - //cplex_solnpool_cap = opt_cplex_solnpool_cap; + // cplex_solnpool_cap = opt_cplex_solnpool_cap; cplex_pop_nsoln = opt_cplex_pop_nsoln; - cplex_pop_cpu_lim = (opt_cplex_pop_cpu_lim > 0) ? opt_cplex_pop_cpu_lim : noLimit; - //trypop_cplextime_ub = (opt_trypop_cplextime_ub > 0) ? opt_trypop_cplextime_ub : noLimit; - //trypop_feedtime_lb = opt_trypop_feedtime_lb; + cplex_pop_cpu_lim = + (opt_cplex_pop_cpu_lim > 0) ? opt_cplex_pop_cpu_lim : noLimit; + // trypop_cplextime_ub = (opt_trypop_cplextime_ub > 0) ? + // opt_trypop_cplextime_ub : noLimit; trypop_feedtime_lb = + // opt_trypop_feedtime_lb; // trypop = opt_trypop; trypop = opt_trypop; conflicts_from_ub = opt_conflicts_from_ub; - if(opt_cplex_pop_nsoln == 0) - trypop = 0; + if (opt_cplex_pop_nsoln == 0) trypop = 0; - //preprocess + // preprocess preprocess = opt_preprocess; wcnf_eqs = opt_prepro_wcnf_eqs; wcnf_harden = opt_prepro_wcnf_harden; wcnf_units = opt_prepro_wcnf_units; - + simplify_and_exit = opt_prepro_simplify_and_exit; mx_find_mxes = opt_prepro_mx_find_mxes; mx_mem_limit = opt_prepro_mx_mem_lim; - mx_transform = opt_prepro_mx_transform; mx_seed_originals = opt_prepro_mx_seed_originals; mx_constrain_hs = opt_prepro_mx_constrain_hs; - mx_hs_use_abstraction = opt_prepro_mx_hs_use_abstraction; mx_sat_preprocess = opt_prepro_mx_sat_preprocess; mx_cpu_lim = (opt_prepro_mx_max_cpu > 0) ? opt_prepro_mx_max_cpu : noLimit; + + abstract = opt_abstract; + abstract_cores2greedy = opt_abstract_cores2greedy; + abstract_max_ave_size = opt_abstract_max_ave_size; + abstract_cplex_cores = opt_abstract_cplex_cores; + abstract_greedy_cores = opt_abstract_greedy_cores; + cplexgreedy = opt_cplexgreedy; + abstract_min_size = opt_abstract_min_size; + abstract_max_core_size = opt_abstract_max_core_size; + abstract_min_cores = opt_abstract_min_cores; + cpu_per_exhaust = opt_cpu_per_exhaust; + cpu_per_exhaust_all_clauses = opt_cpu_per_exhaust_all_clauses; + abstract_gap = opt_abstract_gap; } Params params; diff --git a/maxhs/utils/Params.h b/maxhs/utils/Params.h index e36634f..636e13b 100644 --- a/maxhs/utils/Params.h +++ b/maxhs/utils/Params.h @@ -26,11 +26,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include "maxhs/core/MaxSolverTypes.h" - class Params { - //MaxSolver helper class to manage its settable parameters. - //see MaxSlvParams.cc for description -public: + // MaxSolver helper class to manage its settable parameters. + // see MaxSlvParams.cc for description + public: Params(); ~Params() {} void readOptions(); @@ -50,6 +49,7 @@ class Params { bool printOptions; bool printBstSoln; bool printSoln; + bool printNewFormat; double tolerance; CoreType coreType; @@ -59,12 +59,14 @@ class Params { int seed_max; bool seed_learnts; int seed_all_limit; + double seed_all_cpu; bool bvarDecisions; double frac_to_relax; int frac_rampup_start; int frac_rampup_end; int max_cores_before_cplex; int max_cpu_before_cplex; +// int max_cplex_calls_before_opt; bool lp_harden; int sort_assumps; @@ -87,10 +89,10 @@ class Params { int cplex_pop_nsoln; double cplex_pop_cpu_lim; - //int cplex_solnpool_cap; + // int cplex_solnpool_cap; - //double trypop_cplextime_ub; - //double trypop_feedtime_lb; + // double trypop_cplextime_ub; + // double trypop_feedtime_lb; int trypop; int conflicts_from_ub; bool prepro_output; @@ -102,14 +104,25 @@ class Params { bool simplify_and_exit; int mx_find_mxes; int mx_mem_limit; - int mx_transform; bool mx_seed_originals; + bool abstract; + bool abstract_cores2greedy; + double abstract_max_ave_size; + int abstract_cplex_cores; + int abstract_greedy_cores; + int cplexgreedy; + int abstract_min_size; + int abstract_max_core_size; + int abstract_min_cores; + int abstract_assumps; + double cpu_per_exhaust; + double cpu_per_exhaust_all_clauses; + double abstract_gap; + bool mx_constrain_hs; - int mx_hs_use_abstraction; bool mx_sat_preprocess; double mx_cpu_lim; - std::string instance_file_name; }; diff --git a/maxhs/utils/hash.cc b/maxhs/utils/hash.cc index bc72dde..aeefd33 100644 --- a/maxhs/utils/hash.cc +++ b/maxhs/utils/hash.cc @@ -21,273 +21,58 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ***********/ -#include #include "maxhs/utils/hash.h" +#include -uint32_t rnd_table[256] { - 868768155, - 618522103, - 1552882209, - 80221016, - 115080558, - 1055832139, - 1667253458, - 1049170342, - 1376460154, - 914249860, - 1048465347, - 1798137010, - 1336748788, - 23084515, - 1572777182, - 1617553847, - 1294164067, - 436165975, - 627889307, - 1696589762, - 110522491, - 1640277560, - 1505647036, - 2035382293, - 1236871088, - 1071825181, - 331719265, - 539832202, - 382784115, - 875153970, - 2028029813, - 1441390329, - 1881539726, - 1727691473, - 28040939, - 1878261955, - 1412035312, - 876827580, - 1481410519, - 670824079, - 371938526, - 90214601, - 1827273279, - 354440472, - 1484616607, - 1759502545, - 1008161564, - 2001922703, - 1356933857, - 1321590409, - 590142944, - 1748799407, - 330173733, - 1574583625, - 933847728, - 253430945, - 1298634662, - 631020000, - 2086556703, - 1549606358, - 753578192, - 852859565, - 1501832716, - 343769414, - 703022035, - 1081351353, - 517752345, - 1318618932, - 440881018, - 737017595, - 602960354, - 219203506, - 1412083669, - 430347331, - 23530358, - 332508514, - 1291241068, - 2042008766, - 1961739732, - 615337210, - 1467371089, - 1558149855, - 1933891998, - 1731832553, - 334417547, - 1053999114, - 446362737, - 728685744, - 1428390738, - 628800086, - 711439712, - 1533611090, - 2051997828, - 1420968132, - 61110743, - 1396607964, - 1184387835, - 1040299241, - 447856852, - 1920563169, - 356411821, - 1076025289, - 7174075, - 35064789, - 571032709, - 921053058, - 753423852, - 798435210, - 540736423, - 974030699, - 995027592, - 1152853982, - 299300735, - 1546724501, - 1166822243, - 2025907870, - 972313400, - 60442387, - 609713336, - 1122814210, - 1053310414, - 786974038, - 2054067268, - 1711537086, - 1403090724, - 2079619923, - 1994748628, - 1321869068, - 750557700, - 1816573534, - 995375555, - 1679222171, - 1033847202, - 2107300677, - 1405887001, - 145780680, - 2005813479, - 255280532, - 108724045, - 1898986685, - 32611369, - 822157357, - 1298063381, - 539756210, - 1015967583, - 1276450022, - 1989604415, - 2133683542, - 1806888482, - 180083151, - 1702184492, - 759877507, - 964764915, - 2074884322, - 113540263, - 374289229, - 1204999757, - 1556517059, - 2045067204, - 860774250, - 1699820024, - 1678611466, - 1623108508, - 582914510, - 1021387745, - 1771689028, - 1578106011, - 888634528, - 1760316151, - 1582473338, - 1691490323, - 2065797934, - 1761563298, - 2028511240, - 1841907120, - 424510328, - 75534326, - 520456178, - 1807022906, - 2129606240, - 2018509180, - 598140664, - 137481233, - 1857742107, - 1368716682, - 1222296034, - 1322810350, - 2105305561, - 2100126602, - 1370822212, - 1722231619, - 1747309463, - 1832908485, - 1581620306, - 68894331, - 1702316815, - 523706985, - 581414739, - 1656935161, - 1658415612, - 1950184377, - 49617129, - 100496977, - 867482541, - 1597118940, - 400033582, - 1351334762, - 18196963, - 1396548911, - 440200899, - 51571471, - 1514057376, - 513077239, - 519979830, - 2115280706, - 258504016, - 569855695, - 674587387, - 1830092354, - 477778202, - 298450372, - 716648642, - 1030304697, - 1505035619, - 1473749293, - 1939987286, - 2033186565, - 939405941, - 730447859, - 1302150910, - 1556026393, - 946567952, - 678335411, - 1820823012, - 322717758, - 1838426029, - 1043439820, - 1525087374, - 892977192, - 1441316008, - 1108755771, - 948521121, - 900305804, - 543609543, - 1473238656, - 861457369, - 1533572544, - 1931607662, - 1803982569, - 1920017701, - 1255070917, - 1178515439, - 758426408, - 226456279, - 1986288478, - 2058055390 - }; +uint32_t rnd_table[256]{ + 868768155, 618522103, 1552882209, 80221016, 115080558, 1055832139, + 1667253458, 1049170342, 1376460154, 914249860, 1048465347, 1798137010, + 1336748788, 23084515, 1572777182, 1617553847, 1294164067, 436165975, + 627889307, 1696589762, 110522491, 1640277560, 1505647036, 2035382293, + 1236871088, 1071825181, 331719265, 539832202, 382784115, 875153970, + 2028029813, 1441390329, 1881539726, 1727691473, 28040939, 1878261955, + 1412035312, 876827580, 1481410519, 670824079, 371938526, 90214601, + 1827273279, 354440472, 1484616607, 1759502545, 1008161564, 2001922703, + 1356933857, 1321590409, 590142944, 1748799407, 330173733, 1574583625, + 933847728, 253430945, 1298634662, 631020000, 2086556703, 1549606358, + 753578192, 852859565, 1501832716, 343769414, 703022035, 1081351353, + 517752345, 1318618932, 440881018, 737017595, 602960354, 219203506, + 1412083669, 430347331, 23530358, 332508514, 1291241068, 2042008766, + 1961739732, 615337210, 1467371089, 1558149855, 1933891998, 1731832553, + 334417547, 1053999114, 446362737, 728685744, 1428390738, 628800086, + 711439712, 1533611090, 2051997828, 1420968132, 61110743, 1396607964, + 1184387835, 1040299241, 447856852, 1920563169, 356411821, 1076025289, + 7174075, 35064789, 571032709, 921053058, 753423852, 798435210, + 540736423, 974030699, 995027592, 1152853982, 299300735, 1546724501, + 1166822243, 2025907870, 972313400, 60442387, 609713336, 1122814210, + 1053310414, 786974038, 2054067268, 1711537086, 1403090724, 2079619923, + 1994748628, 1321869068, 750557700, 1816573534, 995375555, 1679222171, + 1033847202, 2107300677, 1405887001, 145780680, 2005813479, 255280532, + 108724045, 1898986685, 32611369, 822157357, 1298063381, 539756210, + 1015967583, 1276450022, 1989604415, 2133683542, 1806888482, 180083151, + 1702184492, 759877507, 964764915, 2074884322, 113540263, 374289229, + 1204999757, 1556517059, 2045067204, 860774250, 1699820024, 1678611466, + 1623108508, 582914510, 1021387745, 1771689028, 1578106011, 888634528, + 1760316151, 1582473338, 1691490323, 2065797934, 1761563298, 2028511240, + 1841907120, 424510328, 75534326, 520456178, 1807022906, 2129606240, + 2018509180, 598140664, 137481233, 1857742107, 1368716682, 1222296034, + 1322810350, 2105305561, 2100126602, 1370822212, 1722231619, 1747309463, + 1832908485, 1581620306, 68894331, 1702316815, 523706985, 581414739, + 1656935161, 1658415612, 1950184377, 49617129, 100496977, 867482541, + 1597118940, 400033582, 1351334762, 18196963, 1396548911, 440200899, + 51571471, 1514057376, 513077239, 519979830, 2115280706, 258504016, + 569855695, 674587387, 1830092354, 477778202, 298450372, 716648642, + 1030304697, 1505035619, 1473749293, 1939987286, 2033186565, 939405941, + 730447859, 1302150910, 1556026393, 946567952, 678335411, 1820823012, + 322717758, 1838426029, 1043439820, 1525087374, 892977192, 1441316008, + 1108755771, 948521121, 900305804, 543609543, 1473238656, 861457369, + 1533572544, 1931607662, 1803982569, 1920017701, 1255070917, 1178515439, + 758426408, 226456279, 1986288478, 2058055390}; void BinClsTable::insert(Lit l1, Lit l2) { auto key = mkKey(l1, l2); auto& v = bins[key]; - if(var(l1) > var(l2)) - swap(l1, l2); + if (var(l1) > var(l2)) swap(l1, l2); v.push_back(l1); v.push_back(l2); } @@ -295,7 +80,6 @@ void BinClsTable::insert(Lit l1, Lit l2) { vector* BinClsTable::get(Var v1, Var v2) { auto key = mkKey(v1, v2); auto it = bins.find(key); - if(it == bins.end()) - return nullptr; + if (it == bins.end()) return nullptr; return &(it->second); } diff --git a/maxhs/utils/hash.h b/maxhs/utils/hash.h index 21aab3a..d7ff8f8 100644 --- a/maxhs/utils/hash.h +++ b/maxhs/utils/hash.h @@ -24,8 +24,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef HASH_h #define HASH_h #include -#include #include +#include #ifdef GLUCOSE #include "glucose/core/SolverTypes.h" @@ -41,82 +41,75 @@ namespace Minisat = Glucose; using Minisat::Lit; using Minisat::Var; -using std::pair; using std::make_pair; +using std::pair; using std::swap; using std::unordered_map; -extern uint32_t rnd_table[256]; - +extern uint32_t rnd_table[256]; -//hash a container range -template -uint32_t hashCode(RAIterator first, RAIterator last) -{ +// hash a container range +template +uint32_t hashCode(RAIterator first, RAIterator last) { uint32_t code = 0; - + /*cout << "hashing ["; - for(auto it = first; it != last; ++it) + for(auto it = first; it != last; ++it) cout << *it << ", "; cout << "] (" << last-first << ")\n";*/ - for(auto it = first; it != last; ++it) { + for (auto it = first; it != last; ++it) { auto item = *it; - char* p = (char*) (&item); - for(size_t i = 0; i < sizeof(*first); i++) { + char* p = (char*)(&item); + for (size_t i = 0; i < sizeof(*first); i++) { code += rnd_table[(uint8_t)*p]; - //cout << "xor-ing with " << (uint32_t) *p << "; "; + // cout << "xor-ing with " << (uint32_t) *p << "; "; p++; } } - //cout << "\nFinal hash code: " << code << "\n"; + // cout << "\nFinal hash code: " << code << "\n"; return code; } -//hash a structure -template -uint32_t hashCode(const C c) -{ +// hash a structure +template +uint32_t hashCode(const C c) { uint32_t code = 0; - char* p = (char*) (&c); - for(size_t i = 0; i < sizeof(c); i++) { + char* p = (char*)(&c); + for (size_t i = 0; i < sizeof(c); i++) { code += rnd_table[(uint8_t)*p]; p++; } return code; } -template +template class BinClsHash { public: - size_t operator() (const pair bin) const { - return hashCode(bin); - } + size_t operator()(const pair bin) const { return hashCode(bin); } }; class BinClsTable { - //has binary clauss + // has binary clauss public: using BinMap = unordered_map, vector, BinClsHash>; - void insert(Lit l1, Lit l2); //insert a binary clause + void insert(Lit l1, Lit l2); // insert a binary clause + + // return pointer to vector of binaries that contain v1 and v2. + // The binaries are stored in sequence, every two elements of the + // vector is a binary clause. If pointer is nil no binaries found + vector* get(Var v1, Var v2); - //return pointer to vector of binaries that contain v1 and v2. - //The binaries are stored in sequence, every two elements of the - //vector is a binary clause. If pointer is nil no binaries found - vector* get(Var v1, Var v2); - BinMap::iterator begin() { return bins.begin(); } BinMap::iterator end() { return bins.end(); } private: BinMap bins; - pair mkKey(Lit l1, Lit l2) { - return mkKey(var(l1), var(l2)); - } + pair mkKey(Lit l1, Lit l2) { return mkKey(var(l1), var(l2)); } pair mkKey(Var v1, Var v2) { - if(v1 > v2) swap(v1, v2); + if (v1 > v2) swap(v1, v2); return make_pair(v1, v2); } }; diff --git a/maxhs/utils/io.h b/maxhs/utils/io.h index 492e3ee..9c281f4 100644 --- a/maxhs/utils/io.h +++ b/maxhs/utils/io.h @@ -44,55 +44,42 @@ using std::ostream; using std::vector; inline ostream& operator<<(ostream& os, const Minisat::Lit& l) { - if(l == Minisat::lit_Undef) + if (l == Minisat::lit_Undef) os << "lit_Undef"; else if (l == Minisat::lit_Error) os << "lit_Error"; else - os << (Minisat::sign(l) ? "-" : "") << (Minisat::var(l)+1); + os << (Minisat::sign(l) ? "-" : "") << (Minisat::var(l) + 1); return os; } inline ostream& operator<<(ostream& os, const Minisat::lbool& l) { - os << ( - (l == Minisat::l_Undef) ? "U" : - (l == Minisat::l_True ? "T" : "F") - ); + os << ((l == Minisat::l_Undef) ? "U" : (l == Minisat::l_True ? "T" : "F")); return os; } -template +template ostream& operator<<(ostream& os, const vector& v) { os << "[ "; - for(const auto& i : v) - os << i << " "; + for (size_t i = 0; i < v.size() && i < 30; ++i) os << v[i] << " "; + if (v.size() > 30) os << "..."; os << "] (" << v.size() << ")"; return os; } -template +template ostream& operator<<(ostream& os, const Minisat::vec& v) { os << "[ "; - for(int i = 0; i < v.size(); i++) - os << v[i] << " "; + for (int i = 0; i < v.size() && i < 30; i++) os << v[i] << " "; + if (v.size() > 30) os << "..."; os << "] (" << v.size() << ")"; return os; } -template -ostream& operator<<(ostream& os, const Minisat::LSet& v) { - os << "[ "; - for(int i = 0; i < v.size(); i++) - os << v[i] << " "; - os << "] (" << v.size() << ")"; - return os; -} - -template +template ostream& operator<<(ostream& os, const Packed_vecs& pv) { - for(const auto& v : pv) { - for(const auto& item : v) - os << item << " "; + for (const auto& v : pv) { + for (const auto& item : v) os << item << " "; os << "0 \n"; } return os; diff --git a/minisat/core/Dimacs.h b/minisat/core/Dimacs.h old mode 100644 new mode 100755 diff --git a/minisat/core/Main.cc b/minisat/core/Main.cc old mode 100644 new mode 100755 diff --git a/minisat/core/Solver.cc b/minisat/core/Solver.cc old mode 100644 new mode 100755 index 91d446d..7573927 --- a/minisat/core/Solver.cc +++ b/minisat/core/Solver.cc @@ -2,1075 +2,964 @@ Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson Copyright (c) 2007-2010, Niklas Sorensson -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, -sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or -substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT -NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT -OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. **************************************************************************************************/ #include -#include -#include "maxhs/utils/io.h" + +#include "minisat/core/Solver.h" #include "minisat/mtl/Alg.h" #include "minisat/mtl/Sort.h" #include "minisat/utils/System.h" -#include "minisat/core/Solver.h" using namespace Minisat; -using std::cout; + //================================================================================================= // Options: - static const char* _cat = "J: MINISAT solver"; -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, 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)", 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", 2, 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 IntOption opt_min_learnts_lim (_cat, "min-learnts", "Minimum learnt clause limit", 0, IntRange(0, INT32_MAX)); -static IntOption opt_sat_verb (_cat, "sverb", "Verbosity level (0=silent, 1=some, 2=more).", 0, IntRange(0, 2)); -static IntOption opt_assump_verb (_cat, "averb", "Verbosity level for new assumption processing (0=silent, 1=some, 2=more).", 0, IntRange(0, 2)); -static BoolOption opt_assumption_2nd (_cat, "assump-reprocess", "Reprocess assumption conflict with separate assumption decision levels", true); - +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, 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)", 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", 2, + 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 IntOption opt_min_learnts_lim(_cat, "min-learnts", + "Minimum learnt clause limit", 0, + IntRange(0, INT32_MAX)); +static IntOption opt_sat_verb(_cat, "sverb", + "Verbosity level (0=silent, 1=some, 2=more).", 0, + IntRange(0, 2)); + +// glucose options + +static IntOption opt_first_reduce_db( + _cat, "firstReduceDB", "The number of conflicts before the first reduce DB", + 2000, IntRange(0, INT32_MAX)); +static IntOption opt_inc_reduce_db(_cat, "incReduceDB", + "Increment for reduce DB", 300, + IntRange(0, INT32_MAX)); +static IntOption opt_spec_inc_reduce_db(_cat, "specialIncReduceDB", + "Special increment for reduce DB", 1000, + IntRange(0, INT32_MAX)); //================================================================================================= // Constructor/Destructor: - -Solver::Solver() : - - // Parameters (user settable): - // - verbosity (opt_sat_verb) - , var_decay (opt_var_decay) - , clause_decay (opt_clause_decay) - , random_var_freq (opt_random_var_freq) - , random_seed (opt_random_seed) - , luby_restart (opt_luby_restart) - , assump_2nd_process (opt_assumption_2nd) - , averbosity (opt_assump_verb) - , ccmin_mode (opt_ccmin_mode) - , phase_saving (opt_phase_saving) - , rnd_pol (false) - , rnd_init_act (opt_rnd_init_act) - , garbage_frac (opt_garbage_frac) - , min_learnts_lim (opt_min_learnts_lim) - , restart_first (opt_restart_first) - , restart_inc (opt_restart_inc) - - // Parameters (the rest): - // - , learntsize_factor((double)1/(double)3), learntsize_inc(1.1) - - // Parameters (experimental): - // - , learntsize_adjust_start_confl (100) - , learntsize_adjust_inc (1.5) - - // Statistics: (formerly in 'SolverStats') - // - , solves(0), starts(0), decisions(0), rnd_decisions(0), propagations(0), conflicts(0) - , dec_vars(0), num_clauses(0), num_learnts(0), clauses_literals(0), learnts_literals(0), max_literals(0), tot_literals(0) - , n_assump_conflicts(0) - , assump_saved (0) - , watches (WatcherDeleted(ca)) - , order_heap (VarOrderLt(activity)) - , ok (true) - , remove_satisfied (true) - , cla_inc (1) - , var_inc (1) - , qhead (0) - , simpDB_assigns (-1) - , simpDB_props (0) - , progress_estimate (0) - , next_var (0) - - // Resource constraints: - // - , conflict_budget (-1) - , propagation_budget (-1) - , asynch_interrupt (false) -{} - - -Solver::~Solver() -{ -} - +Solver::Solver() + : + + // Parameters (user settable): + // + firstReduceDB(opt_first_reduce_db), + incReduceDB(opt_inc_reduce_db), + specialIncReduceDB(opt_spec_inc_reduce_db), + verbosity(opt_sat_verb), + var_decay(opt_var_decay), + clause_decay(opt_clause_decay), + random_var_freq(opt_random_var_freq), + random_seed(opt_random_seed), + luby_restart(opt_luby_restart), + ccmin_mode(opt_ccmin_mode), + phase_saving(opt_phase_saving), + rnd_pol(false), + rnd_init_act(opt_rnd_init_act), + garbage_frac(opt_garbage_frac), + min_learnts_lim(opt_min_learnts_lim), + restart_first(opt_restart_first), + restart_inc(opt_restart_inc) + + // Parameters (the rest): + // + , + learntsize_factor((double)1 / (double)3), + learntsize_inc(1.1) + + // Parameters (experimental): + // + , + learntsize_adjust_start_confl(100), + learntsize_adjust_inc(1.5) + + // Statistics: (formerly in 'SolverStats') + // + , + solves(0), + starts(0), + decisions(0), + rnd_decisions(0), + propagations(0), + conflicts(0), + dec_vars(0), + num_clauses(0), + num_learnts(0), + clauses_literals(0), + learnts_literals(0), + max_literals(0), + tot_literals(0), + curRestart(1), + watches(WatcherDeleted(ca)), + order_heap(VarOrderLt(activity)), + ok(true), + remove_satisfied(true), + cla_inc(1), + var_inc(1), + qhead(0), + simpDB_assigns(-1), + simpDB_props(0), + progress_estimate(0), + next_var(0), + MYFLAG(0), + nbclausesbeforereduce(firstReduceDB), + conflict_budget(-1), + propagation_budget(-1), + asynch_interrupt(false) {} + +Solver::~Solver() {} //================================================================================================= // Minor methods: - -// Creates a new SAT variable in the solver. If 'decision' is cleared, variable will not be -// used as a decision variable (NOTE! This has effects on the meaning of a SATISFIABLE result). +// Creates a new SAT variable in the solver. If 'decision' is cleared, variable +// will not be used as a decision variable (NOTE! This has effects on the +// meaning of a SATISFIABLE result). // -Var Solver::newVar(lbool upol, bool dvar) -{ - Var v; - if (free_vars.size() > 0){ - v = free_vars.last(); - free_vars.pop(); - }else - v = next_var++; - - watches .init(mkLit(v, false)); - watches .init(mkLit(v, true )); - assigns .insert(v, l_Undef); - vardata .insert(v, mkVarData(CRef_Undef, 0, l_Undef)); - activity .insert(v, rnd_init_act ? drand(random_seed) * 0.00001 : 0); - seen .insert(v, 0); - polarity .insert(v, true); - user_pol .insert(v, upol); - decision .reserve(v); - trail .capacity(v+1); - setDecisionVar(v, dvar); - return v; +Var Solver::newVar(lbool upol, bool dvar) { + Var v; + if (free_vars.size() > 0) { + v = free_vars.last(); + free_vars.pop(); + } else + v = next_var++; + + watches.init(mkLit(v, false)); + watches.init(mkLit(v, true)); + assigns.insert(v, l_Undef); + vardata.insert(v, mkVarData(CRef_Undef, 0, l_Undef)); + activity.insert(v, rnd_init_act ? drand(random_seed) * 0.00001 : 0); + seen.insert(v, 0); + permDiff.insert(v, 0); + polarity.insert(v, true); + user_pol.insert(v, upol); + decision.reserve(v); + trail.capacity(v + 1); + setDecisionVar(v, dvar); + return v; } - -// Note: at the moment, only unassigned variable will be released (this is to avoid duplicate -// releases of the same variable). -void Solver::releaseVar(Lit l) -{ - if (value(l) == l_Undef){ - addClause(l); - released_vars.push(var(l)); - } +// Note: at the moment, only unassigned variable will be released (this is to +// avoid duplicate releases of the same variable). +void Solver::releaseVar(Lit l) { + if (value(l) == l_Undef) { + addClause(l); + released_vars.push(var(l)); + } } - -bool Solver::addClause_(vec& ps) -{ - assert(decisionLevel() == 0); - if (!ok) return false; - - // Check if clause is satisfied and remove false/duplicate literals: - sort(ps); - Lit p; int i, j; - for (i = j = 0, p = lit_Undef; i < ps.size(); i++) - if (value(ps[i]) == l_True || ps[i] == ~p) - return true; - else if (value(ps[i]) != l_False && ps[i] != p) - ps[j++] = p = ps[i]; - ps.shrink(i - j); - - if (ps.size() == 0) - return ok = false; - else if (ps.size() == 1){ - uncheckedEnqueue(ps[0]); - return ok = (propagate() == CRef_Undef); - }else{ - CRef cr = ca.alloc(ps, false); - clauses.push(cr); - attachClause(cr); - } - - return true; +bool Solver::addClause_(vec& ps) { + assert(decisionLevel() == 0); + if (!ok) return false; + + // Check if clause is satisfied and remove false/duplicate literals: + sort(ps); + Lit p; + int i, j; + for (i = j = 0, p = lit_Undef; i < ps.size(); i++) + if (value(ps[i]) == l_True || ps[i] == ~p) + return true; + else if (value(ps[i]) != l_False && ps[i] != p) + ps[j++] = p = ps[i]; + ps.shrink(i - j); + + if (ps.size() == 0) + return ok = false; + else if (ps.size() == 1) { + uncheckedEnqueue(ps[0]); + return ok = (propagate() == CRef_Undef); + } else { + CRef cr = ca.alloc(ps, false); + clauses.push(cr); + attachClause(cr); + } + + return true; } - -void Solver::attachClause(CRef cr){ - const Clause& c = ca[cr]; - assert(c.size() > 1); - watches[~c[0]].push(Watcher(cr, c[1])); - watches[~c[1]].push(Watcher(cr, c[0])); - if (c.learnt()) num_learnts++, learnts_literals += c.size(); - else num_clauses++, clauses_literals += c.size(); +void Solver::attachClause(CRef cr) { + const Clause& c = ca[cr]; + assert(c.size() > 1); + watches[~c[0]].push(Watcher(cr, c[1])); + watches[~c[1]].push(Watcher(cr, c[0])); + if (c.learnt()) + num_learnts++, learnts_literals += c.size(); + else + num_clauses++, clauses_literals += c.size(); } - -void Solver::detachClause(CRef cr, bool strict){ - const Clause& c = ca[cr]; - assert(c.size() > 1); - - // Strict or lazy detaching: - if (strict){ - remove(watches[~c[0]], Watcher(cr, c[1])); - remove(watches[~c[1]], Watcher(cr, c[0])); - }else{ - watches.smudge(~c[0]); - watches.smudge(~c[1]); - } - - if (c.learnt()) num_learnts--, learnts_literals -= c.size(); - else num_clauses--, clauses_literals -= c.size(); +void Solver::detachClause(CRef cr, bool strict) { + const Clause& c = ca[cr]; + assert(c.size() > 1); + + // Strict or lazy detaching: + if (strict) { + remove(watches[~c[0]], Watcher(cr, c[1])); + remove(watches[~c[1]], Watcher(cr, c[0])); + } else { + watches.smudge(~c[0]); + watches.smudge(~c[1]); + } + + if (c.learnt()) + num_learnts--, learnts_literals -= c.size(); + else + num_clauses--, clauses_literals -= c.size(); } - void Solver::removeClause(CRef cr) { - Clause& c = ca[cr]; - detachClause(cr); - // Don't leave pointers to free'd memory! - if (locked(c)) vardata[var(c[0])].reason = CRef_Undef; - c.mark(1); - ca.free(cr); + Clause& c = ca[cr]; + detachClause(cr); + // Don't leave pointers to free'd memory! + if (locked(c)) vardata[var(c[0])].reason = CRef_Undef; + c.mark(1); + ca.free(cr); } - bool Solver::satisfied(const Clause& c) const { - for (int i = 0; i < c.size(); i++) - if (value(c[i]) == l_True) - return true; - return false; } - + for (int i = 0; i < c.size(); i++) + if (value(c[i]) == l_True) return true; + return false; +} -// Revert to the state at given level (keeping all assignment at 'level' but not beyond). +// Revert to the state at given level (keeping all assignment at 'level' but not +// beyond). // void Solver::cancelUntil(int level) { - if (decisionLevel() > level){ - for (int c = trail.size()-1; c >= trail_lim[level]; c--){ - Var x = var(trail[c]); - assigns [x] = l_Undef; - if (phase_saving > 1 || (phase_saving == 1 && c > trail_lim.last())) - polarity[x] = sign(trail[c]); - insertVarOrder(x); } - qhead = trail_lim[level]; - trail.shrink(trail.size() - trail_lim[level]); - trail_lim.shrink(trail_lim.size() - level); - } } - + if (decisionLevel() > level) { + for (int c = trail.size() - 1; c >= trail_lim[level]; c--) { + Var x = var(trail[c]); + assigns[x] = l_Undef; + if (phase_saving > 1 || (phase_saving == 1 && c > trail_lim.last())) + polarity[x] = sign(trail[c]); + insertVarOrder(x); + } + qhead = trail_lim[level]; + trail.shrink(trail.size() - trail_lim[level]); + trail_lim.shrink(trail_lim.size() - level); + } +} //================================================================================================= // Major methods: - -Lit Solver::pickBranchLit() -{ - Var next = var_Undef; - // Random decision: - if (drand(random_seed) < random_var_freq && !order_heap.empty()){ - next = order_heap[irand(random_seed,order_heap.size())]; - if (value(next) == l_Undef && decision[next]) - rnd_decisions++; } - - // Activity based decision: - while (next == var_Undef || value(next) != l_Undef || !decision[next]) - if (order_heap.empty()){ - next = var_Undef; - break; - }else - next = order_heap.removeMin(); - - // Choose polarity based on different polarity modes (global or per-variable): - if (next == var_Undef) - return lit_Undef; - else if (user_pol[next] != l_Undef) - return mkLit(next, user_pol[next] == l_True); - else if (rnd_pol) - return mkLit(next, drand(random_seed) < 0.5); - else - return mkLit(next, polarity[next]); +Lit Solver::pickBranchLit() { + Var next = var_Undef; + // Random decision: + if (drand(random_seed) < random_var_freq && !order_heap.empty()) { + next = order_heap[irand(random_seed, order_heap.size())]; + if (value(next) == l_Undef && decision[next]) rnd_decisions++; + } + + // Activity based decision: + while (next == var_Undef || value(next) != l_Undef || !decision[next]) + if (order_heap.empty()) { + next = var_Undef; + break; + } else + next = order_heap.removeMin(); + + // Choose polarity based on different polarity modes (global or per-variable): + if (next == var_Undef) + return lit_Undef; + else if (user_pol[next] != l_Undef) { + return mkLit(next, user_pol[next] == l_True); + } else if (rnd_pol) + return mkLit(next, drand(random_seed) < 0.5); + else + return mkLit(next, polarity[next]); } - -/*_________________________________________________________________________________________________ - | - | analyze : (confl : Clause*) (out_learnt : vec&) (out_btlevel : int&) -> [void] - | - | Description: - | Analyze conflict and produce a reason clause. - | - | Pre-conditions: - | * 'out_learnt' is assumed to be cleared. - | * Current decision level must be greater than root level. - | - | Post-conditions: - | * 'out_learnt[0]' is the asserting literal at level 'out_btlevel'. - | * If out_learnt.size() > 1 then 'out_learnt[1]' has the greatest decision level of the - | rest of literals. There may be others from the same level though. - | - |________________________________________________________________________________________________@*/ -void Solver::analyze(CRef confl, vec& out_learnt, int& out_btlevel) -{ - int pathC = 0; - Lit p = lit_Undef; - - // Generate conflict clause: - // - out_learnt.push(); // (leave room for the asserting literal) - int index = trail.size() - 1; - - do{ - assert(confl != CRef_Undef); // (otherwise should be UIP) - Clause& c = ca[confl]; - - if (c.learnt()) - claBumpActivity(c); - - for (int j = (p == lit_Undef) ? 0 : 1; j < c.size(); j++){ - Lit q = c[j]; - - if (!seen[var(q)] && level(var(q)) > 0){ - varBumpActivity(var(q)); - seen[var(q)] = 1; - if (level(var(q)) >= decisionLevel()) - pathC++; - else - out_learnt.push(q); - } - } - - // Select next clause to look at: - while (!seen[var(trail[index--])]); - p = trail[index+1]; - confl = reason(var(p)); - seen[var(p)] = 0; - pathC--; - - }while (pathC > 0); - out_learnt[0] = ~p; - - // Simplify conflict clause: - // - int i, j; - out_learnt.copyTo(analyze_toclear); - if (ccmin_mode == 2){ - for (i = j = 1; i < out_learnt.size(); i++) - if (reason(var(out_learnt[i])) == CRef_Undef || !litRedundant(out_learnt[i])) - out_learnt[j++] = out_learnt[i]; - - }else if (ccmin_mode == 1){ - for (i = j = 1; i < out_learnt.size(); i++){ - Var x = var(out_learnt[i]); - - if (reason(x) == CRef_Undef) - out_learnt[j++] = out_learnt[i]; - else{ - Clause& c = ca[reason(var(out_learnt[i]))]; - for (int k = 1; k < c.size(); k++) - if (!seen[var(c[k])] && level(var(c[k])) > 0){ - out_learnt[j++] = out_learnt[i]; - break; } - } - } - }else - i = j = out_learnt.size(); - - max_literals += out_learnt.size(); - out_learnt.shrink(i - j); - tot_literals += out_learnt.size(); - - // Find correct backtrack level: - // - if (out_learnt.size() == 1) - out_btlevel = 0; - else{ - int max_i = 1; - // 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]))) - max_i = i; - // Swap-in this literal at index 1: - Lit p = out_learnt[max_i]; - out_learnt[max_i] = out_learnt[1]; - out_learnt[1] = p; - out_btlevel = level(var(p)); +inline unsigned int Solver::computeLBD(const Clause& c) { + int nblevels = 0; + MYFLAG++; + for (int i = 0; i < c.size(); i++) { + int l = level(var(c[i])); + if (permDiff[l] != MYFLAG) { + permDiff[l] = MYFLAG; + nblevels++; } - - for (int j = 0; j < analyze_toclear.size(); j++) seen[var(analyze_toclear[j])] = 0; // ('seen[]' is now cleared) + } + return nblevels; } +/*_________________________________________________________________________________________________ +| +| analyze : (confl : Clause*) (out_learnt : vec&) (out_btlevel : int&) -> +[void] +| +| Description: +| Analyze conflict and produce a reason clause. +| +| Pre-conditions: +| * 'out_learnt' is assumed to be cleared. +| * Current decision level must be greater than root level. +| +| Post-conditions: +| * 'out_learnt[0]' is the asserting literal at level 'out_btlevel'. +| * If out_learnt.size() > 1 then 'out_learnt[1]' has the greatest decision +level of the | rest of literals. There may be others from the same level +though. +| +|________________________________________________________________________________________________@*/ +void Solver::analyze(CRef confl, vec& out_learnt, int& out_btlevel) { + int pathC = 0; + Lit p = lit_Undef; -// Check if 'p' can be removed from a conflict clause. -bool Solver::litRedundant(Lit p) -{ - enum { seen_undef = 0, seen_source = 1, seen_removable = 2, seen_failed = 3 }; - assert(seen[var(p)] == seen_undef || seen[var(p)] == seen_source); - assert(reason(var(p)) != CRef_Undef); - - Clause* c = &ca[reason(var(p))]; - vec& stack = analyze_stack; - stack.clear(); - - for (uint32_t i = 1; ; i++){ - if (i < (uint32_t)c->size()){ - // Checking 'p'-parents 'l': - Lit l = (*c)[i]; - - // Variable at level 0 or previously removable: - if (level(var(l)) == 0 || seen[var(l)] == seen_source || seen[var(l)] == seen_removable){ - continue; } - - // Check variable can not be removed for some local reason: - if (reason(var(l)) == CRef_Undef || seen[var(l)] == seen_failed){ - stack.push(ShrinkStackElem(0, p)); - for (int i = 0; i < stack.size(); i++) - if (seen[var(stack[i].l)] == seen_undef){ - seen[var(stack[i].l)] = seen_failed; - analyze_toclear.push(stack[i].l); - } - - return false; - } - - // Recursively check 'l': - stack.push(ShrinkStackElem(i, p)); - i = 0; - p = l; - c = &ca[reason(var(p))]; - }else{ - // Finished with current element 'p' and reason 'c': - if (seen[var(p)] == seen_undef){ - seen[var(p)] = seen_removable; - analyze_toclear.push(p); - } - - // Terminate with success if stack is empty: - if (stack.size() == 0) break; - - // Continue with top element on stack: - i = stack.last().i; - p = stack.last().l; - c = &ca[reason(var(p))]; - - stack.pop(); - } - } - - return true; -} - -/*---------------------------------------------------------------------------------------- - analyzeFinal. These are special versions of analyze designed to compute a conflict over - the assumptions. That is a conflict clause containing only negated assumption literals. - - In search, analyzeFinal is called in two places. First to compute a conflict when all - assumptions have been set as decisions at level 1, and second to recompute (reprocess) - a conflict when assumptions as decisions at separate levels. + // Generate conflict clause: + // + out_learnt.push(); // (leave room for the asserting literal) + int index = trail.size() - 1; - The basic assumption is that all non-implied literals on the trail - are positive assumption literals. + do { + assert(confl != CRef_Undef); // (otherwise should be UIP) + Clause& c = ca[confl]; - There are options - 1. All decision conflict---does not seem to perform as well - 2. keep all negative assumption literals in the conflict (never resolve these away) - this performs better but on conflict reprocess this can yield a longer conflict - than the original - 3. all-uip conflict. + if (c.learnt()) claBumpActivity(c); - Note that if all assumptions are set first at decision level 1, then all the above - conflicts are the same. Also the all-uip code does not handle the case - when all assumptions are made at the same level 1 (messes up the counts per level). + for (int j = (p == lit_Undef) ? 0 : 1; j < c.size(); j++) { + Lit q = c[j]; - Thus we have different versions. ----------------------------------------------------------------------------------------*/ + if (!seen[var(q)] && level(var(q)) > 0) { + varBumpActivity(var(q)); + seen[var(q)] = 1; + if (level(var(q)) >= decisionLevel()) + pathC++; + else + out_learnt.push(q); + } + } -/*_________________________________________________________________________________________________ - | - | analyzeFinal1 : (confl : CRef) -> [void] - | - | Description: - | -handles all assumptions at level 1. - | -computes conflict of type 2. - ____________________________________________________________________________________________*/ - -inline void Solver::af1_mark_conflict(CRef confl, bool atZero, int &n_toR, LSet& out_conflict) { - //helper for analyzeFinal1--marks literals in conflict, adding some to out_conflict - Clause& c = ca[confl]; - for (int i = (atZero ? 0 : 1); i < c.size(); i++) { - Lit l = c[i]; Var y = var(l); - if(level(y) == 0 || seen[y]) //level zero lits not needed, skip already processed - continue; - if(reason(y) == CRef_Undef || isAssumedFalse(l)) { - out_conflict.insert(l); - assert(isAssumedFalse(l)); - seen[y] = 2; - } - else { //must resolve later - seen[y] = 1; - ++n_toR; - } + // Select next clause to look at: + while (!seen[var(trail[index--])]) + ; + p = trail[index + 1]; + confl = reason(var(p)); + seen[var(p)] = 0; + pathC--; + + } while (pathC > 0); + out_learnt[0] = ~p; + + // Simplify conflict clause: + // + int i, j; + out_learnt.copyTo(analyze_toclear); + if (ccmin_mode == 2) { + for (i = j = 1; i < out_learnt.size(); i++) + if (reason(var(out_learnt[i])) == CRef_Undef || + !litRedundant(out_learnt[i])) + out_learnt[j++] = out_learnt[i]; + + } else if (ccmin_mode == 1) { + for (i = j = 1; i < out_learnt.size(); i++) { + Var x = var(out_learnt[i]); + + if (reason(x) == CRef_Undef) + out_learnt[j++] = out_learnt[i]; + else { + Clause& c = ca[reason(var(out_learnt[i]))]; + for (int k = 1; k < c.size(); k++) + if (!seen[var(c[k])] && level(var(c[k])) > 0) { + out_learnt[j++] = out_learnt[i]; + break; + } + } } + } else + i = j = out_learnt.size(); + + max_literals += out_learnt.size(); + out_learnt.shrink(i - j); + tot_literals += out_learnt.size(); + + // Find correct backtrack level: + // + if (out_learnt.size() == 1) + out_btlevel = 0; + else { + int max_i = 1; + // 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]))) max_i = i; + // Swap-in this literal at index 1: + Lit p = out_learnt[max_i]; + out_learnt[max_i] = out_learnt[1]; + out_learnt[1] = p; + out_btlevel = level(var(p)); + } + + for (int j = 0; j < analyze_toclear.size(); j++) + seen[var(analyze_toclear[j])] = 0; // ('seen[]' is now cleared) } -void Solver::analyzeFinal1(CRef confl, LSet& out_conflict) -{ - out_conflict.clear(); - if (decisionLevel() == 0) - return; - int n_toR {0}; //keep track of number of ltierals still to resolve. - assert(decisionLevel() >= 1); - - //uses seen markers to store the literals that need to be resolved - //out of the conflict. Also uses seen markers to ensure that duplicate - //lits not added to out_conflict. - - af1_mark_conflict(confl, true, n_toR, out_conflict); - //now do resolutions required to obtain final conflict. - for (int i = trail.size()-1; n_toR > 0 && i >= trail_lim[0]; i--) { - Var x = var(trail[i]); - if(seen[x]) { - if(seen[x] == 1) { - //x has a reason and to be resolved away. - --n_toR; - af1_mark_conflict(reason(x), false, n_toR, out_conflict); - } - seen[x] = 0; - } +// Check if 'p' can be removed from a conflict clause. +bool Solver::litRedundant(Lit p) { + enum { seen_undef = 0, seen_source = 1, seen_removable = 2, seen_failed = 3 }; + assert(seen[var(p)] == seen_undef || seen[var(p)] == seen_source); + assert(reason(var(p)) != CRef_Undef); + + Clause* c = &ca[reason(var(p))]; + vec& stack = analyze_stack; + stack.clear(); + + for (uint32_t i = 1;; i++) { + if (i < (uint32_t)c->size()) { + // Checking 'p'-parents 'l': + Lit l = (*c)[i]; + + // Variable at level 0 or previously removable: + if (level(var(l)) == 0 || seen[var(l)] == seen_source || + seen[var(l)] == seen_removable) { + continue; + } + + // Check variable can not be removed for some local reason: + if (reason(var(l)) == CRef_Undef || seen[var(l)] == seen_failed) { + stack.push(ShrinkStackElem(0, p)); + for (int i = 0; i < stack.size(); i++) + if (seen[var(stack[i].l)] == seen_undef) { + seen[var(stack[i].l)] = seen_failed; + analyze_toclear.push(stack[i].l); + } + + return false; + } + + // Recursively check 'l': + stack.push(ShrinkStackElem(i, p)); + i = 0; + p = l; + c = &ca[reason(var(p))]; + } else { + // Finished with current element 'p' and reason 'c': + if (seen[var(p)] == seen_undef) { + seen[var(p)] = seen_removable; + analyze_toclear.push(p); + } + + // Terminate with success if stack is empty: + if (stack.size() == 0) break; + + // Continue with top element on stack: + i = stack.last().i; + p = stack.last().l; + c = &ca[reason(var(p))]; + + stack.pop(); } - //because of terminating when n_toR == 0, we might not have cleared seen for some literals in out_conflict. - for(int i=0; i < out_conflict.size(); i++) - seen[var(out_conflict[i])] = 0; + } + + return true; } /*_________________________________________________________________________________________________ | -| analyzeFinal1 : (p : Lit) -> [void] +| analyzeFinal : (p : Lit) -> [void] | | Description: -| Search has found a forced falsified assumption literal p. -| Hence, given a NEGATED ASSUMPTION LITERAL p we compute a reason A -> p where A contains only positive -| assumption literals. The result is a clause (-A\/p) consisting only of negated assumptions. -| Requires at all decisions (non-implied literals on the trail) are postive assumption literals. +| Specialized analysis procedure to express the final conflict in terms of +assumptions. | Calculates the (possibly empty) set of assumptions that led to +the assignment of 'p', and | stores the result in 'out_conflict'. |________________________________________________________________________________________________@*/ - -void Solver::analyzeFinal1(Lit p, LSet& out_conflict) -{ +void Solver::analyzeFinal(Lit p, LSet& out_conflict) { + if (decisionLevel() == 0) { out_conflict.clear(); out_conflict.insert(p); - if (decisionLevel() == 0 || reason(var(p)) == CRef_Undef) - return; - int n_toR {0}; //keep track of number of literals still to resolve - - //if p is implied we need its reason as the start of A. - af1_mark_conflict(reason(var(p)), false, n_toR, out_conflict); - - //now do resolutions required to obtain final conflict. - for (int i = trail.size()-1; n_toR > 0 && i >= trail_lim[0]; i--) { - Var x = var(trail[i]); - if (seen[x]) { //resolve away x - if(seen[x] == 1) { - //x has a reason and to be resolved away. - --n_toR; - af1_mark_conflict(reason(x), false, n_toR, out_conflict); - } - seen[x] = 0; - } + return; + } + + vec confl; + confl.push(p); + + seen[var(p)] = 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 || isAssumedTrue(trail[i])) { + assert(level(x) > 0); + confl.push(~trail[i]); + } else { + seen[x] = 0; + Clause& c = ca[reason(x)]; + for (int j = 1; j < c.size(); j++) + if (level(var(c[j])) > 0) seen[var(c[j])] = 1; + } } - //because of terminating when n_toR == 0, we might not have cleared seen for some literals in out_conflict. - for(int i=0; i < out_conflict.size(); i++) - seen[var(out_conflict[i])] = 0; + } + seen[var(p)] = 0; + +#if 0 + //DEBUG + for(int k=0; k < confl.size(); k++) { + Lit q = confl[k]; + if(k==0 && seen[var(q)]) + printf("AF: seen marked at confl[0]\n"); + if(k!=0 && !seen[var(q)]) + printf("AF: seen not marked at confl[k] (k>0)\n"); + seen[var(q)] = 0; + } + + for(Var v=0; v< nVars(); v++) + if(seen[v]) + printf("AF: SEEN not cleared\n"); + + for(int k=1; k < confl.size(); k++) { + Lit q = confl[k]; + seen[var(q)] = 1; + } + //DEBUG +#endif + + confl.copyTo(analyze_toclear); + int i, j; + for (i = j = 1; i < confl.size(); i++) + if (reason(var(confl[i])) == CRef_Undef || !litRedundant(confl[i])) + confl[j++] = confl[i]; + + confl.shrink(i - j); + // if(i-j > 0) + // printf("AF: litRedundant removed %d\n", i-j); + + out_conflict.clear(); + for (int k = 0; k < confl.size(); k++) out_conflict.insert(confl[k]); + + for (int j = 0; j < analyze_toclear.size(); j++) + seen[var(analyze_toclear[j])] = 0; } -/* analyzeFinal2 These versions compute all-UIP clause. So resulting conflict from - reprocessing cannot be any bigger. However, they currently won't work with - all assumptions at level 1. So only use when reprocessing the assumption. -*/ - -inline void Solver::af2_mark_conflict(CRef confl, bool atZero, int &n_toR, - vec& n_atLevel, LSet& out_conflict) { - //helper for analyzeFinal2--marks literals in conflict, adding some to out_conflict - Clause& c = ca[confl]; - for(int i = (atZero ? 0 : 1); i < c.size(); i++) { - Lit l = c[i]; Var y = var(l); - if(level(y) == 0 || seen[y]) - continue; - if(reason(y) == CRef_Undef) { - out_conflict.insert(l); - assert(isAssumedFalse(l)); - seen[y] = 2; - ++n_atLevel[level(y)]; - } - else { //must check later to see if to be resolved - seen[y] = 1; - ++n_toR; - ++n_atLevel[level(y)]; - } - } +void Solver::uncheckedEnqueue(Lit p, CRef from) { + assert(value(p) == l_Undef); + assigns[var(p)] = lbool(!sign(p)); + vardata[var(p)] = mkVarData(from, decisionLevel(), vardata[var(p)].isAssumed); + trail.push_(p); } /*_________________________________________________________________________________________________ | -| analyzeFinal2 : (p : Lit) -> [void] +| propagate : [void] -> [Clause*] +| +| Description: +| Propagates all enqueued facts. If a conflict arises, the conflicting clause +is returned, | otherwise CRef_Undef. +| +| Post-conditions: +| * the propagation queue is empty, even if there was a conflict. |________________________________________________________________________________________________@*/ -void Solver::analyzeFinal2(Lit p, LSet& out_conflict) -{ - out_conflict.clear(); - out_conflict.insert(p); - if (decisionLevel() == 0 || reason(var(p)) == CRef_Undef) //(p) is the conflict - return; - int n_toR {0}; //keep track of number of literals still to resolve - vec n_atLevel (decisionLevel() + 1, 0); //n of lit in conflict at decision level. - - //if p is implied we need its reason as the start of A. - af2_mark_conflict(reason(var(p)), false, n_toR, n_atLevel, out_conflict); - - //Now climb up trail materalizing conflict from seen markers---updating seen markers - //as we do resolution steps. - for (int i = trail.size()-1; n_toR > 0 && i >= trail_lim[0]; i--) { - Lit l = trail[i]; Var y = var(l); - if (seen[y]) { - if(seen[y] == 1) { - if(!isAssumedTrue(l) || n_atLevel[level(y)] > 1) { - //~l is not a negated assumption, or we have more than one at this level. - //so we must resolve it away. - af2_mark_conflict(reason(y), false, n_toR, n_atLevel, out_conflict); - } - else { //~l is sole remaining negated assumption at this level. Keep it. - out_conflict.insert(~l); - assert(isAssumedFalse(~l)); - assert(n_atLevel[level(y)] == 1); - } - --n_atLevel[level(y)]; - --n_toR; - } - if(seen[y] == 2) - --n_atLevel[level(y)]; - seen[y] = 0; +[[gnu::hot]] CRef Solver::propagate() { + CRef confl = CRef_Undef; + int num_props = 0; + + while (qhead < trail.size()) { + Lit p = trail[qhead++]; // 'p' is enqueued fact to propagate. + vec& ws = watches.lookup(p); + Watcher *i, *j, *end; + num_props++; + + for (i = j = (Watcher*)ws, end = i + ws.size(); i != end;) { + // Try to avoid inspecting the clause: + Lit blocker = i->blocker; + if (value(blocker) == l_True) { + *j++ = *i++; + continue; + } + + // Make sure the false literal is data[1]: + CRef cr = i->cref; + Clause& c = ca[cr]; + Lit false_lit = ~p; + if (c[0] == false_lit) c[0] = c[1], c[1] = false_lit; + assert(c[1] == false_lit); + i++; + + // If 0th watch is true, then clause is already satisfied. + Lit first = c[0]; + Watcher w = Watcher(cr, first); + if (first != blocker && value(first) == l_True) { + *j++ = w; + continue; + } + + // Look for new watch: + for (int k = 2; k < c.size(); k++) + if (value(c[k]) != l_False) { + c[1] = c[k]; + c[k] = false_lit; + watches[~c[1]].push(w); + goto NextClause; } - } - for(int i=0; i < out_conflict.size(); i++) - seen[var(out_conflict[i])] = 0; -} - -/*_________________________________________________________________________________________________ - | - | analyzeFinal2 : (confl : CRef) -> [void] - |________________________________________________________________________________________________*/ -void Solver::analyzeFinal2(CRef confl, LSet& out_conflict) -{ - out_conflict.clear(); - if (decisionLevel() == 0) - return; - assert(decisionLevel() >= 1); - int n_toR {0}; //keep track of number of ltierals still to resolve. - vec n_atLevel (decisionLevel() + 1, 0); //n of lit in conflict at decision level. - - //Start my marking conflict clause among the seen markers. - af2_mark_conflict(confl, true, n_toR, n_atLevel, out_conflict); - - //no climb up trail doing resolutions - for (int i = trail.size()-1; n_toR > 0 && i >= trail_lim[0]; i--) { - Lit l = trail[i]; Var y = var(l); - if (seen[y]) { - if(seen[y] == 1) { - if(!isAssumedTrue(l) || n_atLevel[level(y)] > 1) { - //~l is not a negated assumption, or we have more than one at this level. - //so we must resolve it away. - af2_mark_conflict(reason(y), false, n_toR, n_atLevel, out_conflict); - } - else { //~l is sole remaining negated assumption at this level. Keep it. - out_conflict.insert(~l); - assert(isAssumedFalse(~l)); - assert(n_atLevel[level(y)] == 1); - } - --n_atLevel[level(y)]; - --n_toR; - } - if(seen[y] == 2) - --n_atLevel[level(y)]; - seen[y] = 0; + // Did not find watch -- clause is unit under assignment: + *j++ = w; + if (value(first) == l_False || + (decisionLevel() != 0 && isAssumedFalse(first))) { + if (value(first) != l_False) { + uncheckedEnqueue(first, cr); + /*//DEBUG + //printf("propagate() falsified assumption!\n"); + bool found = false; + for(int k=0; k< assumptions.size(); k++) + if(assumptions[k] == ~first) + found = true; + if(!found) + printf("ERROR: propagate() not in assumptions\n"); + //DEBUG*/ } - } - for(int i=0; i < out_conflict.size(); i++) - seen[var(out_conflict[i])] = 0; -} - -void Solver::uncheckedEnqueue(Lit p, CRef from) -{ - assert(value(p) == l_Undef); - assigns[var(p)] = lbool(!sign(p)); - vardata[var(p)].reason = from; - vardata[var(p)].level = decisionLevel(); //vardata[var(p)].isAssumed is not changed - trail.push_(p); -} - + confl = cr; + qhead = trail.size(); + // Copy the remaining watches: + while (i < end) *j++ = *i++; + } else + uncheckedEnqueue(first, cr); -/*_________________________________________________________________________________________________ - | - | propagate : [void] -> [Clause*] - | - | Description: - | Propagates all enqueued facts. If a conflict arises, the conflicting clause is returned, - | otherwise CRef_Undef. - | - | Post-conditions: - | * the propagation queue is empty, even if there was a conflict. - |________________________________________________________________________________________________@*/ -[[gnu::hot]] -CRef Solver::propagate() -{ - CRef confl = CRef_Undef; - int num_props = 0; - - while (qhead < trail.size()){ - Lit p = trail[qhead++]; // 'p' is enqueued fact to propagate. - vec& ws = watches.lookup(p); - Watcher *i, *j, *end; - num_props++; - - for (i = j = (Watcher*)ws, end = i + ws.size(); i != end;){ - // Try to avoid inspecting the clause: - Lit blocker = i->blocker; - if (value(blocker) == l_True){ - *j++ = *i++; continue; } - - // Make sure the false literal is data[1]: - CRef cr = i->cref; - Clause& c = ca[cr]; - Lit false_lit = ~p; - if (c[0] == false_lit) - c[0] = c[1], c[1] = false_lit; - assert(c[1] == false_lit); - i++; - - // If 0th watch is true, then clause is already satisfied. - Lit first = c[0]; - Watcher w = Watcher(cr, first); - if (first != blocker && value(first) == l_True){ - *j++ = w; continue; } - - // Look for new watch: - for (int k = 2; k < c.size(); k++) - if (value(c[k]) != l_False){ - c[1] = c[k]; c[k] = false_lit; - watches[~c[1]].push(w); - goto NextClause; } - - // Did not find watch -- clause is unit under assignment: - *j++ = w; - if (value(first) == l_False){ - confl = cr; - qhead = trail.size(); - // Copy the remaining watches: - while (i < end) - *j++ = *i++; - }else - uncheckedEnqueue(first, cr); - - NextClause:; - } - ws.shrink(i - j); + NextClause:; } - propagations += num_props; - simpDB_props -= num_props; + ws.shrink(i - j); + } + propagations += num_props; + simpDB_props -= num_props; - return confl; + return confl; } - /*_________________________________________________________________________________________________ - | - | reduceDB : () -> [void] - | - | Description: - | Remove half of the learnt clauses, minus the clauses locked by the current assignment. Locked - | clauses are clauses that are reason to some assignment. Binary clauses are never removed. - |________________________________________________________________________________________________@*/ +| +| reduceDB : () -> [void] +| +| Description: +| Remove half of the learnt clauses, minus the clauses locked by the current +assignment. Locked | clauses are clauses that are reason to some assignment. +Binary clauses are never removed. +|________________________________________________________________________________________________@*/ struct reduceDB_lt { - ClauseAllocator& ca; - reduceDB_lt(ClauseAllocator& ca_) : ca(ca_) {} - bool operator () (CRef x, CRef y) { - return ca[x].size() > 2 && (ca[y].size() == 2 || ca[x].activity() < ca[y].activity()); } + ClauseAllocator& ca; + reduceDB_lt(ClauseAllocator& ca_) : ca(ca_) {} + bool operator()(CRef x, CRef y) { + if (ca[x].size() > 2 && ca[y].size() == 2) return 1; + if (ca[y].size() > 2 && ca[x].size() == 2) return 0; + if (ca[x].size() == 2 && ca[y].size() == 2) return 0; + // Second one based on literal block distance + if (ca[x].lbd() > ca[y].lbd()) return 1; + if (ca[x].lbd() < ca[y].lbd()) return 0; + + // Finally we can use old activity or size, we choose the last one + return ca[x].activity() < ca[y].activity(); + } }; -void Solver::reduceDB() -{ - int i, j; - double extra_lim = cla_inc / learnts.size(); // Remove any clause below this activity - - sort(learnts, reduceDB_lt(ca)); - // Don't delete binary or locked clauses. From the rest, delete clauses from the first half - // and clauses with activity smaller than 'extra_lim': - for (i = j = 0; i < learnts.size(); i++){ - Clause& c = ca[learnts[i]]; - if (c.size() > 2 && !locked(c) && (i < learnts.size() / 2 || c.activity() < extra_lim)) - removeClause(learnts[i]); - else - learnts[j++] = learnts[i]; +void Solver::reduceDB() { + int i, j; + /*double extra_lim = + cla_inc / learnts.size(); // Remove any clause below this activity*/ + sort(learnts, reduceDB_lt(ca)); + if (ca[learnts[learnts.size() / 2]].lbd() <= 3) + nbclausesbeforereduce += specialIncReduceDB; + + // Don't delete binary or locked clauses. From the rest, delete clauses from + // the first half and clauses with activity smaller than 'extra_lim': + + int limit = learnts.size() / 2; + for (i = j = 0; i < learnts.size(); i++) { + Clause& c = ca[learnts[i]]; + if (c.lbd() > 2 && c.size() > 3 && !locked(c) && (i < limit)) { + removeClause(learnts[i]); + } else { + learnts[j++] = learnts[i]; } - learnts.shrink(i - j); - checkGarbage(); + } + /*for (i = j = 0; i < learnts.size(); i++) { + Clause& c = ca[learnts[i]]; + if (c.size() > 2 && !locked(c) && + (i < learnts.size() / 2 || c.activity() < extra_lim)) + removeClause(learnts[i]); + else + learnts[j++] = learnts[i]; + }*/ + learnts.shrink(i - j); + checkGarbage(); } - -void Solver::removeSatisfied(vec& cs) -{ - int i, j; - for (i = j = 0; i < cs.size(); i++){ - Clause& c = ca[cs[i]]; - if (satisfied(c)) - removeClause(cs[i]); - else{ - // Trim clause: - assert(value(c[0]) == l_Undef && value(c[1]) == l_Undef); - for (int k = 2; k < c.size(); k++) - if (value(c[k]) == l_False){ - c[k--] = c[c.size()-1]; - c.pop(); - } - cs[j++] = cs[i]; +void Solver::removeSatisfied(vec& cs) { + int i, j; + for (i = j = 0; i < cs.size(); i++) { + Clause& c = ca[cs[i]]; + if (satisfied(c)) + removeClause(cs[i]); + else { + // Trim clause: + assert(value(c[0]) == l_Undef && value(c[1]) == l_Undef); + for (int k = 2; k < c.size(); k++) + if (value(c[k]) == l_False) { + c[k--] = c[c.size() - 1]; + c.pop(); } + cs[j++] = cs[i]; } - cs.shrink(i - j); + } + cs.shrink(i - j); } - -void Solver::rebuildOrderHeap() -{ - vec vs; - for (Var v = 0; v < nVars(); v++) - if (decision[v] && value(v) == l_Undef) - vs.push(v); - order_heap.build(vs); +void Solver::rebuildOrderHeap() { + vec vs; + for (Var v = 0; v < nVars(); v++) + if (decision[v] && value(v) == l_Undef) vs.push(v); + order_heap.build(vs); } - /*_________________________________________________________________________________________________ - | - | simplify : [void] -> [bool] - | - | Description: - | Simplify the clause database according to the current top-level assigment. Currently, the only - | thing done here is the removal of satisfied clauses, but more things can be put here. - |________________________________________________________________________________________________@*/ -bool Solver::simplify() -{ - assert(decisionLevel() == 0); - - if (!ok || propagate() != CRef_Undef) - return ok = false; - - if (nAssigns() == simpDB_assigns || (simpDB_props > 0)) - return true; - - // Remove satisfied clauses: - removeSatisfied(learnts); - if (remove_satisfied){ // Can be turned off. - removeSatisfied(clauses); - - // TODO: what todo in if 'remove_satisfied' is false? - - // Remove all released variables from the trail: - for (int i = 0; i < released_vars.size(); i++){ - assert(seen[released_vars[i]] == 0); - seen[released_vars[i]] = 1; - } +| +| simplify : [void] -> [bool] +| +| Description: +| Simplify the clause database according to the current top-level assigment. +Currently, the only | thing done here is the removal of satisfied clauses, +but more things can be put here. +|________________________________________________________________________________________________@*/ +bool Solver::simplify() { + assert(decisionLevel() == 0); - int i, j; - for (i = j = 0; i < trail.size(); i++) - if (seen[var(trail[i])] == 0) - trail[j++] = trail[i]; - trail.shrink(i - j); - //printf("trail.size()= %d, qhead = %d\n", trail.size(), qhead); - qhead = trail.size(); + if (!ok || propagate() != CRef_Undef) return ok = false; - for (int i = 0; i < released_vars.size(); i++) - seen[released_vars[i]] = 0; + if (nAssigns() == simpDB_assigns || (simpDB_props > 0)) return true; - // Released variables are now ready to be reused: - append(released_vars, free_vars); - released_vars.clear(); - } - checkGarbage(); - rebuildOrderHeap(); + // Remove satisfied clauses: + removeSatisfied(learnts); + if (remove_satisfied) { // Can be turned off. + removeSatisfied(clauses); - simpDB_assigns = nAssigns(); - simpDB_props = clauses_literals + learnts_literals; // (shouldn't depend on stats really, but it will do for now) + // TODO: what todo in if 'remove_satisfied' is false? - return true; -} + // Remove all released variables from the trail: + for (int i = 0; i < released_vars.size(); i++) { + assert(seen[released_vars[i]] == 0); + seen[released_vars[i]] = 1; + } + int i, j; + for (i = j = 0; i < trail.size(); i++) + if (seen[var(trail[i])] == 0) trail[j++] = trail[i]; + trail.shrink(i - j); + // printf("trail.size()= %d, qhead = %d\n", trail.size(), qhead); + qhead = trail.size(); + + for (int i = 0; i < released_vars.size(); i++) seen[released_vars[i]] = 0; + + // Released variables are now ready to be reused: + append(released_vars, free_vars); + released_vars.clear(); + } + checkGarbage(); + rebuildOrderHeap(); + + simpDB_assigns = nAssigns(); + simpDB_props = + clauses_literals + learnts_literals; // (shouldn't depend on stats + // really, but it will do for now) + + return true; +} /*_________________________________________________________________________________________________ - | - | search : (nof_conflicts : int) (params : const SearchParams&) -> [lbool] - | - | Description: - | Search for a model the specified number of conflicts. - | NOTE! Use negative value for 'nof_conflicts' indicate infinity. - | - | Output: - | 'l_True' if a partial assigment that is consistent with respect to the clauseset is found. If - | all variables are decision variables, this means that the clause set is satisfiable. 'l_False' - | if the clause set is unsatisfiable. 'l_Undef' if the bound on number of conflicts is reached. - |________________________________________________________________________________________________@*/ -lbool Solver::search(int nof_conflicts) -{ - assert(ok); - int backtrack_level; - int conflictC = 0; - vec learnt_clause; - starts++; - - for (;;){ - CRef confl = propagate(); - if (confl != CRef_Undef){ - // CONFLICT - conflicts++; conflictC++; - if (decisionLevel() == 0) return l_False; //Conflict at level 0 - if (decisionLevel() == 1 && assumptions.size() > 0) { //Conflict among assumptions - analyzeFinal1(confl, conflict); - int orig_size = conflict.size(); ++n_assump_conflicts; - cancelUntil(0); - if (conflict.size() == 1) { - //slight extra. Normally units could escape since they are computed by analyzeFinal (not analyze) - uncheckedEnqueue(conflict[0]); - if(propagate() != CRef_Undef) //could have an even stronger (empty conflict) after prop. - conflict.clear(); - return l_False; - } - - if(!assump_2nd_process) - return l_False; - //with 2nd process we try to get a shorter conflict - //by setting the assumption literals in conflict to true one per - //decision level until one is set to false. Note conflict has negated - //assumptions. First prepare level 0. - if(propagate() != CRef_Undef) { - conflict.clear(); - return l_False; - } - bool did_not_reprocess {true}; - for(int i = 0; i < conflict.size(); i++) { // - Lit a = conflict[i]; //a is a negative assumption literal - if(value(a) == l_False) //assumption is true - continue; - if(value(a) == l_True) { //assumption is false - analyzeFinal2(a, conflict); //note overwrites conflict, so for loop must be terminated after. - did_not_reprocess = false; - break; - } - //else make ~a a new decision and then propagate. - newDecisionLevel(); - uncheckedEnqueue(~a); - CRef con = propagate(); - //It is possible that a subet of the assumptions causes a conflict... - //instead of forcing an assumption to be falsified. - if(con != CRef_Undef) { - analyzeFinal2(con, conflict); - did_not_reprocess = false; - break; - } - } - cancelUntil(0); - if(averbosity > 0 && conflict.size() != orig_size) { - cout << "c 2nd assumption processing changed size. Orig = " - << orig_size << " new = " << conflict.size() << " gain " - << orig_size - conflict.size() << "\n"; - bool ok = true; - for(int i=0; i < conflict.size(); i++) - if(!isAssumedFalse(conflict[i])) - ok=false; - if(!ok) - cout << "conflict does not contain all negated assumptions\n"; - } - assump_saved += orig_size - conflict.size(); - if(did_not_reprocess) - printf("C Warning 2nd attempt did not find second conflict!\n"); - - if (conflict.size() == 1) { - //again might have learnt a new unit so add this to the solver and check if an even shorter conflict is possible - uncheckedEnqueue(conflict[0]); - if(propagate() != CRef_Undef) //could have an even stronger (empty conflict) after prop. - conflict.clear(); - } - return l_False; - } - - //otherwise we got a conflict at a deeper level...do ordinary clause learning - learnt_clause.clear(); - analyze(confl, learnt_clause, backtrack_level); - cancelUntil(backtrack_level); - - if (learnt_clause.size() == 1){ - uncheckedEnqueue(learnt_clause[0]); - }else{ - CRef cr = ca.alloc(learnt_clause, true); - learnts.push(cr); - attachClause(cr); - claBumpActivity(ca[cr]); - uncheckedEnqueue(learnt_clause[0], cr); - } - - varDecayActivity(); - claDecayActivity(); - - if (--learntsize_adjust_cnt == 0){ - learntsize_adjust_confl *= learntsize_adjust_inc; - learntsize_adjust_cnt = (int)learntsize_adjust_confl; - max_learnts *= learntsize_inc; - - if (verbosity >= 1) - printf("| %9d | %7d %8d %8d | %8d %8d %6.0f | %6.3f %% |\n", - (int)conflicts, - (int)dec_vars - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]), nClauses(), (int)clauses_literals, - (int)max_learnts, nLearnts(), (double)learnts_literals/nLearnts(), progressEstimate()*100); - } - - }else{ - // NO CONFLICT - if ((nof_conflicts >= 0 && conflictC >= nof_conflicts) || !withinBudget()){ - // Reached bound on number of conflicts: - progress_estimate = progressEstimate(); - cancelUntil(0); - return l_Undef; } - - // Simplify the set of problem clauses: - if (decisionLevel() == 0 && !simplify()) - return l_False; - - if (learnts.size()-nAssigns() >= max_learnts) - // Reduce the set of learnt clauses: - reduceDB(); - - if (decisionLevel() == 0 && assumptions.size() > 0) { //enqueue all assumptions if finished with dl 0 - newDecisionLevel(); - for (int i = 0; i < assumptions.size(); i++) { - Lit p = assumptions[i]; - if(value(p) == l_False) { - conflict.insert(~p); - return l_False; - } - else if (value(p) == l_Undef) - uncheckedEnqueue(p); - } - } - else { - decisions++; - Lit next = pickBranchLit(); - if(next == lit_Undef) - //Model found - return l_True; - // Increase decision level and enqueue 'next' - newDecisionLevel(); - uncheckedEnqueue(next); - } +| +| search : (nof_conflicts : int) (params : const SearchParams&) -> [lbool] +| +| Description: +| Search for a model the specified number of conflicts. +| NOTE! Use negative value for 'nof_conflicts' indicate infinity. +| +| Output: +| 'l_True' if a partial assigment that is consistent with respect to the +clauseset is found. If | all variables are decision variables, this means +that the clause set is satisfiable. 'l_False' | if the clause set is +unsatisfiable. 'l_Undef' if the bound on number of conflicts is reached. +|________________________________________________________________________________________________@*/ +lbool Solver::search(int nof_conflicts) { + assert(ok); + int backtrack_level; + int conflictC = 0; + vec learnt_clause; + starts++; + for (;;) { + CRef confl = propagate(); + if (confl != CRef_Undef) { + if (value(ca[confl][0]) == l_True) { + // DEBUG + // printf("search() Early falsified assumption\n"); + if (!isAssumedFalse(ca[confl][0])) + printf("ERROR search() in detecting early false assumption\n"); + analyzeFinal(ca[confl][0], conflict); + return l_False; + } + + // CONFLICT + conflicts++; + conflictC++; + if (decisionLevel() == 0) return l_False; + + learnt_clause.clear(); + analyze(confl, learnt_clause, backtrack_level); + + cancelUntil(backtrack_level); + + if (learnt_clause.size() == 1) { + uncheckedEnqueue(learnt_clause[0]); + } else { + CRef cr = ca.alloc(learnt_clause, true); + ca[cr].setLBD(computeLBD(ca[cr])); + learnts.push(cr); + attachClause(cr); + claBumpActivity(ca[cr]); + uncheckedEnqueue(learnt_clause[0], cr); + } + + varDecayActivity(); + claDecayActivity(); + + if (--learntsize_adjust_cnt == 0) { + learntsize_adjust_confl *= learntsize_adjust_inc; + learntsize_adjust_cnt = (int)learntsize_adjust_confl; + max_learnts *= learntsize_inc; + + if (verbosity >= 1) + printf("| %9d | %7d %8d %8d | %8d %8d %6.0f | %6.3f %% |\n", + (int)conflicts, + (int)dec_vars - + (trail_lim.size() == 0 ? trail.size() : trail_lim[0]), + nClauses(), (int)clauses_literals, (int)max_learnts, + nLearnts(), (double)learnts_literals / nLearnts(), + progressEstimate() * 100); + } + + } else { + // NO CONFLICT + if ((nof_conflicts >= 0 && conflictC >= nof_conflicts) || + !withinBudget()) { + // Reached bound on number of conflicts: + progress_estimate = progressEstimate(); + cancelUntil(0); + return l_Undef; + } + + // Simplify the set of problem clauses: + if (decisionLevel() == 0 && !simplify()) return l_False; + + if (conflicts >= + static_cast(curRestart * nbclausesbeforereduce)) { + assert(learnts.size() > 0); + /*cout << "BEFORE: conflicts = " << conflicts + << " nbclausesbeforerestart = " << nbclausesbeforereduce + << " curRestart = " << curRestart << "\n";*/ + + curRestart = (conflicts / nbclausesbeforereduce) + 1; + reduceDB(); + nbclausesbeforereduce += incReduceDB; + + /*cout << "AFTER: conflicts = " << conflicts + << " nbclausesbeforerestart = " << nbclausesbeforereduce + << " curRestart = " << curRestart << "\n";*/ + } + /*if (learnts.size() - nAssigns() >= max_learnts) + // Reduce the set of learnt clauses: + reduceDB();*/ + + Lit next = lit_Undef; + while (decisionLevel() < assumptions.size()) { + // Perform user provided assumption: + Lit p = assumptions[decisionLevel()]; + if (value(p) == l_True) { + // Dummy decision level: + newDecisionLevel(); + } else if (value(p) == l_False) { + analyzeFinal(~p, conflict); + return l_False; + } else { + next = p; + break; } + } + + if (next == lit_Undef) { + // New variable decision: + decisions++; + next = pickBranchLit(); + + if (next == lit_Undef) + // Model found: + return l_True; + } + + // Increase decision level and enqueue 'next' + newDecisionLevel(); + uncheckedEnqueue(next); } + } } -double Solver::progressEstimate() const -{ - double progress = 0; - double F = 1.0 / nVars(); +double Solver::progressEstimate() const { + double progress = 0; + double F = 1.0 / nVars(); - for (int i = 0; i <= decisionLevel(); i++){ - int beg = i == 0 ? 0 : trail_lim[i - 1]; - int end = i == decisionLevel() ? trail.size() : trail_lim[i]; - progress += pow(F, i) * (end - beg); - } + for (int i = 0; i <= decisionLevel(); i++) { + int beg = i == 0 ? 0 : trail_lim[i - 1]; + int end = i == decisionLevel() ? trail.size() : trail_lim[i]; + progress += pow(F, i) * (end - beg); + } - return progress / nVars(); + return progress / nVars(); } /* @@ -1083,112 +972,138 @@ double Solver::progressEstimate() const ... -*/ + */ -static double luby(double y, int x){ +static double luby(double y, int x) { + // Find the finite subsequence that contains index 'x', and the + // size of that subsequence: + int size, seq; + for (size = 1, seq = 0; size < x + 1; seq++, size = 2 * size + 1) + ; - // Find the finite subsequence that contains index 'x', and the - // size of that subsequence: - int size, seq; - for (size = 1, seq = 0; size < x+1; seq++, size = 2*size+1); - - while (size-1 != x){ - size = (size-1)>>1; - seq--; - x = x % size; - } + while (size - 1 != x) { + size = (size - 1) >> 1; + seq--; + x = x % size; + } - return pow(y, seq); + return pow(y, seq); } // NOTE: assumptions passed in member-variable 'assumptions'. -lbool Solver::solve_() -{ - model.clear(); - conflict.clear(); - if (!ok) return l_False; - - solves++; - - max_learnts = nClauses() * learntsize_factor; - if (max_learnts < min_learnts_lim) - max_learnts = min_learnts_lim; - - learntsize_adjust_confl = learntsize_adjust_start_confl; - learntsize_adjust_cnt = (int)learntsize_adjust_confl; - lbool status = l_Undef; - - if (verbosity >= 1){ - printf("============================[ Search Statistics ]==============================\n"); - printf("| Conflicts | ORIGINAL | LEARNT | Progress |\n"); - printf("| | Vars Clauses Literals | Limit Clauses Lit/Cl | |\n"); - printf("===============================================================================\n"); - } - - //Initialize Assumptions - for(int i=0; i < assumptions.size(); i++) { - Lit p = assumptions[i]; - if(isAssumedFalse(p)) { - conflict.clear(); conflict.insert(p); conflict.insert(~p); - status = l_False; - break; - } - setAssumption(p); - } - - if(conflict.size() > 0) - printf("c WARNING: Found conflict in assumptions\n"); - - // Search: - int curr_restarts = 0; - while (status == l_Undef){ - double rest_base = luby_restart ? luby(restart_inc, curr_restarts) : pow(restart_inc, curr_restarts); - status = search(rest_base * restart_first); - if (!withinBudget()) break; - curr_restarts++; +lbool Solver::solve_() { + model.clear(); + conflict.clear(); + if (!ok) return l_False; + + solves++; + + max_learnts = nClauses() * learntsize_factor; + if (max_learnts < min_learnts_lim) max_learnts = min_learnts_lim; + + learntsize_adjust_confl = learntsize_adjust_start_confl; + learntsize_adjust_cnt = (int)learntsize_adjust_confl; + lbool status = l_Undef; + + if (verbosity >= 1) { + printf( + "============================[ Search Statistics " + "]==============================\n"); + printf( + "| Conflicts | ORIGINAL | LEARNT | " + "Progress |\n"); + printf( + "| | Vars Clauses Literals | Limit Clauses Lit/Cl | " + " |\n"); + printf( + "======================================================================" + "=========\n"); + } + + // Search: +#if 0 + //DEBUG + for(Var v=0; v < nVars(); v++) + if(isAssumed(v) != l_Undef) + printf("ERROR: vardata isAssumed flags not cleared\n"); + //DEBUG +#endif + + for (int i = 0; i < assumptions.size(); i++) { + Lit p = assumptions[i]; + if (isAssumedFalse(p)) { + conflict.clear(); + conflict.insert(p); + conflict.insert(~p); + status = l_False; + break; } - - if (verbosity >= 1) - printf("===============================================================================\n"); - - if (status == l_True){ - // Extend & copy model: - model.growTo(nVars()); - for (int i = 0; i < nVars(); i++) model[i] = value(i); - }else if (status == l_False && conflict.size() == 0) - ok = false; - - cancelUntil(0); - for(int i=0; i< assumptions.size(); i++) - unsetAssumption(assumptions[i]); - return status; + setAssumption(p); + } + + if (conflict.size() > 0) { + printf("c WARNING: Found conflict in assumptions\n"); +#if 0 + //DEBUG + printf("Conflict = ["); + for(int i=0; i < conflict.size(); i++) { + Lit p = conflict[i]; + printf("%s%d, ", sign(p) ? "-" : "", var(p)); + } + printf("]\n"); + //DEBUG +#endif + } + + int curr_restarts = 0; + while (status == l_Undef) { + double rest_base = luby_restart ? luby(restart_inc, curr_restarts) + : pow(restart_inc, curr_restarts); + status = search(rest_base * restart_first); + if (!withinBudget()) break; + curr_restarts++; + } + + if (verbosity >= 1) + printf( + "======================================================================" + "=========\n"); + + if (status == l_True) { + // Extend & copy model: + model.growTo(nVars()); + for (int i = 0; i < nVars(); i++) model[i] = value(i); + } else if (status == l_False && conflict.size() == 0) + ok = false; + + cancelUntil(0); + for (int i = 0; i < assumptions.size(); i++) unsetAssumption(assumptions[i]); + + return status; } - -bool Solver::implies(const vec& assumps, vec& out) -{ - trail_lim.push(trail.size()); - for (int i = 0; i < assumps.size(); i++){ - Lit a = assumps[i]; - - if (value(a) == l_False){ - cancelUntil(0); - return false; - }else if (value(a) == l_Undef) - uncheckedEnqueue(a); - } - - unsigned trail_before = trail.size(); - bool ret = true; - if (propagate() == CRef_Undef){ - out.clear(); - for (int j = trail_before; j < trail.size(); j++) - out.push(trail[j]); - }else - ret = false; - - cancelUntil(0); - return ret; +bool Solver::implies(const vec& assumps, vec& out) { + trail_lim.push(trail.size()); + for (int i = 0; i < assumps.size(); i++) { + Lit a = assumps[i]; + + if (value(a) == l_False) { + cancelUntil(0); + return false; + } else if (value(a) == l_Undef) + uncheckedEnqueue(a); + } + + unsigned trail_before = trail.size(); + bool ret = true; + if (propagate() == CRef_Undef) { + out.clear(); + for (int j = trail_before; j < trail.size(); j++) out.push(trail[j]); + } else + ret = false; + + cancelUntil(0); + return ret; } //================================================================================================= @@ -1196,152 +1111,149 @@ bool Solver::implies(const vec& assumps, vec& out) // // FIXME: this needs to be rewritten completely. -static Var mapVar(Var x, vec& map, Var& max) -{ - if (map.size() <= x || map[x] == -1){ - map.growTo(x+1, -1); - map[x] = max++; - } - return map[x]; +static Var mapVar(Var x, vec& map, Var& max) { + if (map.size() <= x || map[x] == -1) { + map.growTo(x + 1, -1); + map[x] = max++; + } + return map[x]; } +void Solver::toDimacs(FILE* f, Clause& c, vec& map, Var& max) { + if (satisfied(c)) return; -void Solver::toDimacs(FILE* f, Clause& c, vec& map, Var& max) -{ - if (satisfied(c)) return; - - for (int i = 0; i < c.size(); i++) - if (value(c[i]) != l_False) - fprintf(f, "%s%d ", sign(c[i]) ? "-" : "", mapVar(var(c[i]), map, max)+1); - fprintf(f, "0\n"); + for (int i = 0; i < c.size(); i++) + if (value(c[i]) != l_False) + fprintf(f, "%s%d ", sign(c[i]) ? "-" : "", + mapVar(var(c[i]), map, max) + 1); + fprintf(f, "0\n"); } - -void Solver::toDimacs(const char *file, const vec& assumps) -{ - FILE* f = fopen(file, "wr"); - if (f == NULL) - fprintf(stderr, "could not open file %s\n", file), exit(1); - toDimacs(f, assumps); - fclose(f); +void Solver::toDimacs(const char* file, const vec& assumps) { + FILE* f = fopen(file, "wr"); + if (f == NULL) fprintf(stderr, "could not open file %s\n", file), exit(1); + toDimacs(f, assumps); + fclose(f); } +void Solver::toDimacs(FILE* f, const vec& assumps) { + // Handle case when solver is in contradictory state: + if (!ok) { + fprintf(f, "p cnf 1 2\n1 0\n-1 0\n"); + return; + } + + vec map; + Var max = 0; + + // Cannot use removeClauses here because it is not safe + // to deallocate them at this point. Could be improved. + int cnt = 0; + for (int i = 0; i < clauses.size(); i++) + if (!satisfied(ca[clauses[i]])) cnt++; + + for (int i = 0; i < clauses.size(); i++) + if (!satisfied(ca[clauses[i]])) { + Clause& c = ca[clauses[i]]; + for (int j = 0; j < c.size(); j++) + if (value(c[j]) != l_False) mapVar(var(c[j]), map, max); + } -void Solver::toDimacs(FILE* f, const vec& assumps) -{ - // Handle case when solver is in contradictory state: - if (!ok){ - fprintf(f, "p cnf 1 2\n1 0\n-1 0\n"); - return; } - - vec map; Var max = 0; - - // Cannot use removeClauses here because it is not safe - // to deallocate them at this point. Could be improved. - int cnt = 0; - for (int i = 0; i < clauses.size(); i++) - if (!satisfied(ca[clauses[i]])) - cnt++; - - for (int i = 0; i < clauses.size(); i++) - if (!satisfied(ca[clauses[i]])){ - Clause& c = ca[clauses[i]]; - for (int j = 0; j < c.size(); j++) - if (value(c[j]) != l_False) - mapVar(var(c[j]), map, max); - } - - // Assumptions are added as unit clauses: - cnt += assumps.size(); + // Assumptions are added as unit clauses: + cnt += assumps.size(); - fprintf(f, "p cnf %d %d\n", max, cnt); + fprintf(f, "p cnf %d %d\n", max, cnt); - for (int i = 0; i < assumps.size(); i++){ - assert(value(assumps[i]) != l_False); - fprintf(f, "%s%d 0\n", sign(assumps[i]) ? "-" : "", mapVar(var(assumps[i]), map, max)+1); - } + for (int i = 0; i < assumps.size(); i++) { + assert(value(assumps[i]) != l_False); + fprintf(f, "%s%d 0\n", sign(assumps[i]) ? "-" : "", + mapVar(var(assumps[i]), map, max) + 1); + } - for (int i = 0; i < clauses.size(); i++) - toDimacs(f, ca[clauses[i]], map, max); + for (int i = 0; i < clauses.size(); i++) + toDimacs(f, ca[clauses[i]], map, max); - if (verbosity > 0) - printf("Wrote DIMACS with %d variables and %d clauses.\n", max, cnt); + if (verbosity > 0) + printf("Wrote DIMACS with %d variables and %d clauses.\n", max, cnt); } - -void Solver::printStats() const -{ - double cpu_time = cpuTime(); - double mem_used = memUsedPeak(); - printf("restarts : %" PRIu64 "\n", starts); - printf("conflicts : %-12" PRIu64 " (%.0f /sec)\n", conflicts , conflicts /cpu_time); - printf("decisions : %-12" PRIu64 " (%4.2f %% random) (%.0f /sec)\n", decisions, (float)rnd_decisions*100 / (float)decisions, decisions /cpu_time); - printf("propagations : %-12" PRIu64 " (%.0f /sec)\n", propagations, propagations/cpu_time); - printf("conflict literals : %-12" PRIu64 " (%4.2f %% deleted)\n", tot_literals, (max_literals - tot_literals)*100 / (double)max_literals); - if (mem_used != 0) printf("Memory used : %.2f MB\n", mem_used); - printf("CPU time : %g s\n", cpu_time); +void Solver::printStats() const { + double cpu_time = cpuTime(); + double mem_used = memUsedPeak(); + printf("restarts : %" PRIu64 "\n", starts); + printf("conflicts : %-12" PRIu64 " (%.0f /sec)\n", conflicts, + conflicts / cpu_time); + printf("decisions : %-12" PRIu64 + " (%4.2f %% random) (%.0f /sec)\n", + decisions, (float)rnd_decisions * 100 / (float)decisions, + decisions / cpu_time); + printf("propagations : %-12" PRIu64 " (%.0f /sec)\n", propagations, + propagations / cpu_time); + printf("conflict literals : %-12" PRIu64 " (%4.2f %% deleted)\n", + tot_literals, + (max_literals - tot_literals) * 100 / (double)max_literals); + if (mem_used != 0) printf("Memory used : %.2f MB\n", mem_used); + printf("CPU time : %g s\n", cpu_time); } - //================================================================================================= // Garbage Collection methods: -void Solver::relocAll(ClauseAllocator& to) -{ - // All watchers: - // - watches.cleanAll(); - for (int v = 0; v < nVars(); v++) - for (int s = 0; s < 2; s++){ - Lit p = mkLit(v, s); - vec& ws = watches[p]; - for (int j = 0; j < ws.size(); j++) - ca.reloc(ws[j].cref, to); - } - - // All reasons: - // - for (int i = 0; i < trail.size(); i++){ - Var v = var(trail[i]); - - // Note: it is not safe to call 'locked()' on a relocated clause. This is why we keep - // 'dangling' reasons here. It is safe and does not hurt. - if (reason(v) != CRef_Undef && (ca[reason(v)].reloced() || locked(ca[reason(v)]))){ - assert(!isRemoved(reason(v))); - ca.reloc(vardata[v].reason, to); - } +void Solver::relocAll(ClauseAllocator& to) { + // All watchers: + // + watches.cleanAll(); + for (int v = 0; v < nVars(); v++) + for (int s = 0; s < 2; s++) { + Lit p = mkLit(v, s); + vec& ws = watches[p]; + for (int j = 0; j < ws.size(); j++) ca.reloc(ws[j].cref, to); } - // All learnt: - // - int i, j; - for (i = j = 0; i < learnts.size(); i++) - if (!isRemoved(learnts[i])){ - ca.reloc(learnts[i], to); - learnts[j++] = learnts[i]; - } - learnts.shrink(i - j); - - // All original: - // - for (i = j = 0; i < clauses.size(); i++) - if (!isRemoved(clauses[i])){ - ca.reloc(clauses[i], to); - clauses[j++] = clauses[i]; - } - clauses.shrink(i - j); + // All reasons: + // + for (int i = 0; i < trail.size(); i++) { + Var v = var(trail[i]); + + // Note: it is not safe to call 'locked()' on a relocated clause. This is + // why we keep 'dangling' reasons here. It is safe and does not hurt. + if (reason(v) != CRef_Undef && + (ca[reason(v)].reloced() || locked(ca[reason(v)]))) { + assert(!isRemoved(reason(v))); + ca.reloc(vardata[v].reason, to); + } + } + + // All learnt: + // + int i, j; + for (i = j = 0; i < learnts.size(); i++) + if (!isRemoved(learnts[i])) { + ca.reloc(learnts[i], to); + learnts[j++] = learnts[i]; + } + learnts.shrink(i - j); + + // All original: + // + for (i = j = 0; i < clauses.size(); i++) + if (!isRemoved(clauses[i])) { + ca.reloc(clauses[i], to); + clauses[j++] = clauses[i]; + } + clauses.shrink(i - j); } - -void Solver::garbageCollect() -{ - // Initialize the next region to a size corresponding to the estimated utilization degree. This - // is not precise but should avoid some unnecessary reallocations for the new region: - ClauseAllocator to(ca.size() - ca.wasted()); - - relocAll(to); - if (verbosity >= 2) - printf("| Garbage collection: %12d bytes => %12d bytes |\n", - ca.size()*ClauseAllocator::Unit_Size, to.size()*ClauseAllocator::Unit_Size); - to.moveTo(ca); +void Solver::garbageCollect() { + // Initialize the next region to a size corresponding to the estimated + // utilization degree. This is not precise but should avoid some unnecessary + // reallocations for the new region: + ClauseAllocator to(ca.size() - ca.wasted()); + + relocAll(to); + if (verbosity >= 2) + printf("| Garbage collection: %12d bytes => %12d bytes |\n", + ca.size() * ClauseAllocator::Unit_Size, + to.size() * ClauseAllocator::Unit_Size); + to.moveTo(ca); } diff --git a/minisat/core/Solver.h b/minisat/core/Solver.h old mode 100644 new mode 100755 index 4c043e0..0a204d7 --- a/minisat/core/Solver.h +++ b/minisat/core/Solver.h @@ -2,32 +2,33 @@ Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson Copyright (c) 2007-2010, Niklas Sorensson -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, -sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or -substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT -NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT -OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. **************************************************************************************************/ #ifndef Minisat_Solver_h #define Minisat_Solver_h -#include "minisat/mtl/Vec.h" -#include "minisat/mtl/Heap.h" +#include "minisat/core/SolverTypes.h" #include "minisat/mtl/Alg.h" +#include "minisat/mtl/Heap.h" #include "minisat/mtl/IntMap.h" +#include "minisat/mtl/Vec.h" #include "minisat/utils/Options.h" -#include "minisat/core/SolverTypes.h" - namespace Minisat { @@ -35,410 +36,595 @@ namespace Minisat { // Solver -- the main class: class Solver { -public: - - // Constructor/Destructor: - // - Solver(); - virtual ~Solver(); - - // Problem specification: - // - Var newVar (lbool upol = l_Undef, bool dvar = true); // Add a new variable with parameters specifying variable mode. - void releaseVar(Lit l); // Make literal true and promise to never refer to variable again. - - bool addClause (const vec& ps); // Add a clause to the solver. - bool addEmptyClause(); // Add the empty clause, making the solver contradictory. - bool addClause (Lit p); // Add a unit clause to the solver. - bool addClause (Lit p, Lit q); // Add a binary clause to the solver. - bool addClause (Lit p, Lit q, Lit r); // Add a ternary clause to the solver. - bool addClause (Lit p, Lit q, Lit r, Lit s); // Add a quaternary clause to the solver. - bool addClause_( vec& ps); // Add a clause to the solver without making superflous internal copy. Will - // change the passed vector 'ps'. - - // Solving: - // - bool simplify (); // Removes already satisfied clauses. - bool solve (const vec& assumps); // Search for a model that respects a given set of assumptions. - lbool solveLimited (const vec& assumps); // Search for a model that respects a given set of assumptions (With resource constraints). - bool solve (); // Search without assumptions. - bool solve (Lit p); // Search for a model that respects a single assumption. - bool solve (Lit p, Lit q); // Search for a model that respects two assumptions. - bool solve (Lit p, Lit q, Lit r); // Search for a model that respects three assumptions. - bool okay () const; // FALSE means solver is in a conflicting state - - bool implies (const vec& assumps, vec& out); - - // Iterate over clauses and top-level assignments: - ClauseIterator clausesBegin() const; - ClauseIterator clausesEnd() const; - TrailIterator trailBegin() const; - TrailIterator trailEnd () const; - - void toDimacs (FILE* f, const vec& assumps); // Write CNF to file in DIMACS-format. - void toDimacs (const char *file, const vec& assumps); - void toDimacs (FILE* f, Clause& c, vec& map, Var& max); - - // Convenience versions of 'toDimacs()': - void toDimacs (const char* file); - void toDimacs (const char* file, Lit p); - void toDimacs (const char* file, Lit p, Lit q); - void toDimacs (const char* file, Lit p, Lit q, Lit r); - - // Variable mode: - // - void setPolarity (Var v, lbool b); // Declare which polarity the decision heuristic should use for a variable. Requires mode 'polarity_user'. - void setDecisionVar (Var v, bool b); // Declare if a variable should be eligible for selection in the decision heuristic. - - // Read state: - // - lbool value (Var x) const; // The current value of a variable. - lbool value (Lit p) const; // The current value of a literal. - lbool modelValue (Var x) const; // The value of a variable in the last model. The last call to solve must have been satisfiable. - lbool modelValue (Lit p) const; // The value of a literal in the last model. The last call to solve must have been satisfiable. - int nAssigns () const; // The current number of assigned literals. - int nClauses () const; // The current number of original clauses. - int nLearnts () const; // The current number of learnt clauses. - int nVars () const; // The current number of variables. - int nFreeVars () const; - void printStats () const; // Print some current statistics to standard output. - - // Resource contraints: - // - void setConfBudget(int64_t x); - void setPropBudget(int64_t x); - void budgetOff(); - void interrupt(); // Trigger a (potentially asynchronous) interruption of the solver. - void clearInterrupt(); // Clear interrupt indicator flag. - - // Memory managment: - // - virtual void garbageCollect(); - void checkGarbage(double gf); - void checkGarbage(); - - // Extra results: (read-only member variable) - // - vec model; // If problem is satisfiable, this vector contains the model (if any). - LSet conflict; // If problem is unsatisfiable (possibly under assumptions), - // this vector represent the final conflict clause expressed in the assumptions. - - // Mode of operation: - // - int verbosity; - double var_decay; - double clause_decay; - double random_var_freq; - double random_seed; - bool luby_restart; - bool assump_2nd_process; //Reporcess assumption conflict with one assumption per level. - int averbosity; - int ccmin_mode; // Controls conflict clause minimization (0=none, 1=basic, 2=deep). - int phase_saving; // Controls the level of phase saving (0=none, 1=limited, 2=full). - bool rnd_pol; // Use random polarities for branching heuristics. - bool rnd_init_act; // Initialize variable activities with a small random value. - double garbage_frac; // The fraction of wasted memory allowed before a garbage collection is triggered. - int min_learnts_lim; // Minimum number to set the learnts limit to. - - int restart_first; // The initial restart limit. (default 100) - double restart_inc; // The factor with which the restart limit is multiplied in each restart. (default 1.5) - double learntsize_factor; // The intitial limit for learnt clauses is a factor of the original clauses. (default 1 / 3) - double learntsize_inc; // The limit for learnt clauses is multiplied with this factor each restart. (default 1.1) - - int learntsize_adjust_start_confl; - double learntsize_adjust_inc; - - // Statistics: (read-only member variable) - // - uint64_t solves, starts, decisions, rnd_decisions, propagations, conflicts; - uint64_t dec_vars, num_clauses, num_learnts, clauses_literals, learnts_literals, max_literals, tot_literals; - uint64_t n_assump_conflicts; - double assump_saved; - -protected: - - // Helper structures: - // - struct VarData { CRef reason; int level; lbool isAssumed;}; - static inline VarData mkVarData(CRef cr, int l, lbool a){ VarData d = {cr, l, a}; return d; } - - struct Watcher { - CRef cref; - Lit blocker; - Watcher(CRef cr, Lit p) : cref(cr), blocker(p) {} - bool operator==(const Watcher& w) const { return cref == w.cref; } - bool operator!=(const Watcher& w) const { return cref != w.cref; } - }; - - struct WatcherDeleted - { - const ClauseAllocator& ca; - WatcherDeleted(const ClauseAllocator& _ca) : ca(_ca) {} - bool operator()(const Watcher& w) const { return ca[w.cref].mark() == 1; } - }; - - struct VarOrderLt { - const IntMap& activity; - bool operator () (Var x, Var y) const { return activity[x] > activity[y]; } - VarOrderLt(const IntMap& act) : activity(act) { } - }; - - struct ShrinkStackElem { - uint32_t i; - Lit l; - ShrinkStackElem(uint32_t _i, Lit _l) : i(_i), l(_l){} - }; - - // Solver state: - // - vec clauses; // List of problem clauses. - vec learnts; // List of learnt clauses. - vec trail; // Assignment stack; stores all assigments made in the order they were made. - vec trail_lim; // Separator indices for different decision levels in 'trail'. - vec assumptions; // Current set of assumptions provided to solve by the user. - - VMap activity; // A heuristic measurement of the activity of a variable. - VMap assigns; // The current assignments. - VMap polarity; // The preferred polarity of each variable. - VMap user_pol; // The users preferred polarity of each variable. - VMap decision; // Declares if a variable is eligible for selection in the decision heuristic. - VMap vardata; // Stores reason and level for each variable. - OccLists, WatcherDeleted, MkIndexLit> - watches; // 'watches[lit]' is a list of constraints watching 'lit' (will go there if literal becomes true). - - Heaporder_heap; // A priority queue of variables ordered with respect to the variable activity. - - bool ok; // If FALSE, the constraints are already unsatisfiable. No part of the solver state may be used! - bool remove_satisfied; // Indicates whether possibly inefficient linear scan for satisfied clauses should be performed in 'simplify'. - double cla_inc; // Amount to bump next clause with. - double var_inc; // Amount to bump next variable with. - int qhead; // Head of queue (as index into the trail -- no more explicit propagation queue in MiniSat). - int simpDB_assigns; // Number of top-level assignments since last execution of 'simplify()'. - int64_t simpDB_props; // Remaining number of propagations that must be made before next execution of 'simplify()'. - double progress_estimate;// Set by 'search()'. - Var next_var; // Next variable to be created. - ClauseAllocator ca; - - vec released_vars; - vec free_vars; - - // Temporaries (to reduce allocation overhead). Each variable is prefixed by the method in which it is - // used, exept 'seen' wich is used in several places. - // - VMap seen; - vecanalyze_stack; - vec analyze_toclear; - vec add_tmp; - - double max_learnts; - double learntsize_adjust_confl; - int learntsize_adjust_cnt; - - // Resource contraints: - // - int64_t conflict_budget; // -1 means no budget. - int64_t propagation_budget; // -1 means no budget. - bool asynch_interrupt; - - // Main internal methods: - // - void insertVarOrder (Var x); // Insert a variable in the decision order priority queue. - virtual Lit pickBranchLit (); // Return the next decision variable. - void newDecisionLevel (); // Begins a new decision level. - void uncheckedEnqueue (Lit p, CRef from = CRef_Undef); // Enqueue a literal. Assumes value of literal is undefined. - bool enqueue (Lit p, CRef from = CRef_Undef); // Test if fact 'p' contradicts current state, enqueue otherwise. - CRef propagate (); // Perform unit propagation. Returns possibly conflicting clause. - void cancelUntil (int level); // Backtrack until a certain level. - void analyze (CRef confl, vec& out_learnt, int& out_btlevel); // (bt = backtrack) - void analyzeFinal1 (CRef confl, LSet& out_conflict); - void analyzeFinal1 (Lit p, LSet& out_conflict); - void analyzeFinal2 (CRef confl, LSet& out_conflict); - void analyzeFinal2 (Lit p, LSet& out_conflict); - bool litRedundant (Lit p); // (helper method for 'analyze()') - lbool search (int nof_conflicts); // Search for a given number of conflicts. - lbool solve_ (); // Main solve method (assumptions given in 'assumptions'). - void reduceDB (); // Reduce the set of learnt clauses. - void removeSatisfied (vec& cs); // Shrink 'cs' to contain only non-satisfied clauses. - void rebuildOrderHeap (); - - // Maintaining Variable/Clause activity: - // - void varDecayActivity (); // Decay all variables with the specified factor. Implemented by increasing the 'bump' value instead. - void varBumpActivity (Var v, double inc); // Increase a variable with the current 'bump' value. - void varBumpActivity (Var v); // Increase a variable with the current 'bump' value. - void claDecayActivity (); // Decay all clauses with the specified factor. Implemented by increasing the 'bump' value instead. - void claBumpActivity (Clause& c); // Increase a clause with the current 'bump' value. - - // Operations on clauses: - // - void attachClause (CRef cr); // Attach a clause to watcher lists. - void detachClause (CRef cr, bool strict = false); // Detach a clause to watcher lists. - void removeClause (CRef cr); // Detach and free a clause. - bool isRemoved (CRef cr) const; // Test if a clause has been removed. - bool locked (const Clause& c) const; // Returns TRUE if a clause is a reason for some implication in the current state. - bool satisfied (const Clause& c) const; // Returns TRUE if a clause is satisfied in the current state. - - // Misc: - // - int decisionLevel () const; // Gives the current decisionlevel. - uint32_t abstractLevel (Var x) const; // Used to represent an abstraction of sets of decision levels. - CRef reason (Var x) const; - int level (Var x) const; -// - lbool isAssumed (Var x) const; - bool isAssumption (Var x) const; - bool isAssumption (Lit p) const; - bool isAssumedTrue (Lit p) const; - bool isAssumedFalse (Lit p) const; - void setAssumption (Lit p); - void unsetAssumption (Lit p); -// - double progressEstimate () const; // DELETE THIS ?? IT'S NOT VERY USEFUL ... - bool withinBudget () const; - void relocAll (ClauseAllocator& to); - - // Static helpers: - // - - // Returns a random float 0 <= x < 1. Seed must never be 0. - static inline double drand(double& seed) { - seed *= 1389796; - int q = (int)(seed / 2147483647); - seed -= (double)q * 2147483647; - return seed / 2147483647; } - - // Returns a random integer 0 <= x < size. Seed must never be 0. - static inline int irand(double& seed, int size) { - return (int)(drand(seed) * size); } - -private: - void inline af1_mark_conflict(CRef, bool, int&, LSet&); - void inline af2_mark_conflict(CRef, bool, int&, vec&, LSet&); + public: + // Constructor/Destructor: + // + Solver(); + virtual ~Solver(); + + // Problem specification: + // + Var newVar(lbool upol = l_Undef, + bool dvar = true); // Add a new variable with parameters + // specifying variable mode. + void releaseVar(Lit l); // Make literal true and promise to never refer to + // variable again. + + bool addClause(const vec& ps); // Add a clause to the solver. + bool + addEmptyClause(); // Add the empty clause, making the solver contradictory. + bool addClause(Lit p); // Add a unit clause to the solver. + bool addClause(Lit p, Lit q); // Add a binary clause to the solver. + bool addClause(Lit p, Lit q, Lit r); // Add a ternary clause to the solver. + bool addClause(Lit p, Lit q, Lit r, + Lit s); // Add a quaternary clause to the solver. + bool addClause_( + vec& ps); // Add a clause to the solver without making superflous + // internal copy. Will change the passed vector 'ps'. + + // Solving: + // + bool simplify(); // Removes already satisfied clauses. + bool solve(const vec& assumps); // Search for a model that respects a + // given set of assumptions. + lbool solveLimited( + const vec& assumps); // Search for a model that respects a given set + // of assumptions (With resource constraints). + bool solve(); // Search without assumptions. + bool solve(Lit p); // Search for a model that respects a single assumption. + bool solve(Lit p, + Lit q); // Search for a model that respects two assumptions. + bool solve(Lit p, Lit q, + Lit r); // Search for a model that respects three assumptions. + bool okay() const; // FALSE means solver is in a conflicting state + + bool implies(const vec& assumps, vec& out); + + // Iterate over clauses and top-level assignments: + ClauseIterator clausesBegin() const; + ClauseIterator clausesEnd() const; + TrailIterator trailBegin() const; + TrailIterator trailEnd() const; + + void toDimacs( + FILE* f, const vec& assumps); // Write CNF to file in DIMACS-format. + void toDimacs(const char* file, const vec& assumps); + void toDimacs(FILE* f, Clause& c, vec& map, Var& max); + + // Convenience versions of 'toDimacs()': + void toDimacs(const char* file); + void toDimacs(const char* file, Lit p); + void toDimacs(const char* file, Lit p, Lit q); + void toDimacs(const char* file, Lit p, Lit q, Lit r); + + // Variable mode: + // + void setPolarity( + Var v, lbool b); // Declare which polarity the decision heuristic should + // use for a variable. Requires mode 'polarity_user'. + void setDecisionVar(Var v, + bool b); // Declare if a variable should be eligible for + // selection in the decision heuristic. + + // Read state: + // + lbool value(Var x) const; // The current value of a variable. + lbool value(Lit p) const; // The current value of a literal. + lbool modelValue( + Var x) const; // The value of a variable in the last model. The last call + // to solve must have been satisfiable. + lbool modelValue( + Lit p) const; // The value of a literal in the last model. The last call + // to solve must have been satisfiable. + int nAssigns() const; // The current number of assigned literals. + int nClauses() const; // The current number of original clauses. + int nLearnts() const; // The current number of learnt clauses. + int nVars() const; // The current number of variables. + int nFreeVars() const; + void printStats() const; // Print some current statistics to standard output. + + // Resource contraints: + // + void setConfBudget(int64_t x); + void setPropBudget(int64_t x); + void budgetOff(); + void interrupt(); // Trigger a (potentially asynchronous) interruption of the + // solver. + void clearInterrupt(); // Clear interrupt indicator flag. + + // Memory managment: + // + virtual void garbageCollect(); + void checkGarbage(double gf); + void checkGarbage(); + + // Extra results: (read-only member variable) + // + vec model; // If problem is satisfiable, this vector contains the + // model (if any). + LSet conflict; // If problem is unsatisfiable (possibly under assumptions), + // this vector represent the final conflict clause expressed + // in the assumptions. + // + int firstReduceDB; + int incReduceDB; + int specialIncReduceDB; + + // Mode of operation: + // + int verbosity; + double var_decay; + double clause_decay; + double random_var_freq; + double random_seed; + bool luby_restart; + int ccmin_mode; // Controls conflict clause minimization (0=none, 1=basic, + // 2=deep). + int phase_saving; // Controls the level of phase saving (0=none, 1=limited, + // 2=full). + bool rnd_pol; // Use random polarities for branching heuristics. + bool rnd_init_act; // Initialize variable activities with a small random + // value. + double garbage_frac; // The fraction of wasted memory allowed before a + // garbage collection is triggered. + int min_learnts_lim; // Minimum number to set the learnts limit to. + + int restart_first; // The initial restart limit. (default 100) + double restart_inc; // The factor with which the restart limit is multiplied + // in each restart. (default 1.5) + double learntsize_factor; // The intitial limit for learnt clauses is a + // factor of the original clauses. (default 1 / 3) + double learntsize_inc; // The limit for learnt clauses is multiplied with + // this factor each restart. (default 1.1) + + int learntsize_adjust_start_confl; + double learntsize_adjust_inc; + + // Statistics: (read-only member variable) + // + uint64_t solves, starts, decisions, rnd_decisions, propagations, conflicts; + uint64_t dec_vars, num_clauses, num_learnts, clauses_literals, + learnts_literals, max_literals, tot_literals; + + protected: + // Helper structures: + // + long curRestart; + + struct VarData { + CRef reason; + int level; + lbool isAssumed; + }; + static inline VarData mkVarData(CRef cr, int l, lbool a) { + VarData d = {cr, l, a}; + return d; + } + + struct Watcher { + CRef cref; + Lit blocker; + Watcher(CRef cr, Lit p) : cref(cr), blocker(p) {} + bool operator==(const Watcher& w) const { return cref == w.cref; } + bool operator!=(const Watcher& w) const { return cref != w.cref; } + }; + + struct WatcherDeleted { + const ClauseAllocator& ca; + WatcherDeleted(const ClauseAllocator& _ca) : ca(_ca) {} + bool operator()(const Watcher& w) const { return ca[w.cref].mark() == 1; } + }; + + struct VarOrderLt { + const IntMap& activity; + bool operator()(Var x, Var y) const { return activity[x] > activity[y]; } + VarOrderLt(const IntMap& act) : activity(act) {} + }; + + struct ShrinkStackElem { + uint32_t i; + Lit l; + ShrinkStackElem(uint32_t _i, Lit _l) : i(_i), l(_l) {} + }; + + // Solver state: + // + vec clauses; // List of problem clauses. + vec learnts; // List of learnt clauses. + vec trail; // Assignment stack; stores all assigments made in the order + // they were made. + vec + trail_lim; // Separator indices for different decision levels in 'trail'. + vec + assumptions; // Current set of assumptions provided to solve by the user. + + VMap + activity; // A heuristic measurement of the activity of a variable. + VMap assigns; // The current assignments. + VMap polarity; // The preferred polarity of each variable. + VMap user_pol; // The users preferred polarity of each variable. + VMap decision; // Declares if a variable is eligible for selection in + // the decision heuristic. + VMap vardata; // Stores reason and level for each variable. + OccLists, WatcherDeleted, MkIndexLit> + watches; // 'watches[lit]' is a list of constraints watching 'lit' (will + // go there if literal becomes true). + + Heap order_heap; // A priority queue of variables ordered + // with respect to the variable activity. + + bool ok; // If FALSE, the constraints are already unsatisfiable. No part of + // the solver state may be used! + bool remove_satisfied; // Indicates whether possibly inefficient linear scan + // for satisfied clauses should be performed in + // 'simplify'. + double cla_inc; // Amount to bump next clause with. + double var_inc; // Amount to bump next variable with. + int qhead; // Head of queue (as index into the trail -- no more explicit + // propagation queue in MiniSat). + int simpDB_assigns; // Number of top-level assignments since last execution + // of 'simplify()'. + int64_t simpDB_props; // Remaining number of propagations that must be made + // before next execution of 'simplify()'. + double progress_estimate; // Set by 'search()'. + Var next_var; // Next variable to be created. + ClauseAllocator ca; + + VMap permDiff; // permDiff[var] contains the current conflict + // number... Used to count the number of LBD + + unsigned long int MYFLAG; + int nbclausesbeforereduce; // To know when it is time to reduce clause + // database + + vec released_vars; + vec free_vars; + + // Temporaries (to reduce allocation overhead). Each variable is prefixed by + // the method in which it is used, exept 'seen' wich is used in several + // places. + // + VMap seen; + vec analyze_stack; + vec analyze_toclear; + vec add_tmp; + + double max_learnts; + double learntsize_adjust_confl; + int learntsize_adjust_cnt; + + // Resource contraints: + // + int64_t conflict_budget; // -1 means no budget. + int64_t propagation_budget; // -1 means no budget. + bool asynch_interrupt; + + // Main internal methods: + // + void insertVarOrder( + Var x); // Insert a variable in the decision order priority queue. + virtual Lit pickBranchLit(); // Return the next decision variable. + void newDecisionLevel(); // Begins a new decision level. + void uncheckedEnqueue( + Lit p, CRef from = CRef_Undef); // Enqueue a literal. Assumes value of + // literal is undefined. + bool enqueue(Lit p, + CRef from = CRef_Undef); // Test if fact 'p' contradicts current + // state, enqueue otherwise. + CRef propagate(); // Perform unit propagation. Returns possibly conflicting + // clause. + void cancelUntil(int level); // Backtrack until a certain level. + void analyze(CRef confl, vec& out_learnt, + int& out_btlevel); // (bt = backtrack) + // virtual void analyzeFinal (Lit p, LSet& out_conflict); // COULD + // THIS BE IMPLEMENTED BY THE ORDINARIY "analyze" BY SOME REASONABLE + // GENERALIZATION? + void analyzeFinal( + Lit p, + LSet& out_conflict); // COULD THIS BE IMPLEMENTED BY THE ORDINARIY + // "analyze" BY SOME REASONABLE GENERALIZATION? + bool litRedundant(Lit p); // (helper method for 'analyze()') + lbool search(int nof_conflicts); // Search for a given number of conflicts. + lbool solve_(); // Main solve method (assumptions given in 'assumptions'). + void reduceDB(); // Reduce the set of learnt clauses. + void removeSatisfied( + vec& cs); // Shrink 'cs' to contain only non-satisfied clauses. + void rebuildOrderHeap(); + + // Maintaining Variable/Clause activity: + // + void + varDecayActivity(); // Decay all variables with the specified factor. + // Implemented by increasing the 'bump' value instead. + void varBumpActivity( + Var v, double inc); // Increase a variable with the current 'bump' value. + void varBumpActivity( + Var v); // Increase a variable with the current 'bump' value. + void + claDecayActivity(); // Decay all clauses with the specified factor. + // Implemented by increasing the 'bump' value instead. + void claBumpActivity( + Clause& c); // Increase a clause with the current 'bump' value. + + // Operations on clauses: + // + void attachClause(CRef cr); // Attach a clause to watcher lists. + void detachClause(CRef cr, + bool strict = false); // Detach a clause to watcher lists. + void removeClause(CRef cr); // Detach and free a clause. + bool isRemoved(CRef cr) const; // Test if a clause has been removed. + bool locked( + const Clause& c) const; // Returns TRUE if a clause is a reason for some + // implication in the current state. + bool satisfied(const Clause& c) + const; // Returns TRUE if a clause is satisfied in the current state. + + + unsigned int computeLBD(const Clause& c); + + // Misc: + // + int decisionLevel() const; // Gives the current decisionlevel. + uint32_t abstractLevel(Var x) + const; // Used to represent an abstraction of sets of decision levels. + CRef reason(Var x) const; + int level(Var x) const; + // + lbool isAssumed(Var x) const; + bool isAssumedTrue(Lit p) const; + bool isAssumedFalse(Lit p) const; + void setAssumption(Lit p); + void unsetAssumption(Lit p); + // + + double progressEstimate() const; // DELETE THIS ?? IT'S NOT VERY USEFUL ... + bool withinBudget() const; + void relocAll(ClauseAllocator& to); + + // Static helpers: + // + + // Returns a random float 0 <= x < 1. Seed must never be 0. + static inline double drand(double& seed) { + seed *= 1389796; + int q = (int)(seed / 2147483647); + seed -= (double)q * 2147483647; + return seed / 2147483647; + } + + // Returns a random integer 0 <= x < size. Seed must never be 0. + static inline int irand(double& seed, int size) { + return (int)(drand(seed) * size); + } }; - //================================================================================================= // Implementation of inline methods: inline CRef Solver::reason(Var x) const { return vardata[x].reason; } -inline int Solver::level (Var x) const { return vardata[x].level; } - -//Assumptions - inline lbool Solver::isAssumed(Var x) const { return vardata[x].isAssumed; } - inline bool Solver::isAssumption(Lit p) const { return isAssumption(var(p)); } - inline bool Solver::isAssumption(Var x) const { return vardata[x].isAssumed != l_Undef; } +inline int Solver::level(Var x) const { return vardata[x].level; } +// Assumptions +inline lbool Solver::isAssumed(Var x) const { return vardata[x].isAssumed; } - inline bool Solver::isAssumedTrue(Lit p) const - { return sign(p) ? isAssumed(var(p)) == l_False : isAssumed(var(p)) == l_True; } +inline bool Solver::isAssumedTrue(Lit p) const { + return sign(p) ? isAssumed(var(p)) == l_False : isAssumed(var(p)) == l_True; +} - inline bool Solver::isAssumedFalse(Lit p) const - { return sign(p) ? isAssumed(var(p)) == l_True : isAssumed(var(p)) == l_False; } +inline bool Solver::isAssumedFalse(Lit p) const { + return sign(p) ? isAssumed(var(p)) == l_True : isAssumed(var(p)) == l_False; +} - inline void Solver::setAssumption(Lit p) { vardata[var(p)].isAssumed = sign(p) ? l_False : l_True; } - inline void Solver::unsetAssumption(Lit p) { vardata[var(p)].isAssumed = l_Undef; } +inline void Solver::setAssumption(Lit p) { + vardata[var(p)].isAssumed = sign(p) ? l_False : l_True; +} +inline void Solver::unsetAssumption(Lit p) { + vardata[var(p)].isAssumed = l_Undef; +} inline void Solver::insertVarOrder(Var x) { - if (!order_heap.inHeap(x) && decision[x]) order_heap.insert(x); } + if (!order_heap.inHeap(x) && decision[x]) order_heap.insert(x); +} inline void Solver::varDecayActivity() { var_inc *= (1 / var_decay); } inline void Solver::varBumpActivity(Var v) { varBumpActivity(v, var_inc); } inline void Solver::varBumpActivity(Var v, double inc) { - if ( (activity[v] += inc) > 1e100 ) { - // Rescale: - for (int i = 0; i < nVars(); i++) - activity[i] *= 1e-100; - var_inc *= 1e-100; } - - // Update order_heap with respect to new activity: - if (order_heap.inHeap(v)) - order_heap.decrease(v); } + if ((activity[v] += inc) > 1e100) { + // Rescale: + for (int i = 0; i < nVars(); i++) activity[i] *= 1e-100; + var_inc *= 1e-100; + } + + // Update order_heap with respect to new activity: + if (order_heap.inHeap(v)) order_heap.decrease(v); +} inline void Solver::claDecayActivity() { cla_inc *= (1 / clause_decay); } -inline void Solver::claBumpActivity (Clause& c) { - if ( (c.activity() += cla_inc) > 1e20 ) { - // Rescale: - for (int i = 0; i < learnts.size(); i++) - ca[learnts[i]].activity() *= 1e-20; - cla_inc *= 1e-20; } } - -inline void Solver::checkGarbage(void){ return checkGarbage(garbage_frac); } -inline void Solver::checkGarbage(double gf){ - if (ca.wasted() > ca.size() * gf) - garbageCollect(); } +inline void Solver::claBumpActivity(Clause& c) { + if ((c.activity() += cla_inc) > 1e20) { + // Rescale: + for (int i = 0; i < learnts.size(); i++) ca[learnts[i]].activity() *= 1e-20; + cla_inc *= 1e-20; + } +} + +inline void Solver::checkGarbage(void) { return checkGarbage(garbage_frac); } +inline void Solver::checkGarbage(double gf) { + if (ca.wasted() > ca.size() * gf) garbageCollect(); +} // NOTE: enqueue does not set the ok flag! (only public methods do) -inline bool Solver::enqueue (Lit p, CRef from) { return value(p) != l_Undef ? value(p) != l_False : (uncheckedEnqueue(p, from), true); } -inline bool Solver::addClause (const vec& ps) { ps.copyTo(add_tmp); return addClause_(add_tmp); } -inline bool Solver::addEmptyClause () { add_tmp.clear(); return addClause_(add_tmp); } -inline bool Solver::addClause (Lit p) { add_tmp.clear(); add_tmp.push(p); return addClause_(add_tmp); } -inline bool Solver::addClause (Lit p, Lit q) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); return addClause_(add_tmp); } -inline bool Solver::addClause (Lit p, Lit q, Lit r) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); add_tmp.push(r); return addClause_(add_tmp); } -inline bool Solver::addClause (Lit p, Lit q, Lit r, Lit s){ add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); add_tmp.push(r); add_tmp.push(s); return addClause_(add_tmp); } - -inline bool Solver::isRemoved (CRef cr) const { return ca[cr].mark() == 1; } -inline bool Solver::locked (const Clause& c) const { return value(c[0]) == l_True && reason(var(c[0])) != CRef_Undef && ca.lea(reason(var(c[0]))) == &c; } -inline void Solver::newDecisionLevel() { trail_lim.push(trail.size()); } - -inline int Solver::decisionLevel () const { return trail_lim.size(); } -inline uint32_t Solver::abstractLevel (Var x) const { return 1 << (level(x) & 31); } -inline lbool Solver::value (Var x) const { return assigns[x]; } -inline lbool Solver::value (Lit p) const { return assigns[var(p)] ^ sign(p); } -inline lbool Solver::modelValue (Var x) const { return model[x]; } -inline lbool Solver::modelValue (Lit p) const { return model[var(p)] ^ sign(p); } -inline int Solver::nAssigns () const { return trail.size(); } -inline int Solver::nClauses () const { return num_clauses; } -inline int Solver::nLearnts () const { return num_learnts; } -inline int Solver::nVars () const { return next_var; } -// TODO: nFreeVars() is not quite correct, try to calculate right instead of adapting it like below: -inline int Solver::nFreeVars () const { return (int)dec_vars - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]); } -inline void Solver::setPolarity (Var v, lbool b){ user_pol[v] = b; } -inline void Solver::setDecisionVar(Var v, bool b) -{ - if ( b && !decision[v]) dec_vars++; - else if (!b && decision[v]) dec_vars--; - - decision[v] = b; - insertVarOrder(v); -} -inline void Solver::setConfBudget(int64_t x){ conflict_budget = conflicts + x; } -inline void Solver::setPropBudget(int64_t x){ propagation_budget = propagations + x; } -inline void Solver::interrupt(){ asynch_interrupt = true; } -inline void Solver::clearInterrupt(){ asynch_interrupt = false; } -inline void Solver::budgetOff(){ conflict_budget = propagation_budget = -1; } -inline bool Solver::withinBudget() const { - return !asynch_interrupt && - (conflict_budget < 0 || conflicts < (uint64_t)conflict_budget) && - (propagation_budget < 0 || propagations < (uint64_t)propagation_budget); } - -// FIXME: after the introduction of asynchronous interrruptions the solve-versions that return a -// pure bool do not give a safe interface. Either interrupts must be possible to turn off here, or -// all calls to solve must return an 'lbool'. I'm not yet sure which I prefer. -inline bool Solver::solve () { budgetOff(); assumptions.clear(); return solve_() == l_True; } -inline bool Solver::solve (Lit p) { budgetOff(); assumptions.clear(); assumptions.push(p); return solve_() == l_True; } -inline bool Solver::solve (Lit p, Lit q) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); return solve_() == l_True; } -inline bool Solver::solve (Lit p, Lit q, Lit r) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); assumptions.push(r); return solve_() == l_True; } -inline bool Solver::solve (const vec& assumps){ budgetOff(); assumps.copyTo(assumptions); return solve_() == l_True; } -inline lbool Solver::solveLimited (const vec& assumps){ assumps.copyTo(assumptions); return solve_(); } -inline bool Solver::okay () const { return ok; } - -inline ClauseIterator Solver::clausesBegin() const { return ClauseIterator(ca, &clauses[0]); } -inline ClauseIterator Solver::clausesEnd () const { return ClauseIterator(ca, &clauses[clauses.size()]); } -inline TrailIterator Solver::trailBegin () const { return TrailIterator(&trail[0]); } -inline TrailIterator Solver::trailEnd () const { - return TrailIterator(&trail[decisionLevel() == 0 ? trail.size() : trail_lim[0]]); } - -inline void Solver::toDimacs (const char* file){ vec as; toDimacs(file, as); } -inline void Solver::toDimacs (const char* file, Lit p){ vec as; as.push(p); toDimacs(file, as); } -inline void Solver::toDimacs (const char* file, Lit p, Lit q){ vec as; as.push(p); as.push(q); toDimacs(file, as); } -inline void Solver::toDimacs (const char* file, Lit p, Lit q, Lit r){ vec as; as.push(p); as.push(q); as.push(r); toDimacs(file, as); } +inline bool Solver::enqueue(Lit p, CRef from) { + return value(p) != l_Undef ? value(p) != l_False + : (uncheckedEnqueue(p, from), true); +} +inline bool Solver::addClause(const vec& ps) { + ps.copyTo(add_tmp); + return addClause_(add_tmp); +} +inline bool Solver::addEmptyClause() { + add_tmp.clear(); + return addClause_(add_tmp); +} +inline bool Solver::addClause(Lit p) { + add_tmp.clear(); + add_tmp.push(p); + return addClause_(add_tmp); +} +inline bool Solver::addClause(Lit p, Lit q) { + add_tmp.clear(); + add_tmp.push(p); + add_tmp.push(q); + return addClause_(add_tmp); +} +inline bool Solver::addClause(Lit p, Lit q, Lit r) { + add_tmp.clear(); + add_tmp.push(p); + add_tmp.push(q); + add_tmp.push(r); + return addClause_(add_tmp); +} +inline bool Solver::addClause(Lit p, Lit q, Lit r, Lit s) { + add_tmp.clear(); + add_tmp.push(p); + add_tmp.push(q); + add_tmp.push(r); + add_tmp.push(s); + return addClause_(add_tmp); +} +inline bool Solver::isRemoved(CRef cr) const { return ca[cr].mark() == 1; } +inline bool Solver::locked(const Clause& c) const { + return value(c[0]) == l_True && reason(var(c[0])) != CRef_Undef && + ca.lea(reason(var(c[0]))) == &c; +} +inline void Solver::newDecisionLevel() { trail_lim.push(trail.size()); } + +inline int Solver::decisionLevel() const { return trail_lim.size(); } +inline uint32_t Solver::abstractLevel(Var x) const { + return 1 << (level(x) & 31); +} +inline lbool Solver::value(Var x) const { return assigns[x]; } +inline lbool Solver::value(Lit p) const { return assigns[var(p)] ^ sign(p); } +inline lbool Solver::modelValue(Var x) const { return model[x]; } +inline lbool Solver::modelValue(Lit p) const { return model[var(p)] ^ sign(p); } +inline int Solver::nAssigns() const { return trail.size(); } +inline int Solver::nClauses() const { return num_clauses; } +inline int Solver::nLearnts() const { return num_learnts; } +inline int Solver::nVars() const { return next_var; } +// TODO: nFreeVars() is not quite correct, try to calculate right instead of +// adapting it like below: +inline int Solver::nFreeVars() const { + return (int)dec_vars - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]); +} +inline void Solver::setPolarity(Var v, lbool b) { user_pol[v] = b; } +inline void Solver::setDecisionVar(Var v, bool b) { + if (b && !decision[v]) + dec_vars++; + else if (!b && decision[v]) + dec_vars--; + + decision[v] = b; + insertVarOrder(v); +} +inline void Solver::setConfBudget(int64_t x) { + conflict_budget = conflicts + x; +} +inline void Solver::setPropBudget(int64_t x) { + propagation_budget = propagations + x; +} +inline void Solver::interrupt() { asynch_interrupt = true; } +inline void Solver::clearInterrupt() { asynch_interrupt = false; } +inline void Solver::budgetOff() { conflict_budget = propagation_budget = -1; } +inline bool Solver::withinBudget() const { + return !asynch_interrupt && + (conflict_budget < 0 || conflicts < (uint64_t)conflict_budget) && + (propagation_budget < 0 || + propagations < (uint64_t)propagation_budget); +} + +// FIXME: after the introduction of asynchronous interrruptions the +// solve-versions that return a pure bool do not give a safe interface. Either +// interrupts must be possible to turn off here, or all calls to solve must +// return an 'lbool'. I'm not yet sure which I prefer. +inline bool Solver::solve() { + budgetOff(); + assumptions.clear(); + return solve_() == l_True; +} +inline bool Solver::solve(Lit p) { + budgetOff(); + assumptions.clear(); + assumptions.push(p); + return solve_() == l_True; +} +inline bool Solver::solve(Lit p, Lit q) { + budgetOff(); + assumptions.clear(); + assumptions.push(p); + assumptions.push(q); + return solve_() == l_True; +} +inline bool Solver::solve(Lit p, Lit q, Lit r) { + budgetOff(); + assumptions.clear(); + assumptions.push(p); + assumptions.push(q); + assumptions.push(r); + return solve_() == l_True; +} +inline bool Solver::solve(const vec& assumps) { + budgetOff(); + assumps.copyTo(assumptions); + return solve_() == l_True; +} +inline lbool Solver::solveLimited(const vec& assumps) { + assumps.copyTo(assumptions); + return solve_(); +} +inline bool Solver::okay() const { return ok; } + +inline ClauseIterator Solver::clausesBegin() const { + return ClauseIterator(ca, &clauses[0]); +} +inline ClauseIterator Solver::clausesEnd() const { + return ClauseIterator(ca, &clauses[clauses.size()]); +} +inline TrailIterator Solver::trailBegin() const { + return TrailIterator(&trail[0]); +} +inline TrailIterator Solver::trailEnd() const { + return TrailIterator( + &trail[decisionLevel() == 0 ? trail.size() : trail_lim[0]]); +} + +inline void Solver::toDimacs(const char* file) { + vec as; + toDimacs(file, as); +} +inline void Solver::toDimacs(const char* file, Lit p) { + vec as; + as.push(p); + toDimacs(file, as); +} +inline void Solver::toDimacs(const char* file, Lit p, Lit q) { + vec as; + as.push(p); + as.push(q); + toDimacs(file, as); +} +inline void Solver::toDimacs(const char* file, Lit p, Lit q, Lit r) { + vec as; + as.push(p); + as.push(q); + as.push(r); + toDimacs(file, as); +} //================================================================================================= // Debug etc: - //================================================================================================= -} +} // namespace Minisat #endif diff --git a/minisat/core/SolverTypes.h b/minisat/core/SolverTypes.h old mode 100644 new mode 100755 index e79d3eb..2ae9d19 --- a/minisat/core/SolverTypes.h +++ b/minisat/core/SolverTypes.h @@ -1,141 +1,174 @@ + /***********************************************************************************[SolverTypes.h] Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson Copyright (c) 2007-2010, Niklas Sorensson -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, -sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or -substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT -NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT -OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. **************************************************************************************************/ - #ifndef Minisat_SolverTypes_h #define Minisat_SolverTypes_h #include -#include "minisat/mtl/IntTypes.h" #include "minisat/mtl/Alg.h" -#include "minisat/mtl/Vec.h" +#include "minisat/mtl/Alloc.h" #include "minisat/mtl/IntMap.h" +#include "minisat/mtl/IntTypes.h" #include "minisat/mtl/Map.h" -#include "minisat/mtl/Alloc.h" +#include "minisat/mtl/Vec.h" namespace Minisat { //================================================================================================= // Variables, literals, lifted booleans, clauses: - -// NOTE! Variables are just integers. No abstraction here. They should be chosen from 0..N, -// so that they can be used as array indices. +// NOTE! Variables are just integers. No abstraction here. They should be chosen +// from 0..N, so that they can be used as array indices. typedef int Var; #if defined(MINISAT_CONSTANTS_AS_MACROS) #define var_Undef (-1) #else - const Var var_Undef = -1; +constexpr Var var_Undef = -1; #endif - struct Lit { - int x; + int x; - // Use this as a constructor: - friend Lit mkLit(Var var, bool sign); + // Use this as a constructor: + friend Lit mkLit(Var var, bool sign); - bool operator == (Lit p) const { return x == p.x; } - bool operator != (Lit p) const { return x != p.x; } - bool operator < (Lit p) const { return x < p.x; } // '<' makes p, ~p adjacent in the ordering. + bool operator==(Lit p) const { return x == p.x; } + bool operator!=(Lit p) const { return x != p.x; } + bool operator<(Lit p) const { + return x < p.x; + } // '<' makes p, ~p adjacent in the ordering. }; - -inline Lit mkLit (Var var, bool sign = false) { Lit p; p.x = var + var + (int)sign; return p; } -inline Lit operator ~(Lit p) { Lit q; q.x = p.x ^ 1; return q; } -inline Lit operator ^(Lit p, bool b) { Lit q; q.x = p.x ^ (unsigned int)b; return q; } -inline bool sign (Lit p) { return p.x & 1; } -inline int var (Lit p) { return p.x >> 1; } +inline Lit mkLit(Var var, bool sign = false) { + Lit p; + p.x = var + var + (int)sign; + return p; +} +inline Lit operator~(Lit p) { + Lit q; + q.x = p.x ^ 1; + return q; +} +inline Lit operator^(Lit p, bool b) { + Lit q; + q.x = p.x ^ (unsigned int)b; + return q; +} +inline bool sign(Lit p) { return p.x & 1; } +inline int var(Lit p) { return p.x >> 1; } // Mapping Literals to and from compact integers suitable for array indexing: -inline int toInt (Var v) { return v; } -inline int toInt (Lit p) { return p.x; } -inline Lit toLit (int i) { Lit p; p.x = i; return p; } +inline int toInt(Var v) { return v; } +inline int toInt(Lit p) { return p.x; } +inline Lit toLit(int i) { + Lit p; + p.x = i; + return p; +} -//const Lit lit_Undef = mkLit(var_Undef, false); // }- Useful special constants. -//const Lit lit_Error = mkLit(var_Undef, true ); // } +// const Lit lit_Undef = mkLit(var_Undef, false); // }- Useful special +// constants. const Lit lit_Error = mkLit(var_Undef, true ); // } -const Lit lit_Undef = { -2 }; // }- Useful special constants. -const Lit lit_Error = { -1 }; // } +const Lit lit_Undef = {-2}; // }- Useful special constants. +const Lit lit_Error = {-1}; // } -struct MkIndexLit { vec::Size operator()(Lit l) const { return vec::Size(l.x); } }; +struct MkIndexLit { + vec::Size operator()(Lit l) const { return vec::Size(l.x); } +}; -template class VMap : public IntMap{}; -template class LMap : public IntMap{}; -class LSet : public IntSet{}; +template +class VMap : public IntMap {}; +template +class LMap : public IntMap {}; +class LSet : public IntSet {}; //================================================================================================= // Lifted booleans: // -// NOTE: this implementation is optimized for the case when comparisons between values are mostly -// between one variable and one constant. Some care had to be taken to make sure that gcc -// does enough constant propagation to produce sensible code, and this appears to be somewhat -// fragile unfortunately. +// NOTE: this implementation is optimized for the case when comparisons between +// values are mostly +// between one variable and one constant. Some care had to be taken to +// make sure that gcc does enough constant propagation to produce sensible +// code, and this appears to be somewhat fragile unfortunately. class lbool { - uint8_t value; - -public: - explicit lbool(uint8_t v) : value(v) { } - - lbool() : value(0) { } - explicit lbool(bool x) : value(!x) { } - - bool operator == (lbool b) const { return ((b.value&2) & (value&2)) | (!(b.value&2)&(value == b.value)); } - bool operator != (lbool b) const { return !(*this == b); } - lbool operator ^ (bool b) const { return lbool((uint8_t)(value^(uint8_t)b)); } - - lbool operator && (lbool b) const { - uint8_t sel = (this->value << 1) | (b.value << 3); - uint8_t v = (0xF7F755F4 >> sel) & 3; - return lbool(v); } + uint8_t value; - lbool operator || (lbool b) const { - uint8_t sel = (this->value << 1) | (b.value << 3); - uint8_t v = (0xFCFCF400 >> sel) & 3; - return lbool(v); } - -//FB added---clunky syntax to avoid confusingly applying ~ to an lbool instead of a lit! - lbool neg() const { - return value&2 ? lbool(value) : ( value ? lbool((uint8_t)0) : lbool((uint8_t)1) ); - } - - friend int toInt (lbool l); - friend lbool toLbool(int v); + public: + //explicit lbool(uint8_t v) : value(v) {} + constexpr lbool(uint8_t v) : value(v) {} + + constexpr lbool() : value(0) {} + constexpr explicit lbool(bool x) : value(!x) {} + + constexpr bool operator==(lbool b) const { + return ((b.value & 2) & (value & 2)) | + (!(b.value & 2) & (value == b.value)); + } + constexpr bool operator!=(lbool b) const { return !(*this == b); } + constexpr lbool operator^(bool b) const { + return value & 2 ? lbool(value) : lbool((uint8_t)(value ^ (uint8_t)b)); + } + + constexpr lbool operator&&(lbool b) const { + uint8_t sel = (this->value << 1) | (b.value << 3); + uint8_t v = (0xF7F755F4 >> sel) & 3; + return lbool(v); + } + + constexpr lbool operator||(lbool b) const { + uint8_t sel = (this->value << 1) | (b.value << 3); + uint8_t v = (0xFCFCF400 >> sel) & 3; + return lbool(v); + } + + // FB added---clunky syntax to avoid confusingly applying ~ to an lbool + // instead of a lit! + constexpr lbool neg() const { + return value & 2 ? lbool(value) + : (value ? lbool((uint8_t)0) : lbool((uint8_t)1)); + } + + friend int toInt(lbool l); + friend lbool toLbool(int v); }; -inline int toInt (lbool l) { return l.value; } -inline lbool toLbool(int v) { return lbool((uint8_t)v); } +inline int toInt(lbool l) { return l.value; } +inline lbool toLbool(int v) { return lbool((uint8_t)v); } #if defined(MINISAT_CONSTANTS_AS_MACROS) - #define l_True (lbool((uint8_t)0)) // gcc does not do constant propagation if these are real constants. - #define l_False (lbool((uint8_t)1)) - #define l_Undef (lbool((uint8_t)2)) +#define l_True \ + (lbool((uint8_t)0)) // gcc does not do constant propagation if these are real + // constants. +#define l_False (lbool((uint8_t)1)) +#define l_Undef (lbool((uint8_t)2)) #else - const lbool l_True ((uint8_t)0); - const lbool l_False((uint8_t)1); - const lbool l_Undef((uint8_t)2); +constexpr lbool l_True((uint8_t)0); +constexpr lbool l_False((uint8_t)1); +constexpr lbool l_Undef((uint8_t)2); #endif - //================================================================================================= // Clause -- a simple class for representing a clause: @@ -143,341 +176,375 @@ class Clause; typedef RegionAllocator::Ref CRef; class Clause { - struct { - unsigned mark : 2; - unsigned learnt : 1; - unsigned has_extra : 1; - unsigned reloced : 1; - unsigned size : 27; } header; - union { Lit lit; float act; uint32_t abs; CRef rel; } data[0]; - - friend class ClauseAllocator; - - // NOTE: This constructor cannot be used directly (doesn't allocate enough memory). - Clause(const vec& ps, bool use_extra, bool learnt) { - header.mark = 0; - header.learnt = learnt; - header.has_extra = use_extra; - header.reloced = 0; - header.size = ps.size(); - - for (int i = 0; i < ps.size(); i++) - data[i].lit = ps[i]; - - if (header.has_extra){ - if (header.learnt) - data[header.size].act = 0; - else - calcAbstraction(); - } - } - - // NOTE: This constructor cannot be used directly (doesn't allocate enough memory). - Clause(const Clause& from, bool use_extra){ - header = from.header; - header.has_extra = use_extra; // NOTE: the copied clause may lose the extra field. - - for (int i = 0; i < from.size(); i++) - data[i].lit = from[i]; - - if (header.has_extra){ - if (header.learnt) - data[header.size].act = from.data[header.size].act; - else - data[header.size].abs = from.data[header.size].abs; + struct { + unsigned mark : 2; + unsigned learnt : 1; + unsigned has_extra : 1; + unsigned reloced : 1; + unsigned lbd : 27; + unsigned size : 32; + } header; + union { + Lit lit; + float act; + uint32_t abs; + CRef rel; + } data[0]; + + friend class ClauseAllocator; + + // NOTE: This constructor cannot be used directly (doesn't allocate enough + // memory). + Clause(const vec& ps, bool use_extra, bool learnt) { + header.mark = 0; + header.learnt = learnt; + header.has_extra = use_extra; + header.reloced = 0; + header.lbd = 0; + header.size = ps.size(); + + for (int i = 0; i < ps.size(); i++) data[i].lit = ps[i]; + + if (header.has_extra) { + if (header.learnt) + data[header.size].act = 0; + else + calcAbstraction(); } + } + + // NOTE: This constructor cannot be used directly (doesn't allocate enough + // memory). + Clause(const Clause& from, bool use_extra) { + header = from.header; + header.has_extra = + use_extra; // NOTE: the copied clause may lose the extra field. + + for (int i = 0; i < from.size(); i++) data[i].lit = from[i]; + + if (header.has_extra) { + if (header.learnt) + data[header.size].act = from.data[header.size].act; + else + data[header.size].abs = from.data[header.size].abs; } + } -public: - void calcAbstraction() { - assert(header.has_extra); - uint32_t abstraction = 0; - for (int i = 0; i < size(); i++) - abstraction |= 1 << (var(data[i].lit) & 31); - data[header.size].abs = abstraction; } - - - int size () const { return header.size; } - void shrink (int i) { assert(i <= size()); if (header.has_extra) data[header.size-i] = data[header.size]; header.size -= i; } - void pop () { shrink(1); } - bool learnt () const { return header.learnt; } - bool has_extra () const { return header.has_extra; } - uint32_t mark () const { return header.mark; } - void mark (uint32_t m) { header.mark = m; } - const Lit& last () const { return data[header.size-1].lit; } - - bool reloced () const { return header.reloced; } - CRef relocation () const { return data[0].rel; } - void relocate (CRef c) { header.reloced = 1; data[0].rel = c; } - - // NOTE: somewhat unsafe to change the clause in-place! Must manually call 'calcAbstraction' afterwards for - // subsumption operations to behave correctly. - Lit& operator [] (int i) { return data[i].lit; } - Lit operator [] (int i) const { return data[i].lit; } - operator const Lit* (void) const { return (Lit*)data; } - - float& activity () { assert(header.has_extra); return data[header.size].act; } - uint32_t abstraction () const { assert(header.has_extra); return data[header.size].abs; } - - Lit subsumes (const Clause& other) const; - void strengthen (Lit p); + public: + void calcAbstraction() { + assert(header.has_extra); + uint32_t abstraction = 0; + for (int i = 0; i < size(); i++) + abstraction |= 1 << (var(data[i].lit) & 31); + data[header.size].abs = abstraction; + } + + int size() const { return header.size; } + void shrink(int i) { + assert(i <= size()); + if (header.has_extra) data[header.size - i] = data[header.size]; + header.size -= i; + } + void pop() { shrink(1); } + bool learnt() const { return header.learnt; } + bool has_extra() const { return header.has_extra; } + uint32_t mark() const { return header.mark; } + void mark(uint32_t m) { header.mark = m; } + const Lit& last() const { return data[header.size - 1].lit; } + + bool reloced() const { return header.reloced; } + CRef relocation() const { return data[0].rel; } + void relocate(CRef c) { + header.reloced = 1; + data[0].rel = c; + } + + // NOTE: somewhat unsafe to change the clause in-place! Must manually call + // 'calcAbstraction' afterwards for + // subsumption operations to behave correctly. + Lit& operator[](int i) { return data[i].lit; } + Lit operator[](int i) const { return data[i].lit; } + operator const Lit*(void)const { return (Lit*)data; } + + float& activity() { + assert(header.has_extra); + return data[header.size].act; + } + uint32_t abstraction() const { + assert(header.has_extra); + return data[header.size].abs; + } + + Lit subsumes(const Clause& other) const; + void strengthen(Lit p); + void setLBD(int i) { header.lbd = i; } + unsigned int lbd() const { return header.lbd; } }; - //================================================================================================= // ClauseAllocator -- a simple class for allocating memory for clauses: const CRef CRef_Undef = RegionAllocator::Ref_Undef; -class ClauseAllocator -{ - RegionAllocator ra; +class ClauseAllocator { + RegionAllocator ra; - static uint32_t clauseWord32Size(int size, bool has_extra){ - return (sizeof(Clause) + (sizeof(Lit) * (size + (int)has_extra))) / sizeof(uint32_t); } + static uint32_t clauseWord32Size(int size, bool has_extra) { + return (sizeof(Clause) + (sizeof(Lit) * (size + (int)has_extra))) / + sizeof(uint32_t); + } public: - enum { Unit_Size = RegionAllocator::Unit_Size }; - - bool extra_clause_field; - - ClauseAllocator(uint32_t start_cap) : ra(start_cap), extra_clause_field(false){} - ClauseAllocator() : extra_clause_field(false){} - - void moveTo(ClauseAllocator& to){ - to.extra_clause_field = extra_clause_field; - ra.moveTo(to.ra); } - - CRef alloc(const vec& ps, bool learnt = false) - { - assert(sizeof(Lit) == sizeof(uint32_t)); - assert(sizeof(float) == sizeof(uint32_t)); - bool use_extra = learnt | extra_clause_field; - CRef cid = ra.alloc(clauseWord32Size(ps.size(), use_extra)); - new (lea(cid)) Clause(ps, use_extra, learnt); - - return cid; - } - - CRef alloc(const Clause& from) - { - bool use_extra = from.learnt() | extra_clause_field; - CRef cid = ra.alloc(clauseWord32Size(from.size(), use_extra)); - new (lea(cid)) Clause(from, use_extra); - return cid; } - - uint32_t size () const { return ra.size(); } - uint32_t wasted () const { return ra.wasted(); } - - // Deref, Load Effective Address (LEA), Inverse of LEA (AEL): - Clause& operator[](CRef r) { return (Clause&)ra[r]; } - const Clause& operator[](CRef r) const { return (Clause&)ra[r]; } - Clause* lea (CRef r) { return (Clause*)ra.lea(r); } - const Clause* lea (CRef r) const { return (Clause*)ra.lea(r);; } - CRef ael (const Clause* t){ return ra.ael((uint32_t*)t); } - - void free(CRef cid) - { - Clause& c = operator[](cid); - ra.free(clauseWord32Size(c.size(), c.has_extra())); + enum { Unit_Size = RegionAllocator::Unit_Size }; + + bool extra_clause_field; + + ClauseAllocator(uint32_t start_cap) + : ra(start_cap), extra_clause_field(false) {} + ClauseAllocator() : extra_clause_field(false) {} + + void moveTo(ClauseAllocator& to) { + to.extra_clause_field = extra_clause_field; + ra.moveTo(to.ra); + } + + CRef alloc(const vec& ps, bool learnt = false) { + assert(sizeof(Lit) == sizeof(uint32_t)); + assert(sizeof(float) == sizeof(uint32_t)); + bool use_extra = learnt | extra_clause_field; + CRef cid = ra.alloc(clauseWord32Size(ps.size(), use_extra)); + new (lea(cid)) Clause(ps, use_extra, learnt); + + return cid; + } + + CRef alloc(const Clause& from) { + bool use_extra = from.learnt() | extra_clause_field; + CRef cid = ra.alloc(clauseWord32Size(from.size(), use_extra)); + new (lea(cid)) Clause(from, use_extra); + return cid; + } + + uint32_t size() const { return ra.size(); } + uint32_t wasted() const { return ra.wasted(); } + + // Deref, Load Effective Address (LEA), Inverse of LEA (AEL): + Clause& operator[](CRef r) { return (Clause&)ra[r]; } + const Clause& operator[](CRef r) const { return (Clause&)ra[r]; } + Clause* lea(CRef r) { return (Clause*)ra.lea(r); } + const Clause* lea(CRef r) const { + return (Clause*)ra.lea(r); + ; + } + CRef ael(const Clause* t) { return ra.ael((uint32_t*)t); } + + void free(CRef cid) { + Clause& c = operator[](cid); + ra.free(clauseWord32Size(c.size(), c.has_extra())); + } + + void reloc(CRef& cr, ClauseAllocator& to) { + Clause& c = operator[](cr); + + if (c.reloced()) { + cr = c.relocation(); + return; } - void reloc(CRef& cr, ClauseAllocator& to) - { - Clause& c = operator[](cr); - - if (c.reloced()) { cr = c.relocation(); return; } - - cr = to.alloc(c); - c.relocate(cr); - } + cr = to.alloc(c); + c.relocate(cr); + } }; //================================================================================================= -// Simple iterator classes (for iterating over clauses and top-level assignments): +// Simple iterator classes (for iterating over clauses and top-level +// assignments): class ClauseIterator { - const ClauseAllocator& ca; - const CRef* crefs; -public: - ClauseIterator(const ClauseAllocator& _ca, const CRef* _crefs) : ca(_ca), crefs(_crefs){} + const ClauseAllocator& ca; + const CRef* crefs; - void operator++(){ crefs++; } - const Clause& operator*() const { return ca[*crefs]; } + public: + ClauseIterator(const ClauseAllocator& _ca, const CRef* _crefs) + : ca(_ca), crefs(_crefs) {} - // NOTE: does not compare that references use the same clause-allocator: - bool operator==(const ClauseIterator& ci) const { return crefs == ci.crefs; } - bool operator!=(const ClauseIterator& ci) const { return crefs != ci.crefs; } -}; + void operator++() { crefs++; } + const Clause& operator*() const { return ca[*crefs]; } + // NOTE: does not compare that references use the same clause-allocator: + bool operator==(const ClauseIterator& ci) const { return crefs == ci.crefs; } + bool operator!=(const ClauseIterator& ci) const { return crefs != ci.crefs; } +}; class TrailIterator { - const Lit* lits; -public: - TrailIterator(const Lit* _lits) : lits(_lits){} + const Lit* lits; + + public: + TrailIterator(const Lit* _lits) : lits(_lits) {} - void operator++() { lits++; } - Lit operator*() const { return *lits; } + void operator++() { lits++; } + Lit operator*() const { return *lits; } - bool operator==(const TrailIterator& ti) const { return lits == ti.lits; } - bool operator!=(const TrailIterator& ti) const { return lits != ti.lits; } + bool operator==(const TrailIterator& ti) const { return lits == ti.lits; } + bool operator!=(const TrailIterator& ti) const { return lits != ti.lits; } }; - //================================================================================================= // OccLists -- a class for maintaining occurence lists with lazy deletion: -template > -class OccLists -{ - IntMap occs; - IntMap dirty; - vec dirties; - Deleted deleted; +template > +class OccLists { + IntMap occs; + IntMap dirty; + vec dirties; + Deleted deleted; public: - OccLists(const Deleted& d, MkIndex _index = MkIndex()) : - occs(_index), - dirty(_index), - deleted(d){} - - void init (const K& idx){ occs.reserve(idx); occs[idx].clear(); dirty.reserve(idx, 0); } - Vec& operator[](const K& idx){ return occs[idx]; } - Vec& lookup (const K& idx){ if (dirty[idx]) clean(idx); return occs[idx]; } - - void cleanAll (); - void clean (const K& idx); - void smudge (const K& idx){ - if (dirty[idx] == 0){ - dirty[idx] = 1; - dirties.push(idx); - } + OccLists(const Deleted& d, MkIndex _index = MkIndex()) + : occs(_index), dirty(_index), deleted(d) {} + + void init(const K& idx) { + occs.reserve(idx); + occs[idx].clear(); + dirty.reserve(idx, 0); + } + Vec& operator[](const K& idx) { return occs[idx]; } + Vec& lookup(const K& idx) { + if (dirty[idx]) clean(idx); + return occs[idx]; + } + + void cleanAll(); + void clean(const K& idx); + void smudge(const K& idx) { + if (dirty[idx] == 0) { + dirty[idx] = 1; + dirties.push(idx); } + } - void clear(bool free = true){ - occs .clear(free); - dirty .clear(free); - dirties.clear(free); - } + void clear(bool free = true) { + occs.clear(free); + dirty.clear(free); + dirties.clear(free); + } }; - -template -void OccLists::cleanAll() -{ - for (int i = 0; i < dirties.size(); i++) - // Dirties may contain duplicates so check here if a variable is already cleaned: - if (dirty[dirties[i]]) - clean(dirties[i]); - dirties.clear(); +template +void OccLists::cleanAll() { + for (int i = 0; i < dirties.size(); i++) + // Dirties may contain duplicates so check here if a variable is already + // cleaned: + if (dirty[dirties[i]]) clean(dirties[i]); + dirties.clear(); } - -template -void OccLists::clean(const K& idx) -{ - Vec& vec = occs[idx]; - int i, j; - for (i = j = 0; i < vec.size(); i++) - if (!deleted(vec[i])) - vec[j++] = vec[i]; - vec.shrink(i - j); - dirty[idx] = 0; +template +void OccLists::clean(const K& idx) { + Vec& vec = occs[idx]; + int i, j; + for (i = j = 0; i < vec.size(); i++) + if (!deleted(vec[i])) vec[j++] = vec[i]; + vec.shrink(i - j); + dirty[idx] = 0; } - //================================================================================================= // CMap -- a class for mapping clauses to values: +template +class CMap { + struct CRefHash { + uint32_t operator()(CRef cr) const { return (uint32_t)cr; } + }; -template -class CMap -{ - struct CRefHash { - uint32_t operator()(CRef cr) const { return (uint32_t)cr; } }; + typedef Map HashTable; + HashTable map; - typedef Map HashTable; - HashTable map; - public: - // Size-operations: - void clear () { map.clear(); } - int size () const { return map.elems(); } - - - // Insert/Remove/Test mapping: - void insert (CRef cr, const T& t){ map.insert(cr, t); } - void growTo (CRef cr, const T& t){ map.insert(cr, t); } // NOTE: for compatibility - void remove (CRef cr) { map.remove(cr); } - bool has (CRef cr, T& t) { return map.peek(cr, t); } - - // Vector interface (the clause 'c' must already exist): - const T& operator [] (CRef cr) const { return map[cr]; } - T& operator [] (CRef cr) { return map[cr]; } - - // Iteration (not transparent at all at the moment): - int bucket_count() const { return map.bucket_count(); } - const vec& bucket(int i) const { return map.bucket(i); } - - // Move contents to other map: - void moveTo(CMap& other){ map.moveTo(other.map); } - - // TMP debug: - void debug(){ - printf(" --- size = %d, bucket_count = %d\n", size(), map.bucket_count()); } + // Size-operations: + void clear() { map.clear(); } + int size() const { return map.elems(); } + + // Insert/Remove/Test mapping: + void insert(CRef cr, const T& t) { map.insert(cr, t); } + void growTo(CRef cr, const T& t) { + map.insert(cr, t); + } // NOTE: for compatibility + void remove(CRef cr) { map.remove(cr); } + bool has(CRef cr, T& t) { return map.peek(cr, t); } + + // Vector interface (the clause 'c' must already exist): + const T& operator[](CRef cr) const { return map[cr]; } + T& operator[](CRef cr) { return map[cr]; } + + // Iteration (not transparent at all at the moment): + int bucket_count() const { return map.bucket_count(); } + const vec& bucket(int i) const { + return map.bucket(i); + } + + // Move contents to other map: + void moveTo(CMap& other) { map.moveTo(other.map); } + + // TMP debug: + void debug() { + printf(" --- size = %d, bucket_count = %d\n", size(), map.bucket_count()); + } }; - /*_________________________________________________________________________________________________ | | subsumes : (other : const Clause&) -> Lit -| +| | Description: -| Checks if clause subsumes 'other', and at the same time, if it can be used to simplify 'other' -| by subsumption resolution. -| +| Checks if clause subsumes 'other', and at the same time, if it can be +used to simplify 'other' | by subsumption resolution. +| | Result: | lit_Error - No subsumption or simplification | lit_Undef - Clause subsumes 'other' | p - The literal p can be deleted from 'other' |________________________________________________________________________________________________@*/ -inline Lit Clause::subsumes(const Clause& other) const -{ - //if (other.size() < size() || (extra.abst & ~other.extra.abst) != 0) - //if (other.size() < size() || (!learnt() && !other.learnt() && (extra.abst & ~other.extra.abst) != 0)) - assert(!header.learnt); assert(!other.header.learnt); - assert(header.has_extra); assert(other.header.has_extra); - if (other.header.size < header.size || (data[header.size].abs & ~other.data[other.header.size].abs) != 0) - return lit_Error; - - Lit ret = lit_Undef; - const Lit* c = (const Lit*)(*this); - const Lit* d = (const Lit*)other; - - for (unsigned i = 0; i < header.size; i++) { - // search for c[i] or ~c[i] - for (unsigned j = 0; j < other.header.size; j++) - if (c[i] == d[j]) - goto ok; - else if (ret == lit_Undef && c[i] == ~d[j]){ - ret = c[i]; - goto ok; - } - - // did not find it - return lit_Error; - ok:; - } - - return ret; +inline Lit Clause::subsumes(const Clause& other) const { + // if (other.size() < size() || (extra.abst & ~other.extra.abst) != 0) + // if (other.size() < size() || (!learnt() && !other.learnt() && (extra.abst & + // ~other.extra.abst) != 0)) + assert(!header.learnt); + assert(!other.header.learnt); + assert(header.has_extra); + assert(other.header.has_extra); + if (other.header.size < header.size || + (data[header.size].abs & ~other.data[other.header.size].abs) != 0) + return lit_Error; + + Lit ret = lit_Undef; + const Lit* c = (const Lit*)(*this); + const Lit* d = (const Lit*)other; + + for (unsigned i = 0; i < header.size; i++) { + // search for c[i] or ~c[i] + for (unsigned j = 0; j < other.header.size; j++) + if (c[i] == d[j]) + goto ok; + else if (ret == lit_Undef && c[i] == ~d[j]) { + ret = c[i]; + goto ok; + } + + // did not find it + return lit_Error; + ok:; + } + + return ret; } -inline void Clause::strengthen(Lit p) -{ - remove(*this, p); - calcAbstraction(); +inline void Clause::strengthen(Lit p) { + remove(*this, p); + calcAbstraction(); } //================================================================================================= -} +} // namespace Minisat #endif diff --git a/minisat/mtl/Alg.h b/minisat/mtl/Alg.h old mode 100644 new mode 100755 diff --git a/minisat/mtl/Alloc.h b/minisat/mtl/Alloc.h old mode 100644 new mode 100755 diff --git a/minisat/mtl/Heap.h b/minisat/mtl/Heap.h old mode 100644 new mode 100755 diff --git a/minisat/mtl/IntMap.h b/minisat/mtl/IntMap.h old mode 100644 new mode 100755 diff --git a/minisat/mtl/IntTypes.h b/minisat/mtl/IntTypes.h old mode 100644 new mode 100755 diff --git a/minisat/mtl/Map.h b/minisat/mtl/Map.h old mode 100644 new mode 100755 diff --git a/minisat/mtl/Queue.h b/minisat/mtl/Queue.h old mode 100644 new mode 100755 diff --git a/minisat/mtl/Rnd.h b/minisat/mtl/Rnd.h old mode 100644 new mode 100755 diff --git a/minisat/mtl/Sort.h b/minisat/mtl/Sort.h old mode 100644 new mode 100755 diff --git a/minisat/mtl/Vec.h b/minisat/mtl/Vec.h old mode 100644 new mode 100755 index 5c188ff..f00a270 --- a/minisat/mtl/Vec.h +++ b/minisat/mtl/Vec.h @@ -2,20 +2,22 @@ Copyright (c) 2003-2007, Niklas Een, Niklas Sorensson Copyright (c) 2007-2010, Niklas Sorensson -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, -sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or -substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT -NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT -OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. **************************************************************************************************/ #ifndef Minisat_Vec_h @@ -33,102 +35,143 @@ namespace Minisat { //================================================================================================= // Automatically resizable arrays // -// NOTE! Don't use this vector on datatypes that cannot be re-located in memory (with realloc) +// NOTE! Don't use this vector on datatypes that cannot be re-located in memory +// (with realloc) -template +template class vec { -public: - typedef _Size Size; -private: - T* data; - Size sz; - Size cap; - - // Don't allow copying (error prone): - vec& operator=(vec& other); - vec (vec& other); - - static inline Size max(Size x, Size y){ return (x > y) ? x : y; } - -public: - // Constructors: - vec() : data(NULL), sz(0), cap(0) { } - explicit vec(Size size) : data(NULL), sz(0), cap(0) { growTo(size); } - vec(Size size, const T& pad) : data(NULL), sz(0), cap(0) { growTo(size, pad); } - ~vec() { clear(true); } - - // Pointer to first element: - operator T* (void) { return data; } - - // Size operations: - Size size (void) const { return sz; } - void shrink (Size nelems) { assert(nelems <= sz); for (Size i = 0; i < nelems; i++) sz--, data[sz].~T(); } - void shrink_ (Size nelems) { assert(nelems <= sz); sz -= nelems; } - int capacity (void) const { return cap; } - void capacity (Size min_cap); - void growTo (Size size); - void growTo (Size size, const T& pad); - void clear (bool dealloc = false); - - // Stack interface: - void push (void) { if (sz == cap) capacity(sz+1); new (&data[sz]) T(); sz++; } - //void push (const T& elem) { if (sz == cap) capacity(sz+1); data[sz++] = elem; } - void push (const T& elem) { if (sz == cap) capacity(sz+1); new (&data[sz++]) T(elem); } - void push_ (const T& elem) { assert(sz < cap); data[sz++] = elem; } - void pop (void) { assert(sz > 0); sz--, data[sz].~T(); } - // NOTE: it seems possible that overflow can happen in the 'sz+1' expression of 'push()', but - // in fact it can not since it requires that 'cap' is equal to INT_MAX. This in turn can not - // happen given the way capacities are calculated (below). Essentially, all capacities are - // even, but INT_MAX is odd. - - const T& last (void) const { return data[sz-1]; } - T& last (void) { return data[sz-1]; } - - // Vector interface: - const T& operator [] (Size index) const { return data[index]; } - T& operator [] (Size index) { return data[index]; } - - // Duplicatation (preferred instead): - void copyTo(vec& copy) const { copy.clear(); copy.growTo(sz); for (Size i = 0; i < sz; i++) copy[i] = data[i]; } - void moveTo(vec& dest) { dest.clear(true); dest.data = data; dest.sz = sz; dest.cap = cap; data = NULL; sz = 0; cap = 0; } + public: + typedef _Size Size; + + private: + T* data; + Size sz; + Size cap; + + // Don't allow copying (error prone): + vec& operator=(vec& other); + vec(vec& other); + + static inline Size max(Size x, Size y) { return (x > y) ? x : y; } + + public: + // Constructors: + vec() : data(NULL), sz(0), cap(0) {} + explicit vec(Size size) : data(NULL), sz(0), cap(0) { growTo(size); } + vec(Size size, const T& pad) : data(NULL), sz(0), cap(0) { + growTo(size, pad); + } + ~vec() { clear(true); } + + // Pointer to first element: + operator T*(void) { return data; } + + // Size operations: + Size size(void) const { return sz; } + void shrink(Size nelems) { + assert(nelems <= sz); + for (Size i = 0; i < nelems; i++) sz--, data[sz].~T(); + } + void shrink_(Size nelems) { + assert(nelems <= sz); + sz -= nelems; + } + int capacity(void) const { return cap; } + void capacity(Size min_cap); + void growTo(Size size); + void growTo(Size size, const T& pad); + void clear(bool dealloc = false); + + // Stack interface: + void push(void) { + if (sz == cap) capacity(sz + 1); + new (&data[sz]) T(); + sz++; + } + // void push (const T& elem) { if (sz == cap) capacity(sz+1); + // data[sz++] = elem; } + void push(const T& elem) { + if (sz == cap) capacity(sz + 1); + new (&data[sz++]) T(elem); + } + void push_(const T& elem) { + assert(sz < cap); + data[sz++] = elem; + } + void pop(void) { + assert(sz > 0); + sz--, data[sz].~T(); + } + // NOTE: it seems possible that overflow can happen in the 'sz+1' expression + // of 'push()', but in fact it can not since it requires that 'cap' is equal + // to INT_MAX. This in turn can not happen given the way capacities are + // calculated (below). Essentially, all capacities are even, but INT_MAX is + // odd. + + const T& last(void) const { return data[sz - 1]; } + T& last(void) { return data[sz - 1]; } + + // Vector interface: + const T& operator[](Size index) const { return data[index]; } + T& operator[](Size index) { return data[index]; } + + // Duplicatation (preferred instead): + void copyTo(vec& copy) const { + copy.clear(); + copy.growTo(sz); + for (Size i = 0; i < sz; i++) copy[i] = data[i]; + } + void moveTo(vec& dest) { + dest.clear(true); + dest.data = data; + dest.sz = sz; + dest.cap = cap; + data = NULL; + sz = 0; + cap = 0; + } }; +template +void vec::capacity(Size min_cap) { + if (cap >= min_cap) return; + Size add = max((min_cap - cap + 1) & ~1, + ((cap >> 1) + 2) & ~1); // NOTE: grow by approximately 3/2 + const Size size_max = std::numeric_limits::max(); + if (((size_max <= std::numeric_limits::max()) && + (add > size_max - cap)) || + (((data = (T*)::realloc((void*)data, (cap += add) * sizeof(T))) == + NULL) && + errno == ENOMEM)) + throw OutOfMemoryException(); +} -template -void vec::capacity(Size min_cap) { - if (cap >= min_cap) return; - Size add = max((min_cap - cap + 1) & ~1, ((cap >> 1) + 2) & ~1); // NOTE: grow by approximately 3/2 - const Size size_max = std::numeric_limits::max(); - if ( ((size_max <= std::numeric_limits::max()) && (add > size_max - cap)) - || (((data = (T*)::realloc(data, (cap += add) * sizeof(T))) == NULL) && errno == ENOMEM) ) - throw OutOfMemoryException(); - } - - -template -void vec::growTo(Size size, const T& pad) { - if (sz >= size) return; - capacity(size); - for (Size i = sz; i < size; i++) data[i] = pad; - sz = size; } - - -template -void vec::growTo(Size size) { - if (sz >= size) return; - capacity(size); - for (Size i = sz; i < size; i++) new (&data[i]) T(); - sz = size; } +template +void vec::growTo(Size size, const T& pad) { + if (sz >= size) return; + capacity(size); + for (Size i = sz; i < size; i++) data[i] = pad; + sz = size; +} +template +void vec::growTo(Size size) { + if (sz >= size) return; + capacity(size); + for (Size i = sz; i < size; i++) new (&data[i]) T(); + sz = size; +} -template -void vec::clear(bool dealloc) { - if (data != NULL){ - for (Size i = 0; i < sz; i++) data[i].~T(); - sz = 0; - if (dealloc) free(data), data = NULL, cap = 0; } } +template +void vec::clear(bool dealloc) { + if (data != NULL) { + for (Size i = 0; i < sz; i++) data[i].~T(); + sz = 0; + if (dealloc) free(data), data = NULL, cap = 0; + } +} //================================================================================================= -} +} // namespace Minisat #endif diff --git a/minisat/mtl/XAlloc.h b/minisat/mtl/XAlloc.h old mode 100644 new mode 100755 diff --git a/minisat/simp/Main.cc b/minisat/simp/Main.cc old mode 100644 new mode 100755 diff --git a/minisat/simp/SimpSolver.cc b/minisat/simp/SimpSolver.cc old mode 100644 new mode 100755 diff --git a/minisat/simp/SimpSolver.h b/minisat/simp/SimpSolver.h old mode 100644 new mode 100755 diff --git a/minisat/utils/Options.cc b/minisat/utils/Options.cc old mode 100644 new mode 100755 diff --git a/minisat/utils/Options.h b/minisat/utils/Options.h old mode 100644 new mode 100755 diff --git a/minisat/utils/ParseUtils.h b/minisat/utils/ParseUtils.h old mode 100644 new mode 100755 diff --git a/minisat/utils/System.cc b/minisat/utils/System.cc old mode 100644 new mode 100755 diff --git a/minisat/utils/System.h b/minisat/utils/System.h old mode 100644 new mode 100755