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/logic_program_types.h>
25 #include <clasp/logic_program.h>
26 #include <clasp/solver.h>
27 #include <clasp/clause.h>
28 #include <clasp/weight_constraint.h>
29 #include <clasp/util/misc_types.h>
30 
31 namespace Clasp {
32 namespace Asp {
33 static_assert((bk_lib::detail::same_type<Potassco::Weight_t, weight_t>::value), "unexpected weight type");
34 static_assert((bk_lib::detail::same_type<Potassco::Atom_t, Var>::value)       , "unexpected atom type");
35 static_assert((bk_lib::detail::same_type<Potassco::Id_t, uint32>::value)      , "unexpected id type");
36 static_assert((bk_lib::detail::same_type<Potassco::Lit_t, int32>::value)      , "unexpected literal type");
37 static_assert((bk_lib::detail::same_type<Potassco::Weight_t, int32>::value)   , "unexpected weight type");
38 /////////////////////////////////////////////////////////////////////////////////////////
39 // class RuleTransform
40 //
41 // class for transforming extended rules to normal rules
42 /////////////////////////////////////////////////////////////////////////////////////////
43 struct RuleTransform::Impl {
ImplClasp::Asp::RuleTransform::Impl44 	Impl() : adapt_(0), prg_(0) { }
45 	struct TodoItem {
TodoItemClasp::Asp::RuleTransform::Impl::TodoItem46 		TodoItem(uint32 i, weight_t w, Atom_t v) : idx(i), bound(w), head(v) {}
47 		uint32   idx;
48 		weight_t bound;
49 		Atom_t   head;
50 	};
51 	struct CmpW {
52 		template <class P>
operator ()Clasp::Asp::RuleTransform::Impl::CmpW53 		bool operator()(const P& lhs, const P& rhs) const {
54 			return Potassco::weight(lhs) > Potassco::weight(rhs);
55 		}
56 	};
57 	typedef PodQueue<TodoItem> TodoQueue;
58 	typedef Potassco::LitVec  LitVec;
59 	typedef Potassco::WLitVec WLitVec;
60 	ProgramAdapter* adapt_;
61 	LogicProgram*   prg_;
62 	LitVec          lits_;
63 	WLitVec         agg_;
64 	SumVec          sumR_;
65 	VarVec          aux_;
66 	TodoQueue       todo_;
67 	weight_t        bound_;
newAtomClasp::Asp::RuleTransform::Impl68 	Atom_t newAtom() const { return prg_ ? prg_->newAtom() : adapt_->newAtom(); }
addRuleClasp::Asp::RuleTransform::Impl69 	uint32 addRule(const Rule& r) const {
70 		if (prg_){ prg_->addRule(r); }
71 		else     { adapt_->addRule(r); }
72 		return 1;
73 	}
addRuleClasp::Asp::RuleTransform::Impl74 	uint32 addRule(Head_t ht, const Potassco::AtomSpan& head, const Potassco::LitSpan& b) const {
75 		return addRule(Rule::normal(ht, head, b));
76 	}
addRuleClasp::Asp::RuleTransform::Impl77 	uint32 addRule(Atom_t h, const Potassco::LitSpan& b) const {
78 		return addRule(Head_t::Disjunctive, Potassco::toSpan(&h, static_cast<std::size_t>(h != 0)), b);
79 	}
80 	uint32 transform(Atom_t head, weight_t bound, const Potassco::WeightLitSpan& lits, Strategy s);
81 	uint32 transformSelect(Atom_t head);
82 	uint32 transformSplit(Atom_t head);
83 	uint32 transformChoice(const Potassco::AtomSpan& r);
84 	uint32 transformDisjunction(const Potassco::AtomSpan& r);
85 	uint32 addRule(Atom_t head, bool add, uint32 idx, weight_t bound);
getAuxVarClasp::Asp::RuleTransform::Impl86 	Atom_t getAuxVar(uint32 idx, weight_t bound) {
87 		assert(bound > 0 && idx < agg_.size());
88 		uint32 k = static_cast<uint32>(bound - 1);
89 		if (aux_[k] == 0) {
90 			todo_.push(TodoItem(idx, bound, aux_[k] = newAtom()));
91 		}
92 		return aux_[k];
93 	}
94 };
95 
RuleTransform(ProgramAdapter & prg)96 RuleTransform::RuleTransform(ProgramAdapter& prg) : impl_(new Impl()) {
97 	impl_->adapt_ = &prg;
98 }
RuleTransform(LogicProgram & prg)99 RuleTransform::RuleTransform(LogicProgram& prg) : impl_(new Impl()) {
100 	impl_->prg_ = &prg;
101 }
~RuleTransform()102 RuleTransform::~RuleTransform() {
103 	delete impl_;
104 }
105 
transform(const Rule & r,Strategy s)106 uint32 RuleTransform::transform(const Rule& r, Strategy s) {
107 	if (r.sum()) {
108 		Atom_t h = !Potassco::empty(r.head) ? r.head[0] : 0;
109 		bool aux = r.ht == Head_t::Choice || size(r.head) > 1;
110 		if (aux) {
111 			h = impl_->newAtom();
112 			Potassco::Lit_t bl = Potassco::lit(h);
113 			impl_->addRule(r.ht, r.head, Potassco::toSpan(&bl, 1));
114 		}
115 		return uint32(aux) + impl_->transform(h, r.agg.bound, r.agg.lits, s);
116 	}
117 	else if (size(r.head) > static_cast<uint32>(r.ht == Head_t::Disjunctive)) {
118 		impl_->lits_.clear();
119 		uint32 nAux = (size(r.cond) > 1) && (size(r.head) > 1) && s != strategy_no_aux;
120 		if (nAux) {
121 			// make body eq to a new aux atom
122 			Atom_t auxB = impl_->newAtom();
123 			impl_->addRule(auxB, r.cond);
124 			impl_->lits_.push_back(Potassco::lit(auxB));
125 		}
126 		else {
127 			impl_->lits_.assign(begin(r.cond), end(r.cond));
128 		}
129 		return nAux + (r.ht == Head_t::Choice ? impl_->transformChoice(r.head) : impl_->transformDisjunction(r.head));
130 	}
131 	return impl_->addRule(r);
132 }
133 
134 // A choice rule {h1,...hn} :- BODY is replaced with:
135 // h1   :- BODY, not aux1.
136 // aux1 :- not h1.
137 // ...
138 // hn   :- BODY, not auxN.
139 // auxN :- not hn.
transformChoice(const Potassco::AtomSpan & atoms)140 uint32 RuleTransform::Impl::transformChoice(const Potassco::AtomSpan& atoms) {
141 	uint32 nRule = 0;
142 	Potassco::Lit_t   bLit = 0;
143 	Potassco::LitSpan bAux = Potassco::toSpan(&bLit, 1);
144 	Atom_t hAux;
145 	for (Potassco::AtomSpan::iterator it = Potassco::begin(atoms), end = Potassco::end(atoms); it != end; ++it) {
146 		hAux = newAtom();
147 		bLit = Potassco::neg(*it);
148 		lits_.push_back(Potassco::neg(hAux));
149 		nRule += addRule(*it, Potassco::toSpan(lits_));
150 		nRule += addRule(hAux, bAux);
151 		lits_.pop_back();
152 	}
153 	return nRule;
154 }
155 
156 // A disjunctive rule h1|...|hn :- BODY is replaced with:
157 // hi   :- BODY, {not hj | 1 <= j != i <= n}.
transformDisjunction(const Potassco::AtomSpan & atoms)158 uint32 RuleTransform::Impl::transformDisjunction(const Potassco::AtomSpan& atoms) {
159 	uint32 bIdx = sizeVec(lits_);
160 	for (Potassco::AtomSpan::iterator it = Potassco::begin(atoms) + 1, end = Potassco::end(atoms); it != end; ++it) {
161 		lits_.push_back(Potassco::neg(*it));
162 	}
163 	uint32 nRule = 0;
164 	for (Potassco::AtomSpan::iterator it = Potassco::begin(atoms), end = Potassco::end(atoms);;) {
165 		nRule += addRule(*it, Potassco::toSpan(lits_));
166 		if (++it == end) { break; }
167 		lits_[bIdx++] = Potassco::neg(*(it-1));
168 	}
169 	return nRule;
170 }
171 
transform(Atom_t head,weight_t bound,const Potassco::WeightLitSpan & wlits,Strategy s)172 uint32 RuleTransform::Impl::transform(Atom_t head, weight_t bound, const Potassco::WeightLitSpan& wlits, Strategy s) {
173 	bound_ = bound;
174 	agg_.assign(begin(wlits), end(wlits));
175 	if (!isSorted(agg_.begin(), agg_.end(), CmpW())) {
176 		std::stable_sort(agg_.begin(), agg_.end(), CmpW());
177 	}
178 	wsum_t sum = 0;
179 	sumR_.resize(agg_.size());
180 	for (WLitVec::size_type i = agg_.size(); i--;) {
181 		agg_[i].weight = std::min(agg_[i].weight, bound_);
182 		sumR_[i] = (sum += agg_[i].weight);
183 		POTASSCO_REQUIRE(agg_[i].weight >= 0 && sum <= CLASP_WEIGHT_T_MAX, "invalid weight rule");
184 	}
185 	if      (bound_ > sum) { return 0; }
186 	else if (bound_ <= 0)  { return addRule(head, Potassco::toSpan<Potassco::Lit_t>()); }
187 	else if ((sum - agg_.back().weight) < bound_) { // normal rule
188 		lits_.clear();
189 		for (WLitVec::const_iterator it = agg_.begin(), end = agg_.end(); it != end; ++it) {
190 			lits_.push_back(lit(*it));
191 		}
192 		return addRule(head, Potassco::toSpan(lits_));
193 	}
194 	else {
195 		return ((s == strategy_no_aux || (sum < 6 && s == strategy_default))
196 			? transformSelect(head)
197 			: transformSplit(head));
198 	}
199 }
200 
201 // Exponential transformation of cardinality and weight constraint.
202 // Creates minimal subsets, no aux atoms.
203 // E.g. a rule h = 2 {a,b,c,d} is translated into the following six rules:
204 // h :- a, b.
205 // h :- a, c.
206 // h :- a, d.
207 // h :- b, c.
208 // h :- b, d.
209 // h :- c, d.
transformSelect(Atom_t h)210 uint32 RuleTransform::Impl::transformSelect(Atom_t h) {
211 	lits_.clear();
212 	uint32 nRule = 0;
213 	wsum_t cw = 0;
214 	assert(sumR_[0] >= bound_ && cw < bound_);
215 	aux_.clear();
216 	for (uint32 it = 0, end = (uint32)agg_.size();;) {
217 		while (cw < bound_) {
218 			cw += Potassco::weight(agg_[it]);
219 			lits_.push_back(Potassco::lit(agg_[it]));
220 			aux_.push_back(it++);
221 		}
222 		nRule += addRule(h, Potassco::toSpan(lits_));
223 		do {
224 			if (aux_.empty()) { return nRule; }
225 			it = aux_.back();
226 			aux_.pop_back();
227 			lits_.pop_back();
228 			cw -= Potassco::weight(agg_[it]);
229 		} while (++it == end || (cw + sumR_[it]) < bound_);
230 	}
231 }
232 // Quadratic transformation of cardinality and weight constraint.
233 // Introduces aux atoms.
234 // E.g. a rule h = 2 {a,b,c,d} is translated into the following eight rules:
235 // h       :- a, aux_1_1.
236 // h       :- aux_1_2.
237 // aux_1_1 :- b.
238 // aux_1_1 :- aux_2_1.
239 // aux_1_2 :- b, aux_2_1.
240 // aux_1_2 :- c, d.
241 // aux_2_1 :- c.
242 // aux_2_1 :- d.
transformSplit(Atom_t h)243 uint32 RuleTransform::Impl::transformSplit(Atom_t h) {
244 	const weight_t bound = bound_;
245 	uint32 nRule = 0;
246 	uint32 level = 0;
247 	aux_.resize(bound, 0);
248 	todo_.clear();
249 	todo_.push(TodoItem(0, bound, h));
250 	while (!todo_.empty()) {
251 		TodoItem i = todo_.pop_ret();
252 		if (i.idx > level) {
253 			// We are about to start a new level of the tree - reset aux_
254 			level = i.idx;
255 			aux_.assign(bound, 0);
256 		}
257 		// For a todo item i with head h and lit x = agg_[i.idx] create at most two rules:
258 		// r1: h :- x, aux(i.idx+1, i.bound-weight(x))
259 		// r2: h :- aux(i.idx+1, i.bound).
260 		// The first rule r1 represents the case where x is true, while
261 		// the second rule encodes the case where the literal is false.
262 		nRule += addRule(i.head, true,  i.idx, i.bound - agg_[i.idx].weight);
263 		nRule += addRule(i.head, false, i.idx, i.bound);
264 	}
265 	return nRule;
266 }
267 
268 // Creates a rule head :- agg_[idx], aux(idx+1, bound) or head :- aux(idx+1, bound) or depending on add.
addRule(Atom_t head,bool add,uint32 bIdx,weight_t bound)269 uint32 RuleTransform::Impl::addRule(Atom_t head, bool add, uint32 bIdx, weight_t bound) {
270 	const weight_t minW = agg_.back().weight;
271 	const wsum_t   maxW = sumR_[bIdx + 1];
272 	if (bound <= 0) {
273 		assert(add);
274 		lits_.assign(1, agg_[bIdx].lit);
275 		return addRule(head, Potassco::toSpan(lits_));
276 	}
277 	if ((maxW - minW) < bound) {
278 		// remaining literals are all needed to satisfy bound
279 		bIdx += static_cast<uint32>(!add);
280 		if (maxW >= bound) {
281 			lits_.clear();
282 			for (; bIdx != agg_.size(); ++bIdx) { lits_.push_back(agg_[bIdx].lit); }
283 			return addRule(head, Potassco::toSpan(lits_));
284 		}
285 		return 0;
286 	}
287 	lits_.clear();
288 	if (add) { lits_.push_back(agg_[bIdx].lit); }
289 	lits_.push_back(Potassco::lit(getAuxVar(bIdx + 1, bound)));
290 	return addRule(head, Potassco::toSpan(lits_));
291 }
292 /////////////////////////////////////////////////////////////////////////////////////////
293 // class SccChecker
294 //
295 // SCC/cycle checking
296 /////////////////////////////////////////////////////////////////////////////////////////
SccChecker(LogicProgram & prg,AtomList & sccAtoms,uint32 startScc)297 SccChecker::SccChecker(LogicProgram& prg, AtomList& sccAtoms, uint32 startScc)
298 	: prg_(&prg), sccAtoms_(&sccAtoms), count_(0), sccs_(startScc) {
299 	for (uint32 i = 0; i != prg.numAtoms(); ++i) {
300 		visit(prg.getAtom(i));
301 	}
302 	for (uint32 i = 0; i != prg.numBodies(); ++i) {
303 		visit(prg.getBody(i));
304 	}
305 }
306 
visitDfs(PrgNode * node,NodeType t)307 void SccChecker::visitDfs(PrgNode* node, NodeType t) {
308 	if (!prg_ || !doVisit(node)) {
309 		return;
310 	}
311 	callStack_.clear();
312 	nodeStack_.clear();
313 	count_ = 0;
314 	addCall(node, t, 0);
315 	while (!callStack_.empty()) {
316 		Call c = callStack_.back();
317 		callStack_.pop_back();
318 		if (!recurse(c)) {
319 			node = unpackNode(c.node);
320 			if (c.min < node->id()) {
321 				node->resetId( c.min, true );
322 			}
323 			else if (c.node == nodeStack_.back()) {
324 				// node is trivially-connected; all such nodes are in the same Pseudo-SCC
325 				if (isNode(nodeStack_.back(), PrgNode::Atom)) {
326 					static_cast<PrgAtom*>(node)->setScc(PrgNode::noScc);
327 				}
328 				node->resetId(PrgNode::noNode, true);
329 				nodeStack_.pop_back();
330 			}
331 			else { // non-trivial SCC
332 				PrgNode* succVertex;
333 				do {
334 					succVertex = unpackNode(nodeStack_.back());
335 					if (isNode(nodeStack_.back(), PrgNode::Atom)) {
336 						static_cast<PrgAtom*>(succVertex)->setScc(sccs_);
337 						sccAtoms_->push_back(static_cast<PrgAtom*>(succVertex));
338 					}
339 					nodeStack_.pop_back();
340 					succVertex->resetId(PrgNode::noNode, true);
341 				} while (succVertex != node);
342 				++sccs_;
343 			}
344 		}
345 	}
346 }
347 
recurse(Call & c)348 bool SccChecker::recurse(Call& c) {
349 	PrgNode* n = unpackNode(c.node);
350 	if (!n->seen()) {
351 		nodeStack_.push_back(c.node);
352 		c.min = count_++;
353 		n->resetId(c.min, true);
354 	}
355 	if (isNode(c.node, PrgNode::Body)) {
356 		PrgBody* b = static_cast<PrgBody*>(n);
357 		PrgHead* h = 0; NodeType t;
358 		for (PrgBody::head_iterator it = b->heads_begin() + c.next, end = b->heads_end(); it != end; ++it) {
359 			if   (it->isAtom()){ h = prg_->getAtom(it->node()); t = PrgNode::Atom; }
360 			else               { h = prg_->getDisj(it->node()); t = PrgNode::Disj; }
361 			if (doVisit(h, false) && onNode(h, t, c, static_cast<uint32>(it-b->heads_begin()))) {
362 				return true;
363 			}
364 		}
365 	}
366 	else if (isNode(c.node, PrgNode::Atom)) {
367 		PrgAtom* a = static_cast<PrgAtom*>(n);
368 		for (PrgAtom::dep_iterator it = a->deps_begin() + c.next, end = a->deps_end(); it != end; ++it) {
369 			if (it->sign()) continue;
370 			PrgBody* bn = prg_->getBody(it->var());
371 			if (doVisit(bn, false) && onNode(bn, PrgNode::Body, c, static_cast<uint32>(it-a->deps_begin()))) {
372 				return true;
373 			}
374 		}
375 	}
376 	else if (isNode(c.node, PrgNode::Disj)) {
377 		PrgDisj* d = static_cast<PrgDisj*>(n);
378 		for (PrgDisj::atom_iterator it = d->begin() + c.next, end = d->end(); it != end; ++it) {
379 			PrgAtom* a = prg_->getAtom(*it);
380 			if (doVisit(a, false) && onNode(a, PrgNode::Atom, c, static_cast<uint32>(it-d->begin()))) {
381 				return true;
382 			}
383 		}
384 	}
385 	return false;
386 }
387 
onNode(PrgNode * n,NodeType t,Call & c,uint32 data)388 bool SccChecker::onNode(PrgNode* n, NodeType t, Call& c, uint32 data) {
389 	if (!n->seen()) {
390 		Call rec = {c.node, c.min, data};
391 		callStack_.push_back(rec);
392 		addCall(n, t, 0);
393 		return true;
394 	}
395 	if (n->id() < c.min) {
396 		c.min = n->id();
397 	}
398 	return false;
399 }
400 /////////////////////////////////////////////////////////////////////////////////////////
PrgNode(uint32 id,bool checkScc)401 PrgNode::PrgNode(uint32 id, bool checkScc)
402 	: litId_(noLit), noScc_(uint32(!checkScc)), id_(id), val_(value_free), eq_(0), seen_(0) {
403 	static_assert(sizeof(PrgNode) == sizeof(uint64), "Unsupported Alignment");
404 	POTASSCO_CHECK(id_ == id, EOVERFLOW, "Id out of range");
405 }
406 /////////////////////////////////////////////////////////////////////////////////////////
407 // class PrgHead
408 /////////////////////////////////////////////////////////////////////////////////////////
PrgHead(uint32 id,NodeType t,uint32 data,bool checkScc)409 PrgHead::PrgHead(uint32 id, NodeType t, uint32 data, bool checkScc)
410 	: PrgNode(id, checkScc)
411 	, data_(data), upper_(0), dirty_(0), freeze_(0), isAtom_(t == PrgNode::Atom)  {
412 	struct X { uint64 x; EdgeVec y; uint32 z; };
413 	static_assert(sizeof(PrgHead) == sizeof(X), "Unsupported Alignment");
414 }
415 // Adds the node with given id as a support to this head
416 // and marks the head as dirty so that any duplicates/false/eq
417 // supports are removed once simplify() is called.
addSupport(PrgEdge r,Simplify s)418 void PrgHead::addSupport(PrgEdge r, Simplify s) {
419 	supports_.push_back(r);
420 	if (s == force_simplify) { dirty_ = (supports_.size() > 1); }
421 }
422 // Removes the given node from the set of supports of this head.
removeSupport(PrgEdge r)423 void PrgHead::removeSupport(PrgEdge r) {
424 	if (relevant()) {
425 		supports_.erase(std::remove(supports_.begin(), supports_.end(), r), supports_.end());
426 	}
427 	dirty_ = 1;
428 }
429 
clearSupports()430 void PrgHead::clearSupports() {
431 	supports_.clear();
432 	upper_  = 0;
433 	dirty_  = 0;
434 }
435 // Simplifies the set of predecessors supporting this head.
436 // Removes false/eq supports and returns the number of
437 // different supporting literals in numDiffSupps.
simplifySupports(LogicProgram & prg,bool strong,uint32 * numDiffSupps)438 bool PrgHead::simplifySupports(LogicProgram& prg, bool strong, uint32* numDiffSupps) {
439 	uint32 numLits = supports();
440 	uint32 choices = 0;
441 	if (dirty_ == 1) {
442 		dirty_                  = 0;
443 		numLits                 = 0;
444 		SharedContext& ctx      = *prg.ctx();
445 		EdgeVec::iterator it,n,j= supports_.begin();
446 		for (it = supports_.begin(); it != supports_.end(); ++it) {
447 			PrgNode* x = prg.getSupp(*it);
448 			if (x->relevant() && x->value() != value_false && (!strong || x->hasVar())) {
449 				if      (!x->seen()) { *j++ = *it; x->setSeen(true); }
450 				else if (!choices)   { continue; }
451 				else                 {
452 					for (n = supports_.begin(); n != it && n->node() != it->node(); ++n) {;}
453 					if (*it < *n)      { *n = *it; }
454 					else               { continue; }
455 				}
456 				choices += (it->isBody() && it->isChoice());
457 				if (strong && !ctx.marked(x->literal())) {
458 					++numLits;
459 					ctx.mark(x->literal());
460 				}
461 			}
462 		}
463 		supports_.erase(j, supports_.end());
464 		uint32 dis = 0;
465 		choices    = 0;
466 		for (it = supports_.begin(); it != supports_.end(); ++it) {
467 			PrgNode* x = prg.getSupp(*it);
468 			x->setSeen(false);
469 			if (strong && ctx.marked(x->literal())) { ctx.unmark(x->var()); }
470 			if (it->isChoice()) {
471 				++choices;
472 				dis += it->isDisj();
473 			}
474 		}
475 		numLits += choices;
476 	}
477 	if (numDiffSupps) { *numDiffSupps = numLits; }
478 	return supports() > 0 || prg.assignValue(this, value_false, PrgEdge::noEdge());
479 }
480 
481 // Assigns a variable to this head.
482 // No support: no var and value false
483 // More than one support or type not normal or no eq: new var
484 // Exactly one support that is normal: use that
assignVar(LogicProgram & prg,PrgEdge support,bool allowEq)485 void PrgHead::assignVar(LogicProgram& prg, PrgEdge support, bool allowEq) {
486 	if (hasVar() || !relevant()) { return; }
487 	uint32 numS = supports();
488 	if (numS == 0 && support == PrgEdge::noEdge()) {
489 		prg.assignValue(this, value_false, support);
490 	}
491 	else {
492 		PrgNode* sup = prg.getSupp(support);
493 		bool  newVar = numS > 1 || (!allowEq && Var_t::isAtom(prg.ctx()->varInfo(sup->var()).type()));
494 		if (support.isNormal() && sup->hasVar() && (!newVar || sup->value() == value_true)) {
495 			// head is equivalent to sup
496 			setLiteral(sup->literal());
497 			prg.ctx()->setVarEq(var(), true);
498 			prg.incEqs(Var_t::Hybrid);
499 		}
500 		else {
501 			setLiteral(posLit(prg.ctx()->addVar(Var_t::Atom, 0)));
502 		}
503 	}
504 }
505 
506 // Backpropagates the value of this head to its supports.
backpropagate(LogicProgram & prg,ValueRep val,bool bpFull)507 bool PrgHead::backpropagate(LogicProgram& prg, ValueRep val, bool bpFull) {
508 	bool ok = true;
509 	if (val == value_false) {
510 		// a false head can't have supports
511 		EdgeVec temp; temp.swap(supports_);
512 		markDirty();
513 		for (EdgeIterator it = temp.begin(), end = temp.end(); it != end && ok; ++it) {
514 			if (it->isBody()) {
515 				ok = prg.getBody(it->node())->propagateAssigned(prg, this, it->type());
516 			}
517 			else { assert(it->isDisj());
518 				ok = prg.getDisj(it->node())->propagateAssigned(prg, this, it->type());
519 			}
520 		}
521 	}
522 	else if (val != value_free && supports() == 1 && bpFull) {
523 		// head must be true and only has one support, thus the only support must
524 		// be true, too.
525 		PrgBody* b   = 0;
526 		if (supports_[0].isBody()) {
527 			b   = prg.getBody(supports_[0].node());
528 		}
529 		else if (supports_[0].isDisj()) {
530 			PrgDisj* d = prg.getDisj(supports_[0].node());
531 			if (d->supports() == 1) { b = prg.getBody(d->supps_begin()->node()); }
532 		}
533 		ok = !b || b->value() == val || (b->assignValue(val) && b->propagateValue(prg, bpFull));
534 	}
535 	return ok;
536 }
537 
538 /////////////////////////////////////////////////////////////////////////////////////////
539 // class PrgAtom
540 /////////////////////////////////////////////////////////////////////////////////////////
PrgAtom(uint32 id,bool checkScc)541 PrgAtom::PrgAtom(uint32 id, bool checkScc)
542 	: PrgHead(id, PrgNode::Atom, PrgNode::noScc, checkScc) {
543 	static_assert(sizeof(PrgAtom) == sizeof(PrgHead) + sizeof(LitVec), "Unsupported Alignment");
544 }
545 
setEqGoal(Literal x)546 void PrgAtom::setEqGoal(Literal x) {
547 	if (eq()) {
548 		data_ = x.sign() ? x.var() : noScc;
549 	}
550 }
eqGoal(bool sign) const551 Literal PrgAtom::eqGoal(bool sign) const {
552 	if (!eq() || sign || data_ == noScc) {
553 		return Literal(id(), sign);
554 	}
555 	return negLit(data_);
556 }
557 
558 // Adds a dependency between this atom and the body with
559 // the given id. If pos is true, atom appears positively
560 // in body, otherwise negatively.
addDep(Var bodyId,bool pos)561 void PrgAtom::addDep(Var bodyId, bool pos) {
562 	deps_.push_back(Literal(bodyId, !pos));
563 }
564 
565 // Removes a dependency between this atom and the body with
566 // the given id. If pos is true, atom appears positively
567 // in body, otherwise negatively.
removeDep(Var bodyId,bool pos)568 void PrgAtom::removeDep(Var bodyId, bool pos) {
569 	LitVec::iterator it = std::find(deps_.begin(), deps_.end(), Literal(bodyId, !pos));
570 	if (it != deps_.end()) { deps_.erase(it); }
571 }
572 
573 // Removes the subset of dependencies given by d
clearDeps(Dependency d)574 void PrgAtom::clearDeps(Dependency d) {
575 	if (d == dep_all) {
576 		deps_.clear();
577 	}
578 	else {
579 		bool sign = d == dep_neg;
580 		LitVec::iterator j = deps_.begin();
581 		for (LitVec::iterator it = deps_.begin(), end = deps_.end(); it != end; ++it) {
582 			if (it->sign() != sign) { *j++ = *it; }
583 		}
584 		deps_.erase(j, deps_.end());
585 	}
586 }
587 
hasDep(Dependency d) const588 bool PrgAtom::hasDep(Dependency d) const {
589 	if (d == dep_all) { return !deps_.empty(); }
590 	for (LitVec::const_iterator it = deps_.begin(), end = deps_.end(); it != end; ++it) {
591 		if (static_cast<Dependency>(it->sign()) == d) { return true; }
592 	}
593 	return false;
594 }
595 
inDisj() const596 bool PrgAtom::inDisj() const {
597 	for (EdgeIterator it= supports_.begin(), end = supports_.end(); it != end; ++it) {
598 		if (it->isDisj()) { return true; }
599 	}
600 	return false;
601 }
602 
603 // Propagates the value of this atom to its depending bodies
604 // and, if backpropagation is enabled, to its supporting bodies/disjunctions.
605 // PRE: value() != value_free
propagateValue(LogicProgram & prg,bool backprop)606 bool PrgAtom::propagateValue(LogicProgram& prg, bool backprop) {
607 	ValueRep val = value();
608 	assert(val != value_free);
609 	// propagate value forward
610 	Literal dep = posLit(id());
611 	for (dep_iterator it = deps_.begin(), end = deps_end(); it != end; ++it) {
612 		if (!prg.getBody(it->var())->propagateAssigned(prg, dep ^ it->sign(), val)) {
613 			return false;
614 		}
615 	}
616 	if (inDisj() && prg.isFact(this)) {
617 		// - atom is true, thus all disjunctive rules containing it are superfluous
618 		EdgeVec temp; temp.swap(supports_);
619 		EdgeVec::iterator j = temp.begin();
620 		EdgeType t          = PrgEdge::Choice;
621 		for (EdgeIterator it= temp.begin(), end = temp.end(); it != end; ++it) {
622 			if      (!it->isDisj())                                            { *j++ = *it; }
623 			else if (!prg.getDisj(it->node())->propagateAssigned(prg, this, t)){ return false; }
624 		}
625 		temp.erase(j, temp.end());
626 		supports_.swap(temp);
627 	}
628 	return backpropagate(prg, val, backprop);
629 }
630 
631 // Adds the atom-oriented nogoods for this atom in form of clauses.
632 // Adds the support clause [~a S1...Sn] (where each Si is a supporting node of a)
633 // representing the tableau-rules BTA and FFA.
634 // Furthermore, adds the clause [a ~Bj] representing tableau-rules FTA and BFA
635 // if Bj supports a via a "normal" edge.
addConstraints(const LogicProgram & prg,ClauseCreator & gc)636 bool PrgAtom::addConstraints(const LogicProgram& prg, ClauseCreator& gc) {
637 	SharedContext& ctx  = *prg.ctx();
638 	EdgeVec::iterator j = supports_.begin();
639 	bool           nant = false;
640 	gc.start().add(~literal());
641 	for (EdgeVec::iterator it = supports_.begin(); it != supports_.end(); ++it) {
642 		PrgNode* n = prg.getSupp(*it);
643 		Literal  B = n->literal();
644 		// consider only bodies which are part of the simplified program, i.e.
645 		// are associated with a variable in the solver.
646 		if (n->relevant() && n->hasVar()) {
647 			*j++ = *it;
648 			nant = nant || it->isChoice();
649 			if (!it->isDisj()) { gc.add(B); }
650 			if (it->isNormal() && !ctx.addBinary(literal(), ~B)) { // FTA/BFA
651 				return false;
652 			}
653 		}
654 	}
655 	supports_.erase(j, supports_.end());
656 	if (nant ||	hasDep(PrgAtom::dep_neg)) { ctx.setNant(var(), true); }
657 	return gc.end(ClauseCreator::clause_force_simplify);
658 }
659 /////////////////////////////////////////////////////////////////////////////////////////
660 // class PrgBody
661 /////////////////////////////////////////////////////////////////////////////////////////
PrgBody(uint32 id,LogicProgram & prg,const Potassco::LitSpan & lits,uint32 pos,bool addDeps)662 PrgBody::PrgBody(uint32 id, LogicProgram& prg, const Potassco::LitSpan& lits, uint32 pos, bool addDeps)
663 	: PrgNode(id, true) {
664 	init(Body_t::Normal, toU32(Potassco::size(lits)));
665 	Norm* c = new (data_)Norm();
666 	unsupp_ = pos;
667 	// store B+ followed by B-
668 	Literal* p[2] = {c->lits, c->lits + pos};
669 	for (Potassco::LitSpan::iterator it = Potassco::begin(lits), end = Potassco::end(lits); it != end; ++it) {
670 		POTASSCO_REQUIRE(*it != 0, "body not simplified");
671 		Literal*& n = p[*it < 0];
672 		*n = toLit(*it);
673 		if (addDeps) { prg.getAtom(n->var())->addDep(id, !n->sign()); }
674 		++n;
675 	}
676 }
677 
PrgBody(uint32 id,LogicProgram & prg,const Potassco::Sum_t & sum,bool hasWeights,uint32 pos,bool addDeps)678 PrgBody::PrgBody(uint32 id, LogicProgram& prg, const Potassco::Sum_t& sum, bool hasWeights, uint32 pos, bool addDeps)
679 	: PrgNode(id, true) {
680 	init(hasWeights ? Body_t::Sum : Body_t::Count, toU32(Potassco::size(sum.lits)));
681 	Agg* a = new (data_) Agg();
682 	if (!hasWeights) {
683 		a->bound = sum.bound;
684 		unsupp_  = sum.bound - static_cast<weight_t>(size_ - pos);
685 	}
686 	else {
687 		a->sum  = SumData::create(size_, sum.bound, 0);
688 		unsupp_ = sum.bound;
689 	}
690 	// store B+ followed by B- followed by optional weights
691 	Literal* base = a->lits;
692 	Literal* p[2] = {base, base + pos};
693 	weight_t* weights = hasWeights ? a->sum->weights : 0;
694 	for (Potassco::WeightLitSpan::iterator it = Potassco::begin(sum.lits), end = Potassco::end(sum.lits); it != end; ++it) {
695 		POTASSCO_REQUIRE(it->lit != 0 && it->weight > 0, "body not simplified");
696 		Literal*& n = p[it->lit < 0];
697 		*n = toLit(Potassco::lit(*it));
698 		if (weights) { weights[n - base] = it->weight; a->sum->sumW += it->weight; if (n->sign()) { unsupp_ -= it->weight; } }
699 		if (addDeps) { prg.getAtom(n->var())->addDep(id, !n->sign()); }
700 		++n;
701 	}
702 }
init(Body_t t,uint32 sz)703 void PrgBody::init(Body_t t, uint32 sz) {
704 	size_ = sz, head_ = 0, type_ = t, sBody_ = 0, sHead_ = 0, freeze_ = 0;
705 }
create(LogicProgram & prg,uint32 id,const Rule & r,uint32 pos,bool addDeps)706 PrgBody* PrgBody::create(LogicProgram& prg, uint32 id, const Rule& r, uint32 pos, bool addDeps) {
707 	static_assert(sizeof(PrgBody) == 24 && sizeof(Agg) == sizeof(void*), "unexpected alignment");
708 	PrgBody* ret = 0;
709 	if (r.normal()) {
710 		size_t bytes = sizeof(PrgBody) + (Potassco::size(r.cond) * sizeof(Literal));
711 		ret = new (::operator new(bytes)) PrgBody(id, prg, r.cond, pos, addDeps);
712 	}
713 	else {
714 		const Potassco::Sum_t& sum = r.agg;
715 		size_t bytes = sizeof(PrgBody) + (Potassco::size(r.agg.lits) * sizeof(Literal)) + sizeof(Agg);
716 		ret = new (::operator new(bytes)) PrgBody(id, prg, sum, r.bt == Body_t::Sum, pos, addDeps);
717 		POTASSCO_REQUIRE(ret->bound() > 0 && ret->sumW() > ret->bound(), "body not simplified");
718 	}
719 	if (ret->bound() == 0) {
720 		ret->assignValue(value_true);
721 		ret->markDirty();
722 	}
723 	return ret;
724 }
725 
~PrgBody()726 PrgBody::~PrgBody() {
727 	clearHeads();
728 	if (hasWeights()) {
729 		sumData()->destroy();
730 	}
731 }
732 
destroy()733 void PrgBody::destroy() {
734 	this->~PrgBody();
735 	::operator delete(this);
736 }
737 
create(uint32 size,weight_t b,weight_t s)738 PrgBody::SumData* PrgBody::SumData::create(uint32 size, weight_t b, weight_t s) {
739 	uint32 bytes = sizeof(SumData) + (size * sizeof(weight_t));
740 	SumData* ret = new (::operator new(bytes)) SumData();
741 	ret->bound = b;
742 	ret->sumW = s;
743 	return ret;
744 }
destroy()745 void PrgBody::SumData::destroy() {
746 	::operator delete(this);
747 }
748 
findLit(const LogicProgram & prg,Literal p) const749 uint32 PrgBody::findLit(const LogicProgram& prg, Literal p) const {
750 	for (const Literal* it = goals_begin(), *end = it + size(); it != end; ++it) {
751 		Literal x = prg.getAtom(it->var())->literal();
752 		if (it->sign()) x = ~x;
753 		if (x == p) return static_cast<uint32>(it - goals_begin());
754 	}
755 	return varMax;
756 }
757 
758 // Sets the unsupported counter back to
759 // bound() - negWeight()
resetSupported()760 bool PrgBody::resetSupported() {
761 	unsupp_ = bound();
762 	for (uint32 x = size(); x && goal(--x).sign(); ) {
763 		unsupp_ -= weight(x);
764 	}
765 	return isSupported();
766 }
767 
768 // Removes all heads from this body *without* notifying them
clearHeads()769 void PrgBody::clearHeads() {
770 	if (!isSmallHead()) { delete largeHead(); }
771 	head_ = 0;
772 }
773 
774 // Makes h a head-successor of this body and adds this
775 // body as a support for h.
addHead(PrgHead * h,EdgeType t)776 void PrgBody::addHead(PrgHead* h, EdgeType t) {
777 	assert(relevant() && h->relevant());
778 	PrgEdge fwdEdge = PrgEdge::newEdge(*h, t);
779 	PrgEdge bwdEdge = PrgEdge::newEdge(*this, t);
780 	uint32  numHeads= static_cast<uint32>(heads_end() - heads_begin());
781 	uint32  numSupps= h->supports();
782 	bool    dup     = false;
783 	if (numHeads && numSupps && std::min(numHeads, numSupps) < 10) {
784 		if (numSupps < numHeads) { dup = std::find(h->supps_begin(), h->supps_end(), bwdEdge) != h->supps_end(); }
785 		else                     { dup = std::find(heads_begin(), heads_end(), fwdEdge) != heads_end(); }
786 	}
787 	if (dup) { return; }
788 	addHead(fwdEdge);
789 	h->addSupport(bwdEdge);
790 	// mark head-set as dirty
791 	if (head_ > 1) {  sHead_ = 1; }
792 }
793 
addHead(PrgEdge h)794 void PrgBody::addHead(PrgEdge h) {
795 	if      (head_ < 2u)    { smallHead()[head_++] = h; }
796 	else if (!isSmallHead()){ largeHead()->push_back(h);    }
797 	else                    {
798 		EdgeVec* t  = new EdgeVec(heads_begin(), heads_end());
799 		headData_.ext = t;
800 		head_ = 3u;
801 		t->push_back(h);
802 	}
803 }
804 
removeHead(PrgHead * h,EdgeType t)805 void PrgBody::removeHead(PrgHead* h, EdgeType t) {
806 	PrgEdge x = PrgEdge::newEdge(*h, t);
807 	if (eraseHead(x)) {
808 		h->removeSupport(PrgEdge::newEdge(*this, t)); // also remove back edge
809 	}
810 }
811 
hasHead(PrgHead * h,EdgeType t) const812 bool PrgBody::hasHead(PrgHead* h, EdgeType t) const {
813 	if (!hasHeads()) { return false;  }
814 	PrgEdge x = PrgEdge::newEdge(*h, t);
815 	head_iterator it = sHead_ != 0 || isSmallHead() ? std::find(heads_begin(), heads_end(), x) : std::lower_bound(heads_begin(), heads_end(), x);
816 	return it != heads_end() && *it == x;
817 }
818 
eraseHead(PrgEdge h)819 bool PrgBody::eraseHead(PrgEdge h) {
820 	PrgEdge* it = const_cast<PrgEdge*>(std::find(heads_begin(), heads_end(), h));
821 	if      (it == heads_end()) { return false; }
822 	else if (isSmallHead())     { *it = smallHead()[1]; --head_; }
823 	else                        { largeHead()->erase(it); }
824 	return true;
825 }
826 
toData(const LogicProgram & prg,Potassco::RuleBuilder & out) const827 bool PrgBody::toData(const LogicProgram& prg, Potassco::RuleBuilder& out) const {
828 	Body_t bt = type();
829 	weight_t sum = 0, bound = this->bound();
830 	bt == Body_t::Normal ?  out.startBody() : out.startSum(bound);
831 	for (uint32 i = 0, end = size(); i != end; ++i) {
832 		Potassco::WeightLit_t wl = {toInt(goal(i)), weight(i)};
833 		if (!prg.frozen() || prg.inProgram(Potassco::atom(wl))) {
834 			out.addGoal(wl);
835 			sum += wl.weight;
836 		}
837 		else if (wl.lit < 0)           { bound -= weight(i); }
838 		else if (bt == Body_t::Normal) { return false; }
839 	}
840 	if (bt != Body_t::Normal) {
841 		out.setBound(bound);
842 		if (bound <= 0 || bound >= sum) {
843 			if      (bound > sum) { return false; }
844 			else if (bound <= 0)  { out.clearBody(); }
845 			else                  { out.weaken(Body_t::Normal); }
846 		}
847 	}
848 	return true;
849 }
850 
851 // Simplifies the body by removing assigned atoms & replacing eq atoms.
852 // Checks whether simplified body must be false (CONTRA) or is
853 // structurally equivalent to some other body.
854 // prg    The program containing this body
855 // strong If true, treats atoms that have no variable associated as false.
856 // eqId   The id of a body in prg that is equivalent to this body
simplifyBody(LogicProgram & prg,bool strong,uint32 * eqId)857 bool PrgBody::simplifyBody(LogicProgram& prg, bool strong, uint32* eqId) {
858 	if (eqId)        { *eqId  = id(); }
859 	if (sBody_ == 0) { return true;   }
860 	// update body - compute old hash value
861 	SharedContext& ctx = *prg.ctx();
862 	uint32 oldHash     = 0;
863 	weight_t bound     = this->bound();
864 	weight_t w         = 1, *jw = hasWeights() ? sumData()->weights : 0;
865 	Literal* lits      = goals_begin();
866 	Literal* j         = lits;
867 	AtomState& todo    = prg.atomState();
868 	Var a;
869 	bool mark, isEq;
870 	int todos = 0;
871 	for (Literal* it = j, *end = j + size(); it != end; ++it) {
872 		a       = it->var();
873 		isEq    = a != prg.getRootId(a);
874 		oldHash+= hashLit(*it);
875 		if (isEq) {
876 			prg.getAtom(a)->removeDep(id(), !it->sign()); // remove old edge
877 			*it = prg.getAtom(a)->eqGoal(it->sign());     // replace with eq goal
878 			a   = it->var();                              // and check it
879 		}
880 		Literal aLit = it->sign() ? ~prg.getAtom(a)->literal() : prg.getAtom(a)->literal();
881 		ValueRep v   = prg.getAtom(a)->value();
882 		mark         = strong || prg.getAtom(a)->hasVar();
883 		if (strong && !prg.getAtom(a)->hasVar()) {
884 			v = value_false;
885 		}
886 		if (v == value_weak_true && it->sign()) {
887 			v = value_true;
888 		}
889 		if (v == value_true || v == value_false) { // truth value is known - remove subgoal
890 			if (v == trueValue(*it)) {
891 				// subgoal is true: decrease necessary lower bound
892 				bound -= weight(uint32(it - lits));
893 			}
894 			prg.getAtom(a)->removeDep(id(), !it->sign());
895 		}
896 		else if (!mark || !ctx.marked(aLit)) {
897 			if (mark) { ctx.mark(aLit); }
898 			if (isEq) { // remember to add edge for new goal later
899 				todo.addToBody(Literal(*it));
900 				++todos;
901 			}
902 			*j++ = *it;  // copy literal and optionally weight
903 			if (jw) { *jw++ = weight(uint32(it - lits)); }
904 		}
905 		else { // body contains aLit more than once
906 			if (type() != Body_t::Normal) { // merge subgoal
907 				if (!jw) {
908 					SumData* sum = SumData::create(size(), this->bound(), this->sumW());
909 					std::fill_n(sum->weights, size(), w = 1);
910 					aggData().sum = sum;
911 					type_ = Body_t::Sum;
912 					jw    = sum->weights + (it - lits);
913 				}
914 				else { w = weight(uint32(it - lits)); }
915 				uint32 pos = findLit(prg, aLit);
916 				sumData()->weights[pos] += w;
917 			}
918 			else { // ignore if normal
919 				--bound;
920 				if (!isEq) { // remove edge
921 					if (todo.inBody(*it)) { todo.clearBody(*it); --todos; }
922 					else                  { prg.getAtom(it->var())->removeDep(id(), !it->sign()); }
923 				}
924 			}
925 		}
926 	}
927 	// unmark atoms, compute new hash value,
928 	// and restore pos | neg partition in case
929 	// we changed some positive goals to negative ones
930 	size_          = j - lits;
931 	if (jw) jw     = sumData()->weights;
932 	uint32 newHash = 0;
933 	weight_t sumW  = 0, reachW = 0;
934 	for (uint32 p = 0, n = size_, i, h; p < n;) {
935 		if      (!lits[p].sign())      { h = hashLit(lits[i = p++]);  }
936 		else if (lits[n-1].sign())     { h = hashLit(lits[i = --n]);  }
937 		else /* restore pos|neg order */ {
938 			std::swap(lits[p], lits[n-1]);
939 			if (jw) { std::swap(jw[p], jw[n-1]); }
940 			continue;
941 		}
942 		a = lits[i].var();
943 		if (todos && todo.inBody(lits[i])) {
944 			prg.getAtom(a)->addDep(id(), !lits[i].sign());
945 			todo.clearBody(lits[i]);
946 			--todos;
947 		}
948 		Var v   = prg.getAtom(a)->var();
949 		w       = !jw ? 1 : jw[i];
950 		sumW   += w;
951 		reachW += w;
952 		if (ctx.marked(posLit(v)) && ctx.marked(negLit(v))) {
953 			// body contains aLit and ~aLit
954 			if  (!hasWeights()) { reachW -= 1; }
955 			else {
956 				Literal other = prg.getAtom(a)->literal() ^ !goal(i).sign();
957 				uint32 pos    = findLit(prg, other);
958 				assert(pos != varMax && pos != i);
959 				reachW       -= std::min(w, jw[pos]);
960 			}
961 		}
962 		ctx.unmark( v );
963 		newHash += h;
964 	}
965 	bool ok = normalize(prg, bound, sumW, reachW, newHash);
966 	if (ok) {
967 		Var xId = id();
968 		if (oldHash != newHash) {
969 			xId = prg.update(this, oldHash, newHash);
970 		}
971 		if (eqId) { *eqId = xId != varMax ? xId : id(); }
972 	}
973 	if (strong) sBody_ = 0;
974 	return ok && (value() == value_free || propagateValue(prg));
975 }
976 
normalize(const LogicProgram & prg,weight_t bound,weight_t sumW,weight_t reachW,uint32 & hashOut)977 bool PrgBody::normalize(const LogicProgram& prg, weight_t bound, weight_t sumW, weight_t reachW, uint32& hashOut) {
978 	Body_t nt = (sumW == bound || size() == 1) ? Body_t::Normal : type();
979 	bool ok = true;
980 	if (sumW >= bound && type() != Body_t::Normal) {
981 		if (hasWeights()) {
982 			sumData()->bound = bound;
983 			sumData()->sumW  = sumW;
984 		}
985 		else {
986 			aggData().bound = bound;
987 		}
988 	}
989 	if (bound <= 0) {
990 		for (uint32 i = 0, myId = id(); i != size_; ++i) {
991 			prg.getAtom(goal(i).var())->removeDep(myId, !goal(i).sign());
992 		}
993 		size_ = 0; hashOut = 0, unsupp_ = 0;
994 		nt    = Body_t::Normal;
995 		ok    = assignValue(value_true);
996 	}
997 	else if (reachW < bound) {
998 		ok     = assignValue(value_false);
999 		sHead_ = 1;
1000 		markRemoved();
1001 	}
1002 	if (nt != type()) {
1003 		assert(nt == Body_t::Normal);
1004 		Literal* from = aggData().lits;
1005 		if (hasWeights()) {
1006 			sumData()->destroy();
1007 		}
1008 		Literal* to = (new (data_)Norm())->lits;
1009 		std::copy(from, from+size(), to);
1010 		type_ = nt;
1011 	}
1012 	return ok;
1013 }
1014 
1015 // Marks the set of heads in rs and removes
1016 // any duplicate heads.
prepareSimplifyHeads(LogicProgram & prg,AtomState & rs)1017 void PrgBody::prepareSimplifyHeads(LogicProgram& prg, AtomState& rs) {
1018 	head_iterator end = heads_end();
1019 	uint32 size       = 0;
1020 	for (PrgEdge*  j  = const_cast<PrgEdge*>(heads_begin()); j != end;) {
1021 		if (!rs.inHead(*j)) {
1022 			rs.addToHead(*j);
1023 			++j; ++size;
1024 		}
1025 		else {
1026 			prg.getHead(*j)->markDirty();
1027 			*j = *--end;
1028 		}
1029 	}
1030 	if (isSmallHead()) { head_ = size; }
1031 	else               { shrinkVecTo(*largeHead(), size); }
1032 }
1033 
1034 // Simplifies the heads of this body wrt target.
1035 // Removes superfluous/eq/unsupported heads and checks for self-blocking
1036 // situations.
1037 // PRE: prepareSimplifyHeads was called
simplifyHeadsImpl(LogicProgram & prg,PrgBody & target,AtomState & rs,bool strong)1038 bool PrgBody::simplifyHeadsImpl(LogicProgram& prg, PrgBody& target, AtomState& rs, bool strong) {
1039 	PrgHead* cur;
1040 	PrgEdge* j     = const_cast<PrgEdge*>(heads_begin());
1041 	uint32 newSize = 0;
1042 	bool merge     = this != &target;
1043 	bool block     = value() == value_false || (merge && target.value() == value_false);
1044 	for (head_iterator it = heads_begin(), end = heads_end(); it != end; ++it) {
1045 		cur  = prg.getHead(*it);
1046 		block= block || target.blockedHead(*it, rs);
1047 		if (!cur->relevant() || (strong && !cur->hasVar())
1048 			|| block || target.superfluousHead(prg, cur, *it, rs) || cur->value() == value_false) {
1049 			// remove superfluous and unsupported heads
1050 			cur->removeSupport(PrgEdge::newEdge(*this, it->type()));
1051 			rs.clearHead(*it);
1052 			block = block || (cur->value() == value_false && it->type() == PrgEdge::Normal);
1053 		}
1054 		else {
1055 			*j++ = *it;
1056 			++newSize;
1057 			if (merge) { target.addHead(cur, it->type()); }
1058 		}
1059 	}
1060 	if (isSmallHead()) { head_ = newSize; }
1061 	else               { shrinkVecTo(*largeHead(), newSize); }
1062 	return !block;
1063 }
1064 
simplifyHeads(LogicProgram & prg,bool strong)1065 bool PrgBody::simplifyHeads(LogicProgram& prg, bool strong) {
1066 	if (sHead_ == 0) { return true; }
1067 	return PrgBody::mergeHeads(prg, *this, strong);
1068 }
1069 
mergeHeads(LogicProgram & prg,PrgBody & heads,bool strong,bool simplify)1070 bool PrgBody::mergeHeads(LogicProgram& prg, PrgBody& heads, bool strong, bool simplify) {
1071 	AtomState& rs = prg.atomState();
1072 	bool       ok = true;
1073 	assert((this == &heads || heads.sHead_ == 0) && "Heads to merge not simplified!");
1074 	if (simplify || &heads == this) {
1075 		// mark the body literals so that we can easily detect superfluous atoms
1076 		// and selfblocking situations.
1077 		for (const Literal* it = goals_begin(), *end = it + size(); it != end; ++it) {
1078 			rs.addToBody(*it);
1079 		}
1080 		// remove duplicate/superfluous heads & check for blocked atoms
1081 		prepareSimplifyHeads(prg, rs);
1082 		if (this == &heads) {
1083 			ok = simplifyHeadsImpl(prg, *this, rs, strong);
1084 		}
1085 		else {
1086 			heads.prepareSimplifyHeads(prg, rs);
1087 			if (!simplifyHeadsImpl(prg, *this, rs, strong) && !assignValue(value_false)) {
1088 				clearRule(rs);
1089 				return false;
1090 			}
1091 			ok = heads.simplifyHeadsImpl(prg, *this, rs, strong);
1092 			if (!ok && (!heads.assignValue(value_false) || !heads.propagateValue(prg, false))) {
1093 				clearRule(rs);
1094 				return false;
1095 			}
1096 		}
1097 		// clear temporary flags & reestablish ordering
1098 		std::sort(const_cast<PrgEdge*>(heads_begin()), const_cast<PrgEdge*>(heads_end()));
1099 		clearRule(rs);
1100 		sHead_ = 0;
1101 	}
1102 	else if (relevant()) {
1103 		for (head_iterator it = heads.heads_begin(), end = heads.heads_end(); it != end; ++it) {
1104 			PrgHead* h = prg.getHead(*it);
1105 			if (h->relevant()) { addHead(h, it->type()); }
1106 		}
1107 	}
1108 	return ok || (assignValue(value_false) && propagateValue(prg));
1109 }
1110 
1111 // Checks whether the head is superfluous w.r.t this body, i.e.
1112 //  - is needed to satisfy the body
1113 //  - it appears in the body and is a choice
1114 //  - it is a disjunction and one of the atoms is needed to satisfy the body
superfluousHead(const LogicProgram & prg,const PrgHead * head,PrgEdge it,const AtomState & rs) const1115 bool PrgBody::superfluousHead(const LogicProgram& prg, const PrgHead* head, PrgEdge it, const AtomState& rs) const {
1116 	if (it.isAtom()) {
1117 		// the head is an atom
1118 		uint32 atomId = it.node();
1119 		weight_t    w = 1;
1120 		if (rs.inBody(posLit(atomId))) {
1121 			if (hasWeights()) {
1122 				const Literal* lits = aggData().lits;
1123 				const Literal* x    = std::find(lits, lits + size(), posLit(atomId));
1124 				assert(x != lits + size());
1125 				w = sumData()->weights[ x - lits ];
1126 			}
1127 			if (it.isChoice() || (sumW() - w) < bound()) {
1128 				return true;
1129 			}
1130 		}
1131 		return it.isChoice() && (rs.inBody(negLit(atomId)) || rs.inHead(atomId));
1132 	}
1133 	else { assert(it.isDisj());
1134 		// check each contained atom
1135 		const PrgDisj* dis = static_cast<const PrgDisj*>(head);
1136 		for (PrgDisj::atom_iterator aIt = dis->begin(), aEnd = dis->end(); aIt != aEnd; ++aIt) {
1137 			if (rs.inBody(posLit(*aIt)) || rs.inHead(*aIt)) {
1138 				return true;
1139 			}
1140 			if (prg.isFact(prg.getAtom(*aIt))) {
1141 				return true;
1142 			}
1143 		}
1144 		// check for subsumption
1145 		if (prg.options().iters == LogicProgram::AspOptions::MAX_EQ_ITERS) {
1146 			for (head_iterator hIt = heads_begin(), hEnd = heads_end(); hIt != hEnd; ++hIt) {
1147 				if (hIt->isDisj() && prg.getDisj(hIt->node())->size() < dis->size()) {
1148 					const PrgDisj* other = prg.getDisj(hIt->node());
1149 					for (PrgDisj::atom_iterator a = other->begin(), aEnd = other->end(); a != aEnd && other; ++a) {
1150 						if (std::find(dis->begin(), dis->end(), *a) == dis->end()) {
1151 							other = 0;
1152 						}
1153 					}
1154 					if (other && other->size() > 0) {
1155 						return true;
1156 					}
1157 				}
1158 			}
1159 		}
1160 	}
1161 	return false;
1162 }
1163 
1164 // Checks whether the rule it.node() :- *this is selfblocking, i.e.
1165 // from TB follows conflict
blockedHead(PrgEdge it,const AtomState & rs) const1166 bool PrgBody::blockedHead(PrgEdge it, const AtomState& rs) const {
1167 	if (it.isAtom() && it.isNormal() && rs.inBody(negLit(it.node()))) {
1168 		weight_t w = 1;
1169 		if (hasWeights()) {
1170 			const Literal* lits = aggData().lits;
1171 			const Literal* x    = std::find(lits, lits + size(), negLit(it.node()));
1172 			assert(x != lits + size());
1173 			w = sumData()->weights[ x - lits ];
1174 		}
1175 		return (sumW() - w) < bound();
1176 	}
1177 	return false;
1178 }
1179 
assignVar(LogicProgram & prg)1180 void PrgBody::assignVar(LogicProgram& prg) {
1181 	if (hasVar() || !relevant()) { return; }
1182 	uint32 size = this->size();
1183 	if (size == 0 || value() == value_true) {
1184 		setLiteral(lit_true());
1185 	}
1186 	else if (size == 1 && prg.getAtom(goal(0).var())->hasVar()) {
1187 		Literal x = prg.getAtom(goal(0).var())->literal();
1188 		setLiteral(goal(0).sign() ? ~x : x);
1189 		prg.ctx()->setVarEq(var(), true);
1190 		prg.incEqs(Var_t::Hybrid);
1191 	}
1192 	else if (value() != value_false) {
1193 		setLiteral(posLit(prg.ctx()->addVar(Var_t::Body, 0)));
1194 	}
1195 	else {
1196 		setLiteral(lit_false());
1197 	}
1198 }
1199 
propagateSupported(Var v)1200 bool PrgBody::propagateSupported(Var v) {
1201 	weight_t w = 1;
1202 	if (hasWeights()) {
1203 		uint32 pos = (uint32)std::distance(goals_begin(), std::find(goals_begin(), goals_end(), posLit(v)));
1204 		w          = weight(pos);
1205 	}
1206 	return (unsupp_ -= w) <= 0;
1207 }
1208 
propagateAssigned(LogicProgram & prg,Literal p,ValueRep v)1209 bool PrgBody::propagateAssigned(LogicProgram& prg, Literal p, ValueRep v) {
1210 	if (!relevant()) return true;
1211 	assert(std::find(goals_begin(), goals_end(), p) != goals_end());
1212 	markDirty();
1213 	ValueRep x = v == value_weak_true ? value_true : v;
1214 	weight_t w = 1; // TODO: find weight of p for weight rule
1215 	if (x == falseValue(p) && (sumW() - w) < bound() && value() != value_false) {
1216 		return assignValue(value_false) && propagateValue(prg);
1217 	}
1218 	else if (x == trueValue(p) && (bound() - w) <= 0 && value() != value_weak_true) {
1219 		return assignValue(value_weak_true) && propagateValue(prg);
1220 	}
1221 	return true;
1222 }
1223 
propagateAssigned(LogicProgram & prg,PrgHead * h,EdgeType t)1224 bool PrgBody::propagateAssigned(LogicProgram& prg, PrgHead* h, EdgeType t) {
1225 	if (!relevant()) return true;
1226 	markHeadsDirty();
1227 	if (h->value() == value_false && eraseHead(PrgEdge::newEdge(*h, t)) && t == PrgEdge::Normal) {
1228 		return value() == value_false || (assignValue(value_false) && propagateValue(prg));
1229 	}
1230 	return true;
1231 }
1232 
propagateValue(LogicProgram & prg,bool backprop)1233 bool PrgBody::propagateValue(LogicProgram& prg, bool backprop) {
1234 	ValueRep val = value();
1235 	assert(value() != value_free);
1236 	// propagate value forward
1237 	for (head_iterator h = heads_begin(), end = heads_end(); h != end; ++h) {
1238 		PrgHead* head = prg.getHead(*h);
1239 		PrgEdge  supp = PrgEdge::newEdge(*this, h->type());
1240 		if (val == value_false) {
1241 			head->removeSupport(supp);
1242 		}
1243 		else if (!h->isChoice() && head->value() != val && !prg.assignValue(head, val, supp)) {
1244 			return false;
1245 		}
1246 	}
1247 	if (val == value_false) { clearHeads(); }
1248 	// propagate value backward
1249 	if (backprop && relevant()) {
1250 		const uint32 W = hasWeights();
1251 		weight_t MAX_W = 1;
1252 		weight_t* wPos = W == 0 ? &MAX_W : sumData()->weights;
1253 		MAX_W          = *std::max_element(wPos, wPos + (size() * W));
1254 		weight_t bound = value()==value_false ? this->bound() : (sumW() - this->bound())+1;
1255 		if (MAX_W >= bound) {
1256 			ValueRep goalVal;
1257 			for (const Literal* it = goals_begin(), *end = goals_end(); it != end; ++it) {
1258 				if ((bound - *wPos) <= 0) {
1259 					if (!it->sign()) { goalVal = val; }
1260 					else             { goalVal = val == value_false ? value_weak_true : value_false; }
1261 					if (!prg.assignValue(prg.getAtom(it->var()), goalVal, PrgEdge::noEdge())) {
1262 						return false;
1263 					}
1264 				}
1265 				wPos += W;
1266 			}
1267 		}
1268 	}
1269 	return true;
1270 }
propagateValue(LogicProgram & prg)1271 bool PrgBody::propagateValue(LogicProgram& prg) {
1272 	return propagateValue(prg, prg.options().backprop != 0);
1273 }
1274 
1275 // Adds nogoods for the tableau-rules FFB and BTB as well as FTB, BFB.
1276 // For normal bodies, clauses are used, i.e:
1277 //   FFB and BTB:
1278 //     - a binary clause [~b s] for every positive subgoal of b
1279 //     - a binary clause [~b ~n] for every negative subgoal of b
1280 //   FTB and BFB:
1281 //     - a clause [b ~s1...~sn n1..nn] where si is a positive and ni a negative subgoal
1282 // For count/sum bodies, a weight constraint is created
addConstraints(const LogicProgram & prg,ClauseCreator & gc)1283 bool PrgBody::addConstraints(const LogicProgram& prg, ClauseCreator& gc) {
1284 	if (type() == Body_t::Normal) {
1285 		bool    taut= false;
1286 		Literal negB= ~literal();
1287 		gc.start().add(literal());
1288 		for (const Literal* it = goals_begin(), *end = goals_end(); it != end; ++it) {
1289 			Literal li = prg.getAtom(it->var())->literal() ^ it->sign();
1290 			if (li == literal()) { taut = true; continue; }
1291 			if (!prg.ctx()->addBinary(negB, li)) { // [~B li]
1292 				return false;
1293 			}
1294 			if (li.var() != negB.var()) { gc.add(~li); }  // [B v ~l1 v ... v ~ln]
1295 		}
1296 		return taut || gc.end();
1297 	}
1298 	WeightLitVec lits;
1299 	for (uint32 i = 0, end = size_; i != end; ++i) {
1300 		Literal eq = prg.getAtom(goal(i).var())->literal() ^ goal(i).sign();
1301 		lits.push_back(WeightLiteral(eq, weight(i)));
1302 	}
1303 	return WeightConstraint::create(*prg.ctx()->master(), literal(), lits, bound()).ok();
1304 }
1305 
1306 // Returns the SCC of body B, i.e.
1307 // - scc if exist atom a in B.heads(), a' in B+, s.th. a.scc == a'.scc
1308 // - noScc otherwise
scc(const LogicProgram & prg) const1309 uint32 PrgBody::scc(const LogicProgram& prg) const {
1310 	uint64 sccMask = 0;
1311 	uint32 end     = size();
1312 	uint32 scc     = PrgNode::noScc;
1313 	bool   large   = false;
1314 	for (uint32 i  = 0; i != end; ++i) {
1315 		if      (goal(i).sign()) {
1316 			end = i;
1317 			break;
1318 		}
1319 		else if ((scc = prg.getAtom(goal(i).var())->scc()) != PrgNode::noScc) {
1320 			sccMask |= uint64(1) << (scc & 63);
1321 			large   |= scc > 63;
1322 		}
1323 	}
1324 	if (sccMask != 0) {
1325 		PrgDisj::atom_iterator aIt = 0, aEnd = 0;
1326 		Var head;
1327 		for (head_iterator h = heads_begin(), hEnd = heads_end(); h != hEnd; ++h) {
1328 			if (h->isAtom()) { head = h->node(); aIt = &head, aEnd = aIt + 1; }
1329 			else             { PrgDisj* d = prg.getDisj(h->node()); aIt = d->begin(), aEnd = d->end(); }
1330 			for (; aIt != aEnd; ++aIt) {
1331 				scc = prg.getAtom(*aIt)->scc();
1332 				if (scc != PrgNode::noScc && (sccMask & (uint64(1) << (scc&63))) != 0) {
1333 					if (!large) { return scc; }
1334 					for (uint32 j = 0; j != end; ++j) {
1335 						if (scc == prg.getAtom(goal(j).var())->scc()) { return scc; }
1336 					}
1337 				}
1338 			}
1339 		}
1340 	}
1341 	return PrgNode::noScc;
1342 }
1343 
1344 /////////////////////////////////////////////////////////////////////////////////////////
1345 // class PrgDisj
1346 //
1347 // Head of a disjunctive rule
1348 /////////////////////////////////////////////////////////////////////////////////////////
create(uint32 id,const Potassco::AtomSpan & head)1349 PrgDisj* PrgDisj::create(uint32 id, const Potassco::AtomSpan& head) {
1350 	void* m = ::operator new(sizeof(PrgDisj) + (Potassco::size(head)*sizeof(Var)));
1351 	return new (m) PrgDisj(id, head);
1352 }
1353 
PrgDisj(uint32 id,const Potassco::AtomSpan & head)1354 PrgDisj::PrgDisj(uint32 id, const Potassco::AtomSpan& head) : PrgHead(id, PrgNode::Disj, (uint32)Potassco::size(head)) {
1355 	std::copy(Potassco::begin(head), Potassco::end(head), atoms_);
1356 	std::sort(atoms_, atoms_+size());
1357 }
~PrgDisj()1358 PrgDisj::~PrgDisj() {}
destroy()1359 void PrgDisj::destroy() {
1360 	this->~PrgDisj();
1361 	::operator delete(this);
1362 }
1363 
detach(LogicProgram & prg)1364 void PrgDisj::detach(LogicProgram& prg) {
1365 	PrgEdge edge = PrgEdge::newEdge(*this, PrgEdge::Choice);
1366 	for (atom_iterator it = begin(), end = this->end(); it != end; ++it) {
1367 		prg.getAtom(*it)->removeSupport(edge);
1368 	}
1369 	EdgeVec temp; temp.swap(supports_);
1370 	for (PrgDisj::sup_iterator it = temp.begin(), end = temp.end(); it != end; ++it) {
1371 		prg.getBody(it->node())->removeHead(this, PrgEdge::Normal);
1372 	}
1373 	setInUpper(false);
1374 	markRemoved();
1375 }
1376 
propagateAssigned(LogicProgram & prg,PrgHead * head,EdgeType t)1377 bool PrgDisj::propagateAssigned(LogicProgram& prg, PrgHead* head, EdgeType t) {
1378 	assert(head->isAtom() && t == PrgEdge::Choice);
1379 	if (prg.isFact(static_cast<PrgAtom*>(head)) || head->value() == value_false) {
1380 		atom_iterator it = std::find(begin(), end(), head->id());
1381 		if (it != end()) {
1382 			if      (head->value() == value_true) { detach(prg); }
1383 			else if (head->value() == value_false){
1384 				head->removeSupport(PrgEdge::newEdge(*this, t));
1385 				std::copy(it+1, end(), const_cast<Var*>(it));
1386 				if (--data_ == 1) {
1387 					PrgAtom* last = prg.getAtom(*begin());
1388 					EdgeVec temp;
1389 					clearSupports(temp);
1390 					for (EdgeVec::const_iterator eIt = temp.begin(), eEnd = temp.end(); eIt != eEnd; ++eIt) {
1391 						prg.getBody(eIt->node())->removeHead(this, PrgEdge::Normal);
1392 						prg.getBody(eIt->node())->addHead(last, PrgEdge::Normal);
1393 					}
1394 					detach(prg);
1395 				}
1396 			}
1397 		}
1398 	}
1399 	return true;
1400 }
1401 } }
1402