1 //
2 // Copyright (c) 2006-2017 Benjamin Kaufmann
3 //
4 // This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to
8 // deal in the Software without restriction, including without limitation the
9 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 // sell copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 // IN THE SOFTWARE.
23 //
24 #include <clasp/asp_preprocessor.h>
25 #include <clasp/logic_program.h>
26 #include <clasp/shared_context.h>
27 namespace Clasp { namespace Asp {
28 /////////////////////////////////////////////////////////////////////////////////////////
29 // simple preprocessing
30 //
31 // Simplifies the program by computing max consequences.
32 // Then assign variables to non-trivial supported bodies and atoms.
33 /////////////////////////////////////////////////////////////////////////////////////////
preprocessSimple()34 bool Preprocessor::preprocessSimple() {
35 if (!prg_->propagate(true)) { return false; }
36 uint32 startVar = prg_->ctx()->numVars() + 1;
37 // start with initially supported bodies
38 VarVec& supported = prg_->getSupportedBodies(true);
39 VarVec unitBodies;
40 for (VarVec::size_type i = 0; i != supported.size(); ++i) {
41 // set up body
42 PrgBody* b = prg_->getBody(supported[i]);
43 if (!b->simplify(*prg_, false)) { return false; }
44 if (b->var() < startVar) {
45 if (b->size() != 1) { b->assignVar(*prg_); }
46 else { unitBodies.push_back(supported[i]); }
47 }
48 // add all heads of b to the "upper"-closure and
49 // remove any false/removed atoms from head
50 if (!addHeadsToUpper(b) || !b->simplifyHeads(*prg_, true)) {
51 return false;
52 }
53 }
54 for (VarVec::const_iterator it = unitBodies.begin(), end = unitBodies.end(); it != end; ++it) {
55 prg_->getBody(*it)->assignVar(*prg_);
56 }
57 return prg_->propagate();
58 }
59
addHeadToUpper(PrgHead * head,PrgEdge support)60 bool Preprocessor::addHeadToUpper(PrgHead* head, PrgEdge support) {
61 assert(head->relevant() && !head->inUpper());
62 head->simplifySupports(*prg_, false);
63 head->assignVar(*prg_, support, eq());
64 head->clearSupports();
65 head->setInUpper(true);
66 if (head->isAtom()) {
67 return propagateAtomVar(static_cast<PrgAtom*>(head), support);
68 }
69 // add all unseen atoms of disjunction to upper
70 PrgDisj* d = static_cast<PrgDisj*>(head);
71 support = PrgEdge::newEdge(*d, PrgEdge::Choice);
72 bool ok = true;
73 for (PrgDisj::atom_iterator it = d->begin(), end = d->end(); it != end && ok; ++it) {
74 PrgAtom* at = prg_->getAtom(*it);
75 if (!at->relevant()) { continue; }
76 if (!at->inUpper()) { ok = addHeadToUpper(at, support); }
77 at->addSupport(support);
78 }
79 return ok;
80 }
81 /////////////////////////////////////////////////////////////////////////////////////////
82 // equivalence preprocessing
83 //
84 // Computes max consequences and minimizes the number of necessary variables
85 // by computing equivalence-classes.
86 /////////////////////////////////////////////////////////////////////////////////////////
preprocessEq(uint32 maxIters)87 bool Preprocessor::preprocessEq(uint32 maxIters) {
88 uint32 startVar = prg_->ctx()->numVars();
89 ValueRep res = value_true;
90 pass_ = 0;
91 maxPass_ = maxIters;
92 HeadRange atoms = HeadRange(prg_->atom_begin() + prg_->startAtom(), prg_->atom_end());
93 bodyInfo_.resize( prg_->numBodies() + 1 );
94 do {
95 if (++pass_ > 1) {
96 for (HeadIter it = prg_->atom_begin(), end = atoms.second; it != end; ) {
97 while (it != atoms.first){ (*it)->setInUpper(false); ++it; }
98 while (it != end) { (*it)->clearLiteral(false); (*it)->setInUpper(false); ++it; }
99 }
100 for (HeadIter it = prg_->disj_begin(), end = prg_->disj_end(); it != end; ++it) {
101 (*it)->clearLiteral(false);
102 (*it)->setInUpper(false);
103 }
104 prg_->ctx()->popVars(prg_->ctx()->numVars() - startVar);
105 litToNode_.clear();
106 }
107 VarVec& supported = prg_->getSupportedBodies(true);
108 if (!classifyProgram(supported)) { return false; }
109 res = simplifyClassifiedProgram(atoms, pass_ != maxPass_, supported);
110 } while (res == value_free && pass_ != maxPass_);
111 return res != value_false;
112 }
113
114 // Computes necessary equivalence-classes starting from the supported bodies
115 // of a program.
classifyProgram(const VarVec & supported)116 bool Preprocessor::classifyProgram(const VarVec& supported) {
117 Var bodyId; PrgBody* body;
118 VarVec::size_type index = 0;
119 follow_.clear();
120 if (!prg_->propagate(true)) { return false; }
121 for (VarVec::size_type i = 0;;) {
122 while ( (bodyId = nextBodyId(index)) != varMax ) {
123 body = addBodyVar(bodyId);
124 if (prg_->hasConflict()) { return false; }
125 if (!addHeadsToUpper(body)) { return false; }
126 }
127 follow_.clear();
128 index = 0;
129 // select next unclassified supported body
130 for (; i < supported.size(); ++i) {
131 bodyId = supported[i];
132 body = prg_->getBody(bodyId);
133 if (bodyInfo_[bodyId].bSeen == 0 && body->relevant()) {
134 follow_.push_back(bodyId);
135 break;
136 }
137 else if (!body->relevant() && body->hasVar()) {
138 body->clearLiteral(false);
139 }
140 }
141 if (follow_.empty()) break;
142 }
143 return !prg_->hasConflict();
144 }
145
simplifyClassifiedProgram(const HeadRange & atoms,bool more,VarVec & supported)146 ValueRep Preprocessor::simplifyClassifiedProgram(const HeadRange& atoms, bool more, VarVec& supported) {
147 ValueRep res = value_true, simp;
148 if (!prg_->propagate()) { return value_false; }
149 supported.clear();
150 // simplify supports
151 for (uint32 i = 0; i != prg_->numBodies(); ++i) {
152 PrgBody* b = prg_->getBody(i);
153 if (bodyInfo_[i].bSeen == 0 || !b->relevant()) {
154 // !bodyInfo_[i].bSeen: body is unsupported
155 // !b->relevant() : body is eq to other body or was derived to false
156 // In either case, body is no longer relevant and can be ignored.
157 b->clearLiteral(true);
158 b->markRemoved();
159 }
160 else if ( (simp = simplifyBody(b, more, supported)) != value_true ) {
161 if (simp == value_false) { return simp; }
162 res = value_free;
163 }
164 }
165 if (!prg_->propagate()) { return value_false; }
166 PrgEdge noSup = PrgEdge::noEdge();
167 for (LogicProgram::VarIter it = prg_->unfreeze_begin(), end = prg_->unfreeze_end(); it != end; ++it) {
168 PrgAtom* a = prg_->getAtom(*it);
169 ValueRep v = a->value();
170 if (!a->simplifySupports(*prg_, true)){ return value_false; }
171 else if (!a->inUpper() && v != value_false){
172 if (!prg_->assignValue(a, value_false, noSup)){ return value_false; }
173 if (more && a->hasDep(PrgAtom::dep_all)) { res = value_free; }
174 }
175 }
176 if (!prg_->propagate()) { return value_false; }
177 bool strong = more && res == value_true;
178 HeadRange heads[2] = { HeadRange(prg_->disj_begin(), prg_->disj_end()), atoms };
179 for (const HeadRange* range = heads, *endR = heads+2; range != endR; ++range) {
180 for (HeadIter it = range->first, end = range->second; it != end; ++it) {
181 PrgHead* head = *it;
182 if ((simp = simplifyHead(head, strong)) != value_true) {
183 if (simp == value_false){ return simp; }
184 else if (strong) { strong = false; res = value_free; }
185 }
186 }
187 }
188 if (!prg_->propagate()) { res = value_false; }
189 return res;
190 }
191
192 // associates a variable with the body if necessary
addBodyVar(Var bodyId)193 PrgBody* Preprocessor::addBodyVar(Var bodyId) {
194 // make sure we don't add an irrelevant body
195 PrgBody* body = prg_->getBody(bodyId);
196 assert((body->isSupported() && !body->eq()) || body->hasVar());
197 body->clearLiteral(false); // clear var in case we are iterating
198 bodyInfo_[bodyId].bSeen = 1; // mark as seen, so we don't classify the body again
199 bool known = bodyInfo_[bodyId].known == body->size();
200 uint32 eqId;
201 if (!body->simplifyBody(*prg_, known, &eqId) || !body->simplifyHeads(*prg_, false)) {
202 prg_->setConflict();
203 return body;
204 }
205 if (superfluous(body)) {
206 body->markRemoved();
207 return body;
208 }
209 if (eqId == bodyId) {
210 // The body is unique
211 body->assignVar(*prg_);
212 PrgAtom* aEq = body->size() == 1 ? prg_->getAtom(body->goal(0).var()) : 0;
213 if (!known) { body->markDirty(); }
214 else if (aEq && aEq->var() == body->var()){
215 // Body is equivalent to an atom or its negation
216 // Check if the atom is itself equivalent to a body.
217 // If so, the body is equivalent to the atom's body.
218 PrgBody* r = 0; // possible eq-body
219 uint32 rId = varMax;
220 if (body->goal(0).sign()) {
221 Var dualAtom = getRootAtom(body->literal());
222 aEq = dualAtom != varMax ? prg_->getAtom(dualAtom) : 0;
223 }
224 if (aEq && aEq->supports() && aEq->supps_begin()->isBody()) {
225 rId = aEq->supps_begin()->node();
226 r = prg_->getBody(rId);
227 if (r && r->var() == aEq->var()) {
228 mergeEqBodies(body, rId, false);
229 }
230 }
231 }
232 }
233 else {
234 // body is eq to eq body
235 mergeEqBodies(body, eqId, true);
236 }
237 return body;
238 }
239
240 // Adds all heads of body to the upper closure if not yet present and
241 // associates variables with the heads if necessary.
242 // The body b is the supported body that provides a support for the heads.
243 // RETURN: true if no conflict
244 // POST : the addition of atoms to the closure was propagated
addHeadsToUpper(PrgBody * body)245 bool Preprocessor::addHeadsToUpper(PrgBody* body) {
246 PrgHead* head;
247 PrgEdge support;
248 bool ok = !prg_->hasConflict();
249 int dirty= 0;
250 for (PrgBody::head_iterator it = body->heads_begin(), end = body->heads_end(); it != end && ok; ++it) {
251 head = prg_->getHead(*it);
252 support= PrgEdge::newEdge(*body, it->type());
253 if (head->relevant() && head->value() != value_false) {
254 if (body->value() == value_true && head->isAtom()) {
255 // Since b is true, it is always a valid support for head, head can never become unfounded.
256 // So ignore it during SCC check and unfounded set computation.
257 head->setIgnoreScc(true);
258 if (support.isNormal() && head->isAtom()) {
259 ok = propagateAtomValue(static_cast<PrgAtom*>(head), value_true, support);
260 }
261 }
262 if (!head->inUpper()) {
263 // first time we see this head - assign var...
264 ok = addHeadToUpper(head, support);
265 }
266 else if (head->supports() && head->supps_begin()->isNormal()) {
267 PrgEdge source = *head->supps_begin();
268 assert(source.isBody());
269 if (prg_->getBody(source.node())->var() == body->var()) {
270 // Check if we really need a new variable for head.
271 head->markDirty();
272 }
273 }
274 head->addSupport(support, PrgHead::no_simplify);
275 }
276 dirty += (head->eq() || head->value() == value_false);
277 }
278 if (dirty) {
279 // remove eq atoms from head
280 prg_->getBody(body->id())->markHeadsDirty();
281 }
282 return ok;
283 }
284
285 // Propagates that a was added to the "upper"-closure.
286 // If atom a has a truth-value or is eq to a', we'll remove
287 // it from all bodies. If there is an atom x, s.th. a.lit == ~x.lit, we mark all
288 // bodies containing both a and x for simplification in order to detect
289 // duplicates/contradictory body-literals.
290 // In case that a == a', we also mark all bodies containing a
291 // for head simplification in order to detect rules like: a' :- a,B. and a' :- B,not a.
propagateAtomVar(PrgAtom * a,PrgEdge source)292 bool Preprocessor::propagateAtomVar(PrgAtom* a, PrgEdge source) {
293 const Var aId = a->id();
294 PrgAtom* comp = 0;
295 ValueRep value = a->value();
296 bool fullEq = eq();
297 bool removeAtom = value == value_true || value == value_false;
298 bool removeNeg = removeAtom || value == value_weak_true;
299 Literal aLit = a->literal();
300 if (fullEq) {
301 if (getRootAtom(aLit) == varMax) {
302 setRootAtom(aLit, aId);
303 }
304 else if (prg_->mergeEqAtoms(a, getRootAtom(aLit))) {
305 assert(source.isBody());
306 removeAtom = true;
307 removeNeg = true;
308 value = a->value();
309 PrgBody* B = prg_->getBody(source.node());
310 a->setEqGoal(posLit(a->id()));
311 // set positive eq goal - replace if a == {not a'}, replace a with not a' in bodies
312 if (getRootAtom(~aLit) != varMax && B->literal() == aLit && B->size() == 1 && B->goal(0).sign()) {
313 a->setEqGoal(negLit(getRootAtom(~aLit)));
314 }
315 a->clearLiteral(true); // equivalent atoms don't need vars
316 }
317 else { return false; }
318 }
319 if (getRootAtom(~aLit) != varMax) {
320 PrgAtom* negA = prg_->getAtom(getRootAtom(~aLit));
321 assert(aLit == ~negA->literal());
322 // propagate any truth-value to complementary eq-class
323 ValueRep cv = value_free;
324 uint32 mark = 0;
325 if (value != value_free && (cv = (value_false | (value^value_true))) != negA->value()) {
326 mark = 1;
327 if (!propagateAtomValue(negA, cv, PrgEdge::noEdge())) {
328 return false;
329 }
330 }
331 if ( !removeAtom ) {
332 for (PrgAtom::dep_iterator it = (comp=negA)->deps_begin(); it != comp->deps_end(); ++it) {
333 bodyInfo_[it->var()].mBody = 1;
334 if (mark) { prg_->getBody(it->var())->markDirty(); }
335 }
336 }
337 }
338 for (PrgAtom::dep_iterator it = a->deps_begin(), end = a->deps_end(); it != end; ++it) {
339 Var bodyId = it->var();
340 PrgBody* bn = prg_->getBody(bodyId);
341 if (bn->relevant()) {
342 bool wasSup = bn->isSupported();
343 bool isSup = wasSup || (value != value_false && !it->sign() && bn->propagateSupported(aId));
344 bool seen = false;
345 bool dirty = removeAtom || (removeNeg && it->sign());
346 if (fullEq) {
347 seen = bodyInfo_[bodyId].bSeen != 0;
348 dirty |= bodyInfo_[bodyId].mBody == 1;
349 if (++bodyInfo_[bodyId].known == bn->size() && !seen && isSup) {
350 follow_.push_back( bodyId );
351 seen = true;
352 }
353 }
354 if (!seen && isSup && !wasSup) {
355 prg_->getSupportedBodies(false).push_back(bodyId);
356 }
357 if (dirty) {
358 bn->markDirty();
359 if (a->eq()) {
360 bn->markHeadsDirty();
361 }
362 }
363 }
364 }
365 if (removeAtom) { a->clearDeps(PrgAtom::dep_all); }
366 else if (removeNeg) { a->clearDeps(PrgAtom::dep_neg); }
367 if (comp) {
368 for (PrgAtom::dep_iterator it = comp->deps_begin(), end = comp->deps_end(); it != end; ++it) {
369 bodyInfo_[it->var()].mBody = 0;
370 }
371 }
372 return true;
373 }
374
375 // Propagates the assignment of val to a.
propagateAtomValue(PrgAtom * atom,ValueRep val,PrgEdge sup)376 bool Preprocessor::propagateAtomValue(PrgAtom* atom, ValueRep val, PrgEdge sup) {
377 // No backpropagation possible because supports are not yet fully established.
378 return prg_->assignValue(atom, val, sup) && prg_->propagate(false);
379 }
380
mergeEqBodies(PrgBody * body,Var rootId,bool equalLits)381 bool Preprocessor::mergeEqBodies(PrgBody* body, Var rootId, bool equalLits) {
382 PrgBody* root = prg_->mergeEqBodies(body, rootId, equalLits, false);
383 if (root && root != body && bodyInfo_[root->id()].bSeen == 0) {
384 // If root is not yet classified, we can ignore body.
385 // The heads of body are added to the "upper"-closure
386 // once root is eventually classified.
387 body->clearHeads();
388 body->markRemoved();
389 }
390 return root != 0;
391 }
392
hasRootLiteral(PrgBody * body) const393 bool Preprocessor::hasRootLiteral(PrgBody* body) const {
394 return body->size() >= 1
395 && getRootAtom(body->literal()) == varMax
396 && getRootAtom(~body->literal())== varMax;
397 }
398
399 // Pre: body is simplified!
superfluous(PrgBody * body) const400 bool Preprocessor::superfluous(PrgBody* body) const {
401 if (!body->relevant()) { return true; }
402 if (!body->inRule()) {
403 if (body->value() == value_free) { return true; }
404 if (body->bound() <= 0) { return true; }
405 if (body->size() == 1) {
406 // unit constraint
407 ValueRep exp = body->value() ^ (int)body->goal(0).sign();
408 ValueRep got = prg_->getAtom(body->goal(0).var())->value();
409 assert(got != value_free || !prg_->options().backprop);
410 return got != value_free && (got&value_true) == (exp&value_true);
411 }
412 }
413 return false;
414 }
415
416 // Simplify the classified body with the given id.
417 // Return:
418 // value_false : conflict
419 // value_true : ok
420 // value_weak_true: ok but program should be reclassified
simplifyBody(PrgBody * b,bool reclass,VarVec & supported)421 ValueRep Preprocessor::simplifyBody(PrgBody* b, bool reclass, VarVec& supported) {
422 assert(b->relevant() && bodyInfo_[b->id()].bSeen == 1);
423 bodyInfo_[b->id()].bSeen = 0;
424 bodyInfo_[b->id()].known = 0;
425 bool hadHeads = b->hasHeads();
426 bool hasRoot = hasRootLiteral(b);
427 uint32 eqId = b->id();
428 if (!b->simplify(*prg_, true, &eqId)) {
429 return value_false;
430 }
431 ValueRep ret = value_true;
432 if (reclass) {
433 if (hadHeads && b->value() == value_false) {
434 assert(b->hasHeads() == false);
435 // New false body. If it was derived to false, we can ignore the body.
436 // Otherwise, we have a new integrity constraint.
437 if (!b->relevant()) {
438 b->clearLiteral(true);
439 }
440 }
441 else if (b->var() != 0 && superfluous(b)) {
442 // Body is no longer needed. All heads are either superfluous or equivalent
443 // to other atoms.
444 // Reclassify only if var is not used
445 if (getRootAtom(b->literal()) == varMax) { ret = value_weak_true; }
446 b->clearLiteral(true);
447 b->markRemoved();
448 }
449 else if (b->value() == value_true && b->var() != 0) {
450 // New fact body
451 for (PrgBody::head_iterator it = b->heads_begin(), end = b->heads_end(); it != end; ++it) {
452 if (it->isNormal() && prg_->getHead(*it)->var() != 0) {
453 ret = value_weak_true;
454 break;
455 }
456 }
457 b->markDirty();
458 }
459 }
460 if (b->relevant() && eqId != b->id() && (reclass || prg_->getBody(eqId)->var() == b->var())) {
461 // Body is now eq to some other body - reclassify if body var is not needed
462 Var bVar = b->var();
463 prg_->mergeEqBodies(b, eqId, true, true);
464 if (hasRoot && bVar != b->var()) {
465 ret = value_weak_true;
466 }
467 }
468 if (b->relevant() && b->resetSupported()) {
469 supported.push_back(b->id());
470 }
471 return ret;
472 }
473
474 // Simplify the classified head h.
475 // Update list of bodies defining this head and check
476 // if atom or disjunction has a distinct var although it is eq to some body.
477 // Return:
478 // value_false : conflict
479 // value_true : ok
480 // value_weak_true: ok but atom should be reclassified
simplifyHead(PrgHead * h,bool more)481 ValueRep Preprocessor::simplifyHead(PrgHead* h, bool more) {
482 if (!h->hasVar() || !h->relevant()) {
483 // unsupported or eq
484 h->clearLiteral(false);
485 h->markRemoved();
486 h->clearSupports();
487 h->setInUpper(false);
488 return value_true;
489 }
490 assert(h->inUpper());
491 ValueRep v = h->value();
492 ValueRep ret = value_true;
493 PrgEdge support = h->supports() ? *h->supps_begin() : PrgEdge::noEdge();
494 uint32 numSuppLits= 0;
495 if (!h->simplifySupports(*prg_, true, &numSuppLits)) {
496 return value_false;
497 }
498 if (v != h->value() && (h->value() == value_false || (h->value() == value_true && h->var() != 0))) {
499 ret = value_weak_true;
500 }
501 if (more) {
502 if (numSuppLits == 0 && h->hasVar()) {
503 // unsupported head does not need a variable
504 ret = value_weak_true;
505 }
506 else if (h->supports() > 0 && h->supps_begin()->rep != support.rep) {
507 // support for head has changed
508 ret = value_weak_true;
509 }
510 else if ((support.isNormal() && h->supports() == 1) || (h->supports() > 1 && numSuppLits == 1 && h->isAtom())) {
511 assert(support.isBody());
512 PrgBody* supBody = prg_->getBody(support.node());
513 if (supBody->literal() != h->literal()) {
514 if (h->supports() > 1) {
515 // atom is equivalent to one of its bodies
516 EdgeVec temp(h->supps_begin(), h->supps_end());
517 h->clearSupports();
518 support = temp[0];
519 for (EdgeIterator it = temp.begin(), end = temp.end(); it != end; ++it) {
520 assert(!it->isDisj());
521 PrgBody* B = prg_->getBody(it->node());
522 if (it->isNormal() && B->size() == 1 && B->goal(0).sign()) {
523 support = *it;
524 }
525 B->removeHead(h, it->type());
526 }
527 supBody = prg_->getBody(support.node());
528 supBody->addHead(h, support.type());
529 if (!supBody->simplifyHeads(*prg_, true)) {
530 return value_false;
531 }
532 }
533 ret = value_weak_true;
534 if (h->value() == value_weak_true || h->value() == value_true) {
535 supBody->assignValue(h->value());
536 supBody->propagateValue(*prg_, true);
537 }
538 }
539 }
540 }
541 return ret;
542 }
543
544 } }
545