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
25 #include <clasp/weight_constraint.h>
26 #include <clasp/clause.h>
27 #include <clasp/solver.h>
28 #include <clasp/util/misc_types.h>
29 #include <algorithm>
30
31 #if defined(__GNUC__) && __GNUC__ >= 8
32 #pragma GCC diagnostic ignored "-Wclass-memaccess"
33 #endif
34
35 namespace Clasp {
36 /////////////////////////////////////////////////////////////////////////////////////////
37 // WeightLitsRep
38 /////////////////////////////////////////////////////////////////////////////////////////
39 // Removes assigned and merges duplicate/complementary literals.
40 // return: achievable weight
41 // post : lits is sorted by decreasing weights
create(Solver & s,WeightLitVec & lits,weight_t bound)42 WeightLitsRep WeightLitsRep::create(Solver& s, WeightLitVec& lits, weight_t bound) {
43 // Step 0: Ensure s has all relevant problem variables
44 if (s.numProblemVars() > s.numVars() && !lits.empty()) {
45 s.acquireProblemVar(std::max_element(lits.begin(), lits.end())->first.var());
46 }
47 // Step 1: remove assigned/superfluous literals and merge duplicate/complementary ones
48 LitVec::size_type j = 0, other;
49 const weight_t MAX_W= std::numeric_limits<weight_t>::max();
50 for (LitVec::size_type i = 0; i != lits.size(); ++i) {
51 Literal x = lits[i].first.unflag();
52 if (lits[i].second != 0 && s.topValue(x.var()) == value_free) {
53 if (lits[i].second < 0) {
54 lits[i].second = -lits[i].second;
55 lits[i].first = x = ~lits[i].first;
56 POTASSCO_REQUIRE(bound < 0 || (MAX_W-bound) >= lits[i].second, "bound out of range");
57 bound += lits[i].second;
58 }
59 if (!s.seen(x.var())) { // first time we see x, keep and mark x
60 if (i != j) lits[j] = lits[i];
61 s.markSeen(x);
62 ++j;
63 }
64 else if (!s.seen(~x)) { // multi-occurrences of x, merge
65 for (other = 0; other != j && lits[other].first != x; ++other) { ; }
66 lits[other].second += lits[i].second;
67 }
68 else { // complementary literals ~x x
69 for (other = 0; other != j && lits[other].first != ~x; ++other) { ; }
70 bound -= lits[i].second; // decrease by min(w(~x), w(x)) ; assume w(~x) > w(x)
71 lits[other].second -= lits[i].second; // keep ~x,
72 if (lits[other].second < 0) { // actually, w(x) > w(~x),
73 lits[other].first = x; // replace ~x with x
74 lits[other].second = -lits[other].second;
75 s.clearSeen(x.var());
76 s.markSeen(x);
77 bound += lits[other].second; // and correct the bound
78 }
79 else if (lits[other].second == 0) { // w(~x) == w(x) - drop both lits
80 s.clearSeen(x.var());
81 std::memmove(&lits[0]+other, &lits[0]+other+1, (j-other-1)*sizeof(lits[other]));
82 --j;
83 }
84 }
85 }
86 else if (s.isTrue(x)) { bound -= lits[i].second; }
87 }
88 lits.erase(lits.begin()+j, lits.end());
89 // Step 2: compute min,max, achievable weight and clear flags set in step 1
90 weight_t sumW = 0;
91 weight_t minW = MAX_W, maxW = 1;
92 weight_t B = std::max(bound, 1);
93 for (LitVec::size_type i = 0; i != lits.size(); ++i) {
94 assert(lits[i].second > 0);
95 s.clearSeen(lits[i].first.var());
96 if (lits[i].second > maxW) { maxW = lits[i].second = std::min(lits[i].second, B); }
97 if (lits[i].second < minW) { minW = lits[i].second; }
98 POTASSCO_CHECK((MAX_W - sumW) >= lits[i].second, EOVERFLOW, "Sum of weights out of range");
99 sumW += lits[i].second;
100 }
101 // Step 3: sort by decreasing weight
102 if (maxW != minW) {
103 std::stable_sort(lits.begin(), lits.end(), compose22(
104 std::greater<weight_t>(),
105 select2nd<WeightLiteral>(),
106 select2nd<WeightLiteral>()));
107 }
108 else if (minW != 1) {
109 // disguised cardinality constraint
110 bound = (bound+(minW-1))/minW;
111 sumW = (sumW+(minW-1))/minW;
112 for (LitVec::size_type i = 0; i != lits.size(); ++i) { lits[i].second = 1; }
113 }
114 WeightLitsRep result = { !lits.empty() ? &lits[0] : 0, (uint32)lits.size(), bound, sumW };
115 return result;
116 }
117
118 // Propagates top-level assignment.
propagate(Solver & s,Literal W)119 bool WeightLitsRep::propagate(Solver& s, Literal W) {
120 if (sat()) { return s.force(W); } // trivially SAT
121 else if (unsat()){ return s.force(~W);} // trivially UNSAT
122 else if (s.topValue(W.var()) == value_free) {
123 return true;
124 }
125 // backward propagate
126 bool bpTrue = s.isTrue(W);
127 weight_t B = bpTrue ? (reach-bound)+1 : bound;
128 while (lits->second >= B) {
129 reach -= lits->second;
130 if (!s.force(bpTrue ? lits->first : ~lits->first, 0)) { return false; }
131 if ((bpTrue && (bound -= lits->second) <= 0) || --size == 0) { return true; }
132 ++lits;
133 }
134 if (lits->second > 1 && lits->second == lits[size-1].second) {
135 B = lits->second;
136 bound = (bound + (B-1)) / B;
137 reach = (reach + (B-1)) / B;
138 for (uint32 i = 0; i != size && lits[i].second != 1; ++i) { lits[i].second = 1; }
139 }
140 return true;
141 }
142 /////////////////////////////////////////////////////////////////////////////////////////
143 // WeightConstraint::WL
144 /////////////////////////////////////////////////////////////////////////////////////////
145 typedef Clasp::Atomic_t<uint32>::type RCType;
WL(uint32 s,bool shared,bool hasW)146 WeightConstraint::WL::WL(uint32 s, bool shared, bool hasW) : sz(s), rc(shared), w(hasW) { }
address()147 uint8* WeightConstraint::WL::address() { return reinterpret_cast<unsigned char*>(this) - (sizeof(uint32) * rc); }
clone()148 WeightConstraint::WL* WeightConstraint::WL::clone(){
149 if (shareable()) {
150 ++*reinterpret_cast<RCType*>(address());
151 return this;
152 }
153 else {
154 uint32 litSize = (size() << uint32(weights()))*sizeof(Literal);
155 WL* x = new (::operator new(sizeof(WL) + litSize)) WL(size(), false, weights());
156 std::memcpy(x->lits, this->lits, litSize);
157 return x;
158 }
159 }
release()160 void WeightConstraint::WL::release() {
161 unsigned char* x = address();
162 if (!shareable() || --*reinterpret_cast<RCType*>(x) == 0) {
163 ::operator delete(x);
164 }
165 }
refCount() const166 uint32 WeightConstraint::WL::refCount() const {
167 assert(shareable());
168 return *reinterpret_cast<const RCType*>(const_cast<WL*>(this)->address());
169 }
170 /////////////////////////////////////////////////////////////////////////////////////////
171 // WeightConstraint
172 /////////////////////////////////////////////////////////////////////////////////////////
create(Solver & s,Literal W,WeightLitVec & lits,weight_t bound,uint32 flags)173 WeightConstraint::CPair WeightConstraint::create(Solver& s, Literal W, WeightLitVec& lits, weight_t bound, uint32 flags) {
174 bool const eq = (flags & create_eq_bound) != 0;
175 WeightLitsRep rep = WeightLitsRep::create(s, lits, bound + static_cast<int>(eq));
176 CPair res;
177 if (eq) {
178 res.con[1] = WeightConstraint::doCreate(s, ~W, rep, flags);
179 rep.bound -= 1;
180 if (!res.ok()) { return res; }
181 // redo coefficient reduction
182 for (unsigned i = 0; i != rep.size && rep.lits[i].second > rep.bound; ++i) {
183 rep.reach -= rep.lits[i].second;
184 rep.reach += (rep.lits[i].second = rep.bound);
185 }
186 }
187 res.con[0] = WeightConstraint::doCreate(s, W, rep, flags);
188 return res;
189 }
create(Solver & s,Literal W,WeightLitsRep & rep,uint32 flags)190 WeightConstraint::CPair WeightConstraint::create(Solver& s, Literal W, WeightLitsRep& rep, uint32 flags) {
191 CPair res;
192 res.con[0] = doCreate(s, W, rep, flags);
193 return res;
194 }
195
doCreate(Solver & s,Literal W,WeightLitsRep & rep,uint32 flags)196 WeightConstraint* WeightConstraint::doCreate(Solver& s, Literal W, WeightLitsRep& rep, uint32 flags) {
197 WeightConstraint* conflict = (WeightConstraint*)0x1;
198 const uint32 onlyOne = create_only_btb|create_only_bfb;
199 uint32 act = 3u;
200 if ((flags & onlyOne) && (flags & onlyOne) != onlyOne) {
201 act = (flags & create_only_bfb) != 0u;
202 }
203 bool addSat = (flags&create_sat) != 0 && rep.size;
204 s.acquireProblemVar(W.var());
205 if (!rep.propagate(s, W)) { return conflict; }
206 if (rep.unsat() || (rep.sat() && !addSat)){ return 0; }
207 if ((rep.bound == 1 || rep.bound == rep.reach) && (flags & create_explicit) == 0 && act == 3u) {
208 LitVec clause; clause.reserve(1 + rep.size);
209 Literal bin[2];
210 bool disj = rep.bound == 1; // con == disjunction or con == conjunction
211 bool sat = false;
212 clause.push_back(W ^ disj);
213 for (LitVec::size_type i = 0; i != rep.size; ++i) {
214 bin[0] = ~clause[0];
215 bin[1] = rep.lits[i].first ^ disj;
216 if (bin[0] != ~bin[1]) {
217 if (bin[0] != bin[1]) { clause.push_back(~bin[1]); }
218 if (!s.add(ClauseRep::create(bin, 2))){ return conflict; }
219 }
220 else { sat = true; }
221 }
222 return (sat || ClauseCreator::create(s, clause, 0)) ? 0 : conflict;
223 }
224 assert(rep.open() || (rep.sat() && addSat));
225 if (!s.sharedContext()->physicalShareProblem()) { flags |= create_no_share; }
226 if (s.sharedContext()->frozen()) { flags |= (create_no_freeze|create_no_share); }
227 bool hasW = rep.hasWeights();
228 uint32 size = 1 + rep.size;
229 uint32 nb = sizeof(WeightConstraint) + (size+uint32(hasW))*sizeof(UndoInfo);
230 uint32 wls = sizeof(WL) + (size << uint32(hasW))*sizeof(Literal);
231 void* m = 0;
232 WL* sL = 0;
233 if ((flags & create_no_share) != 0) {
234 nb = ((nb + 3) / 4)*4;
235 m = ::operator new (nb + wls);
236 sL = new (reinterpret_cast<unsigned char*>(m)+nb) WL(size, false, hasW);
237 }
238 else {
239 static_assert(sizeof(RCType) == sizeof(uint32), "Invalid size!");
240 m = ::operator new(nb);
241 uint8* t = (uint8*)::operator new(wls + sizeof(uint32));
242 *(new (t) RCType) = 1;
243 sL = new (t+sizeof(uint32)) WL(size, true, hasW);
244 }
245 assert(m && (reinterpret_cast<uintp>(m) & 7u) == 0);
246 SharedContext* ctx = (flags & create_no_freeze) == 0 ? const_cast<SharedContext*>(s.sharedContext()) : 0;
247 WeightConstraint* c = new (m) WeightConstraint(s, ctx, W, rep, sL, act);
248 if (!c->integrateRoot(s)) {
249 c->destroy(&s, true);
250 return conflict;
251 }
252 if ((flags & create_no_add) == 0) { s.add(c); }
253 return c;
254 }
WeightConstraint(Solver & s,SharedContext * ctx,Literal W,const WeightLitsRep & rep,WL * out,uint32 act)255 WeightConstraint::WeightConstraint(Solver& s, SharedContext* ctx, Literal W, const WeightLitsRep& rep, WL* out, uint32 act) {
256 typedef unsigned char Byte_t;
257 const bool hasW = rep.hasWeights();
258 lits_ = out;
259 active_ = act;
260 ownsLit_ = !out->shareable();
261 Literal* p = lits_->lits;
262 Literal* h = new (reinterpret_cast<Byte_t*>(undo_)) Literal(W);
263 weight_t w = 1;
264 bound_[FFB_BTB] = (rep.reach-rep.bound)+1; // ffb-btb
265 bound_[FTB_BFB] = rep.bound; // ftb-bfb
266 *p++ = ~W; // store constraint literal
267 if (hasW) *p++ = Literal::fromRep(w); // and weight if necessary
268 if (ctx) ctx->setFrozen(W.var(), true); // exempt from variable elimination
269 if (s.topValue(W.var()) != value_free) { // only one direction is relevant
270 active_ = FFB_BTB+s.isFalse(W);
271 }
272 watched_ = 3u - (active_ != 3u || ctx == 0);
273 WeightLiteral*x = rep.lits;
274 for (uint32 sz = rep.size, j = 1; sz--; ++j, ++x) {
275 h = new (h + 1) Literal(x->first);
276 *p++ = x->first; // store constraint literal
277 w = x->second; // followed by weight
278 if (hasW) *p++= Literal::fromRep(w); // if necessary
279 addWatch(s, j, FTB_BFB); // watches lits[idx]
280 addWatch(s, j, FFB_BTB); // watches ~lits[idx]
281 if (ctx) ctx->setFrozen(h->var(), true); // exempt from variable elimination
282 }
283 // init heuristic
284 h -= rep.size;
285 uint32 off = active_ != NOT_ACTIVE;
286 assert((void*)h == (void*)undo_);
287 s.heuristic()->newConstraint(s, h+off, rep.size+(1-off), Constraint_t::Static);
288 // init undo stack
289 up_ = undoStart(); // undo stack is initially empty
290 undo_[0].data = 0;
291 undo_[up_].data = 0;
292 setBpIndex(1); // where to start back propagation
293 if (s.topValue(W.var()) == value_free){
294 addWatch(s, 0, FTB_BFB); // watch con in both phases
295 addWatch(s, 0, FFB_BTB); // in order to allow for backpropagation
296 }
297 else {
298 uint32 d = active_; // propagate con
299 WeightConstraint::propagate(s, ~lit(0, (ActiveConstraint)active_), d);
300 }
301 }
302
WeightConstraint(Solver & s,const WeightConstraint & other)303 WeightConstraint::WeightConstraint(Solver& s, const WeightConstraint& other) {
304 typedef unsigned char Byte_t;
305 lits_ = other.lits_->clone();
306 ownsLit_ = 0;
307 Literal* heu = new (reinterpret_cast<Byte_t*>(undo_))Literal(~lits_->lit(0));
308 bound_[0] = other.bound_[0];
309 bound_[1] = other.bound_[1];
310 active_ = other.active_;
311 watched_ = other.watched_;
312 if (s.value(heu->var()) == value_free) {
313 addWatch(s, 0, FTB_BFB); // watch con in both phases
314 addWatch(s, 0, FFB_BTB); // in order to allow for backpropagation
315 }
316 for (uint32 i = 1, end = size(); i < end; ++i) {
317 heu = new (heu + 1) Literal(lits_->lit(i));
318 if (s.value(heu->var()) == value_free) {
319 addWatch(s, i, FTB_BFB); // watches lits[i]
320 addWatch(s, i, FFB_BTB); // watches ~lits[i]
321 }
322 }
323 // Initialize heuristic with literals (no weights) in constraint.
324 uint32 off = active_ != NOT_ACTIVE;
325 heu -= (size() - 1);
326 assert((void*)heu == (void*)undo_);
327 s.heuristic()->newConstraint(s, heu+off, size()-off, Constraint_t::Static);
328 // Init undo stack
329 std::memcpy(undo_, other.undo_, sizeof(UndoInfo)*(size()+isWeight()));
330 up_ = other.up_;
331 }
332
~WeightConstraint()333 WeightConstraint::~WeightConstraint() {}
cloneAttach(Solver & other)334 Constraint* WeightConstraint::cloneAttach(Solver& other) {
335 void* m = ::operator new(sizeof(WeightConstraint) + (size()+isWeight())*sizeof(UndoInfo));
336 return new (m) WeightConstraint(other, *this);
337 }
338
integrateRoot(Solver & s)339 bool WeightConstraint::integrateRoot(Solver& s) {
340 if (!s.decisionLevel() || highestUndoLevel(s) >= s.rootLevel() || s.hasConflict()) { return !s.hasConflict(); }
341 // check if constraint has assigned literals
342 uint32 low = s.decisionLevel(), vDL;
343 uint32 np = 0;
344 for (uint32 i = 0, end = size(); i != end; ++i) {
345 Var v = lits_->var(i);
346 if (s.value(v) != value_free && (vDL = s.level(v)) != 0) {
347 ++np;
348 s.markSeen(v);
349 low = std::min(low, vDL);
350 }
351 }
352 // propagate assigned literals in assignment order
353 const LitVec& trail = s.trail();
354 const uint32 end = sizeVec(trail) - s.queueSize();
355 GenericWatch* w = 0;
356 for (uint32 i = s.levelStart(low); i != end && np; ++i) {
357 Literal p = trail[i];
358 if (s.seen(p) && np--) {
359 s.clearSeen(p.var());
360 if (!s.hasConflict() && (w = s.getWatch(trail[i], this)) != 0) {
361 w->propagate(s, p);
362 }
363 }
364 }
365 for (uint32 i = end; i != trail.size() && np; ++i) {
366 if (s.seen(trail[i].var())) { --np; s.clearSeen(trail[i].var()); }
367 }
368 return !s.hasConflict();
369 }
addWatch(Solver & s,uint32 idx,ActiveConstraint c)370 void WeightConstraint::addWatch(Solver& s, uint32 idx, ActiveConstraint c) {
371 // Add watch only if c is relevant.
372 if (uint32(c^1) != active_) {
373 // Use LSB to store the constraint that watches the literal.
374 s.addWatch(~lit(idx, c), this, (idx<<1)+c);
375 }
376 }
377
destroy(Solver * s,bool detach)378 void WeightConstraint::destroy(Solver* s, bool detach) {
379 if (s && detach) {
380 for (uint32 i = 0, end = size(); i != end; ++i) {
381 s->removeWatch( lits_->lit(i), this );
382 s->removeWatch(~lits_->lit(i), this );
383 }
384 for (uint32 last = 0, dl; (dl = highestUndoLevel(*s)) != 0; --up_) {
385 if (dl != last) { s->removeUndoWatch(last = dl, this); }
386 }
387 }
388 if (ownsLit_ == 0) { lits_->release(); }
389 void* mem = static_cast<Constraint*>(this);
390 this->~WeightConstraint();
391 ::operator delete(mem);
392 }
393
setBpIndex(uint32 n)394 void WeightConstraint::setBpIndex(uint32 n){
395 if (isWeight()) undo_[0].data = (n<<1)+(undo_[0].data&1);
396 }
397
398 // Returns the numerical highest decision level watched by this constraint.
highestUndoLevel(Solver & s) const399 uint32 WeightConstraint::highestUndoLevel(Solver& s) const {
400 return up_ != undoStart()
401 ? s.level(lits_->var(undoTop().idx()))
402 : 0;
403 }
404
405 // Updates the bound of sub-constraint c and adds the literal at index idx to the
406 // undo stack. If the current decision level is not watched, an undo watch is added
407 // so that the bound can be adjusted once the solver backtracks.
updateConstraint(Solver & s,uint32 level,uint32 idx,ActiveConstraint c)408 void WeightConstraint::updateConstraint(Solver& s, uint32 level, uint32 idx, ActiveConstraint c) {
409 bound_[c] -= weight(idx);
410 if (highestUndoLevel(s) != level) {
411 s.addUndoWatch(level, this);
412 }
413 undo_[up_].data = (idx<<2) + (c<<1) + (undo_[up_].data & 1);
414 ++up_;
415 assert(!litSeen(idx));
416 toggleLitSeen(idx);
417 }
418
419 // Since clasp uses an eager assignment strategy where literals are assigned as soon
420 // as they are added to the propagation queue, we distinguish processed from unprocessed literals.
421 // Processed literals are those for which propagate was already called and the corresponding bound
422 // was updated; they are flagged in updateConstraint().
423 // Unprocessed literals are either free or were not yet propagated. During propagation
424 // we treat all unprocessed literals as free. This way, conflicts are detected early.
425 // Consider: x :- 3 [a=3, b=2, c=1,d=1] and PropQ: b, ~Body, c.
426 // Initially b, ~Body, c are unprocessed and the bound is 3.
427 // Step 1: propagate(b) : b is marked as processed and bound is reduced to 1.
428 // Now, although we already know that the body is false, we do not backpropagate yet
429 // because the body is unprocessed. Deferring backpropagation until the body is processed
430 // makes reason computation easier.
431 // Step 2: propagate(~Body): ~body is marked as processed and bound is reduced to 0.
432 // Since the body is now part of our reason set, we can start backpropagation.
433 // First we assign the unprocessed and free literal ~a. Literal ~b is skipped, because
434 // its complementary literal was already successfully processed. Finally, we force
435 // the unprocessed but false literal ~c to true. This will generate a conflict and
436 // propagation is stopped. Without the distinction between processed and unprocessed
437 // lits we would have to skip ~c. We would then have to manually trigger the conflict
438 // {b, ~Body, c} in step 3, when propagate(c) sets the bound to -1.
propagate(Solver & s,Literal p,uint32 & d)439 Constraint::PropResult WeightConstraint::propagate(Solver& s, Literal p, uint32& d) {
440 // determine the affected constraint and its body literal
441 ActiveConstraint c = (ActiveConstraint)(d&1);
442 const uint32 idx = d >> 1;
443 const Literal body = lit(0, c);
444 const uint32 level = s.level(p.var());
445 if ( uint32(c^1) == active_ || s.isTrue(body) ) {
446 // the other constraint is active or this constraint is already satisfied;
447 // nothing to do
448 return PropResult(true, true);
449 }
450 if (idx == 0 && level <= s.rootLevel() && watched_ == 3u) {
451 watched_ = c;
452 for (uint32 i = 1, end = size(); i != end; ++i) {
453 s.removeWatch(lit(i, c), this);
454 }
455 }
456 // the constraint is not yet satisfied; update it and
457 // check if we can now propagate any literals.
458 updateConstraint(s, level, idx, c);
459 if (bound_[c] <= 0 || (isWeight() && litSeen(0))) {
460 uint32 reasonData = !isWeight() ? UINT32_MAX : up_;
461 if (!litSeen(0)) {
462 // forward propagate constraint to true
463 active_ = c;
464 return PropResult(s.force(body, this, reasonData), true);
465 }
466 else {
467 // backward propagate false constraint
468 uint32 n = getBpIndex();
469 for (const uint32 end = size(); n != end && (bound_[c] - weight(n)) < 0; ++n) {
470 if (!litSeen(n)) {
471 active_ = c;
472 Literal x = lit(n, c);
473 if (!s.force(x, this, reasonData)) {
474 return PropResult(false, true);
475 }
476 }
477 }
478 assert(n == 1 || n == size() || isWeight());
479 setBpIndex(n);
480 }
481 }
482 return PropResult(true, true);
483 }
484
485 // Builds the reason for p from the undo stack of this constraint.
486 // The reason will only contain literals that were processed by the
487 // active sub-constraint.
reason(Solver & s,Literal p,LitVec & r)488 void WeightConstraint::reason(Solver& s, Literal p, LitVec& r) {
489 assert(active_ != NOT_ACTIVE);
490 Literal x;
491 uint32 stop = !isWeight() ? up_ : s.reasonData(p);
492 assert(stop <= up_);
493 for (uint32 i = undoStart(); i != stop; ++i) {
494 UndoInfo u = undo_[i];
495 // Consider only lits that are relevant to the active constraint
496 if (u.constraint() == (ActiveConstraint)active_) {
497 x = lit(u.idx(), u.constraint());
498 r.push_back( ~x );
499 }
500 }
501 }
502
minimize(Solver & s,Literal p,CCMinRecursive * rec)503 bool WeightConstraint::minimize(Solver& s, Literal p, CCMinRecursive* rec) {
504 assert(active_ != NOT_ACTIVE);
505 Literal x;
506 uint32 stop = !isWeight() ? up_ : s.reasonData(p);
507 assert(stop <= up_);
508 for (uint32 i = undoStart(); i != stop; ++i) {
509 UndoInfo u = undo_[i];
510 // Consider only lits that are relevant to the active constraint
511 if (u.constraint() == (ActiveConstraint)active_) {
512 x = lit(u.idx(), u.constraint());
513 if (!s.ccMinimize(~x, rec)) {
514 return false;
515 }
516 }
517 }
518 return true;
519 }
520
521 // undoes processed assignments
undoLevel(Solver & s)522 void WeightConstraint::undoLevel(Solver& s) {
523 setBpIndex(1);
524 for (UndoInfo u; up_ != undoStart() && s.value(lits_->var((u=undoTop()).idx())) == value_free;) {
525 assert(litSeen(u.idx()));
526 toggleLitSeen(u.idx());
527 bound_[u.constraint()] += weight(u.idx());
528 --up_;
529 }
530 if (!litSeen(0)) {
531 active_ = NOT_ACTIVE;
532 if (watched_ < 2u) {
533 ActiveConstraint other = static_cast<ActiveConstraint>(watched_^1);
534 for (uint32 i = 1, end = size(); i != end; ++i) {
535 addWatch(s, i, other);
536 }
537 watched_ = 3u;
538 }
539 }
540 }
541
simplify(Solver & s,bool)542 bool WeightConstraint::simplify(Solver& s, bool) {
543 if (bound_[0] <= 0 || bound_[1] <= 0) {
544 for (uint32 i = 0, end = size(); i != end; ++i) {
545 s.removeWatch( lits_->lit(i), this );
546 s.removeWatch(~lits_->lit(i), this );
547 }
548 return true;
549 }
550 else if (s.value(lits_->var(0)) != value_free && (active_ == NOT_ACTIVE || isWeight())) {
551 if (active_ == NOT_ACTIVE) {
552 Literal W = ~lits_->lit(0);
553 active_ = FFB_BTB+s.isFalse(W);
554 }
555 for (uint32 i = 0, end = size(); i != end; ++i) {
556 s.removeWatch(lit(i, (ActiveConstraint)active_), this);
557 }
558 }
559 if (lits_->unique() && size() > 4 && (up_ - undoStart()) > size()/2) {
560 Literal* lits = lits_->lits;
561 const uint32 inc = 1 + lits_->weights();
562 uint32 end = lits_->size()*inc;
563 uint32 i, j, idx = 1;
564 // find first assigned literal - must be there otherwise undo stack would be empty
565 for (i = inc; s.value(lits[i].var()) == value_free; i += inc) {
566 assert(!litSeen(idx));
567 ++idx;
568 }
569 // move unassigned literals down
570 // update watches because indices have changed
571 for (j = i, i += inc; i != end; i += inc) {
572 if (s.value(lits[i].var()) == value_free) {
573 lits[j] = lits[i];
574 if (lits_->weights()) { lits[j+1] = lits[i+1]; }
575 undo_[idx].data = 0;
576 assert(!litSeen(idx));
577 if (Clasp::GenericWatch* w = s.getWatch(lits[i], this)) {
578 w->data = (idx<<1) + 1;
579 }
580 if (Clasp::GenericWatch* w = s.getWatch(~lits[i], this)) {
581 w->data = (idx<<1) + 0;
582 }
583 j += inc;
584 ++idx;
585 }
586 else {
587 s.removeWatch(lits[i], this);
588 s.removeWatch(~lits[i], this);
589 }
590 }
591 // clear undo stack & update to new size
592 up_ = undoStart();
593 setBpIndex(1);
594 lits_->sz = idx;
595 }
596 return false;
597 }
598
estimateComplexity(const Solver & s) const599 uint32 WeightConstraint::estimateComplexity(const Solver& s) const {
600 weight_t B = std::min(bound_[0], bound_[1]);
601 uint32 r = 2;
602 for (uint32 i = 1; i != size() && B > 0; ++i) {
603 if (s.value(lits_->var(i)) == value_free) {
604 ++r;
605 B -= weight(i);
606 }
607 }
608 return r;
609 }
610 }
611