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