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