1 //
2 // Copyright (c) 2013-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.h>
25 #include <clasp/shared_context.h>
26 #include <clasp/solver.h>
27 #include <clasp/minimize_constraint.h>
28 #include <clasp/util/misc_types.h>
29 #include <clasp/asp_preprocessor.h>
30 #include <clasp/clause.h>
31 #include <clasp/dependency_graph.h>
32 #include <clasp/parser.h>
33 #include <potassco/theory_data.h>
34 #include <potassco/string_convert.h>
35 #include <stdexcept>
36 #include <cctype>
37 #include <cstdio>
38 namespace Clasp { namespace Asp {
39 /////////////////////////////////////////////////////////////////////////////////////////
40 // Statistics
41 /////////////////////////////////////////////////////////////////////////////////////////
42 #define RK(x) RuleStats::x
toStr(int k)43 const char* RuleStats::toStr(int k) {
44 POTASSCO_ASSERT(k >= 0 && uint32(k) <= numKeys(), "Invalid key");
45 switch (k) {
46 case Normal : return "Normal";
47 case Choice : return "Choice";
48 case Minimize : return "Minimize";
49 case Acyc : return "Acyc";
50 case Heuristic: return "Heuristic";
51 default: return "None";
52 }
53 }
sum() const54 uint32 RuleStats::sum() const {
55 return std::accumulate(key, key + numKeys(), uint32(0));
56 }
toStr(int t)57 const char* BodyStats::toStr(int t) {
58 POTASSCO_ASSERT(t >= 0 && uint32(t) < numKeys(), "Invalid body type!");
59 switch (t) {
60 default : return "Normal";
61 case Body_t::Count: return "Count";
62 case Body_t::Sum : return "Sum";
63 }
64 }
sum() const65 uint32 BodyStats::sum() const {
66 return std::accumulate(key, key + numKeys(), uint32(0));
67 }
68
69 namespace {
70 template <unsigned i>
sumBodies(const LpStats * self)71 double sumBodies(const LpStats* self) { return self->bodies[i].sum(); }
72 template <unsigned i>
sumRules(const LpStats * self)73 double sumRules(const LpStats* self) { return self->rules[i].sum(); }
sumEqs(const LpStats * self)74 double sumEqs(const LpStats* self) { return self->eqs(); }
75 }
76 #define LP_STATS( APPLY ) \
77 APPLY("atoms" , VALUE(atoms)) \
78 APPLY("atoms_aux" , VALUE(auxAtoms)) \
79 APPLY("disjunctions" , VALUE(disjunctions[0])) \
80 APPLY("disjunctions_non_hcf", VALUE(disjunctions[1])) \
81 APPLY("bodies" , FUNC(sumBodies<0>)) \
82 APPLY("bodies_tr" , FUNC(sumBodies<1>)) \
83 APPLY("sum_bodies" , VALUE(bodies[0][Body_t::Sum])) \
84 APPLY("sum_bodies_tr" , VALUE(bodies[1][Body_t::Sum])) \
85 APPLY("count_bodies" , VALUE(bodies[0][Body_t::Count]))\
86 APPLY("count_bodies_tr" , VALUE(bodies[1][Body_t::Count]))\
87 APPLY("sccs" , VALUE(sccs)) \
88 APPLY("sccs_non_hcf" , VALUE(nonHcfs)) \
89 APPLY("gammas" , VALUE(gammas)) \
90 APPLY("ufs_nodes" , VALUE(ufsNodes)) \
91 APPLY("rules" , FUNC(sumRules<0>)) \
92 APPLY("rules_normal" , VALUE(rules[0][RK(Normal)])) \
93 APPLY("rules_choice" , VALUE(rules[0][RK(Choice)])) \
94 APPLY("rules_minimize" , VALUE(rules[0][RK(Minimize)])) \
95 APPLY("rules_acyc" , VALUE(rules[0][RK(Acyc)])) \
96 APPLY("rules_heuristic" , VALUE(rules[0][RK(Heuristic)]))\
97 APPLY("rules_tr" , FUNC(sumRules<1>)) \
98 APPLY("rules_tr_normal" , VALUE(rules[1][RK(Normal)])) \
99 APPLY("rules_tr_choice" , VALUE(rules[1][RK(Choice)])) \
100 APPLY("rules_tr_minimize" , VALUE(rules[1][RK(Minimize)])) \
101 APPLY("rules_tr_acyc" , VALUE(rules[1][RK(Acyc)])) \
102 APPLY("rules_tr_heuristic" , VALUE(rules[1][RK(Heuristic)]))\
103 APPLY("eqs" , FUNC(sumEqs)) \
104 APPLY("eqs_atom" , VALUE(eqs_[Var_t::Atom-1])) \
105 APPLY("eqs_body" , VALUE(eqs_[Var_t::Body-1])) \
106 APPLY("eqs_other" , VALUE(eqs_[Var_t::Hybrid-1]))
107
reset()108 void LpStats::reset() {
109 std::memset(this, 0, sizeof(LpStats));
110 }
111
accu(const LpStats & o)112 void LpStats::accu(const LpStats& o) {
113 atoms += o.atoms;
114 auxAtoms += o.auxAtoms;
115 ufsNodes += o.ufsNodes;
116 if (sccs == PrgNode::noScc || o.sccs == PrgNode::noScc) {
117 sccs = o.sccs;
118 nonHcfs = o.nonHcfs;
119 }
120 else {
121 sccs += o.sccs;
122 nonHcfs+= o.nonHcfs;
123 }
124 for (int i = 0; i != 2; ++i) {
125 disjunctions[i] += o.disjunctions[i];
126 for (uint32 k = 0; k != BodyStats::numKeys(); ++k) {
127 bodies[i][k] += o.bodies[i][k];
128 }
129 for (uint32 k = 0; k != RuleStats::numKeys(); ++k) {
130 rules[i][k] += o.rules[i][k];
131 }
132 }
133 for (int i = 0; i != sizeof(eqs_)/sizeof(eqs_[0]); ++i) {
134 eqs_[i] += o.eqs_[i];
135 }
136 }
137
138 static const char* lpStats_s[] = {
139 #define KEY(x, y) x,
140 LP_STATS(KEY)
141 #undef KEY
142 "lp"
143 };
size()144 uint32 LpStats::size() {
145 return (sizeof(lpStats_s)/sizeof(const char*))-1;
146 }
key(uint32 i)147 const char* LpStats::key(uint32 i) {
148 return i < size() ? lpStats_s[i] : throw std::out_of_range(POTASSCO_FUNC_NAME);
149 }
at(const char * k) const150 StatisticObject LpStats::at(const char* k) const {
151 #define MAP_IF(x, A) if (std::strcmp(k, x) == 0) return A;
152 #define VALUE(X) StatisticObject::value(&(X))
153 #define FUNC(F) StatisticObject::value<LpStats, F>(this)
154 LP_STATS(MAP_IF)
155 #undef VALUE
156 #undef FUNC
157 #undef MAP_IF
158 throw std::out_of_range(POTASSCO_FUNC_NAME);
159 }
160 #undef LP_STATS
161 /////////////////////////////////////////////////////////////////////////////////////////
162 // class LogicProgram
163 /////////////////////////////////////////////////////////////////////////////////////////
164 static PrgAtom trueAtom_g(0, false);
165 static const uint32 falseId = PrgNode::noNode;
166 static const uint32 bodyId = PrgNode::noNode + 1;
167 static bool init_trueAtom_g = (trueAtom_g.setEq(0), true);
isAtom(Id_t uid)168 inline bool isAtom(Id_t uid) { return Potassco::atom(Potassco::lit(uid)) < bodyId; }
isBody(Id_t uid)169 inline bool isBody(Id_t uid) { return Potassco::atom(Potassco::lit(uid)) >= bodyId; }
nodeId(Id_t uid)170 inline Id_t nodeId(Id_t uid) { return Potassco::atom(Potassco::lit(uid)) - (isAtom(uid) ? 0 : bodyId); }
signId(Id_t uid)171 inline bool signId(Id_t uid) { return Potassco::lit(uid) < 0; }
172
173 namespace {
174 typedef std::pair<Atom_t, Potassco::Value_t> AtomVal;
encodeExternal(Atom_t a,Potassco::Value_t value)175 inline uint32 encodeExternal(Atom_t a, Potassco::Value_t value) {
176 return (a << 2) | static_cast<uint32>(value);
177 }
decodeExternal(uint32 x)178 inline AtomVal decodeExternal(uint32 x) {
179 return AtomVal(x >> 2, static_cast<Potassco::Value_t>(x & 3u));
180 }
181 struct LessBodySize {
LessBodySizeClasp::Asp::__anona6df25c30211::LessBodySize182 LessBodySize(const BodyList& bl) : bodies_(&bl) {}
operator ()Clasp::Asp::__anona6df25c30211::LessBodySize183 bool operator()(Id_t b1, Id_t b2 ) const {
184 return (*bodies_)[b1]->size() < (*bodies_)[b2]->size()
185 || ((*bodies_)[b1]->size() == (*bodies_)[b2]->size() && (*bodies_)[b1]->type() < (*bodies_)[b2]->type());
186 }
187 private:
188 const BodyList* bodies_;
189 };
190 // Adds nogoods representing this node to the solver.
191 template <class NT>
toConstraint(NT * node,const LogicProgram & prg,ClauseCreator & c)192 bool toConstraint(NT* node, const LogicProgram& prg, ClauseCreator& c) {
193 if (node->value() != value_free && !prg.ctx()->addUnary(node->trueLit())) {
194 return false;
195 }
196 return !node->relevant() || node->addConstraints(prg, c);
197 }
198 }
199
LogicProgram()200 LogicProgram::LogicProgram() : theory_(0), input_(1, UINT32_MAX), auxData_(0), incData_(0) {
201 POTASSCO_ASSERT(init_trueAtom_g, "invalid static init");
202 }
~LogicProgram()203 LogicProgram::~LogicProgram() { dispose(true); }
Incremental()204 LogicProgram::Incremental::Incremental() : startScc(0) {}
dispose(bool force)205 void LogicProgram::dispose(bool force) {
206 // remove rules
207 std::for_each(bodies_.begin(), bodies_.end(), DestroyObject());
208 std::for_each(disjunctions_.begin(), disjunctions_.end(), DestroyObject());
209 std::for_each(extended_.begin(), extended_.end(), DeleteObject());
210 std::for_each(minimize_.begin(), minimize_.end(), DeleteObject());
211 PodVector<ShowPair>::destruct(show_);
212 delete auxData_; auxData_ = 0;
213 MinList().swap(minimize_);
214 RuleList().swap(extended_);
215 BodyList().swap(bodies_);
216 DisjList().swap(disjunctions_);
217 bodyIndex_.clear();
218 disjIndex_.clear();
219 VarVec().swap(initialSupp_);
220 if (theory_) {
221 theory_->reset();
222 }
223 if (force) {
224 deleteAtoms(0);
225 AtomList().swap(atoms_);
226 AtomState().swap(atomState_);
227 LpLitVec().swap(assume_);
228 delete theory_;
229 delete incData_;
230 VarVec().swap(propQ_);
231 stats.reset();
232 incData_ = 0;
233 theory_ = 0;
234 input_ = AtomRange(1, UINT32_MAX);
235 statsId_ = 0;
236 }
237 rule_.clear();
238 }
deleteAtoms(uint32 start)239 void LogicProgram::deleteAtoms(uint32 start) {
240 for (AtomList::const_iterator it = atoms_.begin() + start, end = atoms_.end(); it != end; ++it) {
241 if (*it != &trueAtom_g) { delete *it; }
242 }
243 }
doStartProgram()244 bool LogicProgram::doStartProgram() {
245 dispose(true);
246 // atom 0 is always true
247 PrgAtom* trueAt = new PrgAtom(0, false);
248 atoms_.push_back(trueAt);
249 trueAt->assignValue(value_true);
250 trueAt->setInUpper(true);
251 trueAt->setLiteral(lit_true());
252 atomState_.set(0, AtomState::fact_flag);
253 auxData_ = new Aux();
254 return true;
255 }
setOptions(const AspOptions & opts)256 void LogicProgram::setOptions(const AspOptions& opts) {
257 opts_ = opts;
258 if (opts.suppMod) { opts_.noSCC = 1; }
259 if (opts.suppMod && ctx() && ctx()->sccGraph.get()) {
260 ctx()->warn("'supp-models' ignored for non-tight programs.");
261 opts_.suppMod = 0;
262 opts_.noSCC = 0;
263 }
264 }
enableDistinctTrue()265 void LogicProgram::enableDistinctTrue() {
266 opts_.distinctTrue();
267 }
doCreateParser()268 ProgramParser* LogicProgram::doCreateParser() {
269 return new AspParser(*this);
270 }
doUpdateProgram()271 bool LogicProgram::doUpdateProgram() {
272 if (!incData_) { incData_ = new Incremental(); }
273 if (!frozen()) { return true; }
274 // delete bodies/disjunctions...
275 dispose(false);
276 setFrozen(false);
277 auxData_ = new Aux();
278 assume_.clear();
279 if (theory_) { theory_->update(); }
280 incData_->unfreeze.clear();
281 input_.hi = std::min(input_.hi, endAtom());
282 // reset prop queue and add supported atoms from previous steps
283 // {ai | ai in P}.
284 PrgBody* support = input_.hi > 1 ? getTrueBody() : 0;
285 propQ_.clear();
286 for (Atom_t i = 1, end = startAuxAtom(); i != end; ++i) {
287 if (getRootId(i) >= end) {
288 // atom is equivalent to some aux atom - make i the new root
289 uint32 r = getRootId(i);
290 std::swap(atoms_[i], atoms_[r]);
291 atoms_[r]->setEq(i);
292 }
293 // remove dangling references
294 PrgAtom* a = atoms_[i];
295 a->clearSupports();
296 a->clearDeps(PrgAtom::dep_all);
297 a->setIgnoreScc(false);
298 if (a->relevant() || a->frozen()) {
299 ValueRep v = a->value();
300 a->setValue(value_free);
301 a->resetId(i, !a->frozen());
302 if (ctx()->master()->value(a->var()) != value_free && !a->frozen()) {
303 v = ctx()->master()->isTrue(a->literal()) ? value_true : value_false;
304 }
305 if (v != value_free) { assignValue(a, v, PrgEdge::noEdge()); }
306 if (!a->frozen() && a->value() != value_false) {
307 a->setIgnoreScc(true);
308 support->addHead(a, PrgEdge::GammaChoice);
309 }
310 }
311 else if (a->removed() || (!a->eq() && a->value() == value_false)) {
312 a->resetId(i, true);
313 a->setValue(value_false);
314 atomState_.set(i, AtomState::false_flag);
315 }
316 }
317 // delete any introduced aux atoms
318 // this is safe because aux atoms are never part of the input program
319 // it is necessary in order to free their ids, i.e. the id of an aux atom
320 // from step I might be needed for a program atom in step I+1
321 deleteAtoms(startAuxAtom());
322 atoms_.erase(atoms_.begin()+startAuxAtom(), atoms_.end());
323 uint32 nAtoms = (uint32)atoms_.size();
324 atomState_.resize(nAtoms);
325 input_ = AtomRange(nAtoms, UINT32_MAX);
326 stats.reset();
327 statsId_ = 0;
328 return true;
329 }
doEndProgram()330 bool LogicProgram::doEndProgram() {
331 if (!frozen() && ctx()->ok()) {
332 prepareProgram(!opts_.noSCC);
333 addConstraints();
334 addDomRules();
335 addAcycConstraint();
336 }
337 return ctx()->ok();
338 }
339
clone(SharedContext & oCtx)340 bool LogicProgram::clone(SharedContext& oCtx) {
341 assert(frozen());
342 if (&oCtx == ctx()) {
343 return true;
344 }
345 for (Var v = oCtx.numVars() + 1; ctx()->validVar(v); ++v) {
346 oCtx.addVar(Var_t::Atom, ctx()->varInfo(v).rep);
347 }
348 SharedContext* t = ctx();
349 setCtx(&oCtx);
350 bool ok = addConstraints();
351 if (ok) {
352 oCtx.output = ctx()->output;
353 oCtx.heuristic = t->heuristic;
354 }
355 setCtx(t);
356 return ok;
357 }
358
addMinimize()359 void LogicProgram::addMinimize() {
360 POTASSCO_ASSERT(frozen());
361 for (MinList::iterator it = minimize_.begin(), end = minimize_.end(); it != end; ++it) {
362 const LpWLitVec& lits = (*it)->lits;
363 const weight_t prio = (*it)->prio;
364 for (LpWLitVec::const_iterator xIt = lits.begin(), xEnd = lits.end(); xIt != xEnd; ++xIt) {
365 addMinLit(prio, WeightLiteral(getLiteral(Potassco::id(xIt->lit)), xIt->weight));
366 }
367 // Make sure minimize constraint is not empty
368 if (lits.empty()) addMinLit(prio, WeightLiteral(lit_false(), 1));
369 }
370 }
outRule(Potassco::AbstractProgram & out,const Rule & r)371 static void outRule(Potassco::AbstractProgram& out, const Rule& r) {
372 if (r.normal()) { out.rule(r.ht, r.head, r.cond); }
373 else { out.rule(r.ht, r.head, r.agg.bound, r.agg.lits); }
374 }
375
accept(Potassco::AbstractProgram & out)376 void LogicProgram::accept(Potassco::AbstractProgram& out) {
377 if (!ok()) {
378 out.rule(Head_t::Disjunctive, Potassco::toSpan<Potassco::Atom_t>(), Potassco::toSpan<Potassco::Lit_t>());
379 return;
380 }
381 // visit external directives
382 for (VarVec::const_iterator it = auxData_->external.begin(), end = auxData_->external.end(); it != end; ++it) {
383 AtomVal x = decodeExternal(*it);
384 out.external(x.first, x.second);
385 }
386 // visit eq- and assigned atoms
387 for (Atom_t i = startAtom(); i < atoms_.size(); ++i) {
388 if (atoms_[i]->eq()) {
389 Potassco::AtomSpan head = Potassco::toSpan(&i, 1);
390 Potassco::Lit_t body = Potassco::lit(getRootId(i));
391 if (isFact(Potassco::atom(body))) {
392 out.rule(Head_t::Disjunctive, head, Potassco::toSpan<Potassco::Lit_t>());
393 }
394 else if (inProgram(Potassco::atom(body))) {
395 out.rule(Head_t::Disjunctive, head, Potassco::toSpan(&body, 1));
396 }
397 }
398 else if (!atomState_.isFact(i) && atoms_[i]->value() != value_free) {
399 Potassco::AtomSpan head = Potassco::toSpan<Potassco::Atom_t>();
400 Potassco::Lit_t body = Potassco::neg(i);
401 if (atoms_[i]->value() != value_false) {
402 out.rule(Head_t::Disjunctive, head, Potassco::toSpan(&body, 1));
403 }
404 else if (inProgram(i)) {
405 body = Potassco::neg(body);
406 out.rule(Head_t::Disjunctive, head, Potassco::toSpan(&body, 1));
407 }
408 }
409 }
410 // visit program rules
411 const bool simp = frozen();
412 using Potassco::Lit_t;
413 VarVec choice;
414 for (BodyList::iterator bIt = bodies_.begin(); bIt != bodies_.end(); ++bIt) {
415 rule_.clear();
416 choice.clear();
417 Atom_t head;
418 Lit_t auxB;
419 PrgBody* b = *bIt;
420 if (b->relevant() && (b->inRule() || b->value() == value_false) && b->toData(*this, rule_)) {
421 if (b->value() == value_false) {
422 outRule(out, rule_.rule());
423 continue;
424 }
425 uint32 numDis = 0;
426 Rule r = rule_.rule();
427 for (PrgBody::head_iterator hIt = b->heads_begin(); hIt != b->heads_end(); ++hIt) {
428 if (hIt->isGamma() || (simp && !getHead(*hIt)->hasVar())) { continue; }
429 if (hIt->isAtom() && hIt->node() && inProgram(hIt->node())) {
430 if (hIt->isNormal()) { r.head = Potassco::toSpan(&(head = hIt->node()), 1); outRule(out, r); }
431 else if (hIt->isChoice()) { choice.push_back(hIt->node()); }
432 if (simp && getRootAtom(hIt->node())->var() == b->var() && !r.normal()) {
433 // replace complex body with head atom
434 auxB = Potassco::lit(hIt->node());
435 if (getRootAtom(hIt->node())->literal() != b->literal()) { auxB *= -1; }
436 r.bt = Body_t::Normal;
437 r.cond = Potassco::toSpan(&auxB, 1);
438 }
439 }
440 else if (hIt->isDisj()) { ++numDis; }
441 }
442 if (!choice.empty()) {
443 r.head = Potassco::toSpan(choice);
444 r.ht = Head_t::Choice;
445 outRule(out, r);
446 }
447 for (PrgBody::head_iterator hIt = b->heads_begin(); hIt != b->heads_end() && numDis; ++hIt) {
448 if (hIt->isDisj()) {
449 PrgDisj* d = getDisj(hIt->node());
450 r.head = Potassco::toSpan(d->begin(), d->size());
451 r.ht = Head_t::Disjunctive;
452 outRule(out, r);
453 --numDis;
454 }
455 }
456 }
457 }
458 LpWLitVec wlits;
459 for (MinList::iterator it = minimize_.begin(), end = minimize_.end(); it != end; ++it) {
460 Potassco::WeightLitSpan ws = Potassco::toSpan((*it)->lits);
461 for (const Potassco::WeightLit_t* x = Potassco::begin(ws), *xEnd = Potassco::end(ws); x != xEnd; ++x) {
462 if (x->weight == 0 || !inProgram(Potassco::atom(*x))) { // simplify literals
463 wlits.assign(Potassco::begin(ws), x);
464 for (; x != xEnd; ++x) {
465 if (x->weight != 0 && (x->weight < 0 || x->lit < 0 || inProgram(Potassco::atom(*x)))) {
466 wlits.push_back(*x);
467 }
468 }
469 ws = Potassco::toSpan(wlits);
470 break;
471 }
472 }
473 out.minimize((*it)->prio, ws);
474 }
475 Potassco::LitVec lits;
476 // visit output directives
477 for (ShowVec::const_iterator it = show_.begin(); it != show_.end(); ++it) {
478 if (extractCondition(it->first, lits)) {
479 out.output(Potassco::toSpan(it->second.c_str(), std::strlen(it->second.c_str())), Potassco::toSpan(lits));
480 }
481 }
482 // visit projection directives
483 if (!auxData_->project.empty()) {
484 out.project(auxData_->project.back() ? Potassco::toSpan(auxData_->project) : Potassco::toSpan<Atom_t>());
485 }
486 // visit assumptions
487 if (!assume_.empty()) {
488 out.assume(Potassco::toSpan(assume_));
489 }
490 // visit heuristics
491 if (!auxData_->dom.empty()) {
492 for (DomRules::const_iterator it = auxData_->dom.begin(), end = auxData_->dom.end(); it != end; ++it) {
493 if (extractCondition(it->cond, lits)) {
494 out.heuristic(it->atom, static_cast<DomModType>(it->type), it->bias, it->prio, Potassco::toSpan(lits));
495 }
496 }
497 }
498 // visit acyc edges
499 if (!auxData_->acyc.empty()) {
500 for (AcycRules::const_iterator it = auxData_->acyc.begin(), end = auxData_->acyc.end(); it != end; ++it) {
501 if (extractCondition(it->cond, lits)) {
502 out.acycEdge(it->node[0], it->node[1], Potassco::toSpan(lits));
503 }
504 }
505 }
506 if (theory_ && theory_->numAtoms()) {
507 struct This : public Potassco::TheoryData::Visitor {
508 This(const Asp::LogicProgram& p, Potassco::AbstractProgram& o, Potassco::LitVec& c) : self(&p), out(&o), cond(&c) {}
509 virtual void visit(const Potassco::TheoryData& data, Potassco::Id_t termId, const Potassco::TheoryTerm& t) {
510 if (!addSeen(termId, 1)) { return; }
511 data.accept(t, *this, Potassco::TheoryData::visit_current);
512 Potassco::print(*out, termId, t);
513 }
514 virtual void visit(const Potassco::TheoryData& data, Potassco::Id_t elemId, const Potassco::TheoryElement& e) {
515 if (!addSeen(elemId, 2)) { return; }
516 data.accept(e, *this, Potassco::TheoryData::visit_current);
517 cond->clear();
518 if (e.condition()) { self->extractCondition(e.condition(), *cond); }
519 out->theoryElement(elemId, e.terms(), Potassco::toSpan(*cond));
520 }
521 virtual void visit(const Potassco::TheoryData& data, const Potassco::TheoryAtom& a) {
522 data.accept(a, *this, Potassco::TheoryData::visit_current);
523 Potassco::print(*out, a);
524 const Atom_t id = a.atom();
525 if (self->validAtom(id) && self->atomState_.isSet(id, AtomState::false_flag) && !self->inProgram(id)) {
526 Potassco::Lit_t x = Potassco::lit(id);
527 out->rule(Head_t::Disjunctive, Potassco::toSpan<Potassco::Atom_t>(), Potassco::toSpan(&x, 1));
528 }
529 }
530 bool addSeen(Potassco::Id_t id, unsigned char n) {
531 if (id >= seen.size()) { seen.resize(id + 1, 0); }
532 unsigned char old = seen[id];
533 return (seen[id] |= n) != old;
534 }
535 typedef PodVector<unsigned char>::type IdSet;
536 const Asp::LogicProgram* self;
537 Potassco::AbstractProgram* out;
538 Potassco::LitVec* cond;
539 IdSet seen;
540 } self(*this, out, lits);
541 theory_->accept(self, Potassco::TheoryData::visit_current);
542 }
543 }
544 /////////////////////////////////////////////////////////////////////////////////////////
545 // Program mutating functions
546 /////////////////////////////////////////////////////////////////////////////////////////
547 #define check_not_frozen() POTASSCO_REQUIRE(!frozen(), "Can't update frozen program!")
548 #define check_modular(x, atomId) (void)( (!!(x)) || (throw RedefinitionError((atomId), this->findName((atomId))), 0))
RedefinitionError(unsigned atomId,const char * name)549 RedefinitionError::RedefinitionError(unsigned atomId, const char* name)
550 : std::logic_error(POTASSCO_FORMAT("redefinition of atom <'%s',%u>", name && *name ? name : "_", atomId)) {
551 }
552
newAtom()553 Atom_t LogicProgram::newAtom() {
554 check_not_frozen();
555 Atom_t id = static_cast<Atom_t>(atoms_.size());
556 atoms_.push_back( new PrgAtom(id) );
557 return id;
558 }
newCondition(const Potassco::LitSpan & cond)559 Id_t LogicProgram::newCondition(const Potassco::LitSpan& cond) {
560 check_not_frozen();
561 SRule meta;
562 if (simplifyNormal(Head_t::Disjunctive, Potassco::toSpan<Potassco::Atom_t>(), cond, rule_, meta)) {
563 Rule r = rule_.rule();
564 if (r.cond.size == 0) { return 0; }
565 if (r.cond.size == 1) { return Potassco::id(r.cond[0]); }
566 PrgBody* b = getBodyFor(r, meta);
567 b->markFrozen();
568 return static_cast<Id_t>(Clasp::Asp::bodyId | b->id());
569 }
570 return static_cast<Id_t>(Clasp::Asp::falseId);
571 }
addOutput(const ConstString & str,const Potassco::LitSpan & cond)572 LogicProgram& LogicProgram::addOutput(const ConstString& str, const Potassco::LitSpan& cond) {
573 if (cond.size == 1) {
574 POTASSCO_REQUIRE(Potassco::atom(cond[0]) < bodyId, "Atom out of bounds");
575 return addOutput(str, Potassco::id(cond[0]));
576 }
577 if (!ctx()->output.filter(str)) {
578 show_.push_back(ShowPair(newCondition(cond), str));
579 }
580 return *this;
581 }
addOutput(const ConstString & str,Id_t id)582 LogicProgram& LogicProgram::addOutput(const ConstString& str, Id_t id) {
583 if (!ctx()->output.filter(str) && id != falseId) {
584 if (Potassco::atom(id) < bodyId) {
585 resize(Potassco::atom(id));
586 }
587 show_.push_back(ShowPair(id, str));
588 }
589 return *this;
590 }
591
addProject(const Potassco::AtomSpan & atoms)592 LogicProgram& LogicProgram::addProject(const Potassco::AtomSpan& atoms) {
593 check_not_frozen();
594 VarVec& pro = auxData_->project;
595 if (!Potassco::empty(atoms)) {
596 if (!pro.empty() && pro.back() == 0) { pro.pop_back(); }
597 pro.insert(pro.end(), Potassco::begin(atoms), Potassco::end(atoms));
598 }
599 else if (pro.empty()) {
600 pro.push_back(0);
601 }
602 return *this;
603 }
604
theoryData()605 TheoryData& LogicProgram::theoryData() {
606 if (!theory_) { theory_ = new TheoryData(); }
607 return *theory_;
608 }
609
pushFrozen(PrgAtom * atom,ValueRep value)610 void LogicProgram::pushFrozen(PrgAtom* atom, ValueRep value) {
611 if (!atom->frozen()) { frozen_.push_back(atom->id()); }
612 atom->markFrozen(value);
613 }
614
addExternal(Atom_t atomId,Potassco::Value_t value)615 LogicProgram& LogicProgram::addExternal(Atom_t atomId, Potassco::Value_t value) {
616 check_not_frozen();
617 PrgAtom* a = resize(atomId);
618 if (a->supports() == 0 && (isNew(a->id()) || a->frozen())) {
619 ValueRep fv = static_cast<ValueRep>(value);
620 if (value == Potassco::Value_t::Release) {
621 // add dummy edge - will be removed once we update the set of frozen atoms
622 a->addSupport(PrgEdge::noEdge());
623 fv = value_free;
624 }
625 pushFrozen(a, fv);
626 auxData_->external.push_back(encodeExternal(a->id(), value));
627 }
628 return *this;
629 }
630
freeze(Atom_t atomId,ValueRep value)631 LogicProgram& LogicProgram::freeze(Atom_t atomId, ValueRep value) {
632 POTASSCO_ASSERT(value < value_weak_true);
633 return addExternal(atomId, static_cast<Potassco::Value_t>(value));
634 }
635
unfreeze(Atom_t atomId)636 LogicProgram& LogicProgram::unfreeze(Atom_t atomId) {
637 return addExternal(atomId, Potassco::Value_t::Release);
638 }
setMaxInputAtom(uint32 n)639 void LogicProgram::setMaxInputAtom(uint32 n) {
640 check_not_frozen();
641 resize(n++);
642 POTASSCO_REQUIRE(n >= startAtom(), "invalid input range");
643 input_.hi = n;
644 }
startAuxAtom() const645 Atom_t LogicProgram::startAuxAtom() const {
646 return validAtom(input_.hi) ? input_.hi : (uint32)atoms_.size();
647 }
supportsSmodels() const648 bool LogicProgram::supportsSmodels() const {
649 if (incData_ || theory_) { return false; }
650 if (!auxData_->dom.empty()) { return false; }
651 if (!auxData_->acyc.empty()) { return false; }
652 if (!assume_.empty()) { return false; }
653 if (!auxData_->project.empty()) { return false; }
654 for (ShowVec::const_iterator it = show_.begin(), end = show_.end(); it != end; ++it) {
655 Potassco::Lit_t lit = Potassco::lit(it->first);
656 if (lit <= 0 || static_cast<uint32>(lit) >= bodyId) { return false; }
657 }
658 return true;
659 }
660
isExternal(Atom_t aId) const661 bool LogicProgram::isExternal(Atom_t aId) const {
662 if (!aId || !validAtom(aId)) { return false; }
663 PrgAtom* a = getRootAtom(aId);
664 return a->frozen() && (a->supports() == 0 || frozen());
665 }
isFact(Atom_t aId) const666 bool LogicProgram::isFact(Atom_t aId) const {
667 return validAtom(aId) && (atomState_.isFact(aId) || atomState_.isFact(getRootId(aId)));
668 }
isDefined(Atom_t aId) const669 bool LogicProgram::isDefined(Atom_t aId) const {
670 if (!validAtom(aId) || getAtom(aId)->removed()) {
671 return false;
672 }
673 PrgAtom* a = getAtom(aId);
674 return isFact(aId) || (a->relevant() && a->supports() && !isExternal(aId));
675 }
inProgram(Atom_t id) const676 bool LogicProgram::inProgram(Atom_t id) const {
677 if (PrgAtom* a = (validAtom(id) ? getAtom(id) : 0)) {
678 return a->relevant() && (a->supports() || a->frozen() || !isNew(id));
679 }
680 return false;
681 }
addAssumption(const Potassco::LitSpan & lits)682 LogicProgram& LogicProgram::addAssumption(const Potassco::LitSpan& lits) {
683 assume_.insert(assume_.end(), Potassco::begin(lits), Potassco::end(lits));
684 return *this;
685 }
686
addAcycEdge(uint32 n1,uint32 n2,Id_t condId)687 LogicProgram& LogicProgram::addAcycEdge(uint32 n1, uint32 n2, Id_t condId) {
688 if (condId != falseId) {
689 AcycArc arc = { condId, {n1, n2} };
690 auxData_->acyc.push_back(arc);
691 }
692 upStat(RK(Acyc), 1);
693 return *this;
694 }
695
addDomHeuristic(Atom_t atom,DomModType t,int bias,unsigned prio)696 LogicProgram& LogicProgram::addDomHeuristic(Atom_t atom, DomModType t, int bias, unsigned prio) {
697 return addDomHeuristic(atom, t, bias, prio, Potassco::toSpan<Potassco::Lit_t>());
698 }
addDomHeuristic(Atom_t atom,DomModType type,int bias,unsigned prio,Id_t cond)699 LogicProgram& LogicProgram::addDomHeuristic(Atom_t atom, DomModType type, int bias, unsigned prio, Id_t cond) {
700 static_assert(sizeof(DomRule) == sizeof(uint32[3]), "Invalid DomRule size");
701 if (cond != falseId) {
702 auxData_->dom.push_back(DomRule());
703 DomRule& x = auxData_->dom.back();
704 x.atom = atom;
705 x.type = type;
706 x.cond = cond;
707 x.bias = (int16)Range<int>(INT16_MIN, INT16_MAX).clamp(bias);
708 x.prio = (uint16)Range<unsigned>(unsigned(0), unsigned(~uint16(0)) ).clamp(prio);
709 }
710 upStat(RK(Heuristic), 1);
711 return *this;
712 }
addRule(const Rule & rule)713 LogicProgram& LogicProgram::addRule(const Rule& rule) {
714 check_not_frozen();
715 SRule meta;
716 if (simplifyRule(rule, rule_, meta)) {
717 Rule sRule = rule_.rule();
718 upStat(sRule.ht);
719 if (handleNatively(sRule)) { // and can be handled natively
720 addRule(sRule, meta);
721 }
722 else {
723 upStat(sRule.bt);
724 if (Potassco::size(sRule.head) <= 1 && transformNoAux(sRule)) {
725 // rule transformation does not require aux atoms - do it now
726 int oId = statsId_;
727 statsId_ = 1;
728 RuleTransform tm(*this);
729 upStat(sRule.bt, -1);
730 upStat(rule.ht, -1);
731 tm.transform(sRule, RuleTransform::strategy_no_aux);
732 statsId_ = oId;
733 }
734 else {
735 // make sure we have all head atoms
736 for (Potassco::AtomSpan::iterator it = Potassco::begin(sRule.head), end = Potassco::end(sRule.head); it != end; ++it) {
737 resize(*it);
738 }
739 extended_.push_back(new RuleBuilder(rule_));
740 }
741 }
742 }
743 if (statsId_ == 0) {
744 // Assume all (new) heads are initially in "upper" closure.
745 for (Potassco::AtomSpan::iterator it = Potassco::begin(rule.head), end = Potassco::end(rule.head); it != end; ++it) {
746 if (!isNew(*it)) continue;
747 if (validAtom(*it))
748 getAtom(*it)->setInUpper(true);
749 else
750 auxData_->skippedHeads.insert(*it);
751 }
752 }
753 rule_.clear();
754 return *this;
755 }
756
addRule(Head_t ht,const Potassco::AtomSpan & head,const Potassco::LitSpan & body)757 LogicProgram& LogicProgram::addRule(Head_t ht, const Potassco::AtomSpan& head, const Potassco::LitSpan& body) {
758 return addRule(Rule::normal(ht, head, body));
759 }
addRule(Head_t ht,const Potassco::AtomSpan & head,Potassco::Weight_t bound,const Potassco::WeightLitSpan & lits)760 LogicProgram& LogicProgram::addRule(Head_t ht, const Potassco::AtomSpan& head, Potassco::Weight_t bound, const Potassco::WeightLitSpan& lits) {
761 return addRule(Rule::sum(ht, head, bound, lits));
762 }
addRule(Potassco::RuleBuilder & rb)763 LogicProgram& LogicProgram::addRule(Potassco::RuleBuilder& rb) {
764 LogicProgramAdapter prg(*this);
765 rb.end(&prg);
766 return *this;
767 }
addMinimize(weight_t prio,const Potassco::WeightLitSpan & lits)768 LogicProgram& LogicProgram::addMinimize(weight_t prio, const Potassco::WeightLitSpan& lits) {
769 SingleOwnerPtr<Min> n(new Min());
770 n->prio = prio;
771 MinList::iterator it = std::lower_bound(minimize_.begin(), minimize_.end(), n.get(), CmpMin());
772 if (it == minimize_.end() || (*it)->prio != prio) {
773 n->lits.assign(Potassco::begin(lits), Potassco::end(lits));
774 minimize_.insert(it, n.get());
775 n.release();
776 upStat(RuleStats::Minimize);
777 }
778 else {
779 (*it)->lits.insert((*it)->lits.end(), Potassco::begin(lits), Potassco::end(lits));
780 }
781 // Touch all atoms in minimize -> these are input atoms even if they won't occur in a head.
782 for (Potassco::WeightLitSpan::iterator wIt = Potassco::begin(lits), end = Potassco::end(lits); wIt != end; ++wIt) {
783 resize(Potassco::atom(*wIt));
784 }
785 return *this;
786 }
787 #undef check_not_frozen
788 /////////////////////////////////////////////////////////////////////////////////////////
789 // Query functions
790 /////////////////////////////////////////////////////////////////////////////////////////
isFact(PrgAtom * a) const791 bool LogicProgram::isFact(PrgAtom* a) const {
792 uint32 eqId = getRootId(a->id());
793 if (atomState_.isFact(eqId)) {
794 return true;
795 }
796 if (a->value() == value_true) {
797 for (PrgAtom::sup_iterator it = a->supps_begin(), end = a->supps_end(); it != end; ++it) {
798 if (it->isBody() && it->isNormal() && getBody(it->node())->bound() == 0) {
799 return true;
800 }
801 }
802 }
803 return false;
804 }
getLiteral(Id_t id,MapLit_t m) const805 Literal LogicProgram::getLiteral(Id_t id, MapLit_t m) const {
806 Literal out = lit_false();
807 Potassco::Id_t nId = nodeId(id);
808 if (isAtom(id) && validAtom(nId)) {
809 out = getRootAtom(nId)->literal();
810 if (m == MapLit_t::Refined) {
811 IndexMap::const_iterator dom;
812 if ((dom = domEqIndex_.find(nId)) != domEqIndex_.end()) {
813 out = posLit(dom->second);
814 }
815 else if (isSentinel(out) && incData_ && !incData_->steps.empty()) {
816 Var v = isNew(id)
817 ? incData_->steps.back().second
818 : std::lower_bound(incData_->steps.begin(), incData_->steps.end(), Incremental::StepTrue(nId, 0))->second;
819 out = Literal(v, out.sign());
820 }
821 }
822 }
823 else if (isBody(id)) {
824 POTASSCO_ASSERT(validBody(nId), "Invalid condition");
825 out = getBody(getEqBody(nId))->literal();
826 }
827 return out ^ signId(id);
828 }
doGetAssumptions(LitVec & out) const829 void LogicProgram::doGetAssumptions(LitVec& out) const {
830 for (VarVec::const_iterator it = frozen_.begin(), end = frozen_.end(); it != end; ++it) {
831 Literal lit = getRootAtom(*it)->assumption();
832 if (lit != lit_true()) { out.push_back( lit ); }
833 }
834 for (Potassco::LitVec::const_iterator it = assume_.begin(), end = assume_.end(); it != end; ++it) {
835 out.push_back(getLiteral(Potassco::id(*it)));
836 }
837 }
extractCore(const LitVec & solverCore,Potassco::LitVec & prgLits) const838 bool LogicProgram::extractCore(const LitVec& solverCore, Potassco::LitVec& prgLits) const {
839 uint32 marked = 0;
840 prgLits.clear();
841 for (LitVec::const_iterator it = solverCore.begin(); it != solverCore.end(); ++it) {
842 if (!ctx()->validVar(it->var())) { break; }
843 ctx()->mark(*it);
844 ++marked;
845 }
846 if (marked == solverCore.size()) {
847 for (VarVec::const_iterator it = frozen_.begin(), end = frozen_.end(); it != end && marked; ++it) {
848 PrgAtom* atom = getRootAtom(*it);
849 Literal lit = atom->assumption();
850 if (lit == lit_true() || !ctx()->marked(lit)) continue;
851 prgLits.push_back(atom->literal() == lit ? Potassco::lit(*it) : Potassco::neg(*it));
852 ctx()->unmark(lit.var());
853 --marked;
854 }
855 for (Potassco::LitVec::const_iterator it = assume_.begin(), end = assume_.end(); it != end && marked; ++it) {
856 Literal lit = getLiteral(Potassco::id(*it));
857 if (!ctx()->marked(lit)) continue;
858 prgLits.push_back(*it);
859 ctx()->unmark(lit.var());
860 --marked;
861 }
862 }
863 for (LitVec::const_iterator it = solverCore.begin(); it != solverCore.end(); ++it) {
864 if (ctx()->validVar(it->var()))
865 ctx()->unmark(it->var());
866 }
867 return prgLits.size() == solverCore.size();
868 }
869 /////////////////////////////////////////////////////////////////////////////////////////
870 // Program definition - private
871 /////////////////////////////////////////////////////////////////////////////////////////
addRule(const Rule & r,const SRule & meta)872 void LogicProgram::addRule(const Rule& r, const SRule& meta) {
873 if (Potassco::size(r.head) <= 1 && r.ht == Head_t::Disjunctive) {
874 if (Potassco::empty(r.head)) { addIntegrity(r, meta); return; }
875 else if (r.normal() && r.cond.size == 0) { addFact(r.head); return; }
876 }
877 PrgBody* b = getBodyFor(r, meta);
878 // only a non-false body can define atoms
879 if (b->value() != value_false) {
880 bool const disjunctive = Potassco::size(r.head) > 1 && r.ht == Head_t::Disjunctive;
881 const EdgeType t = r.ht == Head_t::Disjunctive ? PrgEdge::Normal : PrgEdge::Choice;
882 uint32 headHash = 0;
883 bool ignoreScc = opts_.noSCC || b->size() == 0;
884 for (Potassco::AtomSpan::iterator it = Potassco::begin(r.head), end = Potassco::end(r.head); it != end; ++it) {
885 PrgAtom* a = resize(*it);
886 check_modular(isNew(*it) || a->frozen() || a->value() == value_false, *it);
887 if (!disjunctive) {
888 // Note: b->heads may now contain duplicates. They are removed in PrgBody::simplifyHeads.
889 b->addHead(a, t);
890 if (ignoreScc) { a->setIgnoreScc(ignoreScc); }
891 }
892 else {
893 headHash += hashLit(posLit(*it));
894 atomState_.addToHead(*it);
895 }
896 }
897 if (disjunctive) {
898 PrgDisj* d = getDisjFor(r.head, headHash);
899 b->addHead(d, t);
900 }
901 }
902 }
addFact(const Potassco::AtomSpan & head)903 void LogicProgram::addFact(const Potassco::AtomSpan& head) {
904 PrgBody* tb = 0;
905 for (Potassco::AtomSpan::iterator it = Potassco::begin(head), end = Potassco::end(head); it != end; ++it) {
906 PrgAtom* a = resize(*it);
907 check_modular(isNew(*it) || a->frozen() || a->value() == value_false, *it);
908 if (*it != a->id() || atomState_.isFact(*it)) { continue; }
909 a->setIgnoreScc(true);
910 atomState_.set(*it, AtomState::fact_flag);
911 if (!a->hasDep(PrgAtom::dep_all) && !a->frozen()) {
912 if (!a->assignValue(value_true) || !a->propagateValue(*this, false)) {
913 setConflict();
914 }
915 for (PrgAtom::sup_iterator bIt = a->supps_begin(), bEnd = a->supps_end(); bIt != bEnd; ++bIt) {
916 if (bIt->isBody()) { getBody(bIt->node())->markHeadsDirty(); }
917 else if (bIt->isDisj()) { getDisj(bIt->node())->markDirty(); }
918 }
919 atoms_[*it] = &trueAtom_g;
920 delete a;
921 }
922 else {
923 if (!tb) tb = getTrueBody();
924 tb->addHead(a, PrgEdge::Normal);
925 assignValue(a, value_true, PrgEdge::newEdge(*tb, PrgEdge::Normal));
926 }
927 }
928 }
addIntegrity(const Rule & r,const SRule & meta)929 void LogicProgram::addIntegrity(const Rule& r, const SRule& meta) {
930 if (r.sum() || r.cond.size != 1 || meta.bid != varMax) {
931 PrgBody* B = getBodyFor(r, meta);
932 if (!B->assignValue(value_false) || !B->propagateValue(*this, true)) {
933 setConflict();
934 }
935 }
936 else {
937 PrgAtom* a = resize(Potassco::atom(r.cond[0]));
938 ValueRep v = r.cond[0] > 0 ? value_false : value_weak_true;
939 assignValue(a, v, PrgEdge::noEdge());
940 }
941 }
assignValue(PrgAtom * a,ValueRep v,PrgEdge reason)942 bool LogicProgram::assignValue(PrgAtom* a, ValueRep v, PrgEdge reason) {
943 if (a->eq()) { a = getRootAtom(a->id()); }
944 ValueRep old = a->value();
945 if (old == value_weak_true && v != value_weak_true) old = value_free;
946 if (!a->assignValue(v)) { setConflict(); return false; }
947 if (old == value_free) { propQ_.push_back(a->id()); }
948 if (v == value_false) {
949 atomState_.set(a->id(), AtomState::false_flag);
950 }
951 else if (v == value_true && reason.isBody() && reason.isNormal() && getBody(reason.node())->bound() == 0) {
952 atomState_.set(a->id(), AtomState::fact_flag);
953 }
954 return true;
955 }
assignValue(PrgHead * h,ValueRep v,PrgEdge reason)956 bool LogicProgram::assignValue(PrgHead* h, ValueRep v, PrgEdge reason) {
957 return !h->isAtom() || assignValue(static_cast<PrgAtom*>(h), v, reason);
958 }
959
handleNatively(const Rule & r) const960 bool LogicProgram::handleNatively(const Rule& r) const {
961 ExtendedRuleMode m = opts_.erMode;
962 if (m == mode_native || (r.normal() && r.ht == Head_t::Disjunctive)) {
963 return true;
964 }
965 else if (m == mode_transform_integ || m == mode_transform_scc || m == mode_transform_nhcf) {
966 return true;
967 }
968 else if (m == mode_transform) {
969 return false;
970 }
971 else if (m == mode_transform_dynamic) {
972 return r.normal() || transformNoAux(r) == false;
973 }
974 else if (m == mode_transform_choice) {
975 return r.ht != Head_t::Choice;
976 }
977 else if (m == mode_transform_card) {
978 return r.bt != Body_t::Count;
979 }
980 else if (m == mode_transform_weight) {
981 return r.normal();
982 }
983 assert(false && "unhandled extended rule mode");
984 return true;
985 }
986
transformNoAux(const Rule & r) const987 bool LogicProgram::transformNoAux(const Rule& r) const {
988 return r.ht == Head_t::Disjunctive && r.sum() && (r.agg.bound == 1 || (Potassco::size(r.agg.lits) <= 6 && choose(toU32(Potassco::size(r.agg.lits)), r.agg.bound) <= 15));
989 }
990
transformExtended()991 void LogicProgram::transformExtended() {
992 uint32 a = numAtoms();
993 RuleTransform tm(*this);
994 for (RuleList::size_type i = 0; i != extended_.size(); ++i) {
995 Rule r = extended_[i]->rule();
996 upStat(r.ht, -1);
997 upStat(r.bt, -1);
998 if (r.normal() || (r.ht == Head_t::Disjunctive && Potassco::size(r.head) < 2)) {
999 tm.transform(r);
1000 }
1001 else {
1002 using Potassco::Lit_t;
1003 Atom_t aux = newAtom();
1004 Lit_t auxB = Potassco::lit(aux);
1005 Rule rAux1 = r; // aux :- body
1006 rAux1.ht = Head_t::Disjunctive;
1007 rAux1.head = Potassco::toSpan(&aux, 1);
1008 Rule rAux2 = Rule::normal(r.ht, r.head, Potassco::toSpan(&auxB, 1)); // head :- auxB
1009 if (handleNatively(rAux1)) { addRule(rAux1); }
1010 else {
1011 RuleTransform::Strategy st = transformNoAux(rAux1) ? RuleTransform::strategy_no_aux : RuleTransform::strategy_default;
1012 tm.transform(rAux1, st);
1013 }
1014 if (handleNatively(rAux2)) { addRule(rAux2); }
1015 else { tm.transform(rAux2); }
1016 }
1017 delete extended_[i];
1018 }
1019 extended_.clear();
1020 incTrAux(numAtoms() - a);
1021 }
1022
transformIntegrity(uint32 nAtoms,uint32 maxAux)1023 void LogicProgram::transformIntegrity(uint32 nAtoms, uint32 maxAux) {
1024 if (stats.bodies[1][Body_t::Count] == 0) { return; }
1025 // find all constraint rules that are integrity constraints
1026 BodyList integrity;
1027 for (uint32 i = 0, end = static_cast<uint32>(bodies_.size()); i != end; ++i) {
1028 PrgBody* b = bodies_[i];
1029 if (b->relevant() && b->type() == Body_t::Count && b->value() == value_false) {
1030 integrity.push_back(b);
1031 }
1032 }
1033 if (!integrity.empty() && (integrity.size() == 1 || (nAtoms/double(bodies_.size()) > 0.5 && integrity.size() / double(bodies_.size()) < 0.01))) {
1034 uint32 aux = static_cast<uint32>(atoms_.size());
1035 RuleTransform tr(*this);
1036 RuleBuilder temp;
1037 // transform integrity constraints
1038 for (BodyList::size_type i = 0; i != integrity.size(); ++i) {
1039 PrgBody* b = integrity[i];
1040 uint32 est = b->bound()*( b->sumW()-b->bound() );
1041 if (est > maxAux) {
1042 // reached limit on aux atoms - stop transformation
1043 break;
1044 }
1045 if (b->toData(*this, temp) && temp.bodyType() != Body_t::Normal) {
1046 maxAux -= est;
1047 // transform rule
1048 setFrozen(false);
1049 upStat(Head_t::Disjunctive, -1);
1050 upStat(Body_t::Count, -1);
1051 tr.transform(Rule::sum(Head_t::Disjunctive, Potassco::toSpan<Potassco::Atom_t>(), temp.sum()));
1052 setFrozen(true);
1053 // propagate integrity condition to new rules
1054 propagate(true);
1055 b->markRemoved();
1056 }
1057 temp.clear();
1058 }
1059 // create vars for new atoms/bodies
1060 for (uint32 i = aux; i != atoms_.size(); ++i) {
1061 PrgAtom* a = atoms_[i];
1062 for (PrgAtom::sup_iterator it = a->supps_begin(); it != a->supps_end(); ++it) {
1063 PrgBody* nb = bodies_[it->node()];
1064 assert(nb->value() != value_false);
1065 nb->assignVar(*this);
1066 }
1067 a->assignVar(*this, a->supports() ? *a->supps_begin() : PrgEdge::noEdge());
1068 }
1069 incTrAux(static_cast<uint32>(atoms_.size()) - aux);
1070 }
1071 }
1072
prepareExternals()1073 void LogicProgram::prepareExternals() {
1074 if (auxData_->external.empty()) { return; }
1075 VarVec& external = auxData_->external;
1076 VarVec::iterator j = external.begin();
1077 for (VarVec::const_iterator it = j, end = external.end(); it != end; ++it) {
1078 Atom_t id = getRootId(decodeExternal(*it).first);
1079 const PrgAtom* atom = getAtom(id);
1080 if (!atomState_.inHead(id) && (atom->supports() == 0 || *atom->supps_begin() == PrgEdge::noEdge())) {
1081 Potassco::Value_t value = atom->supports() == 0 ? static_cast<Potassco::Value_t>(atom->freezeValue()) : Potassco::Value_t::Release;
1082 atomState_.addToHead(id);
1083 *j++ = encodeExternal(id, value);
1084 }
1085 }
1086 external.erase(j, external.end());
1087 for (VarVec::const_iterator it = external.begin(), end = external.end(); it != end; ++it) {
1088 atomState_.clearRule(decodeExternal(*it).first);
1089 }
1090 }
updateFrozenAtoms()1091 void LogicProgram::updateFrozenAtoms() {
1092 if (frozen_.empty()) { return; }
1093 PrgBody* support = 0;
1094 VarVec::iterator j = frozen_.begin();
1095 for (VarVec::const_iterator it = j, end = frozen_.end(); it != end; ++it) {
1096 Id_t id = getRootId(*it);
1097 PrgAtom* a = getAtom(id);
1098 assert(a->frozen());
1099 a->resetId(id, false);
1100 if (a->supports() == 0) {
1101 assert(a->relevant());
1102 POTASSCO_REQUIRE(id < startAuxAtom(), "frozen atom shall be an input atom");
1103 if (!support) { support = getTrueBody(); }
1104 a->setIgnoreScc(true);
1105 support->addHead(a, PrgEdge::GammaChoice);
1106 *j++ = id; // still frozen
1107 }
1108 else {
1109 a->clearFrozen();
1110 if (*a->supps_begin() == PrgEdge::noEdge()) {
1111 // remove dummy edge added in unfreeze()
1112 a->removeSupport(PrgEdge::noEdge());
1113 }
1114 if (!isNew(id) && incData_) {
1115 // add to unfreeze so that we can later perform completion
1116 incData_->unfreeze.push_back(id);
1117 }
1118 }
1119 }
1120 frozen_.erase(j, frozen_.end());
1121 }
1122
prepareProgram(bool checkSccs)1123 void LogicProgram::prepareProgram(bool checkSccs) {
1124 assert(!frozen());
1125 prepareExternals();
1126 // Given that freezeTheory() might introduce otherwise
1127 // unused atoms, it must be called before we fix the
1128 // number of input atoms. It must also be called before resetting
1129 // the initial "upper" closure so that we can correctly classify
1130 // theory atoms.
1131 freezeTheory();
1132 // Prepare for preprocessing by resetting our "upper" closure.
1133 for (uint32 i = startAtom(); i != endAtom(); ++i) {
1134 getAtom(i)->setInUpper(false);
1135 }
1136 uint32 nAtoms = (input_.hi = std::min(input_.hi, endAtom()));
1137 stats.auxAtoms += endAtom() - nAtoms;
1138 for (uint32 i = 0; i != RuleStats::numKeys(); ++i) {
1139 stats.rules[1][i] += stats.rules[0][i];
1140 }
1141 for (uint32 i = 0; i != BodyStats::numKeys(); ++i) {
1142 stats.bodies[1][i] += stats.bodies[0][i];
1143 }
1144 statsId_ = 1;
1145 transformExtended();
1146 updateFrozenAtoms();
1147 PrgAtom* suppAtom = 0;
1148 if (opts_.suppMod) {
1149 VarVec h;
1150 suppAtom = getAtom(newAtom());
1151 h.assign(1, suppAtom->id());
1152 addRule(Head_t::Choice, Potassco::toSpan(h), Potassco::toSpan<Potassco::Lit_t>());
1153 Potassco::Lit_t body = Potassco::lit(suppAtom->id());
1154 h.clear();
1155 for (Atom_t v = startAtom(), end = suppAtom->id(); v != end; ++v) {
1156 if (atoms_[v]->supports() != 0) { h.push_back(v); }
1157 }
1158 addRule(Head_t::Choice, Potassco::toSpan(h), Potassco::toSpan(&body, 1));
1159 }
1160 setFrozen(true);
1161 Preprocessor p;
1162 if (hasConflict() || !propagate(true) || !p.preprocess(*this, opts_.iters != 0 ? Preprocessor::full_eq : Preprocessor::no_eq, opts_.iters, opts_.dfOrder != 0)) {
1163 setConflict();
1164 return;
1165 }
1166 if (suppAtom && (!assignValue(suppAtom, value_false, PrgEdge::noEdge()) || !propagate(true))) {
1167 setConflict();
1168 return;
1169 }
1170 if (opts_.erMode == mode_transform_integ || opts_.erMode == mode_transform_dynamic) {
1171 nAtoms -= startAtom();
1172 transformIntegrity(nAtoms, std::min(uint32(15000), nAtoms*2));
1173 }
1174 addMinimize();
1175 uint32 sccs = 0;
1176 if (checkSccs) {
1177 uint32 startScc = incData_ ? incData_->startScc : 0;
1178 SccChecker c(*this, auxData_->scc, startScc);
1179 sccs = c.sccs();
1180 stats.sccs = (sccs-startScc);
1181 if (incData_) { incData_->startScc = c.sccs(); }
1182 if (!disjunctions_.empty() || (opts_.erMode == mode_transform_scc && sccs)) {
1183 // reset node ids changed by scc checking
1184 for (uint32 i = 0; i != bodies_.size(); ++i) {
1185 if (getBody(i)->relevant()) { getBody(i)->resetId(i, true); }
1186 }
1187 for (uint32 i = 0; i != atoms_.size(); ++i) {
1188 if (getAtom(i)->relevant()) { getAtom(i)->resetId(i, true); }
1189 }
1190 }
1191 }
1192 else { stats.sccs = PrgNode::noScc; }
1193 finalizeDisjunctions(p, sccs);
1194 prepareComponents();
1195 prepareOutputTable();
1196 freezeAssumptions();
1197 if (incData_ && options().distTrue) {
1198 for (Var a = startAtom(), end = startAuxAtom(); a != end; ++a) {
1199 if (isSentinel(getRootAtom(a)->literal())) {
1200 Incremental::StepTrue t(end - 1, 0);
1201 if (!incData_->steps.empty()) { t.second = ctx()->addVar(Var_t::Atom, 0); }
1202 incData_->steps.push_back(t);
1203 break;
1204 }
1205 }
1206 }
1207 if (theory_) {
1208 TFilter f = { this };
1209 theory_->filter(f);
1210 }
1211 stats.atoms = static_cast<uint32>(atoms_.size()) - startAtom();
1212 bodyIndex_.clear();
1213 disjIndex_.clear();
1214 }
freezeTheory()1215 void LogicProgram::freezeTheory() {
1216 if (theory_) {
1217 const IdSet& skippedHeads = auxData_->skippedHeads;
1218 for (TheoryData::atom_iterator it = theory_->currBegin(), end = theory_->end(); it != end; ++it) {
1219 const Potassco::TheoryAtom& a = **it;
1220 if (isFact(a.atom()) || !isNew(a.atom())) { continue; }
1221 PrgAtom* atom = resize(a.atom());
1222 bool inUpper = atom->inUpper() || skippedHeads.count(a.atom()) != 0;
1223 if (!atom->frozen() && atom->supports() == 0 && atom->relevant() && !inUpper) {
1224 pushFrozen(atom, value_free);
1225 }
1226 }
1227 }
1228 }
operator ()(const Potassco::TheoryAtom & a) const1229 bool LogicProgram::TFilter::operator()(const Potassco::TheoryAtom& a) const {
1230 Atom_t id = a.atom();
1231 if (self->getLiteral(id) != lit_false() && self->getRootAtom(id)->value() != value_false) {
1232 self->ctx()->setFrozen(self->getLiteral(id).var(), true);
1233 return false;
1234 }
1235 PrgAtom* at = self->getRootAtom(id);
1236 return !at->frozen();
1237 }
1238 struct LogicProgram::DlpTr : public RuleTransform::ProgramAdapter {
DlpTrClasp::Asp::LogicProgram::DlpTr1239 DlpTr(LogicProgram* x, EdgeType et) : self(x), type(et), scc(PrgNode::noScc) {}
newAtomClasp::Asp::LogicProgram::DlpTr1240 Atom_t newAtom() {
1241 Atom_t x = self->newAtom();
1242 PrgAtom* a = self->getAtom(x);
1243 a->setScc(scc);
1244 a->setSeen(true);
1245 atoms.push_back(x);
1246 if (scc != PrgNode::noScc) { self->auxData_->scc.push_back(a); }
1247 return x;
1248 }
addRuleClasp::Asp::LogicProgram::DlpTr1249 virtual void addRule(const Rule& r) {
1250 SRule meta;
1251 if (!self->simplifyRule(r, rule, meta)) { return; }
1252 bool gamma = type == PrgEdge::Gamma;
1253 Rule rs = rule.rule();
1254 PrgAtom* a = self->getAtom(rs.head[0]);
1255 PrgBody* B = self->assignBodyFor(rs, meta, type, gamma);
1256 if (B->value() != value_false && !B->hasHead(a, PrgEdge::Normal)) {
1257 B->addHead(a, type);
1258 self->stats.gammas += uint32(gamma);
1259 }
1260 }
assignAuxAtomsClasp::Asp::LogicProgram::DlpTr1261 void assignAuxAtoms() {
1262 self->incTrAux(sizeVec(atoms));
1263 while (!atoms.empty()) {
1264 PrgAtom* ax = self->getAtom(atoms.back());
1265 atoms.pop_back();
1266 if (ax->supports()) {
1267 ax->setInUpper(true);
1268 ax->assignVar(*self, *ax->supps_begin());
1269 }
1270 else { self->assignValue(ax, value_false, PrgEdge::noEdge()); }
1271 }
1272 }
1273 LogicProgram* self;
1274 EdgeType type;
1275 uint32 scc;
1276 VarVec atoms;
1277 RuleBuilder rule;
1278 };
1279
1280 // replace disjunctions with gamma (shifted) and delta (component-shifted) rules
finalizeDisjunctions(Preprocessor & p,uint32 numSccs)1281 void LogicProgram::finalizeDisjunctions(Preprocessor& p, uint32 numSccs) {
1282 if (disjunctions_.empty()) { return; }
1283 VarVec head; BodyList supports;
1284 disjIndex_.clear();
1285 SccMap sccMap;
1286 sccMap.resize(numSccs, 0);
1287 enum SccFlag { seen_scc = 1u, is_scc_non_hcf = 128u };
1288 // replace disjunctions with shifted rules and non-hcf-disjunctions
1289 DisjList disj; disj.swap(disjunctions_);
1290 setFrozen(false);
1291 uint32 shifted = 0;
1292 stats.nonHcfs = uint32(nonHcfs_.size());
1293 Literal bot = lit_false();
1294 Potassco::LitVec rb;
1295 VarVec rh;
1296 DlpTr tr(this, PrgEdge::Gamma);
1297 for (uint32 id = 0, maxId = sizeVec(disj); id != maxId; ++id) {
1298 PrgDisj* d = disj[id];
1299 Literal dx = d->inUpper() ? d->literal() : bot;
1300 d->resetId(id, true); // id changed during scc checking
1301 PrgEdge e = PrgEdge::newEdge(*d, PrgEdge::Choice);
1302 // remove from program and
1303 // replace with shifted rules or component-shifted disjunction
1304 head.clear(); supports.clear();
1305 for (PrgDisj::atom_iterator it = d->begin(), end = d->end(); it != end; ++it) {
1306 uint32 aId = *it;
1307 PrgAtom* at = getAtom(aId);
1308 at->removeSupport(e);
1309 if (dx == bot) { continue; }
1310 if (at->eq()) {
1311 at = getAtom(aId = getRootId(aId));
1312 }
1313 if (isFact(at)){
1314 dx = bot;
1315 continue;
1316 }
1317 if (at->inUpper()) {
1318 head.push_back(aId);
1319 if (at->scc() != PrgNode::noScc){ sccMap[at->scc()] = seen_scc; }
1320 }
1321 }
1322 EdgeVec temp;
1323 d->clearSupports(temp);
1324 for (EdgeVec::iterator it = temp.begin(), end = temp.end(); it != end; ++it) {
1325 PrgBody* b = getBody(it->node());
1326 if (b->relevant() && b->value() != value_false) { supports.push_back(b); }
1327 b->removeHead(d, PrgEdge::Normal);
1328 }
1329 d->destroy();
1330 // create shortcut for supports to avoid duplications during shifting
1331 Literal supportLit = dx != bot ? getEqAtomLit(dx, supports, p, sccMap) : dx;
1332 // create shifted rules and split disjunctions into non-hcf components
1333 RuleTransform shifter(tr);
1334 for (VarVec::iterator hIt = head.begin(), hEnd = head.end(); hIt != hEnd; ++hIt) {
1335 uint32 scc = getAtom(*hIt)->scc();
1336 if (scc == PrgNode::noScc || (sccMap[scc] & seen_scc) != 0) {
1337 if (scc != PrgNode::noScc) { sccMap[scc] &= ~seen_scc; }
1338 else { scc = UINT32_MAX; }
1339 rh.assign(1, *hIt);
1340 rb.clear();
1341 if (supportLit.var() != 0) { rb.push_back(toInt(supportLit)); }
1342 else if (supportLit.sign()){ continue; }
1343 for (VarVec::iterator oIt = head.begin(); oIt != hEnd; ++oIt) {
1344 if (oIt != hIt) {
1345 if (getAtom(*oIt)->scc() == scc) { rh.push_back(*oIt); }
1346 else { rb.push_back(Potassco::neg(*oIt)); }
1347 }
1348 }
1349 SRule meta;
1350 if (!simplifyRule(Rule::normal(Head_t::Disjunctive, Potassco::toSpan(rh), Potassco::toSpan(rb)), rule_, meta)) {
1351 continue;
1352 }
1353 Rule sr = rule_.rule();
1354 PrgBody* B = assignBodyFor(sr, meta, PrgEdge::Normal, true);
1355 if (B->value() != value_false && Potassco::size(sr.head) == 1) {
1356 ++shifted;
1357 B->addHead(getAtom(sr.head[0]), PrgEdge::Normal);
1358 }
1359 else if (B->value() != value_false && Potassco::size(sr.head) > 1) {
1360 PrgDisj* x = getDisjFor(sr.head, 0);
1361 B->addHead(x, PrgEdge::Normal);
1362 x->assignVar(*this, *x->supps_begin());
1363 x->setInUpper(true);
1364 x->setSeen(true);
1365 if ((sccMap[scc] & is_scc_non_hcf) == 0) {
1366 sccMap[scc] |= is_scc_non_hcf;
1367 nonHcfs_.add(scc);
1368 }
1369 if (!options().noGamma) {
1370 shifter.transform(sr, Potassco::size(sr.cond) < 4 ? RuleTransform::strategy_no_aux : RuleTransform::strategy_default);
1371 }
1372 else {
1373 // only add support edge
1374 for (PrgDisj::atom_iterator a = x->begin(), end = x->end(); a != end; ++a) {
1375 B->addHead(getAtom(*a), PrgEdge::GammaChoice);
1376 }
1377 }
1378 }
1379 }
1380 }
1381 }
1382 tr.assignAuxAtoms();
1383 if (!disjunctions_.empty() && nonHcfs_.config == 0) {
1384 nonHcfs_.config = ctx()->configuration()->config("tester");
1385 }
1386 upStat(RK(Normal), shifted);
1387 stats.nonHcfs = uint32(nonHcfs_.size()) - stats.nonHcfs;
1388 rh.clear();
1389 setFrozen(true);
1390 }
1391 // optionally transform extended rules in sccs
prepareComponents()1392 void LogicProgram::prepareComponents() {
1393 int trRec = opts_.erMode == mode_transform_scc;
1394 // HACK: force transformation of extended rules in non-hcf components
1395 // REMOVE this once minimality check supports aggregates
1396 if (!disjunctions_.empty() && trRec != 1) {
1397 trRec = 2;
1398 }
1399 if (trRec != 0) {
1400 DlpTr tr(this, PrgEdge::Normal);
1401 RuleTransform trans(tr);
1402 RuleBuilder temp;
1403 setFrozen(false);
1404 EdgeVec heads;
1405 // find recursive aggregates
1406 for (uint32 bIdx = 0, bEnd = numBodies(); bIdx != bEnd; ++bIdx) {
1407 PrgBody* B = bodies_[bIdx];
1408 if (B->type() == Body_t::Normal || !B->hasVar() || B->value() == value_false) { continue; } // not aggregate or not relevant
1409 tr.scc = B->scc(*this);
1410 if (tr.scc == PrgNode::noScc || (trRec == 2 && !nonHcfs_.find(tr.scc))) { continue; } // not recursive
1411 // transform all rules a :- B, where scc(a) == scc(B):
1412 heads.clear();
1413 for (PrgBody::head_iterator hIt = B->heads_begin(), hEnd = B->heads_end(); hIt != hEnd; ++hIt) {
1414 assert(hIt->isAtom());
1415 if (getAtom(hIt->node())->scc() == tr.scc) { heads.push_back(*hIt); }
1416 }
1417 if (heads.empty()) { continue; }
1418 using Potassco::Lit_t;
1419 Head_t ht = !isChoice(heads[0].type()) ? Head_t::Disjunctive : Head_t::Choice;
1420 Atom_t h = heads[0].node();
1421 Lit_t aux = 0;
1422 if (heads.size() > 1) { // more than one head, make body eq to some new aux atom
1423 ht = Head_t::Disjunctive;
1424 h = tr.newAtom();
1425 aux = Potassco::lit(h);
1426 }
1427 temp.clear();
1428 if (!B->toData(*this, temp) || temp.bodyType() == Body_t::Normal) {
1429 B->simplify(*this, true, 0);
1430 continue;
1431 }
1432 trans.transform(Rule::sum(ht, Potassco::toSpan(&h, 1), temp.sum()));
1433 for (EdgeVec::const_iterator hIt = heads.begin(); hIt != heads.end(); ++hIt) {
1434 B->removeHead(getAtom(hIt->node()), hIt->type());
1435 if (h != hIt->node()) {
1436 ht = !isChoice(hIt->type()) ? Head_t::Disjunctive : Head_t::Choice;
1437 h = hIt->node();
1438 tr.addRule(Rule::normal(ht, Potassco::toSpan(&h, 1), Potassco::toSpan(&aux, 1)));
1439 }
1440 }
1441 }
1442 tr.assignAuxAtoms();
1443 setFrozen(true);
1444 }
1445 }
prepareOutputTable()1446 void LogicProgram::prepareOutputTable() {
1447 OutputTable& out = ctx()->output;
1448 // add new output predicates in program order to output table
1449 std::stable_sort(show_.begin(), show_.end(), compose22(std::less<Id_t>(), select1st<ShowPair>(), select1st<ShowPair>()));
1450 for (ShowVec::iterator it = show_.begin(), end = show_.end(); it != end; ++it) {
1451 Literal lit = getLiteral(it->first);
1452 bool isAtom = it->first < startAuxAtom();
1453 if (!isSentinel(lit)) { out.add(it->second, lit, it->first); if (isAtom) ctx()->setOutput(lit.var(), true); }
1454 else if (lit == lit_true()) { out.add(it->second); }
1455 }
1456 if (!auxData_->project.empty()) {
1457 for (VarVec::const_iterator it = auxData_->project.begin(), end = auxData_->project.end(); it != end; ++it) {
1458 out.addProject(getLiteral(*it));
1459 }
1460 }
1461 }
1462
1463 // Make assumptions/externals exempt from sat-preprocessing
freezeAssumptions()1464 void LogicProgram::freezeAssumptions() {
1465 for (VarVec::const_iterator it = frozen_.begin(), end = frozen_.end(); it != end; ++it) {
1466 ctx()->setFrozen(getRootAtom(*it)->var(), true);
1467 }
1468 for (Potassco::LitVec::const_iterator it = assume_.begin(), end = assume_.end(); it != end; ++it) {
1469 ctx()->setFrozen(getLiteral(Potassco::id(*it)).var(), true);
1470 }
1471 }
1472
1473 // add (completion) nogoods
addConstraints()1474 bool LogicProgram::addConstraints() {
1475 ClauseCreator gc(ctx()->master());
1476 if (options().iters == 0) {
1477 gc.addDefaultFlags(ClauseCreator::clause_force_simplify);
1478 }
1479 ctx()->startAddConstraints();
1480 // handle initial conflict, if any
1481 if (!ctx()->ok() || !ctx()->addUnary(getTrueAtom()->trueLit())) {
1482 return false;
1483 }
1484 if (incData_ && !incData_->steps.empty() && !ctx()->addUnary(posLit(incData_->steps.back().second))) {
1485 return false;
1486 }
1487 if (options().noGamma && !disjunctions_.empty()) {
1488 // add "rule" nogoods for disjunctions
1489 for (DisjList::const_iterator it = disjunctions_.begin(); it != disjunctions_.end(); ++it) {
1490 gc.start().add(~(*it)->literal());
1491 for (PrgDisj::atom_iterator a = (*it)->begin(); a != (*it)->end(); ++a) {
1492 gc.add(getAtom(*a)->literal());
1493 }
1494 if (!gc.end()) { return false; }
1495 }
1496 }
1497 // add bodies from this step
1498 for (BodyList::const_iterator it = bodies_.begin(); it != bodies_.end(); ++it) {
1499 if (!toConstraint((*it), *this, gc)) { return false; }
1500 }
1501 // add atoms thawed in this step
1502 for (VarIter it = unfreeze_begin(), end = unfreeze_end(); it != end; ++it) {
1503 if (!toConstraint(getAtom(*it), *this, gc)) { return false; }
1504 }
1505 // add atoms from this step
1506 const bool freezeAll = incData_ != 0;
1507 const uint32 hiAtom = startAuxAtom();
1508 uint32 id = startAtom();
1509 for (AtomList::const_iterator it = atoms_.begin()+startAtom(), end = atoms_.end(); it != end; ++it, ++id) {
1510 if (!toConstraint(*it, *this, gc)) { return false; }
1511 if (id < hiAtom && (*it)->hasVar()){
1512 if (freezeAll) { ctx()->setFrozen((*it)->var(), true); }
1513 ctx()->setInput((*it)->var(), true);
1514 }
1515 }
1516 if (!auxData_->scc.empty()) {
1517 if (ctx()->sccGraph.get() == 0) {
1518 ctx()->sccGraph = new PrgDepGraph(static_cast<PrgDepGraph::NonHcfMapType>(opts_.oldMap == 0));
1519 }
1520 uint32 oldNodes = ctx()->sccGraph->nodes();
1521 ctx()->sccGraph->addSccs(*this, auxData_->scc, nonHcfs_);
1522 stats.ufsNodes = ctx()->sccGraph->nodes()-oldNodes;
1523 }
1524 return true;
1525 }
addDomRules()1526 void LogicProgram::addDomRules() {
1527 if (auxData_->dom.empty()) { return; }
1528 VarVec domVec;
1529 EqVec eqVec;
1530 DomRules& doms = auxData_->dom;
1531 Solver const& s = *ctx()->master();
1532 // mark any previous domain atoms so that we can decide
1533 // whether existing variables can be used for the atoms in doms
1534 if (incData_) {
1535 domVec.swap(incData_->doms);
1536 for (VarVec::const_iterator it = domVec.begin(); it != domVec.end(); ++it) {
1537 if (s.value(*it) == value_free) { ctx()->mark(posLit(*it)); }
1538 }
1539 }
1540 DomRules::iterator j;
1541 IndexMap::const_iterator eq;
1542 DomRule r;
1543 for (DomRules::iterator it = (j = doms.begin()), end = doms.end(); it != end; ++it) {
1544 Literal cond = getLiteral(it->cond);
1545 Literal slit = getLiteral(it->atom);
1546 Var svar = slit.var();
1547 if (s.isFalse(cond) || s.value(svar) != value_free) { continue; }
1548 if (s.isTrue(cond)) { it->cond = 0; cond = lit_true(); }
1549 // check if atom is the root for its var
1550 if (!atomState_.isSet(it->atom, AtomState::dom_flag)) {
1551 if (!ctx()->marked(posLit(svar))) {
1552 // var(it->atom) is not yet used - make it->atom its root
1553 ctx()->mark(posLit(svar));
1554 atomState_.set(it->atom, AtomState::dom_flag);
1555 domVec.push_back(svar);
1556 }
1557 else if ((eq = domEqIndex_.find(it->atom)) != domEqIndex_.end()) {
1558 // var(it->atom) is used but we already created a new var for it->atom
1559 slit = posLit(svar = eq->second);
1560 }
1561 else {
1562 // var(it->atom) is used - introduce new aux var and make it eq to lit(atom)
1563 Eq n = { ctx()->addVar(Var_t::Atom, VarInfo::Nant), slit };
1564 eqVec.push_back(n);
1565 svar = n.var;
1566 slit = posLit(svar);
1567 domEqIndex_.insert(IndexMap::value_type(static_cast<uint32>(it->atom), svar));
1568 }
1569 }
1570 *j++ = (r = *it);
1571 if (slit.sign()) {
1572 if (r.type == DomModType::Sign) { r.bias = r.bias != 0 ? -r.bias : 0; }
1573 else if (r.type == DomModType::True) { r.type = DomModType::False; }
1574 else if (r.type == DomModType::False) { r.type = DomModType::True; }
1575 }
1576 ctx()->heuristic.add(svar, static_cast<DomModType>(r.type), r.bias, r.prio, cond);
1577 }
1578 if (j != doms.end()) {
1579 upStat(RK(Heuristic), -static_cast<int>(doms.end() - j));
1580 doms.erase(j, doms.end());
1581 }
1582 // cleanup var flags
1583 for (VarVec::const_iterator it = domVec.begin(); it != domVec.end(); ++it) {
1584 ctx()->unmark(*it);
1585 }
1586 if (incData_) {
1587 incData_->doms.swap(domVec);
1588 }
1589 if (!eqVec.empty()) {
1590 ctx()->startAddConstraints();
1591 for (EqVec::const_iterator it = eqVec.begin(), end = eqVec.end(); it != end; ++it) {
1592 // it->var == it->lit
1593 ctx()->addBinary(~it->lit, posLit(it->var));
1594 ctx()->addBinary( it->lit, negLit(it->var));
1595 }
1596 }
1597 }
1598
addAcycConstraint()1599 void LogicProgram::addAcycConstraint() {
1600 AcycRules& acyc = auxData_->acyc;
1601 if (acyc.empty()) { return; }
1602 SharedContext& ctx = *this->ctx();
1603 ExtDepGraph* graph = ctx.extGraph.get();
1604 const Solver& s = *ctx.master();
1605 if (graph) { graph->update(); }
1606 else { ctx.extGraph = (graph = new ExtDepGraph()); }
1607 for (AcycRules::const_iterator it = acyc.begin(), end = acyc.end(); it != end; ++it) {
1608 Literal lit = getLiteral(it->cond);
1609 if (!s.isFalse(lit)) {
1610 graph->addEdge(lit, it->node[0], it->node[1]);
1611 }
1612 else {
1613 upStat(RK(Acyc), -1);
1614 }
1615 }
1616 if (graph->finalize(ctx) == 0) { ctx.extGraph = 0; }
1617 }
1618 #undef check_modular
1619 /////////////////////////////////////////////////////////////////////////////////////////
1620 // misc/helper functions
1621 /////////////////////////////////////////////////////////////////////////////////////////
resize(Atom_t atomId)1622 PrgAtom* LogicProgram::resize(Atom_t atomId) {
1623 while (atoms_.size() <= AtomList::size_type(atomId)) {
1624 newAtom();
1625 }
1626 return getRootAtom(atomId);
1627 }
1628
propagate(bool backprop)1629 bool LogicProgram::propagate(bool backprop) {
1630 assert(frozen());
1631 bool oldB = opts_.backprop != 0;
1632 opts_.backprop = backprop;
1633 for (VarVec::size_type i = 0; i != propQ_.size(); ++i) {
1634 PrgAtom* a = getAtom(propQ_[i]);
1635 if (!a->relevant()) { continue; }
1636 if (!a->propagateValue(*this, backprop)) {
1637 setConflict();
1638 return false;
1639 }
1640 if (a->hasVar() && a->id() < startAtom() && !ctx()->addUnary(a->trueLit())) {
1641 setConflict();
1642 return false;
1643 }
1644 }
1645 opts_.backprop = oldB;
1646 propQ_.clear();
1647 return true;
1648 }
litVal(const PrgAtom * a,bool pos) const1649 ValueRep LogicProgram::litVal(const PrgAtom* a, bool pos) const {
1650 if (a->value() != value_free || !a->relevant()) {
1651 bool vSign = a->value() == value_false || !a->relevant();
1652 if (vSign == pos) { return value_false; }
1653 return a->value() != value_weak_true ? value_true : value_free;
1654 }
1655 return value_free;
1656 }
1657
1658 // Simplifies the given normal rule H :- l1, ..., ln
1659 // - removes true and duplicate literals from body: {T,a,b,a} -> {a, b}.
1660 // - checks for contradictions and false literals in body: {a, not a} -> F
1661 // - checks for satisfied head and removes false atoms from head
1662 // POST: if true out contains the simplified normal rule.
simplifyNormal(Head_t ht,const Potassco::AtomSpan & head,const Potassco::LitSpan & body,RuleBuilder & out,SRule & meta)1663 bool LogicProgram::simplifyNormal(Head_t ht, const Potassco::AtomSpan& head, const Potassco::LitSpan& body, RuleBuilder& out, SRule& meta) {
1664 out.clear();
1665 out.startBody();
1666 meta = SRule();
1667 bool ok = true;
1668 for (Potassco::LitSpan::iterator it = Potassco::begin(body), end = Potassco::end(body); it != end; ++it) {
1669 POTASSCO_CHECK(Potassco::atom(*it) < bodyId, EOVERFLOW, "Atom out of bounds");
1670 PrgAtom* a = resize(Potassco::atom(*it));
1671 Literal p = Literal(a->id(), *it < 0);// replace any eq atoms
1672 ValueRep v = litVal(a, !p.sign());
1673 if (v == value_false || atomState_.inBody(~p)) {
1674 ok = false;
1675 break;
1676 }
1677 else if (v != value_true && !atomState_.inBody(p)) {
1678 atomState_.addToBody(p);
1679 out.addGoal(toInt(p));
1680 meta.pos += !p.sign();
1681 meta.hash += hashLit(p);
1682 }
1683 }
1684 uint32_t bs = toU32(size(out.body()));
1685 meta.bid = ok ? findBody(meta.hash, Body_t::Normal, bs) : varMax;
1686 ok = ok && pushHead(ht, head, 0, out);
1687 for (const Potassco::Lit_t* it = out.lits_begin(); bs--;) {
1688 atomState_.clearRule(Potassco::atom(*it++));
1689 }
1690 return ok;
1691 }
1692
1693 struct IsLit {
IsLitClasp::Asp::IsLit1694 IsLit(Potassco::Lit_t x) : lhs(x) {}
1695 template <class P>
operator ()Clasp::Asp::IsLit1696 bool operator()(const P& rhs) const { return lhs == Potassco::lit(rhs); }
1697 Potassco::Lit_t lhs;
1698 };
1699
1700 // Simplifies the given sum rule: H :- lb { l1 = w1 ... ln = wn }.
1701 // - removes assigned literals and updates lb accordingly
1702 // - removes literals li with weight wi = 0
1703 // - reduces weights wi > bound() to bound
1704 // - merges duplicate literals in sum, i.e. lb {a=w1, b=w2, a=w3} -> lb {a=w1+w3, b=w2}
1705 // - checks for contradiction, i.e. sum contains both p and not p and both are needed
1706 // - replaces sum with count if all weights are equal
1707 // - replaces sum with normal body if all literals must be true for the sum to be satisfied
1708 // POST: if true out contains the simplified rule.
simplifySum(Head_t ht,const Potassco::AtomSpan & head,const Potassco::Sum_t & body,RuleBuilder & out,SRule & meta)1709 bool LogicProgram::simplifySum(Head_t ht, const Potassco::AtomSpan& head, const Potassco::Sum_t& body, RuleBuilder& out, SRule& meta) {
1710 meta = SRule();
1711 weight_t bound = body.bound, maxW = 1, minW = CLASP_WEIGHT_T_MAX, sumW = 0, dirty = 0;
1712 out.clear();
1713 out.startSum(bound);
1714 for (Potassco::WeightLitSpan::iterator it = Potassco::begin(body.lits), end = Potassco::end(body.lits); it != end && bound > 0; ++it) {
1715 POTASSCO_CHECK(it->weight >= 0, EDOM, "Non-negative weight expected!");
1716 POTASSCO_CHECK(Potassco::atom(*it) < bodyId, EOVERFLOW, "Atom out of bounds");
1717 if (it->weight == 0) continue; // skip irrelevant lits
1718 PrgAtom* a = resize(Potassco::atom(*it));
1719 Literal p = Literal(a->id(), Potassco::lit(*it) < 0);// replace any eq atoms
1720 ValueRep v = litVal(a, !p.sign());
1721 weight_t w = Potassco::weight(*it);
1722 if (v == value_true) { bound -= w; }
1723 else if (v != value_false) {
1724 POTASSCO_CHECK((CLASP_WEIGHT_T_MAX-sumW)>= w, EOVERFLOW, "Integer overflow!");
1725 sumW += w;
1726 if (!atomState_.inBody(p)) {
1727 atomState_.addToBody(p);
1728 out.addGoal(toInt(p), w);
1729 meta.pos += !p.sign();
1730 meta.hash += hashLit(p);
1731 }
1732 else { // Merge duplicate lits
1733 Potassco::WeightLit_t* pos = std::find_if(out.wlits_begin(), out.wlits_end(), IsLit(toInt(p)));
1734 POTASSCO_ASSERT(pos != out.wlits_end());
1735 w = (pos->weight += w);
1736 ++dirty;
1737 }
1738 if (w > maxW) { maxW = w; }
1739 if (w < minW) { minW = w; }
1740 dirty += static_cast<weight_t>(atomState_.inBody(~p));
1741 }
1742 }
1743 weight_t sumR = sumW;
1744 if (bound > 0 && (dirty || maxW > bound)) {
1745 sumR = 0, minW = CLASP_WEIGHT_T_MAX;
1746 for (Potassco::WeightLit_t* it = out.wlits_begin(), *end = out.wlits_end(); it != end; ++it) {
1747 Literal p = toLit(it->lit);
1748 weight_t w = it->weight;
1749 if (w > bound) { sumW -= (w - bound); it->weight = (maxW = w = bound); }
1750 if (w < minW) { minW = w; }
1751 sumR += w;
1752 if (p.sign() && atomState_.inBody(~p)) {
1753 // body contains p and ~p: we can achieve at most max(weight(p), weight(~p))
1754 sumR -= std::min(w, std::find_if(out.wlits_begin(), end, IsLit(Potassco::neg(it->lit)))->weight);
1755 }
1756 }
1757 }
1758 out.setBound(bound);
1759 if (bound <= 0 || sumR < bound) {
1760 for (const Potassco::WeightLit_t* it = out.wlits_begin(), *end = out.wlits_end(); it != end; ++it) { atomState_.clearRule(Potassco::atom(*it)); }
1761 return bound <= 0 && simplifyNormal(ht, head, Potassco::toSpan<Potassco::Lit_t>(), out, meta);
1762 }
1763 else if ((sumW - minW) < bound) {
1764 out.weaken(Body_t::Normal);
1765 meta.bid = findBody(meta.hash, Body_t::Normal, toU32(size(out.body())));
1766 bool ok = pushHead(ht, head, 0, out);
1767 for (const Potassco::Lit_t* it = out.lits_begin(), *end = out.lits_end(); it != end; ++it) {
1768 atomState_.clearRule(Potassco::atom(*it));
1769 }
1770 return ok;
1771 }
1772 else if (minW == maxW) {
1773 out.weaken(Body_t::Count, maxW != 1);
1774 bound = out.bound();
1775 }
1776 meta.bid = findBody(meta.hash, out.bodyType(), (uint32_t)std::distance(out.wlits_begin(), out.wlits_end()), out.bound(), out.wlits_begin());
1777 bool ok = pushHead(ht, head, sumW - out.bound(), out);
1778 for (const Potassco::WeightLit_t* it = out.wlits_begin(), *end = out.wlits_end(); it != end; ++it) {
1779 atomState_.clearRule(Potassco::atom(*it));
1780 }
1781 return ok;
1782 }
1783
1784 // Pushes the given rule head to the body given in out.
1785 // Pre: Body literals are marked and lits is != 0 if body is a sum.
pushHead(Head_t ht,const Potassco::AtomSpan & head,weight_t slack,RuleBuilder & out)1786 bool LogicProgram::pushHead(Head_t ht, const Potassco::AtomSpan& head, weight_t slack, RuleBuilder& out) {
1787 const uint8 ignoreMask = AtomState::false_flag|AtomState::head_flag;
1788 uint32 hs = 0;
1789 bool sat = false, sum = out.bodyType() == Body_t::Sum;
1790 out.start(ht);
1791 for (Potassco::AtomSpan::iterator it = Potassco::begin(head), end = Potassco::end(head); it != end; ++it) {
1792 if (!atomState_.isSet(*it, AtomState::simp_mask)) {
1793 out.addHead(*it);
1794 atomState_.addToHead(*it);
1795 ++hs;
1796 }
1797 else if (!atomState_.isSet(*it, ignoreMask)) { // h occurs in B+ and/or B- or is true
1798 weight_t wp = weight_t(atomState_.inBody(posLit(*it))), wn = weight_t(atomState_.inBody(negLit(*it)));
1799 if (wp && sum) { wp = std::find_if(out.wlits_begin(), out.wlits_end(), IsLit(Potassco::lit(*it)))->weight; }
1800 if (wn && sum) { wn = std::find_if(out.wlits_begin(), out.wlits_end(), IsLit(Potassco::neg(*it)))->weight; }
1801 if (atomState_.isFact(*it) || wp > slack) { sat = true; }
1802 else if (wn <= slack) {
1803 out.addHead(*it);
1804 atomState_.addToHead(*it);
1805 ++hs;
1806 }
1807 }
1808 }
1809 for (const Atom_t* it = out.head_begin(), *end = it + hs; it != end; ++it) {
1810 atomState_.clearRule(*it);
1811 }
1812 return !sat || (ht == Head_t::Choice && hs);
1813 }
1814
simplifyRule(const Rule & r,Potassco::RuleBuilder & out,SRule & meta)1815 bool LogicProgram::simplifyRule(const Rule& r, Potassco::RuleBuilder& out, SRule& meta) {
1816 return r.normal()
1817 ? simplifyNormal(r.ht, r.head, r.cond, out, meta)
1818 : simplifySum(r.ht, r.head, r.agg, out, meta);
1819 }
1820 // create new atom aux representing supports, i.e.
1821 // aux == S1 v ... v Sn
getEqAtomLit(Literal lit,const BodyList & supports,Preprocessor & p,const SccMap & sccMap)1822 Literal LogicProgram::getEqAtomLit(Literal lit, const BodyList& supports, Preprocessor& p, const SccMap& sccMap) {
1823 if (supports.empty() || lit == lit_false()) {
1824 return lit_false();
1825 }
1826 else if (supports.size() == 1 && supports[0]->size() < 2 && supports.back()->literal() == lit) {
1827 return supports[0]->size() == 0 ? lit_true() : supports[0]->goal(0);
1828 }
1829 else if (p.getRootAtom(lit) != varMax) {
1830 return posLit(p.getRootAtom(lit));
1831 }
1832 incTrAux(1);
1833 Atom_t auxV = newAtom();
1834 PrgAtom* aux = getAtom(auxV);
1835 uint32 scc = PrgNode::noScc;
1836 aux->setLiteral(lit);
1837 aux->setSeen(true);
1838 p.setRootAtom(aux->literal(), auxV);
1839 for (BodyList::const_iterator sIt = supports.begin(); sIt != supports.end(); ++sIt) {
1840 PrgBody* b = *sIt;
1841 if (b->relevant() && b->value() != value_false) {
1842 for (uint32 g = 0; scc == PrgNode::noScc && g != b->size() && !b->goal(g).sign(); ++g) {
1843 uint32 aScc = getAtom(b->goal(g).var())->scc();
1844 if (aScc != PrgNode::noScc && (sccMap[aScc] & 1u)) { scc = aScc; }
1845 }
1846 b->addHead(aux, PrgEdge::Normal);
1847 if (b->value() != value_free && !assignValue(aux, b->value(), PrgEdge::newEdge(*b, PrgEdge::Normal))) {
1848 break;
1849 }
1850 aux->setInUpper(true);
1851 }
1852 }
1853 if (!aux->inUpper()) {
1854 aux->setValue(value_false);
1855 return lit_false();
1856 }
1857 else if (scc != PrgNode::noScc) {
1858 aux->setScc(scc);
1859 auxData_->scc.push_back(aux);
1860 }
1861 return posLit(auxV);
1862 }
1863
getBodyFor(const Rule & r,const SRule & meta,bool addDeps)1864 PrgBody* LogicProgram::getBodyFor(const Rule& r, const SRule& meta, bool addDeps) {
1865 if (meta.bid < bodies_.size()) {
1866 return getBody(meta.bid);
1867 }
1868 // no corresponding body exists, create a new object
1869 PrgBody* b = PrgBody::create(*this, numBodies(), r, meta.pos, addDeps);
1870 bodyIndex_.insert(IndexMap::value_type(meta.hash, b->id()));
1871 bodies_.push_back(b);
1872 if (b->isSupported()) {
1873 initialSupp_.push_back(b->id());
1874 }
1875 upStat(r.bt);
1876 return b;
1877 }
getTrueBody()1878 PrgBody* LogicProgram::getTrueBody() {
1879 uint32 id = findBody(0, Body_t::Normal, 0);
1880 if (id < bodies_.size()) {
1881 return getBody(id);
1882 }
1883 return getBodyFor(Rule::normal(Head_t::Choice, Potassco::toSpan<Atom_t>(), Potassco::toSpan<Potassco::Lit_t>()), SRule());
1884 }
assignBodyFor(const Rule & r,const SRule & meta,EdgeType depEdge,bool simpStrong)1885 PrgBody* LogicProgram::assignBodyFor(const Rule& r, const SRule& meta, EdgeType depEdge, bool simpStrong) {
1886 PrgBody* b = getBodyFor(r, meta, depEdge != PrgEdge::Gamma);
1887 if (!b->hasVar() && !b->seen()) {
1888 uint32 eqId;
1889 b->markDirty();
1890 b->simplify(*this, simpStrong, &eqId);
1891 if (eqId != b->id()) {
1892 assert(b->id() == bodies_.size()-1);
1893 removeBody(b, meta.hash);
1894 bodies_.pop_back();
1895 if (depEdge != PrgEdge::Gamma) {
1896 for (uint32 i = 0; i != b->size(); ++i) {
1897 getAtom(b->goal(i).var())->removeDep(b->id(), !b->goal(i).sign());
1898 }
1899 }
1900 b->destroy();
1901 b = bodies_[eqId];
1902 }
1903 }
1904 b->setSeen(true);
1905 b->assignVar(*this);
1906 return b;
1907 }
1908
equalLits(const PrgBody & b,const WeightLitSpan & lits) const1909 bool LogicProgram::equalLits(const PrgBody& b, const WeightLitSpan& lits) const {
1910 WeightLitSpan::iterator lBeg = Potassco::begin(lits), lEnd = Potassco::end(lits);
1911 for (uint32 i = 0, end = b.size(); i != end; ++i) {
1912 Potassco::WeightLit_t wl = { toInt(b.goal(i)), b.weight(i) };
1913 if (!std::binary_search(lBeg, lEnd, wl)) { return false; }
1914 }
1915 return true;
1916 }
1917
1918 // Pre: all literals in body are marked.
findBody(uint32 hash,Body_t type,uint32 size,weight_t bound,Potassco::WeightLit_t * sum)1919 uint32 LogicProgram::findBody(uint32 hash, Body_t type, uint32 size, weight_t bound, Potassco::WeightLit_t* sum) {
1920 IndexRange bodies = bodyIndex_.equal_range(hash);
1921 bool sorted = false;
1922 if (type == Body_t::Normal) { bound = static_cast<weight_t>(size); }
1923 for (IndexIter it = bodies.first; it != bodies.second; ++it) {
1924 const PrgBody& b = *getBody(it->second);
1925 if (!checkBody(b, type, size, bound) || !atomState_.inBody(b.goals_begin(), b.goals_end())) {
1926 continue;
1927 }
1928 else if (!b.hasWeights()) {
1929 return b.id();
1930 }
1931 else if (sum) {
1932 if (!sorted) {
1933 std::sort(sum, sum + size);
1934 sorted = true;
1935 }
1936 if (equalLits(b, Potassco::toSpan(sum, size))) { return b.id(); }
1937 }
1938 }
1939 return varMax;
1940 }
1941
findEqBody(const PrgBody * b,uint32 hash)1942 uint32 LogicProgram::findEqBody(const PrgBody* b, uint32 hash) {
1943 IndexRange bodies = bodyIndex_.equal_range(hash);
1944 if (bodies.first == bodies.second) { return varMax; }
1945 uint32 eqId = varMax, n = 0, r = 0;
1946 for (IndexIter it = bodies.first; it != bodies.second && eqId == varMax; ++it) {
1947 const PrgBody& rhs = *getBody(it->second);
1948 if (!checkBody(rhs, b->type(), b->size(), b->bound())) { continue; }
1949 else if (b->size() == 0) { eqId = rhs.id(); }
1950 else if (b->size() == 1) { eqId = b->goal(0) == rhs.goal(0) && b->weight(0) == rhs.weight(0) ? rhs.id() : varMax; }
1951 else {
1952 if (++n == 1) { std::for_each(b->goals_begin(), b->goals_end(), std::bind1st(std::mem_fun(&AtomState::addToBody), &atomState_)); }
1953 if (!atomState_.inBody(rhs.goals_begin(), rhs.goals_end())) { continue; }
1954 else if (!b->hasWeights()) { eqId = rhs.id(); }
1955 else {
1956 if (n == 1 || r == 0) {
1957 rule_.clear();
1958 if (!b->toData(*this, rule_) || rule_.bodyType() != Body_t::Sum) {
1959 rule_.clear();
1960 continue;
1961 }
1962 r = 1;
1963 std::sort(rule_.wlits_begin(), rule_.wlits_end());
1964 }
1965 if (equalLits(rhs, rule_.sum().lits)) { eqId = rhs.id(); }
1966 }
1967 }
1968 }
1969 if (n) {
1970 rule_.clear();
1971 std::for_each(b->goals_begin(), b->goals_end(), std::bind1st(std::mem_fun(&AtomState::clearBody), &atomState_));
1972 }
1973 return eqId;
1974 }
1975
getDisjFor(const Potassco::AtomSpan & head,uint32 headHash)1976 PrgDisj* LogicProgram::getDisjFor(const Potassco::AtomSpan& head, uint32 headHash) {
1977 PrgDisj* d = 0;
1978 if (headHash) {
1979 LogicProgram::IndexRange eqRange = disjIndex_.equal_range(headHash);
1980 for (; eqRange.first != eqRange.second; ++eqRange.first) {
1981 PrgDisj& o = *disjunctions_[eqRange.first->second];
1982 if (o.relevant() && o.size() == Potassco::size(head) && atomState_.allMarked(o.begin(), o.end(), AtomState::head_flag)) {
1983 assert(o.id() == eqRange.first->second);
1984 d = &o;
1985 break;
1986 }
1987 }
1988 for (Potassco::AtomSpan::iterator it = Potassco::begin(head), end = Potassco::end(head); it != end; ++it) {
1989 atomState_.clearRule(*it);
1990 }
1991 }
1992 if (!d) {
1993 // no corresponding disjunction exists, create a new object
1994 // and link it to all atoms
1995 ++stats.disjunctions[statsId_];
1996 d = PrgDisj::create((uint32)disjunctions_.size(), head);
1997 disjunctions_.push_back(d);
1998 PrgEdge edge = PrgEdge::newEdge(*d, PrgEdge::Choice);
1999 for (Potassco::AtomSpan::iterator it = Potassco::begin(head), end = Potassco::end(head); it != end; ++it) {
2000 getAtom(*it)->addSupport(edge);
2001 }
2002 if (headHash) {
2003 disjIndex_.insert(IndexMap::value_type(headHash, d->id()));
2004 }
2005 }
2006 return d;
2007 }
2008
2009 // body has changed - update index
update(PrgBody * body,uint32 oldHash,uint32 newHash)2010 uint32 LogicProgram::update(PrgBody* body, uint32 oldHash, uint32 newHash) {
2011 uint32 id = removeBody(body, oldHash);
2012 if (body->relevant()) {
2013 uint32 eqId = findEqBody(body, newHash);
2014 if (eqId == varMax) {
2015 // No equivalent body found.
2016 // Add new entry to index
2017 bodyIndex_.insert(IndexMap::value_type(newHash, id));
2018 }
2019 return eqId;
2020 }
2021 return varMax;
2022 }
2023
2024 // body b has changed - remove old entry from body node index
removeBody(PrgBody * b,uint32 hash)2025 uint32 LogicProgram::removeBody(PrgBody* b, uint32 hash) {
2026 IndexRange ra = bodyIndex_.equal_range(hash);
2027 uint32 id = b->id();
2028 for (; ra.first != ra.second; ++ra.first) {
2029 if (bodies_[ra.first->second] == b) {
2030 id = ra.first->second;
2031 bodyIndex_.erase(ra.first);
2032 break;
2033 }
2034 }
2035 return id;
2036 }
2037
mergeEqAtoms(PrgAtom * a,Id_t rootId)2038 PrgAtom* LogicProgram::mergeEqAtoms(PrgAtom* a, Id_t rootId) {
2039 PrgAtom* root = getAtom(rootId = getRootId(rootId));
2040 ValueRep mv = getMergeValue(a, root);
2041 assert(!a->eq() && !root->eq() && !a->frozen());
2042 if (a->ignoreScc()) { root->setIgnoreScc(true); }
2043 if (mv != a->value() && !assignValue(a, mv, PrgEdge::noEdge())) { return 0; }
2044 if (mv != root->value() && !assignValue(root, mv, PrgEdge::noEdge())){ return 0; }
2045 a->setEq(rootId);
2046 incEqs(Var_t::Atom);
2047 return root;
2048 }
2049
2050 // returns whether posSize(root) <= posSize(body)
positiveLoopSafe(PrgBody * body,PrgBody * root) const2051 bool LogicProgram::positiveLoopSafe(PrgBody* body, PrgBody* root) const {
2052 uint32 i = 0, end = std::min(body->size(), root->size());
2053 while (i != end && body->goal(i).sign() == root->goal(i).sign()) { ++i; }
2054 return i == root->size() || root->goal(i).sign();
2055 }
2056
mergeEqBodies(PrgBody * b,Id_t rootId,bool hashEq,bool atomsAssigned)2057 PrgBody* LogicProgram::mergeEqBodies(PrgBody* b, Id_t rootId, bool hashEq, bool atomsAssigned) {
2058 PrgBody* root = getBody(rootId = getEqNode(bodies_, rootId));
2059 bool bp = options().backprop != 0;
2060 if (b == root) { return root; }
2061 assert(!b->eq() && !root->eq() && (hashEq || b->literal() == root->literal()));
2062 if (!b->simplifyHeads(*this, atomsAssigned) || (b->value() != root->value() && (!mergeValue(b, root) || !root->propagateValue(*this, bp) || !b->propagateValue(*this, bp)))) {
2063 setConflict();
2064 return 0;
2065 }
2066 if (hashEq || positiveLoopSafe(b, root)) {
2067 b->setLiteral(root->literal());
2068 if (!root->mergeHeads(*this, *b, atomsAssigned, !hashEq)) {
2069 setConflict();
2070 return 0;
2071 }
2072 incEqs(Var_t::Body);
2073 b->setEq(rootId);
2074 return root;
2075 }
2076 return b;
2077 }
2078
findName(Atom_t x) const2079 const char* LogicProgram::findName(Atom_t x) const {
2080 for (OutputTable::pred_iterator it = ctx()->output.pred_begin(), end = ctx()->output.pred_end(); it != end; ++it) {
2081 if (it->user == x) { return it->name; }
2082 }
2083 for (ShowVec::const_iterator it = show_.begin(), end = show_.end(); it != end; ++it) {
2084 if (it->first == x){ return it->second; }
2085 }
2086 return "";
2087 }
getSupportedBodies(bool sorted)2088 VarVec& LogicProgram::getSupportedBodies(bool sorted) {
2089 if (sorted) {
2090 std::stable_sort(initialSupp_.begin(), initialSupp_.end(), LessBodySize(bodies_));
2091 }
2092 return initialSupp_;
2093 }
2094
falseAtom()2095 Atom_t LogicProgram::falseAtom() {
2096 Atom_t aFalse = 0;
2097 for (Var i = 1; i < atoms_.size() && !aFalse; ++i) {
2098 if (atoms_[i]->value() == value_false || atomState_.isSet(i, AtomState::false_flag)) {
2099 aFalse = i;
2100 }
2101 }
2102 if (!aFalse) {
2103 bool s = frozen();
2104 setFrozen(false);
2105 aFalse = newAtom();
2106 assignValue(getAtom(aFalse), value_false, PrgEdge::noEdge());
2107 setFrozen(s);
2108 }
2109 return aFalse;
2110 }
2111
extractCondition(Id_t id,Potassco::LitVec & out) const2112 bool LogicProgram::extractCondition(Id_t id, Potassco::LitVec& out) const {
2113 out.clear();
2114 if (id == Clasp::Asp::falseId || (frozen() && getLiteral(id) == lit_false())) { return false; }
2115 if (!id || isAtom(id)) {
2116 out.assign(id != 0, Potassco::lit(id));
2117 return true;
2118 }
2119 Id_t bId = nodeId(id);
2120 POTASSCO_ASSERT(validBody(bId), "Invalid literal");
2121 const PrgBody* B = getBody(getEqBody(bId));
2122 out.reserve(B->size());
2123 for (PrgBody::goal_iterator it = B->goals_begin(), end = B->goals_end(); it != end; ++it) {
2124 out.push_back( toInt(*it) );
2125 }
2126 return true;
2127 }
2128 #undef RT
2129 /////////////////////////////////////////////////////////////////////////////////////////
2130 // class LogicProgramAdapter
2131 /////////////////////////////////////////////////////////////////////////////////////////
LogicProgramAdapter(LogicProgram & prg)2132 LogicProgramAdapter::LogicProgramAdapter(LogicProgram& prg) : lp_(&prg), inc_(false) {}
initProgram(bool inc)2133 void LogicProgramAdapter::initProgram(bool inc) {
2134 inc_ = inc;
2135 }
beginStep()2136 void LogicProgramAdapter::beginStep() {
2137 if (inc_ || lp_->frozen()) { lp_->updateProgram(); }
2138 }
endStep()2139 void LogicProgramAdapter::endStep() {
2140
2141 }
rule(Potassco::Head_t ht,const Potassco::AtomSpan & head,const Potassco::LitSpan & body)2142 void LogicProgramAdapter::rule(Potassco::Head_t ht, const Potassco::AtomSpan& head, const Potassco::LitSpan& body) {
2143 lp_->addRule(ht, head, body);
2144 }
rule(Potassco::Head_t ht,const Potassco::AtomSpan & head,Potassco::Weight_t bound,const Potassco::WeightLitSpan & body)2145 void LogicProgramAdapter::rule(Potassco::Head_t ht, const Potassco::AtomSpan& head, Potassco::Weight_t bound, const Potassco::WeightLitSpan& body) {
2146 lp_->addRule(ht, head, bound, body);
2147 }
minimize(Potassco::Weight_t prio,const Potassco::WeightLitSpan & lits)2148 void LogicProgramAdapter::minimize(Potassco::Weight_t prio, const Potassco::WeightLitSpan& lits) {
2149 lp_->addMinimize(prio, lits);
2150 }
project(const Potassco::AtomSpan & atoms)2151 void LogicProgramAdapter::project(const Potassco::AtomSpan& atoms) {
2152 lp_->addProject(atoms);
2153 }
output(const Potassco::StringSpan & str,const Potassco::LitSpan & cond)2154 void LogicProgramAdapter::output(const Potassco::StringSpan& str, const Potassco::LitSpan& cond) {
2155 lp_->addOutput(ConstString(str), cond);
2156 }
external(Potassco::Atom_t a,Potassco::Value_t v)2157 void LogicProgramAdapter::external(Potassco::Atom_t a, Potassco::Value_t v) {
2158 lp_->addExternal(a, v);
2159 }
assume(const Potassco::LitSpan & lits)2160 void LogicProgramAdapter::assume(const Potassco::LitSpan& lits) {
2161 lp_->addAssumption(lits);
2162 }
heuristic(Potassco::Atom_t a,Potassco::Heuristic_t t,int bias,unsigned prio,const Potassco::LitSpan & cond)2163 void LogicProgramAdapter::heuristic(Potassco::Atom_t a, Potassco::Heuristic_t t, int bias, unsigned prio, const Potassco::LitSpan& cond) {
2164 lp_->addDomHeuristic(a, t, bias, prio, cond);
2165 }
acycEdge(int s,int t,const Potassco::LitSpan & cond)2166 void LogicProgramAdapter::acycEdge(int s, int t, const Potassco::LitSpan& cond) {
2167 lp_->addAcycEdge(static_cast<uint32>(s), static_cast<uint32>(t), cond);
2168 }
theoryTerm(Potassco::Id_t termId,int number)2169 void LogicProgramAdapter::theoryTerm(Potassco::Id_t termId, int number) {
2170 lp_->theoryData().addTerm(termId, number);
2171 }
theoryTerm(Potassco::Id_t termId,const Potassco::StringSpan & name)2172 void LogicProgramAdapter::theoryTerm(Potassco::Id_t termId, const Potassco::StringSpan& name) {
2173 lp_->theoryData().addTerm(termId, name);
2174 }
theoryTerm(Potassco::Id_t termId,int cId,const Potassco::IdSpan & args)2175 void LogicProgramAdapter::theoryTerm(Potassco::Id_t termId, int cId, const Potassco::IdSpan& args) {
2176 if (cId >= 0) { lp_->theoryData().addTerm(termId, static_cast<Potassco::Id_t>(cId), args); }
2177 else { lp_->theoryData().addTerm(termId, static_cast<Potassco::Tuple_t>(cId), args); }
2178 }
theoryElement(Potassco::Id_t elementId,const Potassco::IdSpan & terms,const Potassco::LitSpan & cond)2179 void LogicProgramAdapter::theoryElement(Potassco::Id_t elementId, const Potassco::IdSpan& terms, const Potassco::LitSpan& cond) {
2180 lp_->theoryData().addElement(elementId, terms, lp_->newCondition(cond));
2181 }
theoryAtom(Potassco::Id_t atomOrZero,Potassco::Id_t termId,const Potassco::IdSpan & elements)2182 void LogicProgramAdapter::theoryAtom(Potassco::Id_t atomOrZero, Potassco::Id_t termId, const Potassco::IdSpan& elements) {
2183 lp_->theoryData().addAtom(atomOrZero, termId, elements);
2184 }
theoryAtom(Potassco::Id_t atomOrZero,Potassco::Id_t termId,const Potassco::IdSpan & elements,Potassco::Id_t op,Potassco::Id_t rhs)2185 void LogicProgramAdapter::theoryAtom(Potassco::Id_t atomOrZero, Potassco::Id_t termId, const Potassco::IdSpan& elements, Potassco::Id_t op, Potassco::Id_t rhs) {
2186 lp_->theoryData().addAtom(atomOrZero, termId, elements, op, rhs);
2187 }
2188 } } // end namespace Asp
2189
2190