1 //
2 // Copyright (c) 2006-2017 Benjamin Kaufmann
3 //
4 // This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to
8 // deal in the Software without restriction, including without limitation the
9 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 // sell copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 // IN THE SOFTWARE.
23 //
24 #include <clasp/solver.h>
25 #include <clasp/clause.h>
26 #include POTASSCO_EXT_INCLUDE(unordered_set)
27 namespace Clasp {
28 namespace {
29 typedef POTASSCO_EXT_NS::unordered_set<Constraint*> ConstraintSet;
30 struct InSet {
operator ()Clasp::__anonc4147af90111::InSet31 bool operator()(Constraint* c) const { return set->find(c) != set->end(); }
operator ()Clasp::__anonc4147af90111::InSet32 bool operator()(const ClauseWatch& w) const { return (*this)(w.head); }
operator ()Clasp::__anonc4147af90111::InSet33 bool operator()(const GenericWatch&w) const { return (*this)(w.con); }
34 const ConstraintSet* set;
35 };
36 }
~DecisionHeuristic()37 DecisionHeuristic::~DecisionHeuristic() {}
38 static SelectFirst null_heuristic_g;
39 /////////////////////////////////////////////////////////////////////////////////////////
40 // CCMinRecursive
41 /////////////////////////////////////////////////////////////////////////////////////////
42 struct CCMinRecursive {
43 enum State { state_open = 0, state_removable = 1, state_poison = 2 };
encodeStateClasp::CCMinRecursive44 uint32 encodeState(State st) const { return open + uint32(st); }
decodeStateClasp::CCMinRecursive45 State decodeState(uint32 epoch) const { return epoch <= open ? state_open : static_cast<State>(epoch - open); }
pushClasp::CCMinRecursive46 void push(Literal p) { todo.push_back(p); }
popClasp::CCMinRecursive47 Literal pop() { Literal p = todo.back(); todo.pop_back(); return p; }
48 LitVec todo;
49 uint32 open;
50 };
51 /////////////////////////////////////////////////////////////////////////////////////////
52 // SelectFirst selection strategy
53 /////////////////////////////////////////////////////////////////////////////////////////
54 // selects the first free literal
doSelect(Solver & s)55 Literal SelectFirst::doSelect(Solver& s) {
56 for (Var i = 1; i <= s.numVars(); ++i) {
57 if (s.value(i) == value_free) {
58 return selectLiteral(s, i, 0);
59 }
60 }
61 assert(!"SelectFirst::doSelect() - precondition violated!\n");
62 return Literal();
63 }
64 /////////////////////////////////////////////////////////////////////////////////////////
65 // Dirty list
66 /////////////////////////////////////////////////////////////////////////////////////////
67 struct Solver::Dirty {
68 static const std::size_t min_size = static_cast<std::size_t>(4);
DirtyClasp::Solver::Dirty69 Dirty() : last(0) {}
addClasp::Solver::Dirty70 bool add(Literal p, WatchList& wl, Constraint* c) {
71 if (wl.right_size() <= min_size) { return false; }
72 uintp o = wl.left_size() > 0 ? reinterpret_cast<uintp>(wl.left_begin()->head) : 0;
73 if (add(wl.right_begin()->con, o, c)) { dirty.push_left(p); }
74 return true;
75 }
addClasp::Solver::Dirty76 bool add(Literal p, WatchList& wl, ClauseHead* c) {
77 if (wl.left_size() <= min_size) { return false; }
78 uintp o = wl.right_size() > 0 ? reinterpret_cast<uintp>(wl.right_begin()->con) : 0;
79 if (add(wl.left_begin()->head, o, c)) { dirty.push_left(p); }
80 return true;
81 }
addClasp::Solver::Dirty82 bool add(uint32 dl, ConstraintDB& wl, Constraint* c) {
83 if (wl.size() <= min_size) { return false; }
84 if (add(wl[0], 0, c)) { dirty.push_right(dl); }
85 return true;
86 }
87 template <class T>
addClasp::Solver::Dirty88 bool add(T*& list, uintp other, Constraint* c) {
89 other |= reinterpret_cast<uintp>(list);
90 list = reinterpret_cast<T*>( set_bit(reinterpret_cast<uintp>(list), 0) );
91 if (c != last) { cons.insert(last = c); }
92 return !test_bit(other, 0);
93 }
94 template <class T>
test_and_clearClasp::Solver::Dirty95 bool test_and_clear(T*& x) const {
96 uintp old = reinterpret_cast<uintp>(x);
97 return test_bit(old, 0) && (x = reinterpret_cast<T*>(clear_bit(old, 0))) != 0;
98 }
cleanupClasp::Solver::Dirty99 void cleanup(Watches& watches, DecisionLevels& levels) {
100 InSet inCons = { &cons };
101 const uint32 maxId = (uint32)watches.size();
102 for (DirtyList::left_iterator it = dirty.left_begin(), end = dirty.left_end(); it != end; ++it) {
103 uint32 id = it->id();
104 if (id >= maxId)
105 continue;
106 WatchList& wl = watches[id];
107 if (wl.left_size() && test_and_clear(wl.left_begin()->head)) { wl.shrink_left(std::remove_if(wl.left_begin(), wl.left_end(), inCons)); }
108 if (wl.right_size()&& test_and_clear(wl.right_begin()->con)) { wl.shrink_right(std::remove_if(wl.right_begin(), wl.right_end(), inCons)); }
109 }
110 ConstraintDB* db = 0;
111 for (DirtyList::right_iterator it = dirty.right_begin(), end = dirty.right_end(); it != end; ++it) {
112 if (*it < levels.size() && !(db = levels[*it].undo)->empty() && test_and_clear(*db->begin())) {
113 db->erase(std::remove_if(db->begin(), db->end(), inCons), db->end());
114 }
115 }
116 dirty.clear();
117 cons.clear();
118 last = 0;
119 }
120 typedef bk_lib::left_right_sequence<Literal, uint32, 0> DirtyList;
121 DirtyList dirty;
122 ConstraintSet cons;
123 Constraint* last;
124 };
125 /////////////////////////////////////////////////////////////////////////////////////////
126 // Solver: Construction/Destruction/Setup
127 /////////////////////////////////////////////////////////////////////////////////////////
128 #define FOR_EACH_POST(x, head) \
129 for (PostPropagator** __r__ = (head), *x; (x = *__r__) != 0; __r__ = (x == *__r__) ? &x->next : __r__)
130
131 static PostPropagator* sent_list;
Solver(SharedContext * ctx,uint32 id)132 Solver::Solver(SharedContext* ctx, uint32 id)
133 : shared_(ctx)
134 , heuristic_(&null_heuristic_g, Ownership_t::Retain)
135 , ccMin_(0)
136 , postHead_(&sent_list)
137 , undoHead_(0)
138 , enum_(0)
139 , memUse_(0)
140 , lazyRem_(0)
141 , ccInfo_(Constraint_t::Conflict)
142 , dbIdx_(0)
143 , lastSimp_(0)
144 , shufSimp_(0)
145 , initPost_(0){
146 Var trueVar = assign_.addVar();
147 assign_.setValue(trueVar, value_true);
148 markSeen(trueVar);
149 strategy_.id = id;
150 }
151
~Solver()152 Solver::~Solver() {
153 freeMem();
154 }
155
freeMem()156 void Solver::freeMem() {
157 std::for_each( constraints_.begin(), constraints_.end(), DestroyObject());
158 std::for_each( learnts_.begin(), learnts_.end(), DestroyObject() );
159 constraints_.clear();
160 learnts_.clear();
161 post_.clear();
162 if (enum_) { enum_->destroy(); }
163 resetHeuristic(0);
164 PodVector<WatchList>::destruct(watches_);
165 // free undo lists
166 // first those still in use
167 for (DecisionLevels::size_type i = 0; i != levels_.size(); ++i) {
168 delete levels_[i].undo;
169 }
170 // then those in the free list
171 for (ConstraintDB* x = undoHead_; x; ) {
172 ConstraintDB* t = x;
173 x = (ConstraintDB*)x->front();
174 delete t;
175 }
176 delete ccMin_;
177 ccMin_ = 0;
178 memUse_ = 0;
179 }
180
satPrepro() const181 SatPreprocessor* Solver::satPrepro() const { return shared_->satPrepro.get(); }
searchConfig() const182 const SolveParams& Solver::searchConfig() const { return shared_->configuration()->search(id()); }
183
reset()184 void Solver::reset() {
185 SharedContext* myCtx = shared_;
186 uint32 myId = strategy_.id;
187 this->~Solver();
188 new (this) Solver(myCtx, myId);
189 }
setHeuristic(DecisionHeuristic * h,Ownership_t::Type t)190 void Solver::setHeuristic(DecisionHeuristic* h, Ownership_t::Type t) {
191 POTASSCO_REQUIRE(h, "Heuristic must not be null");
192 resetHeuristic(this, h, t);
193 }
resetHeuristic(Solver * s,DecisionHeuristic * h,Ownership_t::Type t)194 void Solver::resetHeuristic(Solver* s, DecisionHeuristic* h, Ownership_t::Type t) {
195 if (s && heuristic_.get()) { heuristic_->detach(*this); }
196 if (!h) { h = &null_heuristic_g; t = Ownership_t::Retain; }
197 HeuristicPtr(h, t).swap(heuristic_);
198 }
resetConfig()199 void Solver::resetConfig() {
200 if (strategy_.hasConfig) {
201 if (PostPropagator* pp = getPost(PostPropagator::priority_reserved_look)) { pp->destroy(this, true); }
202 delete ccMin_;
203 ccMin_ = 0;
204 }
205 strategy_.hasConfig = 0;
206 }
startInit(uint32 numConsGuess,const SolverParams & params)207 void Solver::startInit(uint32 numConsGuess, const SolverParams& params) {
208 assert(!lazyRem_ && decisionLevel() == 0);
209 if (watches_.empty()) {
210 assign_.trail.reserve(shared_->numVars() + 2);
211 watches_.reserve((shared_->numVars() + 2)<<1);
212 assign_.reserve(shared_->numVars() + 2);
213 }
214 updateVars();
215 // pre-allocate some memory
216 constraints_.reserve(numConsGuess/2);
217 levels_.reserve(25);
218 if (undoHead_ == 0) {
219 for (uint32 i = 0; i != 25; ++i) {
220 undoFree(new ConstraintDB(10));
221 }
222 }
223 if (!popRootLevel(rootLevel())) { return; }
224 if (!strategy_.hasConfig) {
225 uint32 id = this->id();
226 uint32 hId = strategy_.heuId; // remember active heuristic
227 strategy_ = params;
228 strategy_.id = id; // keep id
229 strategy_.hasConfig = 1; // strategy is now "up to date"
230 if (!params.ccMinRec) { delete ccMin_; ccMin_ = 0; }
231 else if (!ccMin_) { ccMin_ = new CCMinRecursive; }
232 if (id == params.id || !shared_->seedSolvers()) {
233 rng.srand(params.seed);
234 }
235 else {
236 RNG x(14182940); for (uint32 i = 0; i != id; ++i) { x.rand(); }
237 rng.srand(x.seed());
238 }
239 if (hId != params.heuId) { // heuristic has changed
240 resetHeuristic(this);
241 }
242 else if (heuristic_.is_owner()) {
243 heuristic_->setConfig(params.heuristic);
244 }
245 }
246 if (heuristic_.get() == &null_heuristic_g) {
247 heuristic_.reset(shared_->configuration()->heuristic(id()));
248 }
249 postHead_ = &sent_list; // disable post propagators during setup
250 heuristic_->startInit(*this);
251 }
252
updateVars()253 void Solver::updateVars() {
254 if (numVars() > shared_->numVars()) {
255 popVars(numVars() - shared_->numVars(), false, 0);
256 }
257 else {
258 assign_.resize(shared_->numVars() + 1);
259 watches_.resize(assign_.numVars()<<1);
260 }
261 }
262
cloneDB(const ConstraintDB & db)263 bool Solver::cloneDB(const ConstraintDB& db) {
264 while (dbIdx_ < (uint32)db.size() && !hasConflict()) {
265 if (Constraint* c = db[dbIdx_++]->cloneAttach(*this)) {
266 constraints_.push_back(c);
267 }
268 }
269 return !hasConflict();
270 }
preparePost()271 bool Solver::preparePost() {
272 if (hasConflict()) { return false; }
273 if (initPost_ == 0){
274 initPost_ = 1;
275 FOR_EACH_POST(x, post_.head()) {
276 if (!x->init(*this)) { return false; }
277 }
278 }
279 return shared_->configuration()->addPost(*this);
280 }
281
endInit()282 bool Solver::endInit() {
283 if (hasConflict()) { return false; }
284 heuristic_->endInit(*this);
285 if (strategy_.signFix) {
286 for (Var v = 1; v <= numVars(); ++v) {
287 Literal x = DecisionHeuristic::selectLiteral(*this, v, 0);
288 setPref(v, ValueSet::user_value, x.sign() ? value_false : value_true);
289 }
290 }
291 postHead_ = post_.head(); // enable all post propagators
292 return propagate() && simplify();
293 }
294
endStep(uint32 top,const SolverParams & params)295 bool Solver::endStep(uint32 top, const SolverParams& params) {
296 initPost_ = 0; // defer calls to PostPropagator::init()
297 if (!popRootLevel(rootLevel())) { return false; }
298 popAuxVar();
299 Literal x = shared_->stepLiteral();
300 top = std::min(top, (uint32)lastSimp_);
301 if (PostPropagator* pp = getPost(PostPropagator::priority_reserved_look)) {
302 pp->destroy(this, true);
303 }
304 if ((value(x.var()) != value_free || force(~x)) && simplify() && this != shared_->master() && shared_->ok()) {
305 Solver& m = *shared_->master();
306 for (uint32 end = (uint32)assign_.trail.size(); top < end; ++top) {
307 Literal u = assign_.trail[top];
308 if (u.var() != x.var() && !m.force(u)) { break; }
309 }
310 }
311 if (params.forgetLearnts()) { reduceLearnts(1.0f); }
312 if (params.forgetHeuristic()) { resetHeuristic(this); }
313 if (params.forgetSigns()) { resetPrefs(); }
314 if (params.forgetActivities()){ resetLearntActivities(); }
315 return true;
316 }
317
add(Constraint * c)318 void Solver::add(Constraint* c) {
319 constraints_.push_back(c);
320 }
add(const ClauseRep & c,bool isNew)321 bool Solver::add(const ClauseRep& c, bool isNew) {
322 typedef ShortImplicationsGraph::ImpType ImpType;
323 if (c.prep == 0) {
324 return ClauseCreator::create(*this, c, ClauseCreator::clause_force_simplify).ok();
325 }
326 int added = 0;
327 if (c.size > 1) {
328 if (allowImplicit(c)) { added = shared_->addImp(static_cast<ImpType>(c.size), c.lits, c.info.type()); }
329 else { return ClauseCreator::create(*this, c, ClauseCreator::clause_explicit).ok(); }
330 }
331 else {
332 Literal u = c.size ? c.lits[0] : lit_false();
333 uint32 ts= sizeVec(trail());
334 force(u);
335 added = int(ts != trail().size());
336 }
337 if (added > 0 && isNew && c.info.learnt()) {
338 stats.addLearnt(c.size, c.info.type());
339 distribute(c.lits, c.size, c.info);
340 }
341 return !hasConflict();
342 }
addPost(PostPropagator * p,bool init)343 bool Solver::addPost(PostPropagator* p, bool init) {
344 post_.add(p);
345 return !init || p->init(*this);
346 }
addPost(PostPropagator * p)347 bool Solver::addPost(PostPropagator* p) { return addPost(p, initPost_ != 0); }
removePost(PostPropagator * p)348 void Solver::removePost(PostPropagator* p){ post_.remove(p); }
getPost(uint32 prio) const349 PostPropagator* Solver::getPost(uint32 prio) const { return post_.find(prio); }
receive(SharedLiterals ** out,uint32 maxOut) const350 uint32 Solver::receive(SharedLiterals** out, uint32 maxOut) const {
351 if (shared_->distributor.get()) {
352 return shared_->distributor->receive(*this, out, maxOut);
353 }
354 return 0;
355 }
distribute(const Literal * lits,uint32 size,const ConstraintInfo & extra)356 SharedLiterals* Solver::distribute(const Literal* lits, uint32 size, const ConstraintInfo& extra) {
357 if (shared_->distributor.get() && !extra.aux() && (size <= 3 || shared_->distributor->isCandidate(size, extra.lbd(), extra.type()))) {
358 uint32 initialRefs = shared_->concurrency() - (size <= ClauseHead::MAX_SHORT_LEN || !shared_->physicalShare(extra.type()));
359 SharedLiterals* x = SharedLiterals::newShareable(lits, size, extra.type(), initialRefs);
360 shared_->distributor->publish(*this, x);
361 stats.addDistributed(extra.lbd(), extra.type());
362 return initialRefs == shared_->concurrency() ? x : 0;
363 }
364 return 0;
365 }
setEnumerationConstraint(Constraint * c)366 void Solver::setEnumerationConstraint(Constraint* c) {
367 if (enum_) enum_->destroy(this, true);
368 enum_ = c;
369 }
370
numConstraints() const371 uint32 Solver::numConstraints() const {
372 return static_cast<uint32>(constraints_.size())
373 + (shared_ ? shared_->numBinary()+shared_->numTernary() : 0);
374 }
375
pushAuxVar()376 Var Solver::pushAuxVar() {
377 assert(!lazyRem_);
378 Var aux = assign_.addVar();
379 setPref(aux, ValueSet::def_value, value_false);
380 watches_.insert(watches_.end(), 2, WatchList());
381 heuristic_->updateVar(*this, aux, 1);
382 return aux;
383 }
384
acquireProblemVar(Var var)385 void Solver::acquireProblemVar(Var var) {
386 if (validVar(var) || shared_->frozen() || numProblemVars() <= numVars() || !shared_->ok())
387 return;
388 shared_->startAddConstraints();
389 }
390
popAuxVar(uint32 num,ConstraintDB * auxCons)391 void Solver::popAuxVar(uint32 num, ConstraintDB* auxCons) {
392 num = numVars() >= shared_->numVars() ? std::min(numVars() - shared_->numVars(), num) : 0;
393 if (!num) { return; }
394 shared_->report("removing aux vars", this);
395 Dirty dirty;
396 lazyRem_ = &dirty;
397 popVars(num, true, auxCons);
398 lazyRem_ = 0;
399 shared_->report("removing aux watches", this);
400 dirty.cleanup(watches_, levels_);
401 }
popVars(uint32 num,bool popLearnt,ConstraintDB * popAux)402 Literal Solver::popVars(uint32 num, bool popLearnt, ConstraintDB* popAux) {
403 Literal pop = posLit(assign_.numVars() - num);
404 uint32 dl = decisionLevel() + 1;
405 for (ImpliedList::iterator it = impliedLits_.begin(); it != impliedLits_.end(); ++it) {
406 if (!(it->lit < pop)) { dl = std::min(dl, it->level); }
407 }
408 for (Var v = pop.var(), end = pop.var()+num; v != end; ++v) {
409 if (value(v) != value_free) { dl = std::min(dl, level(v)); }
410 }
411 // 1. remove aux vars from assignment and watch lists
412 if (dl > rootLevel()) {
413 undoUntil(dl-1, undo_pop_proj_level);
414 }
415 else {
416 popRootLevel((rootLevel() - dl) + 1);
417 if (dl == 0) { // top-level has aux vars - cleanup manually
418 uint32 j = shared_->numUnary();
419 uint32 nUnits = assign_.units(), nFront = assign_.front, nSimps = lastSimp_;
420 for (uint32 i = j, end = sizeVec(assign_.trail), endUnits = nUnits, endFront = nFront, endSimps = lastSimp_; i != end; ++i) {
421 if (assign_.trail[i] < pop) { assign_.trail[j++] = assign_.trail[i]; }
422 else {
423 nUnits -= (i < endUnits);
424 nFront -= (i < endFront);
425 nSimps -= (i < endSimps);
426 }
427 }
428 shrinkVecTo(assign_.trail, j);
429 assign_.front = nFront;
430 assign_.setUnits(nUnits);
431 lastSimp_ = nSimps;
432 }
433 }
434 for (uint32 n = num; n--;) {
435 watches_.back().clear(true);
436 watches_.pop_back();
437 watches_.back().clear(true);
438 watches_.pop_back();
439 }
440 // 2. remove learnt constraints over aux
441 if (popLearnt) {
442 shared_->report("removing aux constraints", this);
443 ConstraintDB::size_type i, j, end = learnts_.size();
444 LitVec cc;
445 for (i = j = 0; i != end; ++i) {
446 learnts_[j++] = learnts_[i];
447 ClauseHead* c = learnts_[i]->clause();
448 if (c && c->aux()) {
449 cc.clear();
450 c->toLits(cc);
451 if (std::find_if(cc.begin(), cc.end(), std::not1(std::bind2nd(std::less<Literal>(), pop))) != cc.end()) {
452 c->destroy(this, true);
453 --j;
454 }
455 }
456 }
457 learnts_.erase(learnts_.begin()+j, learnts_.end());
458 }
459 if (popAux) { destroyDB(*popAux); }
460 // 3. remove vars from solver and heuristic
461 assign_.resize(assign_.numVars()-num);
462 if (!validVar(tag_.var())) { tag_ = lit_true(); }
463 heuristic_->updateVar(*this, pop.var(), num);
464 return pop;
465 }
466
pushRoot(const LitVec & path)467 bool Solver::pushRoot(const LitVec& path) {
468 // make sure we are on the current root level
469 if (!popRootLevel(0) || !simplify() || !propagate()) { return false; }
470 // push path
471 stats.addPath(path.size());
472 for (LitVec::const_iterator it = path.begin(), end = path.end(); it != end; ++it) {
473 if (!pushRoot(*it)) { return false; }
474 }
475 ccInfo_.setActivity(1);
476 return true;
477 }
478
pushRoot(Literal x)479 bool Solver::pushRoot(Literal x) {
480 if (hasConflict()) { return false; }
481 if (decisionLevel()!= rootLevel()) { popRootLevel(0); }
482 if (queueSize() && !propagate()) { return false; }
483 if (value(x.var()) != value_free) { return isTrue(x);}
484 assume(x); --stats.choices;
485 pushRootLevel();
486 return propagate();
487 }
488
popRootLevel(uint32 n,LitVec * popped,bool aux)489 bool Solver::popRootLevel(uint32 n, LitVec* popped, bool aux) {
490 clearStopConflict();
491 uint32 newRoot = levels_.root - std::min(n, rootLevel());
492 if (popped && newRoot < rootLevel()) {
493 for (uint32 i = newRoot+1; i <= rootLevel(); ++i) {
494 Literal x = decision(i);
495 if (aux || !auxVar(x.var())) { popped->push_back(x); }
496 }
497 }
498 if (n) { ccInfo_.setActivity(1); }
499 levels_.root = newRoot;
500 levels_.flip = rootLevel();
501 levels_.mode = 0;
502 impliedLits_.front = 0;
503 bool tagActive = isTrue(tagLiteral());
504 // go back to new root level and re-assert still implied literals
505 undoUntil(rootLevel(), undo_pop_proj_level);
506 if (tagActive && !isTrue(tagLiteral())) {
507 removeConditional();
508 }
509 return !hasConflict();
510 }
511
clearAssumptions()512 bool Solver::clearAssumptions() {
513 return popRootLevel(rootLevel())
514 && simplify();
515 }
516
clearStopConflict()517 void Solver::clearStopConflict() {
518 if (hasStopConflict()) {
519 levels_.root = conflict_[1].rep();
520 levels_.flip = conflict_[2].rep();
521 assign_.front = conflict_[3].rep();
522 conflict_.clear();
523 }
524 }
525
setStopConflict()526 void Solver::setStopConflict() {
527 if (!hasConflict()) {
528 // we use the nogood {FALSE} to represent the unrecoverable conflict -
529 // note that {FALSE} can otherwise never be a violated nogood because
530 // TRUE is always true in every solver
531 conflict_.push_back(lit_false());
532 // remember the current root-level
533 conflict_.push_back(Literal::fromRep(rootLevel()));
534 // remember the current bt-level
535 conflict_.push_back(Literal::fromRep(backtrackLevel()));
536 // remember the current propagation queue
537 conflict_.push_back(Literal::fromRep(assign_.front));
538 }
539 // artificially increase root level -
540 // this way, the solver is prevented from resolving the conflict
541 pushRootLevel(decisionLevel());
542 }
543
copyGuidingPath(LitVec & gpOut)544 void Solver::copyGuidingPath(LitVec& gpOut) {
545 uint32 aux = rootLevel()+1;
546 gpOut.clear();
547 for (uint32 i = 1, end = rootLevel()+1; i != end; ++i) {
548 Literal x = decision(i);
549 if (!auxVar(x.var())) { gpOut.push_back(x); }
550 else if (i < aux) { aux = i; }
551 }
552 for (ImpliedList::iterator it = impliedLits_.begin(); it != impliedLits_.end(); ++it) {
553 if (it->level <= rootLevel() && (it->ante.ante().isNull() || it->level < aux) && !auxVar(it->lit.var())) {
554 gpOut.push_back(it->lit);
555 }
556 }
557 }
splittable() const558 bool Solver::splittable() const {
559 if (decisionLevel() == rootLevel() || frozenLevel(rootLevel()+1)) { return false; }
560 if (numAuxVars()) { // check if gp would contain solver local aux var
561 uint32 minAux = rootLevel() + 2;
562 for (uint32 i = 1; i != minAux; ++i) {
563 if (auxVar(decision(i).var()) && decision(i) != tag_) { return false; }
564 }
565 for (ImpliedList::iterator it = impliedLits_.begin(); it != impliedLits_.end(); ++it) {
566 if (it->ante.ante().isNull() && it->level < minAux && auxVar(it->lit.var()) && it->lit != tag_) { return false; }
567 }
568 }
569 return true;
570 }
split(LitVec & out)571 bool Solver::split(LitVec& out) {
572 if (!splittable()) { return false; }
573 copyGuidingPath(out);
574 pushRootLevel();
575 out.push_back(~decision(rootLevel()));
576 stats.addSplit();
577 return true;
578 }
579 /////////////////////////////////////////////////////////////////////////////////////////
580 // Solver: Watch management
581 ////////////////////////////////////////////////////////////////////////////////////////
numWatches(Literal p) const582 uint32 Solver::numWatches(Literal p) const {
583 assert( validVar(p.var()) );
584 if (!validWatch(p)) return 0;
585 uint32 n = static_cast<uint32>(watches_[p.id()].size());
586 if (!auxVar(p.var())){
587 n += shared_->shortImplications().numEdges(p);
588 }
589 return n;
590 }
591
hasWatch(Literal p,Constraint * c) const592 bool Solver::hasWatch(Literal p, Constraint* c) const {
593 return getWatch(p, c) != 0;
594 }
595
hasWatch(Literal p,ClauseHead * h) const596 bool Solver::hasWatch(Literal p, ClauseHead* h) const {
597 if (!validWatch(p)) return false;
598 const WatchList& pList = watches_[p.id()];
599 return !pList.empty() && std::find_if(pList.left_begin(), pList.left_end(), ClauseWatch::EqHead(h)) != pList.left_end();
600 }
601
getWatch(Literal p,Constraint * c) const602 GenericWatch* Solver::getWatch(Literal p, Constraint* c) const {
603 if (!validWatch(p)) return 0;
604 const WatchList& pList = watches_[p.id()];
605 WatchList::const_right_iterator it = std::find_if(pList.right_begin(), pList.right_end(), GenericWatch::EqConstraint(c));
606 return it != pList.right_end()
607 ? &const_cast<GenericWatch&>(*it)
608 : 0;
609 }
610
removeWatch(const Literal & p,Constraint * c)611 void Solver::removeWatch(const Literal& p, Constraint* c) {
612 if (!validWatch(p)) { return; }
613 WatchList& pList = watches_[p.id()];
614 if (!lazyRem_ || !lazyRem_->add(p, pList, c)) {
615 pList.erase_right(std::find_if(pList.right_begin(), pList.right_end(), GenericWatch::EqConstraint(c)));
616 }
617 }
618
removeWatch(const Literal & p,ClauseHead * h)619 void Solver::removeWatch(const Literal& p, ClauseHead* h) {
620 if (!validWatch(p)) { return; }
621 WatchList& pList = watches_[p.id()];
622 if (!lazyRem_ || !lazyRem_->add(p, pList, h)) {
623 pList.erase_left(std::find_if(pList.left_begin(), pList.left_end(), ClauseWatch::EqHead(h)));
624 }
625 }
626
removeUndoWatch(uint32 dl,Constraint * c)627 bool Solver::removeUndoWatch(uint32 dl, Constraint* c) {
628 assert(dl != 0 && dl <= decisionLevel() );
629 if (levels_[dl-1].undo) {
630 ConstraintDB& uList = *levels_[dl-1].undo;
631 if (!lazyRem_ || !lazyRem_->add(dl - 1, uList, c)) {
632 ConstraintDB::iterator it = std::find(uList.begin(), uList.end(), c);
633 if (it != uList.end()) {
634 *it = uList.back();
635 uList.pop_back();
636 return true;
637 }
638 }
639 }
640 return false;
641 }
destroyDB(ConstraintDB & db)642 void Solver::destroyDB(ConstraintDB& db) {
643 if (!db.empty()) {
644 Dirty dirty;
645 if (!lazyRem_) { lazyRem_ = &dirty; }
646 for (ConstraintDB::const_iterator it = db.begin(), end = db.end(); it != end; ++it) {
647 (*it)->destroy(this, true);
648 }
649 db.clear();
650 if (lazyRem_ == &dirty) {
651 lazyRem_ = 0;
652 dirty.cleanup(watches_, levels_);
653 }
654 }
655 }
656 /////////////////////////////////////////////////////////////////////////////////////////
657 // Solver: Basic DPLL-functions
658 ////////////////////////////////////////////////////////////////////////////////////////
659
660 // removes all satisfied binary and ternary clauses as well
661 // as all constraints for which Constraint::simplify returned true.
simplify()662 bool Solver::simplify() {
663 if (decisionLevel() != 0) return true;
664 if (hasConflict()) return false;
665 if (lastSimp_ != (uint32)assign_.trail.size()) {
666 uint32 old = lastSimp_;
667 if (!simplifySAT()) { return false; }
668 assert(lastSimp_ == (uint32)assign_.trail.size());
669 heuristic_->simplify(*this, old);
670 }
671 if (shufSimp_) { simplifySAT(); }
672 return true;
673 }
pushTagVar(bool pushToRoot)674 Var Solver::pushTagVar(bool pushToRoot) {
675 if (isSentinel(tag_)) { tag_ = posLit(pushAuxVar()); }
676 if (pushToRoot) { pushRoot(tag_); }
677 return tag_.var();
678 }
removeConditional()679 void Solver::removeConditional() {
680 Literal p = ~tagLiteral();
681 if (!isSentinel(p)) {
682 ConstraintDB::size_type i, j, end = learnts_.size();
683 for (i = j = 0; i != end; ++i) {
684 ClauseHead* c = learnts_[i]->clause();
685 if (!c || !c->tagged()) {
686 learnts_[j++] = learnts_[i];
687 }
688 else {
689 c->destroy(this, true);
690 }
691 }
692 learnts_.erase(learnts_.begin()+j, learnts_.end());
693 }
694 }
695
strengthenConditional()696 void Solver::strengthenConditional() {
697 Literal p = ~tagLiteral();
698 if (!isSentinel(p)) {
699 ConstraintDB::size_type i, j, end = learnts_.size();
700 for (i = j = 0; i != end; ++i) {
701 ClauseHead* c = learnts_[i]->clause();
702 if (!c || !c->tagged() || !c->strengthen(*this, p, true).second) {
703 learnts_[j++] = learnts_[i];
704 }
705 else {
706 assert((decisionLevel() == rootLevel() || !c->locked(*this)) && "Solver::strengthenConditional(): must not remove locked constraint!");
707 c->destroy(this, false);
708 }
709 }
710 learnts_.erase(learnts_.begin()+j, learnts_.end());
711 }
712 }
713
simplifySAT()714 bool Solver::simplifySAT() {
715 if (queueSize() > 0 && !propagate()) {
716 return false;
717 }
718 assert(assign_.qEmpty());
719 uint32 start = lastSimp_;
720 assign_.front = start;
721 lastSimp_ = (uint32)assign_.trail.size();
722 for (Literal p; !assign_.qEmpty(); ) {
723 p = assign_.qPop();
724 releaseVec(watches_[p.id()]);
725 releaseVec(watches_[(~p).id()]);
726 }
727 bool shuffle = shufSimp_ != 0;
728 shufSimp_ = 0;
729 if (shuffle) {
730 std::random_shuffle(constraints_.begin(), constraints_.end(), rng);
731 std::random_shuffle(learnts_.begin(), learnts_.end(), rng);
732 }
733 if (isMaster()) { shared_->simplify(start, shuffle); }
734 else { simplifyDB(*this, constraints_, shuffle); }
735 simplifyDB(*this, learnts_, shuffle);
736 FOR_EACH_POST(x, postHead_) {
737 if (x->simplify(*this, shuffle)) {
738 post_.remove(x);
739 x->destroy(this, false);
740 }
741 }
742 if (enum_ && enum_->simplify(*this, shuffle)) {
743 enum_->destroy(this, false);
744 enum_ = 0;
745 }
746 return true;
747 }
748
setConflict(Literal p,const Antecedent & a,uint32 data)749 void Solver::setConflict(Literal p, const Antecedent& a, uint32 data) {
750 ++stats.conflicts;
751 conflict_.push_back(~p);
752 if (searchMode() != SolverStrategies::no_learning && !a.isNull()) {
753 if (data == UINT32_MAX) {
754 a.reason(*this, p, conflict_);
755 }
756 else {
757 // temporarily replace old data with new data
758 uint32 saved = assign_.data(p.var());
759 assign_.setData(p.var(), data);
760 // extract conflict using new data
761 a.reason(*this, p, conflict_);
762 // restore old data
763 assign_.setData(p.var(), saved);
764 }
765 }
766 }
force(const ImpliedLiteral & p)767 bool Solver::force(const ImpliedLiteral& p) {
768 // Already implied?
769 if (isTrue(p.lit)) {
770 if (level(p.lit.var()) <= p.level) { return true; }
771 if (ImpliedLiteral* x = impliedLits_.find(p.lit)) {
772 if (x->level > p.level) {
773 *x = p;
774 setReason(p.lit, p.ante.ante(), p.ante.data());
775 }
776 return true;
777 }
778 }
779 if (undoUntil(p.level) != p.level) {
780 // Logically the implication is on level p.level.
781 // Store enough information so that p can be re-assigned once we backtrack.
782 impliedLits_.add(decisionLevel(), p);
783 }
784 return (isTrue(p.lit) && setReason(p.lit, p.ante.ante(), p.ante.data())) || force(p.lit, p.ante.ante(), p.ante.data());
785 }
786
assume(const Literal & p)787 bool Solver::assume(const Literal& p) {
788 if (value(p.var()) == value_free) {
789 assert(decisionLevel() != assign_.maxLevel());
790 ++stats.choices;
791 levels_.push_back(DLevel(numAssignedVars(), 0));
792 return assign_.assign(p, decisionLevel(), Antecedent());
793 }
794 return isTrue(p);
795 }
796
cancelPropagation()797 void Solver::cancelPropagation() {
798 assign_.qReset();
799 for (PostPropagator* r = *postHead_; r; r = r->next) { r->reset(); }
800 }
801
propagate()802 bool Solver::propagate() {
803 if (unitPropagate() && postPropagate(0)) {
804 assert(queueSize() == 0);
805 return true;
806 }
807 cancelPropagation();
808 return false;
809 }
810
propagateUntil(PostPropagator * p)811 bool Solver::propagateUntil(PostPropagator* p) {
812 assert((!p || *postHead_) && "OP not allowed during init!");
813 return unitPropagate() && (p == *postHead_ || postPropagate(p));
814 }
815
propagate(Solver & s,Literal p,uint32 &)816 Constraint::PropResult ClauseHead::propagate(Solver& s, Literal p, uint32&) {
817 Literal* head = head_;
818 uint32 wLit = (head[1] == ~p); // pos of false watched literal
819 if (s.isTrue(head[1-wLit])) {
820 return Constraint::PropResult(true, true);
821 }
822 else if (!s.isFalse(head[2])) {
823 assert(!isSentinel(head[2]) && "Invalid ClauseHead!");
824 head[wLit] = head[2];
825 head[2] = ~p;
826 s.addWatch(~head[wLit], ClauseWatch(this));
827 return Constraint::PropResult(true, false);
828 }
829 else if (updateWatch(s, wLit)) {
830 assert(!s.isFalse(head_[wLit]));
831 s.addWatch(~head[wLit], ClauseWatch(this));
832 return Constraint::PropResult(true, false);
833 }
834 return PropResult(s.force(head_[1^wLit], this), true);
835 }
836
unitPropagate()837 bool Solver::unitPropagate() {
838 assert(!hasConflict());
839 Literal p, q, r;
840 uint32 idx, ignore, DL = decisionLevel();
841 const ShortImplicationsGraph& btig = shared_->shortImplications();
842 const uint32 maxIdx = btig.size();
843 while ( !assign_.qEmpty() ) {
844 p = assign_.qPop();
845 idx = p.id();
846 WatchList& wl = watches_[idx];
847 // first: short clause BCP
848 if (idx < maxIdx && !btig.propagate(*this, p)) {
849 return false;
850 }
851 // second: clause BCP
852 if (wl.left_size() != 0) {
853 WatchList::left_iterator it, end, j = wl.left_begin();
854 Constraint::PropResult res;
855 for (it = wl.left_begin(), end = wl.left_end(); it != end; ) {
856 ClauseWatch& w = *it++;
857 res = w.head->ClauseHead::propagate(*this, p, ignore);
858 if (res.keepWatch) {
859 *j++ = w;
860 }
861 if (!res.ok) {
862 wl.shrink_left(std::copy(it, end, j));
863 return false;
864 }
865 }
866 wl.shrink_left(j);
867 }
868 // third: general constraint BCP
869 if (wl.right_size() != 0) {
870 WatchList::right_iterator it, end, j = wl.right_begin();
871 Constraint::PropResult res;
872 for (it = wl.right_begin(), end = wl.right_end(); it != end; ) {
873 GenericWatch& w = *it++;
874 res = w.propagate(*this, p);
875 if (res.keepWatch) {
876 *j++ = w;
877 }
878 if (!res.ok) {
879 wl.shrink_right(std::copy(it, end, j));
880 return false;
881 }
882 }
883 wl.shrink_right(j);
884 }
885 }
886 return DL || assign_.markUnits();
887 }
888
postPropagate(PostPropagator * stop)889 bool Solver::postPropagate(PostPropagator* stop) {
890 for (PostPropagator** r = postHead_, *t; *r != stop;) {
891 t = *r;
892 if (!t->propagateFixpoint(*this, stop)) { return false; }
893 assert(queueSize() == 0);
894 if (t == *r) { r = &t->next; }
895 // else: t was removed during propagate
896 }
897 return true;
898 }
899
test(Literal p,PostPropagator * c)900 bool Solver::test(Literal p, PostPropagator* c) {
901 assert(value(p.var()) == value_free && !hasConflict());
902 assume(p); --stats.choices;
903 uint32 pLevel = decisionLevel();
904 freezeLevel(pLevel); // can't split-off this level
905 if (propagateUntil(c)) {
906 assert(decisionLevel() == pLevel && "Invalid PostPropagators");
907 if (c) c->undoLevel(*this);
908 undoUntil(pLevel-1);
909 return true;
910 }
911 assert(decisionLevel() == pLevel && "Invalid PostPropagators");
912 unfreezeLevel(pLevel);
913 cancelPropagation();
914 return false;
915 }
916
resolveConflict()917 bool Solver::resolveConflict() {
918 assert(hasConflict());
919 if (decisionLevel() > rootLevel()) {
920 if (decisionLevel() != backtrackLevel() && searchMode() != SolverStrategies::no_learning) {
921 uint32 uipLevel = analyzeConflict();
922 stats.addConflict(decisionLevel(), uipLevel, backtrackLevel(), ccInfo_.lbd());
923 if (shared_->reportMode()) {
924 sharedContext()->report(NewConflictEvent(*this, cc_, ccInfo_));
925 }
926 undoUntil( uipLevel );
927 return ClauseCreator::create(*this, cc_, ClauseCreator::clause_no_prepare, ccInfo_);
928 }
929 else {
930 return backtrack();
931 }
932 }
933 return false;
934 }
935
backtrack()936 bool Solver::backtrack() {
937 Literal lastChoiceInverted;
938 do {
939 if (decisionLevel() == rootLevel()) {
940 setStopConflict();
941 return false;
942 }
943 lastChoiceInverted = ~decision(decisionLevel());
944 undoUntil(decisionLevel() - 1, undo_pop_proj_level);
945 setBacktrackLevel(decisionLevel(), undo_pop_bt_level);
946 } while (hasConflict() || !force(lastChoiceInverted, 0));
947 // remember flipped literal for copyGuidingPath()
948 impliedLits_.add(decisionLevel(), ImpliedLiteral(lastChoiceInverted, decisionLevel(), 0));
949 return true;
950 }
951
assign(Solver & s)952 bool ImpliedList::assign(Solver& s) {
953 assert(front <= lits.size());
954 bool ok = !s.hasConflict();
955 const uint32 DL = s.decisionLevel();
956 VecType::iterator j = lits.begin() + front;
957 for (VecType::iterator it = j, end = lits.end(); it != end; ++it) {
958 if(it->level <= DL) {
959 ok = ok && s.force(it->lit, it->ante.ante(), it->ante.data());
960 if (it->level < DL || it->ante.ante().isNull()) { *j++ = *it; }
961 }
962 }
963 lits.erase(j, lits.end());
964 level = DL * uint32(!lits.empty());
965 front = level > s.rootLevel() ? front : sizeVec(lits);
966 return ok;
967 }
isUndoLevel() const968 bool Solver::isUndoLevel() const {
969 return decisionLevel() > backtrackLevel();
970 }
undoUntilImpl(uint32 level,bool forceSave)971 uint32 Solver::undoUntilImpl(uint32 level, bool forceSave) {
972 level = std::max( level, backtrackLevel() );
973 if (level >= decisionLevel()) { return decisionLevel(); }
974 uint32& n = (levels_.jump = decisionLevel() - level);
975 bool sp = forceSave || (strategy_.saveProgress > 0 && ((uint32)strategy_.saveProgress) <= n);
976 bool ok = conflict_.empty() && levels_.back().freeze == 0;
977 conflict_.clear();
978 heuristic_->undoUntil( *this, levels_[level].trailPos);
979 undoLevel(sp && ok);
980 while (--n) { undoLevel(sp); }
981 return level;
982 }
undoUntil(uint32 level,uint32 mode)983 uint32 Solver::undoUntil(uint32 level, uint32 mode) {
984 assert(backtrackLevel() >= rootLevel());
985 if (level < backtrackLevel() && mode >= levels_.mode) {
986 levels_.flip = std::max(rootLevel(), level);
987 }
988 level = undoUntilImpl(level, (mode & undo_save_phases) != 0);
989 if (impliedLits_.active(level)) {
990 impliedLits_.assign(*this);
991 }
992 return level;
993 }
estimateBCP(const Literal & p,int rd) const994 uint32 Solver::estimateBCP(const Literal& p, int rd) const {
995 if (value(p.var()) != value_free) return 0;
996 LitVec::size_type first = assign_.assigned();
997 LitVec::size_type i = first;
998 Solver& self = const_cast<Solver&>(*this);
999 self.assign_.setValue(p.var(), trueValue(p));
1000 self.assign_.trail.push_back(p);
1001 const ShortImplicationsGraph& btig = shared_->shortImplications();
1002 const uint32 maxIdx = btig.size();
1003 do {
1004 Literal x = assign_.trail[i++];
1005 if (x.id() < maxIdx && !btig.propagateBin(self.assign_, x, 0)) {
1006 break;
1007 }
1008 } while (i < assign_.assigned() && rd-- != 0);
1009 i = assign_.assigned()-first;
1010 while (self.assign_.assigned() != first) {
1011 self.assign_.undoLast();
1012 }
1013 return (uint32)i;
1014 }
1015
inDegree(WeightLitVec & out)1016 uint32 Solver::inDegree(WeightLitVec& out) {
1017 if (decisionLevel() == 0) { return 1; }
1018 assert(!hasConflict());
1019 out.reserve((numAssignedVars()-levelStart(1))/10);
1020 uint32 maxIn = 1;
1021 uint32 i = sizeVec(assign_.trail), stop = levelStart(1);
1022 for (LitVec temp; i-- != stop; ) {
1023 Literal x = assign_.trail[i];
1024 uint32 xLev = assign_.level(x.var());
1025 uint32 xIn = 0;
1026 Antecedent xAnte = assign_.reason(x.var());
1027 if (!xAnte.isNull() && xAnte.type() != Antecedent::Binary) {
1028 xAnte.reason(*this, x, temp);
1029 for (LitVec::const_iterator it = temp.begin(); it != temp.end(); ++it) {
1030 xIn += level(it->var()) != xLev;
1031 }
1032 if (xIn) {
1033 out.push_back(WeightLiteral(x, xIn));
1034 maxIn = std::max(xIn, maxIn);
1035 }
1036 temp.clear();
1037 }
1038 }
1039 return maxIn;
1040 }
1041 /////////////////////////////////////////////////////////////////////////////////////////
1042 // Solver: Private helper functions
1043 ////////////////////////////////////////////////////////////////////////////////////////
allocUndo(Constraint * c)1044 Solver::ConstraintDB* Solver::allocUndo(Constraint* c) {
1045 if (undoHead_ == 0) {
1046 return new ConstraintDB(1, c);
1047 }
1048 assert(undoHead_->size() == 1);
1049 ConstraintDB* r = undoHead_;
1050 undoHead_ = (ConstraintDB*)undoHead_->front();
1051 r->clear();
1052 r->push_back(c);
1053 return r;
1054 }
undoFree(ConstraintDB * x)1055 void Solver::undoFree(ConstraintDB* x) {
1056 // maintain a single-linked list of undo lists
1057 x->clear();
1058 x->push_back((Constraint*)undoHead_);
1059 undoHead_ = x;
1060 }
1061 // removes the current decision level
undoLevel(bool sp)1062 void Solver::undoLevel(bool sp) {
1063 assert(decisionLevel() != 0 && levels_.back().trailPos != assign_.trail.size() && "Decision Level must not be empty");
1064 assign_.undoTrail(levels_.back().trailPos, sp);
1065 if (levels_.back().undo) {
1066 const ConstraintDB& undoList = *levels_.back().undo;
1067 for (ConstraintDB::size_type i = 0, end = undoList.size(); i != end; ++i) {
1068 undoList[i]->undoLevel(*this);
1069 }
1070 undoFree(levels_.back().undo);
1071 }
1072 levels_.pop_back();
1073 }
1074
clause(const Antecedent & ante)1075 inline ClauseHead* clause(const Antecedent& ante) {
1076 return ante.isNull() || ante.type() != Antecedent::Generic ? 0 : ante.constraint()->clause();
1077 }
1078
resolveToFlagged(const LitVec & in,uint8 vf,LitVec & out,uint32 & outLbd) const1079 bool Solver::resolveToFlagged(const LitVec& in, uint8 vf, LitVec& out, uint32& outLbd) const {
1080 return const_cast<Solver&>(*this).resolveToFlagged(in, vf, out, outLbd);
1081 }
resolveToFlagged(const LitVec & in,const uint8 vf,LitVec & out,uint32 & outLbd)1082 bool Solver::resolveToFlagged(const LitVec& in, const uint8 vf, LitVec& out, uint32& outLbd) {
1083 const LitVec& trail = assign_.trail;
1084 const LitVec* rhs = ∈
1085 LitVec temp; out.clear();
1086 bool ok = true, first = true;
1087 for (LitVec::size_type tp = trail.size(), resolve = 0;; first = false) {
1088 Literal p; Var v;
1089 for (LitVec::const_iterator it = rhs->begin(), end = rhs->end(); it != end; ++it) {
1090 p = *it ^ first; v = p.var();
1091 if (!seen(v)) {
1092 markSeen(v);
1093 if (varInfo(v).hasAll(vf)) { markLevel(level(v)); out.push_back(~p); }
1094 else if (!reason(p).isNull()) { ++resolve; }
1095 else { clearSeen(v); ok = false; break; }
1096 }
1097 }
1098 if (resolve-- == 0) { break; }
1099 // find next literal to resolve
1100 while (!seen(trail[--tp]) || varInfo(trail[tp].var()).hasAll(vf)) { ; }
1101 clearSeen((p = trail[tp]).var());
1102 reason(p, temp);
1103 rhs = &temp;
1104 }
1105 LitVec::size_type outSize = out.size();
1106 if (ok && !first) {
1107 const uint32 ccAct = strategy_.ccMinKeepAct;
1108 const uint32 antes = SolverStrategies::all_antes;
1109 strategy_.ccMinKeepAct = 1;
1110 if (ccMin_) { ccMinRecurseInit(*ccMin_); }
1111 for (LitVec::size_type i = 0; i != outSize;) {
1112 if (!ccRemovable(~out[i], antes, ccMin_)) { ++i; }
1113 else {
1114 std::swap(out[i], out[--outSize]);
1115 }
1116 }
1117 strategy_.ccMinKeepAct = ccAct;
1118 }
1119 POTASSCO_ASSERT(!ok || outSize != 0, "Invalid empty clause - was %u!\n", out.size());
1120 outLbd = 0;
1121 for (uint32 i = 0, dl, root = 0; i != outSize; ++i) {
1122 Var v = out[i].var();
1123 dl = level(v);
1124 clearSeen(v);
1125 if (dl && hasLevel(dl)) {
1126 unmarkLevel(dl);
1127 outLbd += (dl > rootLevel()) || (++root == 1);
1128 }
1129 }
1130 for (Var v; outSize != out.size(); out.pop_back()) {
1131 clearSeen(v = out.back().var());
1132 unmarkLevel(level(v));
1133 }
1134 return ok;
1135 }
resolveToCore(LitVec & out)1136 void Solver::resolveToCore(LitVec& out) {
1137 POTASSCO_REQUIRE(hasConflict() && !hasStopConflict(), "Function requires valid conflict");
1138 cc_.clear();
1139 std::swap(cc_, conflict_);
1140 if (searchMode() == SolverStrategies::no_learning) {
1141 for (uint32 i = 1, end = decisionLevel() + 1; i != end; ++i) { cc_.push_back(decision(i)); }
1142 }
1143 const LitVec& trail = assign_.trail;
1144 const LitVec* r = &cc_;
1145 // resolve all-last uip
1146 for (uint32 marked = 0, tPos = (uint32)trail.size();; r = &conflict_) {
1147 for (LitVec::const_iterator it = r->begin(), end = r->end(); it != end; ++it) {
1148 if (!seen(it->var())) {
1149 assert(level(it->var()) <= decisionLevel());
1150 markSeen(it->var());
1151 ++marked;
1152 }
1153 }
1154 if (marked-- == 0) { break; }
1155 // search for the last marked literal
1156 while (!seen(trail[--tPos].var())) { ; }
1157 Literal p = trail[tPos];
1158 uint32 dl = level(p.var());
1159 assert(dl);
1160 clearSeen(p.var());
1161 conflict_.clear();
1162 if (!reason(p).isNull()) { reason(p).reason(*this, p, conflict_); }
1163 else if (p == decision(dl)) { out.push_back(p); }
1164 }
1165 // restore original conflict
1166 std::swap(cc_, conflict_);
1167 }
1168
1169 // computes the First-UIP clause and stores it in cc_, where cc_[0] is the asserting literal (inverted UIP)
1170 // and cc_[1] is a literal from the asserting level (if > 0)
1171 // RETURN: asserting level of the derived conflict clause
analyzeConflict()1172 uint32 Solver::analyzeConflict() {
1173 // must be called here, because we unassign vars during analyzeConflict
1174 heuristic_->undoUntil( *this, levels_.back().trailPos );
1175 uint32 onLevel = 0; // number of literals from the current DL in resolvent
1176 uint32 resSize = 0; // size of current resolvent
1177 Literal p; // literal to be resolved out next
1178 cc_.assign(1, p); // will later be replaced with asserting literal
1179 Antecedent lhs, rhs, last; // resolve operands
1180 const bool doOtfs = strategy_.otfs > 0;
1181 for (bumpAct_.clear();;) {
1182 uint32 lhsSize = resSize;
1183 uint32 rhsSize = 0;
1184 heuristic_->updateReason(*this, conflict_, p);
1185 for (LitVec::size_type i = 0; i != conflict_.size(); ++i) {
1186 Literal& q = conflict_[i];
1187 uint32 cl = level(q.var());
1188 rhsSize += (cl != 0);
1189 if (!seen(q.var())) {
1190 ++resSize;
1191 assert(isTrue(q) && "Invalid literal in reason set!");
1192 assert(cl > 0 && "Top-Level implication not marked!");
1193 markSeen(q.var());
1194 if (cl == decisionLevel()) {
1195 ++onLevel;
1196 }
1197 else {
1198 cc_.push_back(~q);
1199 markLevel(cl);
1200 }
1201 }
1202 }
1203 if (resSize != lhsSize) { lhs = 0; }
1204 if (rhsSize != resSize) { rhs = 0; }
1205 if (doOtfs && (!rhs.isNull() || !lhs.isNull())) {
1206 // resolvent subsumes rhs and possibly also lhs
1207 otfs(lhs, rhs, p, onLevel == 1);
1208 }
1209 assert(onLevel > 0 && "CONFLICT MUST BE ANALYZED ON CONFLICT LEVEL!");
1210 // search for the last assigned literal that needs to be analyzed...
1211 while (!seen(assign_.last().var())) {
1212 assign_.undoLast();
1213 }
1214 p = assign_.last();
1215 rhs = reason(p);
1216 clearSeen(p.var());
1217 if (--onLevel == 0) {
1218 break;
1219 }
1220 --resSize; // p will be resolved out next
1221 last = rhs;
1222 reason(p, conflict_);
1223 }
1224 cc_[0] = ~p; // store the 1-UIP
1225 assert(decisionLevel() == level(cc_[0].var()));
1226 ClauseHead* lastRes = 0;
1227 if (strategy_.otfs > 1 || !lhs.isNull()) {
1228 if (!lhs.isNull()) {
1229 lastRes = clause(lhs);
1230 }
1231 else if (cc_.size() <= (conflict_.size()+1)) {
1232 lastRes = clause(last);
1233 }
1234 }
1235 if (strategy_.bumpVarAct && reason(p).learnt()) {
1236 bumpAct_.push_back(WeightLiteral(p, reason(p).constraint()->activity().lbd()));
1237 }
1238 return simplifyConflictClause(cc_, ccInfo_, lastRes);
1239 }
1240
otfs(Antecedent & lhs,const Antecedent & rhs,Literal p,bool final)1241 void Solver::otfs(Antecedent& lhs, const Antecedent& rhs, Literal p, bool final) {
1242 ClauseHead* cLhs = clause(lhs), *cRhs = clause(rhs);
1243 ClauseHead::BoolPair x;
1244 if (cLhs) {
1245 x = cLhs->strengthen(*this, ~p, !final);
1246 if (!x.first || x.second) {
1247 cLhs = !x.first ? 0 : otfsRemove(cLhs, 0);
1248 }
1249 }
1250 lhs = cLhs;
1251 if (cRhs) {
1252 x = cRhs->strengthen(*this, p, !final);
1253 if (!x.first || (x.second && otfsRemove(cRhs, 0) == 0)) {
1254 if (x.first && reason(p) == cRhs) { setReason(p, 0); }
1255 cRhs = 0;
1256 }
1257 if (cLhs && cRhs) {
1258 // lhs and rhs are now equal - only one of them is needed
1259 if (!cLhs->learnt()) {
1260 std::swap(cLhs, cRhs);
1261 }
1262 otfsRemove(cLhs, 0);
1263 }
1264 lhs = cRhs;
1265 }
1266 }
1267
otfsRemove(ClauseHead * c,const LitVec * newC)1268 ClauseHead* Solver::otfsRemove(ClauseHead* c, const LitVec* newC) {
1269 bool remStatic = !newC || (newC->size() <= 3 && shared_->allowImplicit(Constraint_t::Conflict));
1270 if (c->learnt() || remStatic) {
1271 ConstraintDB& db = (c->learnt() ? learnts_ : constraints_);
1272 ConstraintDB::iterator it;
1273 if ((it = std::find(db.begin(), db.end(), c)) != db.end()) {
1274 if (isMaster() && &db == &constraints_) {
1275 shared_->removeConstraint(static_cast<uint32>(it - db.begin()), true);
1276 }
1277 else {
1278 db.erase(it);
1279 c->destroy(this, true);
1280 }
1281 c = 0;
1282 }
1283 }
1284 return c;
1285 }
1286
1287 // minimizes the conflict clause in cc w.r.t selected strategies.
1288 // PRE:
1289 // - cc is a valid conflict clause and cc[0] is the UIP-literal
1290 // - all literals in cc except cc[0] are marked
1291 // - all decision levels of literals in cc are marked
1292 // - rhs is 0 or a clause that might be subsumed by cc
1293 // RETURN: finalizeConflictClause(cc, info)
simplifyConflictClause(LitVec & cc,ConstraintInfo & info,ClauseHead * rhs)1294 uint32 Solver::simplifyConflictClause(LitVec& cc, ConstraintInfo& info, ClauseHead* rhs) {
1295 // 1. remove redundant literals from conflict clause
1296 temp_.clear();
1297 uint32 onAssert = ccMinimize(cc, temp_, strategy_.ccMinAntes, ccMin_);
1298 uint32 jl = cc.size() > 1 ? level(cc[1].var()) : 0;
1299 // clear seen flags of removed literals - keep levels marked
1300 for (LitVec::size_type x = 0, stop = temp_.size(); x != stop; ++x) {
1301 clearSeen(temp_[x].var());
1302 }
1303 // 2. check for inverse arcs
1304 if (onAssert == 1 && strategy_.reverseArcs > 0) {
1305 uint32 maxN = (uint32)strategy_.reverseArcs;
1306 if (maxN > 2) maxN = UINT32_MAX;
1307 else if (maxN > 1) maxN = static_cast<uint32>(cc.size() / 2);
1308 markSeen(cc[0].var());
1309 Antecedent ante = ccHasReverseArc(cc[1], jl, maxN);
1310 if (!ante.isNull()) {
1311 // resolve with inverse arc
1312 conflict_.clear();
1313 ante.reason(*this, ~cc[1], conflict_);
1314 ccResolve(cc, 1, conflict_);
1315 }
1316 clearSeen(cc[0].var());
1317 }
1318 // 3. check if final clause subsumes rhs
1319 if (rhs) {
1320 conflict_.clear();
1321 rhs->toLits(conflict_);
1322 uint32 open = (uint32)cc.size();
1323 markSeen(cc[0].var());
1324 for (LitVec::const_iterator it = conflict_.begin(), end = conflict_.end(); it != end && open; ++it) {
1325 // NOTE: at this point the DB might not be fully simplified,
1326 // e.g. because of mt or lookahead, hence we must explicitly
1327 // check for literals assigned on DL 0
1328 open -= level(it->var()) > 0 && seen(it->var());
1329 }
1330 rhs = open ? 0 : otfsRemove(rhs, &cc);
1331 if (rhs) { // rhs is subsumed by cc but could not be removed.
1332 // TODO: we could reuse rhs instead of learning cc
1333 // but this would complicate the calling code.
1334 ClauseHead::BoolPair r(true, false);
1335 if (cc_.size() < conflict_.size()) {
1336 // For now, we only try to strengthen rhs.
1337 for (LitVec::const_iterator it = conflict_.begin(), end = conflict_.end(); it != end && r.first; ++it) {
1338 if (!seen(it->var()) || level(it->var()) == 0) {
1339 r = rhs->strengthen(*this, *it, false);
1340 }
1341 }
1342 if (!r.first) { rhs = 0; }
1343 }
1344 }
1345 clearSeen(cc[0].var());
1346 }
1347 // 4. finalize
1348 uint32 repMode = cc.size() < std::max(strategy_.compress, decisionLevel()+1) ? 0 : strategy_.ccRepMode;
1349 jl = finalizeConflictClause(cc, info, repMode);
1350 // 5. bump vars implied by learnt constraints with small lbd
1351 if (!bumpAct_.empty()) {
1352 WeightLitVec::iterator j = bumpAct_.begin();
1353 weight_t newLbd = info.lbd();
1354 for (WeightLitVec::iterator it = bumpAct_.begin(), end = bumpAct_.end(); it != end; ++it) {
1355 if (it->second < newLbd) {
1356 it->second = 1 + (it->second <= 2);
1357 *j++ = *it;
1358 }
1359 }
1360 bumpAct_.erase(j, bumpAct_.end());
1361 heuristic_->bump(*this, bumpAct_, 1.0);
1362 }
1363 bumpAct_.clear();
1364 // 6. clear level flags of redundant literals
1365 for (LitVec::size_type x = 0, stop = temp_.size(); x != stop; ++x) {
1366 unmarkLevel(level(temp_[x].var()));
1367 }
1368 temp_.clear();
1369 return jl;
1370 }
1371
1372 // conflict clause minimization
1373 // PRE:
1374 // - cc is an asserting clause and cc[0] is the asserting literal
1375 // - all literals in cc are marked as seen
1376 // - if ccMin != 0, all decision levels of literals in cc are marked
1377 // POST:
1378 // - redundant literals were added to removed
1379 // - if (cc.size() > 1): cc[1] is a literal from the asserting level
1380 // RETURN
1381 // - the number of literals from the asserting level
ccMinimize(LitVec & cc,LitVec & removed,uint32 antes,CCMinRecursive * ccMin)1382 uint32 Solver::ccMinimize(LitVec& cc, LitVec& removed, uint32 antes, CCMinRecursive* ccMin) {
1383 if (ccMin) { ccMinRecurseInit(*ccMin); }
1384 // skip the asserting literal
1385 LitVec::size_type j = 1;
1386 uint32 assertLevel = 0;
1387 uint32 assertPos = 1;
1388 uint32 onAssert = 0;
1389 uint32 varLevel = 0;
1390 for (LitVec::size_type i = 1; i != cc.size(); ++i) {
1391 if (antes == SolverStrategies::no_antes || !ccRemovable(~cc[i], antes, ccMin)) {
1392 if ( (varLevel = level(cc[i].var())) > assertLevel ) {
1393 assertLevel = varLevel;
1394 assertPos = static_cast<uint32>(j);
1395 onAssert = 0;
1396 }
1397 onAssert += (varLevel == assertLevel);
1398 cc[j++] = cc[i];
1399 }
1400 else {
1401 removed.push_back(cc[i]);
1402 }
1403 }
1404 cc.erase(cc.begin()+j, cc.end());
1405 if (assertPos != 1) {
1406 std::swap(cc[1], cc[assertPos]);
1407 }
1408 return onAssert;
1409 }
ccMinRecurseInit(CCMinRecursive & ccMin)1410 void Solver::ccMinRecurseInit(CCMinRecursive& ccMin) {
1411 ccMin.open = incEpoch(numVars() + 1, 2) - 2;
1412 }
ccMinRecurse(CCMinRecursive & ccMin,Literal p) const1413 bool Solver::ccMinRecurse(CCMinRecursive& ccMin, Literal p) const {
1414 CCMinRecursive::State st = ccMin.decodeState(epoch_[p.var()]);
1415 if (st == CCMinRecursive::state_poison) { return false; }
1416 if (st == CCMinRecursive::state_open) { ccMin.push(p.unflag()); }
1417 return true;
1418 }
1419
1420 // returns true if p is redundant in current conflict clause
ccRemovable(Literal p,uint32 antes,CCMinRecursive * ccMin)1421 bool Solver::ccRemovable(Literal p, uint32 antes, CCMinRecursive* ccMin) {
1422 const Antecedent& ante = reason(p);
1423 if (ante.isNull() || !(antes <= (uint32)ante.type())) {
1424 return false;
1425 }
1426 if (!ccMin) { return ante.minimize(*this, p, 0); }
1427 // recursive minimization
1428 assert(ccMin->todo.empty());
1429 CCMinRecursive::State dfsState = CCMinRecursive::state_removable;
1430 ccMin->push(p.unflag());
1431 for (Literal x;; ) {
1432 x = ccMin->pop();
1433 assert(!seen(x.var()) || x == p);
1434 if (x.flagged()) {
1435 if (x == p) return dfsState == CCMinRecursive::state_removable;
1436 epoch_[x.var()] = ccMin->encodeState(dfsState);
1437 }
1438 else if (dfsState != CCMinRecursive::state_poison) {
1439 CCMinRecursive::State temp = ccMin->decodeState(epoch_[x.var()]);
1440 if (temp == CCMinRecursive::state_open) {
1441 assert(value(x.var()) != value_free && hasLevel(level(x.var())));
1442 ccMin->push(x.flag());
1443 const Antecedent& next = reason(x);
1444 if (next.isNull() || !(antes <= (uint32)next.type()) || !next.minimize(*this, x, ccMin)) {
1445 dfsState = CCMinRecursive::state_poison;
1446 }
1447 }
1448 else if (temp == CCMinRecursive::state_poison) {
1449 dfsState = temp;
1450 }
1451 }
1452 }
1453 }
1454
1455 // checks whether there is a valid "inverse arc" for the given literal p that can be used
1456 // to resolve p out of the current conflict clause
1457 // PRE:
1458 // - all literals in the current conflict clause are marked
1459 // - p is a literal of the current conflict clause and level(p) == maxLevel
1460 // RETURN
1461 // - An antecedent that is an "inverse arc" for p or null if no such antecedent exists.
ccHasReverseArc(Literal p,uint32 maxLevel,uint32 maxNew)1462 Antecedent Solver::ccHasReverseArc(Literal p, uint32 maxLevel, uint32 maxNew) {
1463 assert(seen(p.var()) && isFalse(p) && level(p.var()) == maxLevel);
1464 const ShortImplicationsGraph& btig = shared_->shortImplications();
1465 Antecedent ante;
1466 if (p.id() < btig.size() && btig.reverseArc(*this, p, maxLevel, ante)) { return ante; }
1467 WatchList& wl = watches_[p.id()];
1468 for (WatchList::left_iterator it = wl.left_begin(), end = wl.left_end(); it != end; ++it) {
1469 if (it->head->isReverseReason(*this, ~p, maxLevel, maxNew)) {
1470 return it->head;
1471 }
1472 }
1473 return ante;
1474 }
1475
1476 // removes cc[pos] by resolving cc with reason
ccResolve(LitVec & cc,uint32 pos,const LitVec & reason)1477 void Solver::ccResolve(LitVec& cc, uint32 pos, const LitVec& reason) {
1478 heuristic_->updateReason(*this, reason, cc[pos]);
1479 Literal x;
1480 for (LitVec::size_type i = 0; i != reason.size(); ++i) {
1481 x = reason[i];
1482 assert(isTrue(x));
1483 if (!seen(x.var())) {
1484 markLevel(level(x.var()));
1485 cc.push_back(~x);
1486 }
1487 }
1488 clearSeen(cc[pos].var());
1489 unmarkLevel(level(cc[pos].var()));
1490 cc[pos] = cc.back();
1491 cc.pop_back();
1492 }
1493
1494 // computes asserting level and lbd of cc and clears flags.
1495 // POST:
1496 // - literals and decision levels in cc are no longer marked
1497 // - if cc.size() > 1: cc[1] is a literal from the asserting level
1498 // RETURN: asserting level of conflict clause.
finalizeConflictClause(LitVec & cc,ConstraintInfo & info,uint32 ccRepMode)1499 uint32 Solver::finalizeConflictClause(LitVec& cc, ConstraintInfo& info, uint32 ccRepMode) {
1500 // 2. clear flags and compute lbd
1501 uint32 lbd = 1;
1502 uint32 onRoot = 0;
1503 uint32 varLevel = 0;
1504 uint32 assertLevel = 0;
1505 uint32 assertPos = 1;
1506 uint32 maxVar = cc[0].var();
1507 Literal tagLit = ~tagLiteral();
1508 bool tagged = false;
1509 for (LitVec::size_type i = 1; i != cc.size(); ++i) {
1510 Var v = cc[i].var();
1511 clearSeen(v);
1512 if (cc[i] == tagLit) { tagged = true; }
1513 if (v > maxVar) { maxVar = v; }
1514 if ( (varLevel = level(v)) > assertLevel ) {
1515 assertLevel = varLevel;
1516 assertPos = static_cast<uint32>(i);
1517 }
1518 if (hasLevel(varLevel)) {
1519 unmarkLevel(varLevel);
1520 lbd += (varLevel > rootLevel()) || (++onRoot == 1);
1521 }
1522 }
1523 if (assertPos != 1) { std::swap(cc[1], cc[assertPos]); }
1524 if (ccRepMode == SolverStrategies::cc_rep_dynamic) {
1525 ccRepMode = double(lbd)/double(decisionLevel()) > .66 ? SolverStrategies::cc_rep_decision : SolverStrategies::cc_rep_uip;
1526 }
1527 if (ccRepMode) {
1528 maxVar = cc[0].var(), tagged = false, lbd = 1;
1529 if (ccRepMode == SolverStrategies::cc_rep_decision) {
1530 // replace cc with decision sequence
1531 cc.resize(assertLevel+1);
1532 for (uint32 i = assertLevel; i;){
1533 Literal x = ~decision(i--);
1534 cc[lbd++] = x;
1535 if (x == tagLit) { tagged = true; }
1536 if (x.var() > maxVar){ maxVar = x.var(); }
1537 }
1538 }
1539 else {
1540 // replace cc with all uip clause
1541 uint32 marked = sizeVec(cc) - 1;
1542 while (cc.size() > 1) { markSeen(~cc.back()); cc.pop_back(); }
1543 for (LitVec::const_iterator tr = assign_.trail.end(), next, stop; marked;) {
1544 while (!seen(*--tr)) { ; }
1545 bool n = --marked != 0 && !reason(*tr).isNull();
1546 clearSeen(tr->var());
1547 if (n) { for (next = tr, stop = assign_.trail.begin() + levelStart(level(tr->var())); next-- != stop && !seen(*next);) { ; } }
1548 if (!n || level(next->var()) != level(tr->var())) {
1549 cc.push_back(~*tr);
1550 if (tr->var() == tagLit.var()){ tagged = true; }
1551 if (tr->var() > maxVar) { maxVar = tr->var(); }
1552 }
1553 else {
1554 for (reason(*tr, conflict_); !conflict_.empty(); conflict_.pop_back()) {
1555 if (!seen(conflict_.back())) { ++marked; markSeen(conflict_.back()); }
1556 }
1557 }
1558 }
1559 lbd = sizeVec(cc);
1560 }
1561 }
1562 info.setScore(makeScore(ccInfo_.activity(), lbd));
1563 info.setTagged(tagged);
1564 info.setAux(auxVar(maxVar));
1565 return assertLevel;
1566 }
1567
1568 // (inefficient) default implementation
minimize(Solver & s,Literal p,CCMinRecursive * rec)1569 bool Constraint::minimize(Solver& s, Literal p, CCMinRecursive* rec) {
1570 LitVec temp;
1571 reason(s, p, temp);
1572 for (LitVec::size_type i = 0; i != temp.size(); ++i) {
1573 if (!s.ccMinimize(temp[i], rec)) {
1574 return false;
1575 }
1576 }
1577 return true;
1578 }
1579
1580 // Selects next branching literal
decideNextBranch(double f)1581 bool Solver::decideNextBranch(double f) {
1582 if (f <= 0.0 || rng.drand() >= f || numFreeVars() == 0) {
1583 return heuristic_->select(*this);
1584 }
1585 // select randomly
1586 Literal choice;
1587 uint32 maxVar = numVars() + 1;
1588 for (uint32 v = rng.irand(maxVar);;) {
1589 if (value(v) == value_free) {
1590 choice = heuristic_->selectLiteral(*this, v, 0);
1591 break;
1592 }
1593 if (++v == maxVar) { v = 1; }
1594 }
1595 return assume(choice);
1596 }
resetLearntActivities()1597 void Solver::resetLearntActivities() {
1598 for (ConstraintDB::size_type i = 0, end = learnts_.size(); i != end; ++i) {
1599 learnts_[i]->resetActivity();
1600 }
1601 }
1602 // Removes up to remFrac% of the learnt nogoods but
1603 // keeps those that are locked or are highly active.
reduceLearnts(float remFrac,const ReduceStrategy & rs)1604 Solver::DBInfo Solver::reduceLearnts(float remFrac, const ReduceStrategy& rs) {
1605 uint32 oldS = numLearntConstraints();
1606 uint32 remM = static_cast<uint32>(oldS * std::max(0.0f, remFrac));
1607 DBInfo r = {0,0,0};
1608 CmpScore cmp(learnts_, (ReduceStrategy::Score)rs.score, rs.glue, rs.protect);
1609 if (remM >= oldS || !remM || rs.algo == ReduceStrategy::reduce_sort) {
1610 r = reduceSortInPlace(remM, cmp, false);
1611 }
1612 else if (rs.algo == ReduceStrategy::reduce_stable) { r = reduceSort(remM, cmp); }
1613 else if (rs.algo == ReduceStrategy::reduce_heap) { r = reduceSortInPlace(remM, cmp, true);}
1614 else { r = reduceLinear(remM, cmp); }
1615 stats.addDeleted(oldS - r.size);
1616 shrinkVecTo(learnts_, r.size);
1617 return r;
1618 }
1619 // Removes up to maxR of the learnt nogoods.
1620 // Keeps those that are locked or have a high activity and
1621 // does not reorder learnts_.
reduceLinear(uint32 maxR,const CmpScore & sc)1622 Solver::DBInfo Solver::reduceLinear(uint32 maxR, const CmpScore& sc) {
1623 // compute average activity
1624 uint64 scoreSum = 0;
1625 for (LitVec::size_type i = 0; i != learnts_.size(); ++i) {
1626 scoreSum += sc.score(learnts_[i]->activity());
1627 }
1628 double avgAct = (scoreSum / (double) numLearntConstraints());
1629 // constraints with socre > 1.5 times the average are "active"
1630 double scoreThresh = avgAct * 1.5;
1631 double scoreMax = (double)sc.score(makeScore(Clasp::ACT_MAX, 1));
1632 if (scoreThresh > scoreMax) {
1633 scoreThresh = (scoreMax + (scoreSum / (double) numLearntConstraints())) / 2.0;
1634 }
1635 // remove up to maxR constraints but keep "active" and locked once
1636 DBInfo res = {0,0,0};
1637 typedef ConstraintScore ScoreType;
1638 for (LitVec::size_type i = 0; i != learnts_.size(); ++i) {
1639 Constraint* c = learnts_[i];
1640 ScoreType a = c->activity();
1641 bool isLocked = c->locked(*this);
1642 bool isGlue = sc.score(a) > scoreThresh || sc.isGlue(a);
1643 if (maxR == 0 || isLocked || isGlue || sc.isFrozen(a)) {
1644 res.pinned += isGlue;
1645 res.locked += isLocked;
1646 learnts_[res.size++] = c;
1647 c->decreaseActivity();
1648 }
1649 else {
1650 --maxR;
1651 c->destroy(this, true);
1652 }
1653 }
1654 return res;
1655 }
1656
1657 // Sorts learnt constraints by score and removes the
1658 // maxR constraints with the lowest score without
1659 // reordering learnts_.
reduceSort(uint32 maxR,const CmpScore & sc)1660 Solver::DBInfo Solver::reduceSort(uint32 maxR, const CmpScore& sc) {
1661 typedef PodVector<CmpScore::ViewPair>::type HeapType;
1662 DBInfo res = {0,0,0};
1663 HeapType heap;
1664 heap.reserve(maxR = std::min(maxR, (uint32)learnts_.size()));
1665 bool isGlue, isLocked;
1666 for (LitVec::size_type i = 0; i != learnts_.size(); ++i) {
1667 Constraint* c = learnts_[i];
1668 CmpScore::ViewPair vp(toU32(i), c->activity());
1669 res.pinned += (isGlue = sc.isGlue(vp.second));
1670 res.locked += (isLocked = c->locked(*this));
1671 if (!isLocked && !isGlue && !sc.isFrozen(vp.second)) {
1672 if (maxR) { // populate heap with first maxR constraints
1673 heap.push_back(vp);
1674 if (--maxR == 0) { std::make_heap(heap.begin(), heap.end(), sc); }
1675 }
1676 else if (sc(vp, heap[0])) { // replace max element in heap
1677 std::pop_heap(heap.begin(), heap.end(), sc);
1678 heap.back() = vp;
1679 std::push_heap(heap.begin(), heap.end(), sc);
1680 }
1681 }
1682 }
1683 // Remove all constraints in heap - those are "inactive".
1684 for (HeapType::const_iterator it = heap.begin(), end = heap.end(); it != end; ++it) {
1685 learnts_[it->first]->destroy(this, true);
1686 learnts_[it->first] = 0;
1687 }
1688 // Cleanup db and decrease activity of remaining constraints.
1689 uint32 j = 0;
1690 for (LitVec::size_type i = 0; i != learnts_.size(); ++i) {
1691 if (Constraint* c = learnts_[i]) {
1692 c->decreaseActivity();
1693 learnts_[j++] = c;
1694 }
1695 }
1696 res.size = j;
1697 return res;
1698 }
1699
1700 // Sorts the learnt db by score and removes the first
1701 // maxR constraints (those with the lowest score).
reduceSortInPlace(uint32 maxR,const CmpScore & sc,bool partial)1702 Solver::DBInfo Solver::reduceSortInPlace(uint32 maxR, const CmpScore& sc, bool partial) {
1703 DBInfo res = {0,0,0};
1704 ConstraintDB::iterator nEnd = learnts_.begin();
1705 maxR = std::min(maxR, (uint32)learnts_.size());
1706 bool isGlue, isLocked;
1707 typedef ConstraintScore ScoreType;
1708 if (!partial) {
1709 // sort whole db and remove first maxR constraints
1710 if (maxR && maxR != learnts_.size()) std::stable_sort(learnts_.begin(), learnts_.end(), sc);
1711 for (ConstraintDB::iterator it = learnts_.begin(), end = learnts_.end(); it != end; ++it) {
1712 Constraint* c = *it;
1713 ScoreType a = c->activity();
1714 res.pinned += (isGlue = sc.isGlue(a));
1715 res.locked += (isLocked = c->locked(*this));
1716 if (!maxR || isLocked || isGlue || sc.isFrozen(a)) {
1717 c->decreaseActivity();
1718 *nEnd++ = c;
1719 }
1720 else {
1721 c->destroy(this, true);
1722 --maxR;
1723 }
1724 }
1725 }
1726 else {
1727 ConstraintDB::iterator hBeg = learnts_.begin();
1728 ConstraintDB::iterator hEnd = learnts_.begin();
1729 for (ConstraintDB::iterator it = learnts_.begin(), end = learnts_.end(); it != end; ++it) {
1730 Constraint* c = *it;
1731 ScoreType a = c->activity();
1732 res.pinned += (isGlue = sc.isGlue(a));
1733 res.locked += (isLocked = c->locked(*this));
1734 if (isLocked || isGlue || sc.isFrozen(a)) { continue; }
1735 else if (maxR) {
1736 *it = *hEnd;
1737 *hEnd++ = c;
1738 if (--maxR == 0) { std::make_heap(hBeg, hEnd, sc); }
1739 }
1740 else if (sc(c, learnts_[0])) {
1741 std::pop_heap(hBeg, hEnd, sc);
1742 *it = *(hEnd-1);
1743 *(hEnd-1)= c;
1744 std::push_heap(hBeg, hEnd, sc);
1745 }
1746 }
1747 // remove all constraints in heap
1748 for (ConstraintDB::iterator it = hBeg; it != hEnd; ++it) {
1749 (*it)->destroy(this, true);
1750 }
1751 // copy remaining constraints down
1752 for (ConstraintDB::iterator it = hEnd, end = learnts_.end(); it != end; ++it) {
1753 Constraint* c = *it;
1754 c->decreaseActivity();
1755 *nEnd++ = c;
1756 }
1757 }
1758 res.size = static_cast<uint32>(std::distance(learnts_.begin(), nEnd));
1759 return res;
1760 }
incEpoch(uint32 size,uint32 n)1761 uint32 Solver::incEpoch(uint32 size, uint32 n) {
1762 if (size > epoch_.size()) { epoch_.resize(size, 0u); }
1763 if ((UINT32_MAX - epoch_[0]) < n) { epoch_.assign(epoch_.size(), 0u); }
1764 return epoch_[0] += n;
1765 }
countLevels(const Literal * first,const Literal * last,uint32 maxLevel)1766 uint32 Solver::countLevels(const Literal* first, const Literal* last, uint32 maxLevel) {
1767 if (maxLevel < 2) { return uint32(maxLevel && first != last); }
1768 POTASSCO_ASSERT(!ccMin_ || ccMin_->todo.empty(), "Must not be called during minimization!");
1769 uint32 n = 0;
1770 for (uint32 epoch = incEpoch(sizeVec(levels_) + 1); first != last; ++first) {
1771 assert(value(first->var()) != value_free);
1772 uint32& levEpoch = epoch_[level(first->var())];
1773 if (levEpoch != epoch) {
1774 levEpoch = epoch;
1775 if (++n == maxLevel) { break; }
1776 }
1777 }
1778 return n;
1779 }
1780
updateBranch(uint32 n)1781 void Solver::updateBranch(uint32 n) {
1782 int32 dl = (int32)decisionLevel(), xl = static_cast<int32>(cflStamp_.size())-1;
1783 if (xl > dl) { do { n += cflStamp_.back(); cflStamp_.pop_back(); } while (--xl != dl); }
1784 else if (dl > xl) { cflStamp_.insert(cflStamp_.end(), dl - xl, 0); }
1785 cflStamp_.back() += n;
1786 }
reduceReached(const SearchLimits & limits) const1787 bool Solver::reduceReached(const SearchLimits& limits) const {
1788 return numLearntConstraints() > limits.learnts || memUse_ > limits.memory;
1789 }
restartReached(const SearchLimits & limits) const1790 bool Solver::restartReached(const SearchLimits& limits) const {
1791 uint64 n = !limits.restart.local || cflStamp_.empty() ? limits.used : cflStamp_.back();
1792 return n >= limits.restart.conflicts || (limits.restart.dynamic && limits.restart.dynamic->reached());
1793 }
1794 /////////////////////////////////////////////////////////////////////////////////////////
1795 // The basic DPLL-like search-function
1796 /////////////////////////////////////////////////////////////////////////////////////////
search(SearchLimits & limit,double rf)1797 ValueRep Solver::search(SearchLimits& limit, double rf) {
1798 assert(!isFalse(tagLiteral()));
1799 SearchLimits::BlockPtr block = limit.restart.block;
1800 rf = std::max(0.0, std::min(1.0, rf));
1801 lower.reset();
1802 if (limit.restart.local && decisionLevel() == rootLevel()) { cflStamp_.assign(decisionLevel()+1, 0); }
1803 do {
1804 for (bool conflict = hasConflict() || !propagate() || !simplify(), local = limit.restart.local;;) {
1805 if (conflict) {
1806 uint32 n = 1, ts;
1807 do {
1808 if (block && block->push(ts = numAssignedVars()) && ts > block->scaled()) {
1809 if (limit.restart.dynamic) { limit.restart.dynamic->resetRun(); }
1810 else { limit.restart.conflicts += block->inc; }
1811 block->next = block->n + block->inc;
1812 }
1813 } while (resolveConflict() && !propagate() && (++n, true));
1814 limit.used += n;
1815 if (local) { updateBranch(n); }
1816 if (hasConflict() || (decisionLevel() == 0 && !simplify())) { return value_false; }
1817 if (numFreeVars()) {
1818 if (limit.used >= limit.conflicts) { return value_free; }
1819 if (restartReached(limit)) { return value_free; }
1820 if (reduceReached(limit)) { return value_free; }
1821 }
1822 }
1823 if (decideNextBranch(rf)) { conflict = !propagate(); }
1824 else { break; }
1825 }
1826 } while (!isModel());
1827 temp_.clear();
1828 model.clear(); model.reserve(numVars()+1);
1829 for (Var v = 0; v <= numVars(); ++v) { model.push_back(value(v)); }
1830 if (satPrepro()) { satPrepro()->extendModel(model, temp_); }
1831 return value_true;
1832 }
search(uint64 maxC,uint32 maxL,bool local,double rp)1833 ValueRep Solver::search(uint64 maxC, uint32 maxL, bool local, double rp) {
1834 SearchLimits limit;
1835 limit.restart.conflicts = maxC;
1836 limit.restart.local = local;
1837 limit.learnts = maxL;
1838 return search(limit, rp);
1839 }
isModel()1840 bool Solver::isModel() {
1841 if (hasConflict()) { return false; }
1842 FOR_EACH_POST(x, post_.head()) {
1843 if (!x->isModel(*this)) { return false; }
1844 }
1845 return !enumerationConstraint() || enumerationConstraint()->valid(*this);
1846 }
1847 /////////////////////////////////////////////////////////////////////////////////////////
1848 // Free functions
1849 /////////////////////////////////////////////////////////////////////////////////////////
destroyDB(Solver::ConstraintDB & db,Solver * s,bool detach)1850 void destroyDB(Solver::ConstraintDB& db, Solver* s, bool detach) {
1851 if (s && detach) {
1852 s->destroyDB(db);
1853 return;
1854 }
1855 while (!db.empty()) {
1856 db.back()->destroy(s, detach);
1857 db.pop_back();
1858 }
1859 }
1860 }
1861