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 != ⌖
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