logic_program.cpp
Go to the documentation of this file.
00001 //
00002 // Copyright (c) 2013 Benjamin Kaufmann
00003 //
00004 // This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/
00005 //
00006 // Clasp is free software; you can redistribute it and/or modify
00007 // it under the terms of the GNU General Public License as published by
00008 // the Free Software Foundation; either version 2 of the License, or
00009 // (at your option) any later version.
00010 //
00011 // Clasp is distributed in the hope that it will be useful,
00012 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 // GNU General Public License for more details.
00015 //
00016 // You should have received a copy of the GNU General Public License
00017 // along with Clasp; if not, write to the Free Software
00018 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00019 //
00020 #if defined(_MSC_VER)
00021 #pragma warning (disable : 4146) // unary minus operator applied to unsigned type, result still unsigned
00022 #pragma warning (disable : 4996) // 'std::_Fill_n' was declared deprecated
00023 #endif
00024 
00025 #include <clasp/logic_program.h>
00026 #include <clasp/shared_context.h>
00027 #include <clasp/solver.h>
00028 #include <clasp/minimize_constraint.h>
00029 #include <clasp/util/misc_types.h>
00030 #include <clasp/asp_preprocessor.h>
00031 #include <clasp/clause.h>
00032 #include <clasp/dependency_graph.h>
00033 #include <clasp/parser.h>
00034 #include <stdexcept>
00035 #include <sstream>
00036 #include <climits>
00037 namespace Clasp { namespace Asp {
00039 // class LpStats
00041 void LpStats::reset() {
00042         std::memset(this, 0, sizeof(LpStats));
00043 }
00044 uint32 LpStats::rules() const {
00045         uint32 sum = 0;
00046         for (uint32 i = 0; i != NUM_RULE_TYPES; ++i) { sum += rules_[i].second; }
00047         return sum;
00048 }
00049 void LpStats::accu(const LpStats& o) {
00050         bodies   += o.bodies;
00051         atoms    += o.atoms;
00052         auxAtoms += o.auxAtoms;
00053         ufsNodes += o.ufsNodes;
00054         if (sccs == PrgNode::noScc || o.sccs == PrgNode::noScc) {
00055                 sccs    = o.sccs;
00056                 nonHcfs = o.nonHcfs;
00057         }
00058         else {
00059                 sccs   += o.sccs;
00060                 nonHcfs+= o.nonHcfs;
00061         }
00062         for (int i = 0; i != sizeof(eqs_)/sizeof(eqs_[0]); ++i) {
00063                 eqs_[i] += o.eqs_[i];
00064         }
00065         for (int i = 0; i != sizeof(rules_)/sizeof(rules_[0]); ++i) {
00066                 rules_[i].first  += o.rules_[i].first;
00067                 rules_[i].second += o.rules_[i].second;
00068         }
00069 }
00070 double LpStats::operator[](const char* key) const {
00071 #define RETURN_IF(x) if (std::strcmp(key, #x) == 0) return double(x)
00072 #define MAP_IF(x, A) if (std::strcmp(key, x) == 0)  return double( (A) )
00073         RETURN_IF(bodies);
00074         RETURN_IF(atoms);
00075         RETURN_IF(auxAtoms);
00076         RETURN_IF(sccs);
00077         RETURN_IF(nonHcfs);
00078         RETURN_IF(gammas);
00079         RETURN_IF(ufsNodes);
00080         MAP_IF("rules"             , rules());
00081         MAP_IF("basicRules"        , rules(BASICRULE).first);
00082         MAP_IF("choiceRules"       , rules(CHOICERULE).first);
00083         MAP_IF("constraintRules"   , rules(CONSTRAINTRULE).first);
00084         MAP_IF("weightRules"       , rules(WEIGHTRULE).first);
00085         MAP_IF("disjunctiveRules"  , rules(DISJUNCTIVERULE).first);
00086         MAP_IF("optimizeRules"     , rules(OPTIMIZERULE).first);
00087         MAP_IF("basicRulesTr"      , rules(BASICRULE).second);
00088         MAP_IF("choiceRulesTr"     , rules(CHOICERULE).second);
00089         MAP_IF("constraintRulesTr" , rules(CONSTRAINTRULE).second);
00090         MAP_IF("weightRulesTr"     , rules(WEIGHTRULE).second);
00091         MAP_IF("disjunctiveRulesTr", rules(DISJUNCTIVERULE).second);    
00092         MAP_IF("optimizeRulesTr"   , rules(OPTIMIZERULE).second);
00093         MAP_IF("eqs"               , eqs());
00094         MAP_IF("atomEqs"           , eqs(Var_t::atom_var));
00095         MAP_IF("bodyEqs"           , eqs(Var_t::body_var));
00096         MAP_IF("otherEqs"          , eqs(Var_t::atom_body_var));
00097 #undef RETURN_IF
00098         return -1.0;
00099 }
00100 const char* LpStats::keys(const char* path) {
00101         if (!path || !*path) {
00102                 return "bodies\0atoms\0auxAtoms\0sccs\0nonHcfs\0gammas\0ufsNodes\0rules\0"
00103                        "basicRules\0choiceRules\0constraintRules\0weightRules\0disjunctiveRules\0optimizeRules\0"
00104                        "basicRulesTr\0choiceRulesTr\0constraintRulesTr\0weightRulesTr\0disjunctiveRulesTr\0optimizeRulesTr\0"
00105                        "eqs\0atomEqs\0bodyEqs\0otherEqs\0";
00106         }
00107         return 0;
00108 }
00110 // class LogicProgram
00112 namespace {
00113 struct LessBodySize {
00114         LessBodySize(const BodyList& bl) : bodies_(&bl) {}
00115         bool operator()(Var b1, Var b2 ) const {
00116                 return (*bodies_)[b1]->size() < (*bodies_)[b2]->size()
00117                         || ((*bodies_)[b1]->size() == (*bodies_)[b2]->size() && (*bodies_)[b1]->type() < (*bodies_)[b2]->type());
00118         }
00119 private:
00120         const BodyList* bodies_;
00121 };
00122 
00123 // Adds nogoods representing this node to the solver.
00124 template <class NT>
00125 bool toConstraint(NT* node, const LogicProgram& prg, ClauseCreator& c) {
00126         if (node->value() != value_free && !prg.ctx()->addUnary(node->trueLit())) {
00127         return false;
00128         }
00129         return !node->relevant() || node->addConstraints(prg, c);
00130 }
00131 }
00132 LogicProgram::LogicProgram() : nonHcfCfg_(0), minimize_(0), incData_(0) { accu = 0; }
00133 LogicProgram::~LogicProgram() { dispose(true); }
00134 LogicProgram::Incremental::Incremental() : startAtom(1), startAux(1), startScc(0) {}
00135 void LogicProgram::dispose(bool force) {
00136         // remove rules
00137         std::for_each( bodies_.begin(), bodies_.end(), DestroyObject() );
00138         std::for_each( disjunctions_.begin(), disjunctions_.end(), DestroyObject() );
00139         AtomList().swap(sccAtoms_);
00140         BodyList().swap(bodies_);
00141         DisjList().swap(disjunctions_);
00142         bodyIndex_.clear();
00143         disjIndex_.clear();
00144         MinimizeRule* r = minimize_;
00145         while (r) {
00146                 MinimizeRule* t = r;
00147                 r = r->next_;
00148                 delete t;
00149         }
00150         minimize_ = 0;
00151         for (RuleList::size_type i = 0; i != extended_.size(); ++i) {
00152                 delete extended_[i];
00153         }
00154         extended_.clear();
00155         VarVec().swap(initialSupp_);
00156         rule_.clear();
00157         if (force) {
00158                 std::for_each( atoms_.begin(), atoms_.end(), DeleteObject() );
00159                 AtomList().swap(atoms_);
00160                 delete incData_;
00161                 VarVec().swap(propQ_);
00162                 ruleState_.clearAll();
00163         }
00164         else if (incData_) {
00165                 // clean up atoms
00166                 // reset prop queue
00167                 propQ_.assign(1, getFalseId());
00168                 uint32 startAux = incData_->startAux;
00169                 assert(startAux <= atoms_.size());
00170                 // remove rule associations
00171                 for (VarVec::size_type i = 1; i != startAux; ++i) {
00172                         PrgAtom* a = atoms_[i];
00173                         // remove any dangling references
00174                         a->clearSupports();
00175                         a->clearDeps(PrgAtom::dep_all);
00176                         a->setIgnoreScc(false);
00177                         if (a->eq() && getEqAtom(i) >= startAux) {
00178                                 // atom i is equivalent to some aux atom 
00179                                 // make i the new root
00180                                 PrgAtom* eq = atoms_[getEqAtom(i)];
00181                                 assert(!eq->eq());
00182                                 eq->setEq(i);
00183                                 a->resetId(i, false);
00184                                 a->setLiteral(eq->literal());
00185                         }
00186                         if (a->relevant()) {
00187                                 ValueRep v = a->value();
00188                                 a->setValue(value_free);
00189                                 if (!a->frozen()) {
00190                                         a->resetId(i, false);
00191                                         if (ctx()->master()->value(a->var()) != value_free) {
00192                                                 v = ctx()->master()->isTrue(a->literal()) ? value_true : value_false;
00193                                         } 
00194                                         assert(!a->eq() || a->id() < startAux);
00195                                 }
00196                                 if (v != value_free) { assignValue(a, v); }
00197                         }
00198                         else if (!a->eq() && a->value() == value_false) {
00199                                 a->setEq(getFalseId());
00200                         }
00201                 }
00202                 // delete any introduced aux atoms
00203                 // this is safe because aux atoms are never part of the input program
00204                 // it is necessary in order to free their ids, i.e. the id of an aux atom
00205                 // from step I might be needed for a program atom in step I+1
00206                 for (VarVec::size_type i = startAux; i != atoms_.size(); ++i) {
00207                         delete atoms_[i];
00208                 }
00209                 atoms_.erase(atoms_.begin()+startAux, atoms_.end());
00210         }
00211         activeHead_.clear();
00212         activeBody_.reset();
00213         stats.reset();
00214 }
00215 bool LogicProgram::doStartProgram() {
00216         dispose(true);
00217         // atom 0 is always false
00218         atoms_.push_back( new PrgAtom(0, false) );
00219         assignValue(getAtom(0), value_false);
00220         getFalseAtom()->setLiteral(negLit(0));
00221         nonHcfCfg_= 0;
00222         incData_  = 0;
00223         ctx()->symbolTable().clear();
00224         ctx()->symbolTable().startInit();
00225         return true;
00226 }
00227 void LogicProgram::setOptions(const AspOptions& opts) {
00228         opts_ = opts;
00229         if (opts_.suppMod) {
00230                 if (opts_.iters != 5 && ctx()) { ctx()->report(warning(Event::subsystem_prepare, "'supp-models' implies 'eq=0'")); }
00231                 opts_.noEq();
00232                 opts_.noScc();
00233         }
00234 }
00235 bool LogicProgram::doParse(StreamSource& prg) { return DefaultLparseParser(*this).parse(prg); }
00236 bool LogicProgram::doUpdateProgram() {
00237         CLASP_ASSERT_CONTRACT(frozen() || !incData_);
00238         if (!incData_)  { incData_ = new Incremental(); }
00239         if (!frozen())  { return true; }
00240         // delete bodies/disjunctions...
00241         dispose(false);
00242         setFrozen(false);
00243         incData_->startAtom = (uint32)atoms_.size();
00244         incData_->startAux  = (uint32)atoms_.size();
00245         incData_->update.clear();
00246         incData_->frozen.swap(incData_->update);
00247         ctx()->symbolTable().startInit();
00248         // add supported atoms from previous steps 
00249         // {ai | ai in P}.
00250         PrgBody* support = incData_->startAux > 1 ? getBodyFor(activeBody_) : 0;
00251         for (VarVec::size_type i = 1; i != incData_->startAux; ++i) {
00252                 PrgAtom* a = atoms_[i];
00253                 if (a->relevant() && !a->frozen() && a->value() != value_false) {
00254                         a->setIgnoreScc(true);
00255                         support->addHead(a, PrgEdge::CHOICE_EDGE);
00256                 }
00257         }
00258         return true;
00259 }
00260 bool LogicProgram::doEndProgram() {
00261         if (!frozen() && ctx()->ok()) {
00262                 prepareProgram(!opts_.noSCC);
00263                 addConstraints();
00264                 if (accu) { accu->accu(stats); }
00265         }
00266         return ctx()->ok();
00267 }
00268 
00269 bool LogicProgram::clone(SharedContext& oCtx, bool shareSymbols) {
00270         assert(frozen());
00271         if (&oCtx == ctx()) {
00272                 return true;
00273         }
00274         oCtx.cloneVars(*ctx(), shareSymbols ? SharedContext::init_share_symbols : SharedContext::init_copy_symbols);
00275         SharedContext* t = ctx();
00276         setCtx(&oCtx);
00277         bool r = addConstraints();
00278         setCtx(t);
00279         return r;
00280 }
00281 
00282 void LogicProgram::addMinimize() {
00283         CLASP_ASSERT_CONTRACT(frozen());
00284         if (hasMinimize()) {
00285                 if (opts_.iters != 0) { simplifyMinimize(); }
00286                 WeightLitVec lits;
00287                 for (MinimizeRule* r = minimize_; r; r = r->next_) {
00288                         for (WeightLitVec::iterator it = r->lits_.begin(); it != r->lits_.end(); ++it) {
00289                                 PrgAtom* h = resize(it->first.var()); // checks for eq
00290                                 lits.push_back(WeightLiteral(it->first.sign() ? ~h->literal() : h->literal(), it->second));
00291                         }
00292                         addMinRule(lits);
00293                         lits.clear();
00294                 }
00295         }
00296 }
00297 
00298 void LogicProgram::write(std::ostream& os) {
00299         const char* const delimiter = "0";
00300         // first write all minimize rules - revert order!
00301         PodVector<MinimizeRule*>::type mr;
00302         for (MinimizeRule* r = minimize_; r; r = r->next_) {
00303                 mr.push_back(r);
00304         }
00305         std::stringstream body;
00306         for (PodVector<MinimizeRule*>::type::reverse_iterator rit = mr.rbegin(); rit != mr.rend(); ++rit) {
00307                 MinimizeRule* r = *rit;
00308                 transform(*r, activeBody_);
00309                 writeBody(activeBody_, body);
00310                 os << OPTIMIZERULE << " " << 0 << " " << body.str() << "\n";
00311                 body.clear();
00312                 body.str("");
00313         }
00314         std::stringstream choice;
00315         uint32 numChoice = 0;
00316         uint32 falseAtom = 0;
00317         bool  oldFreeze  = frozen();
00318         setFrozen(false);
00319         // write all bodies together with their heads
00320         for (BodyList::iterator it = bodies_.begin(); it != bodies_.end(); ++it) {
00321                 PrgBody* b = *it;
00322                 if (b->relevant() && (b->hasVar() || b->value() == value_false) && transform(*b, activeBody_)) {
00323                         writeBody(activeBody_, body);
00324                         if (b->hasHeads() && b->value() != value_false) {
00325                                 for (PrgBody::head_iterator it = b->heads_begin(); it != b->heads_end(); ++it) {
00326                                         PrgHead* head = getHead(*it);
00327                                         if (head->hasVar()) {
00328                                                 if (it->isAtom()) {
00329                                                         if (it->isNormal()) {
00330                                                                 os << activeBody_.ruleType() << " " << it->node() << " " << body.str() << "\n";
00331                                                         }
00332                                                         else if (it->isChoice()) {
00333                                                                 choice << it->node() << " ";
00334                                                                 ++numChoice;
00335                                                         }
00336                                                 }
00337                                                 else if (it->isDisj()) {
00338                                                         PrgDisj* d = static_cast<PrgDisj*>(head);
00339                                                         os << DISJUNCTIVERULE << " " << d->size() << " ";
00340                                                         for (PrgDisj::atom_iterator a = d->begin(), aEnd = d->end(); a != aEnd; ++a) {
00341                                                                 os << a->node() << " ";
00342                                                         }
00343                                                         os << body.str() << "\n";
00344                                                 }
00345                                         }
00346                                 }
00347                                 if (numChoice) {
00348                                         os << CHOICERULE << " " << numChoice << " " << choice.str() << body.str() << "\n";
00349                                 }
00350                         }
00351                         else if (b->value() == value_false) {
00352                                 // write integrity constraint
00353                                 if (falseAtom == 0 && (falseAtom = findLpFalseAtom()) == 0) {
00354                                         setCompute(falseAtom = newAtom(), false);
00355                                 }
00356                                 os << activeBody_.ruleType() << " " << falseAtom << " " << body.str() << "\n";
00357                         }
00358                         body.clear();
00359                         body.str("");
00360                         choice.clear(); choice.str(""); numChoice = 0;
00361                 }
00362         }
00363         // write eq-atoms, symbol-table and compute statement
00364         std::stringstream bp, bm, symTab;
00365         Literal comp;
00366         SymbolTable::const_iterator sym = ctx()->symbolTable().begin();
00367         for (AtomList::size_type i = 1; i < atoms_.size(); ++i) {
00368                 // write the equivalent atoms
00369                 if (atoms_[i]->eq()) {
00370                         os << "1 " << i << " 1 0 " << getEqAtom(Var(i)) << " \n";
00371                 }
00372                 if ( (i == falseAtom || atoms_[i]->inUpper()) && atoms_[i]->value() != value_free ) {
00373                         std::stringstream& str = atoms_[i]->value() == value_false ? bm : bp;
00374                         str << i << "\n";
00375                 }
00376                 if (sym != ctx()->symbolTable().end() && Var(i) == sym->first) {
00377                         if (sym->second.lit != negLit(sentVar) && !sym->second.name.empty()) {
00378                                 symTab << i << " " << sym->second.name.c_str() << "\n";
00379                         }
00380                         ++sym;
00381                 }
00382         }
00383         os << delimiter << "\n";
00384         os << symTab.str();
00385         os << delimiter << "\n";
00386         os << "B+\n" << bp.str() << "0\n"
00387                  << "B-\n" << bm.str() << "0\n1\n";
00388         setFrozen(oldFreeze);
00389 }
00390 
00392 // Program mutating functions
00394 #define check_not_frozen() CLASP_ASSERT_CONTRACT_MSG(!frozen(), "Can't update frozen program!")
00395 #define check_modular(x, atomId) (void)( (!!(x)) || (throw RedefinitionError((atomId), this->getAtomName((atomId))), 0))
00396 RedefinitionError::RedefinitionError(unsigned atomId, const char* name) : std::logic_error(clasp_format_error("Program not modular: Redefinition of atom <%u,'%s'>", atomId, name)) {
00397 }
00398 
00399 Var LogicProgram::newAtom() {
00400         check_not_frozen();
00401         Var id = static_cast<Var>(atoms_.size());
00402         atoms_.push_back( new PrgAtom(id) );
00403         return id;
00404 }
00405 
00406 LogicProgram& LogicProgram::setAtomName(Var atomId, const char* name) {
00407         check_not_frozen();
00408         check_modular(atomId >= startAtom(), atomId);
00409         resize(atomId);
00410         ctx()->symbolTable().addUnique(atomId, name);
00411         return *this;
00412 }
00413 
00414 LogicProgram& LogicProgram::setCompute(Var atomId, bool pos) {
00415         resize(atomId);
00416         ValueRep v = pos ? value_weak_true : value_false;
00417         PrgAtom* a = atoms_[atomId];
00418         assert(!a->hasVar() || a->state() != PrgAtom::state_normal);
00419         assignValue(a, v);
00420         return *this;
00421 }
00422 
00423 LogicProgram& LogicProgram::freeze(Var atomId, ValueRep value) {
00424         check_not_frozen();
00425         CLASP_ASSERT_CONTRACT_MSG(incData_, "LogicProgram::updateProgram() not called!");
00426         assert(incData_->frozen.empty());
00427         PrgAtom* a = resize(atomId);
00428         if (!a->inFlux() && (a->frozen() || (atomId >= startAtom() && !a->supports()))) {
00429                 CLASP_ASSERT_CONTRACT(value == value_false || value == value_true);
00430                 if (!a->frozen()) { incData_->update.push_back(atomId); }
00431                 a->setState(value == value_false ? PrgAtom::state_freeze : PrgAtom::state_freeze_true);
00432         }
00433         // else: atom is defined or from a previous step - ignore!
00434         return *this;
00435 }
00436 
00437 LogicProgram& LogicProgram::unfreeze(Var atomId) {
00438         check_not_frozen();
00439         CLASP_ASSERT_CONTRACT_MSG(incData_, "LogicProgram::updateProgram() not called!");
00440         PrgAtom* a = resize(atomId);
00441         if (!a->inFlux() && (atomId >= startAtom() || a->frozen())) {
00442                 if (!a->frozen()) { incData_->update.push_back(atomId); }
00443                 a->setState(PrgAtom::state_in_flux);
00444         }
00445         // else: atom is from a previous step - ignore!
00446         return *this;
00447 }
00448 
00449 LogicProgram& LogicProgram::addRule(const Rule& r) {
00450         check_not_frozen();
00451         // simplify rule
00452         RuleType t = simplifyRule(r, activeHead_, activeBody_);
00453         if (t != ENDRULE) { // rule is relevant
00454                 upRules(t, 1);
00455                 if (handleNatively(t, activeBody_)) { // and can be handled natively
00456                         addRuleImpl(t, activeHead_, activeBody_);
00457                 }
00458                 else {
00459                         bool  aux  = transformNoAux(t, activeBody_) == false;
00460                         Rule* temp = new Rule();
00461                         temp->setType(t);
00462                         temp->setBound(activeBody_.bound());
00463                         temp->heads.swap(activeHead_);
00464                         temp->body.swap(activeBody_.lits);
00465                         if (aux) {
00466                                 // Since rule transformation needs aux atoms, we must
00467                                 // defer the transformation until all rules were added
00468                                 // because only then we can safely assign new unique consecutive atom ids.
00469                                 extended_.push_back(temp);
00470                         }
00471                         else {
00472                                 RuleTransform rt;
00473                                 incTr(t, rt.transformNoAux(*this, *temp));
00474                                 delete temp;
00475                         }
00476                 }
00477         }
00478         activeBody_.reset();
00479         return *this;
00480 }
00481 #undef check_not_frozen
00482 
00483 // Query functions
00485 Literal LogicProgram::getLiteral(Var atomId) const {
00486         CLASP_ASSERT_CONTRACT_MSG(atomId < atoms_.size(), "Atom out of bounds!");
00487         return getAtom(getEqAtom(atomId))->literal();
00488 }
00489 void LogicProgram::doGetAssumptions(LitVec& out) const {
00490         if (incData_) {
00491                 for (VarVec::const_iterator it = incData_->frozen.begin(), end = incData_->frozen.end(); it != end; ++it) {
00492                         out.push_back( getAtom(getEqAtom(*it))->assumption() );
00493                 }
00494         }
00495 }
00497 // Program definition - private
00499 void LogicProgram::addRuleImpl(RuleType r, const VarVec& heads, BodyInfo& body) {
00500         if (r != OPTIMIZERULE) {
00501                 assert(!heads.empty() && (r != DISJUNCTIVERULE || heads.size() > 1));
00502                 PrgBody* b = getBodyFor(body);
00503                 // only a non-false body can define atoms
00504                 if (b->value() != value_false) {
00505                         EdgeType t      = r != CHOICERULE ? PrgEdge::NORMAL_EDGE : PrgEdge::CHOICE_EDGE;
00506                         uint32 headHash = 0;
00507                         bool ignoreScc  = opts_.noSCC || b->size() == 0;
00508                         for (VarVec::const_iterator it = heads.begin(), end = heads.end(); it != end; ++it) {
00509                                 PrgAtom* a = resize(*it);
00510                                 if (a->frozen() && a->supports() == 0) { unfreeze(*it); }
00511                                 check_modular(*it >= startAtom() || a->inFlux() || a->value() == value_false, *it);
00512                                 if (r != DISJUNCTIVERULE) {
00513                                         // Note: b->heads may now contain duplicates. They are removed in PrgBody::simplifyHeads.
00514                                         b->addHead(a, t);
00515                                         if (ignoreScc) { a->setIgnoreScc(ignoreScc); }
00516                                 }
00517                                 else {
00518                                         headHash += hashId(*it);
00519                                         ruleState_.addToHead(*it);
00520                                 }
00521                         }
00522                         if (r == DISJUNCTIVERULE) {
00523                                 assert(headHash != 0);
00524                                 PrgDisj* d = getDisjFor(heads, headHash);
00525                                 b->addHead(d, t);
00526                         }
00527                 }       
00528         }
00529         else {
00530                 CLASP_ASSERT_CONTRACT(heads.empty());
00531                 LogicProgram::MinimizeRule* mr = new LogicProgram::MinimizeRule;
00532                 mr->lits_ = body.lits;
00533                 mr->next_ = minimize_;
00534                 minimize_ = mr;
00535         }
00536 }
00537 
00538 bool LogicProgram::assignValue(PrgAtom* a, ValueRep v) {
00539         if (a->eq())            { a = getAtom(getEqAtom(a->id())); }
00540         ValueRep old = a->value();
00541         if (old == value_weak_true && v != value_weak_true) old = value_free;
00542         if (!a->assignValue(v)) { setConflict(); return false; }
00543         if (old == value_free)  { propQ_.push_back(a->id()); }
00544         return true;
00545 }
00546 bool LogicProgram::assignValue(PrgHead* h, ValueRep v) {
00547         return !h->isAtom() || assignValue(static_cast<PrgAtom*>(h), v);
00548 }
00549 
00550 bool LogicProgram::handleNatively(RuleType r, const BodyInfo& body) const {
00551         ExtendedRuleMode m = opts_.erMode;
00552         if (r == BASICRULE || r == OPTIMIZERULE || m == mode_native) {
00553                 return true;
00554         }
00555         else if (m == mode_transform_integ || m == mode_transform_scc || m == mode_transform_nhcf) {
00556                 return true;
00557         }
00558         else if (m == mode_transform) {
00559                 return r == DISJUNCTIVERULE;
00560         }
00561         else if (m == mode_transform_dynamic) {
00562                 return (r != CONSTRAINTRULE && r != WEIGHTRULE)
00563                         || transformNoAux(r, body) == false;
00564         }
00565         else if (m == mode_transform_choice) {
00566                 return r != CHOICERULE;
00567         }
00568         else if (m == mode_transform_card)   {
00569                 return r != CONSTRAINTRULE;
00570         }
00571         else if (m == mode_transform_weight) {
00572                 return r != CONSTRAINTRULE && r != WEIGHTRULE;
00573         }
00574         assert(false && "unhandled extended rule mode");
00575         return true;
00576 }
00577 
00578 bool LogicProgram::transformNoAux(RuleType r, const BodyInfo& body) const {
00579         return r != CHOICERULE && (body.bound() == 1 || (body.size() <= 6 && choose(body.size(), body.bound()) <= 15));
00580 }
00581 
00582 void LogicProgram::transformExtended() {
00583         uint32 a   = numAtoms();
00584         if (incData_) {
00585                 // remember starting position of aux atoms so
00586                 // that we can remove them on next incremental step
00587                 incData_->startAux = (uint32)atoms_.size();
00588         }
00589         RuleTransform tm;
00590         for (RuleList::size_type i = 0; i != extended_.size(); ++i) {
00591                 incTr(extended_[i]->type(), tm.transform(*this, *extended_[i]));
00592                 delete extended_[i];
00593         }
00594         extended_.clear();
00595         incTrAux(numAtoms() - a);
00596 }
00597 
00598 void LogicProgram::transformIntegrity(uint32 maxAux) {
00599         if (stats.rules(CONSTRAINTRULE).second == 0) { return; }
00600         // find all constraint rules that are integrity constraints
00601         BodyList integrity;
00602         for (uint32 i = 0, end = static_cast<uint32>(bodies_.size()); i != end; ++i) {
00603                 PrgBody* b = bodies_[i];
00604                 if (b->relevant() && b->type() == BodyInfo::COUNT_BODY && b->value() == value_false) {
00605                         integrity.push_back(b);
00606                 }
00607         }
00608         if (!integrity.empty() && (integrity.size() == 1 || (atoms_.size()/double(bodies_.size()) > 0.5 && integrity.size() / double(bodies_.size()) < 0.01))) {
00609                 uint32 A      = static_cast<uint32>(atoms_.size());
00610                 // transform integrity constraints
00611                 for (BodyList::size_type i = 0; i != integrity.size(); ++i) {
00612                         PrgBody* b = integrity[i];
00613                         uint32 est = b->bound()*( b->sumW()-b->bound() );
00614                         if (est > maxAux) { 
00615                                 // reached limit on aux atoms - stop transformation
00616                                 break; 
00617                         }
00618                         maxAux -= est;
00619                         // transform rule
00620                         Rule* r;
00621                         extended_.push_back(r = new Rule());
00622                         r->setType(CONSTRAINTRULE);
00623                         r->setBound(b->bound());
00624                         r->addHead(getFalseId());
00625                         for (uint32 g = 0; g != b->size(); ++g) {
00626                                 r->addToBody(b->goal(g).var(), !b->goal(g).sign());
00627                         }
00628                         setFrozen(false);
00629                         transformExtended();
00630                         setFrozen(true);
00631                         // propagate integrity condition to new rules
00632                         propQ_.push_back(getFalseId());
00633                         propagate(true);
00634                         b->markRemoved();
00635                 }
00636                 // create vars for new atoms/bodies
00637                 for (uint32 i = A; i != atoms_.size(); ++i) {
00638                         PrgAtom* a = atoms_[i];
00639                         for (PrgAtom::sup_iterator it = a->supps_begin(); it != a->supps_end(); ++it) {
00640                                 PrgBody* nb = bodies_[it->node()];
00641                                 assert(nb->value() != value_false);
00642                                 nb->assignVar(*this);
00643                         }
00644                         a->assignVar(*this, a->supports() ? *a->supps_begin() : PrgEdge::noEdge());     
00645                 }
00646         }
00647 }
00648 
00649 // replace equivalent atoms in minimize rules
00650 void LogicProgram::simplifyMinimize() {
00651         assert(hasMinimize());
00652         for (LogicProgram::MinimizeRule* r = minimize_; r; r = r->next_) {
00653                 for (WeightLitVec::iterator it = r->lits_.begin(); it != r->lits_.end(); ++it) {
00654                         it->first = Literal(getEqAtom(it->first.var()), it->first.sign());
00655                 }
00656         }
00657 }
00658 
00659 void LogicProgram::updateFrozenAtoms() {
00660         if (!incData_) { return; }
00661         assert(incData_->frozen.empty());
00662         activeHead_.clear();
00663         activeBody_.reset();
00664         PrgBody* support   = 0;
00665         VarVec::iterator j = incData_->update.begin();
00666         for (VarVec::iterator it = j, end = incData_->update.end(); it != end; ++it) {
00667                 Var id     = getEqAtom(*it);
00668                 PrgAtom* a = getAtom(id);
00669                 if (a->inFlux()) {
00670                         assert(a->id() == id && a->scc() == PrgNode::noScc && !a->inUpper());
00671                         a->setState(PrgAtom::state_normal);
00672                         if (id < startAtom()) {
00673                                 // unfreeze previously frozen atom
00674                                 a->markSeen(false);
00675                                 a->markDirty();
00676                                 *j++ = id; // keep in list so that we can later perform completion
00677                         }
00678                 }
00679                 else if (a->frozen()) {
00680                         assert(a->relevant() && a->supports() == 0);
00681                         a->resetId(id, false);
00682                         if (!support) { support = getBodyFor(activeBody_); }
00683                         a->setIgnoreScc(true);
00684                         support->addHead(a, PrgEdge::CHOICE_EDGE);
00685                         incData_->frozen.push_back(id); // still frozen
00686                 }
00687         }
00688         incData_->update.erase(j, incData_->update.end());
00689 }
00690 
00691 void LogicProgram::prepareProgram(bool checkSccs) {
00692         assert(!frozen());
00693         transformExtended();
00694         if (opts_.normalize) { /* normalize(); */ assert(false);  }
00695         stats.atoms = numAtoms() - (startAtom()-1);
00696         stats.bodies= numBodies();
00697         updateFrozenAtoms();
00698         setFrozen(true);
00699         Preprocessor p;
00700         if (hasConflict() || !propagate(true) || !p.preprocess(*this, opts_.iters != 0 && !opts_.suppMod ? Preprocessor::full_eq : Preprocessor::no_eq, opts_.iters, opts_.dfOrder)) {
00701                 setConflict();
00702                 return;
00703         }
00704         if (opts_.erMode == mode_transform_integ || opts_.erMode == mode_transform_dynamic) {
00705                 transformIntegrity(std::min(uint32(15000), uint32(numAtoms())<<1));
00706         }
00707         addMinimize();
00708         uint32 sccs = 0;
00709         if (checkSccs) {
00710                 uint32 startScc = incData_ ? incData_->startScc : 0;
00711                 SccChecker c(*this, sccAtoms_, startScc);
00712                 sccs       = c.sccs();
00713                 stats.sccs = (sccs-startScc);
00714                 if (incData_) { incData_->startScc = c.sccs(); }
00715                 if (!disjunctions_.empty() || (opts_.erMode == mode_transform_scc && sccs)) {
00716                         // reset node ids changed by scc checking
00717                         for (uint32 i = 0; i != bodies_.size(); ++i) {
00718                                 if (getBody(i)->relevant()) { getBody(i)->resetId(i, true); }
00719                         }
00720                         for (uint32 i = 0; i != atoms_.size(); ++i) {
00721                                 if (getAtom(i)->relevant()) { getAtom(i)->resetId(i, true); }
00722                         }
00723                 }
00724         }
00725         else { stats.sccs = PrgNode::noScc; }
00726         finalizeDisjunctions(p, sccs);
00727         prepareComponents();
00728         stats.atoms = numAtoms() - (startAtom()-1);
00729         bodyIndex_.clear();
00730         disjIndex_.clear();
00731 }
00732 
00733 // replace disjunctions with gamma (shifted) and delta (component-shifted) rules
00734 void LogicProgram::finalizeDisjunctions(Preprocessor& p, uint32 numSccs) {
00735         if (disjunctions_.empty()) { return; }
00736         VarVec head; BodyList supports;
00737         disjIndex_.clear();
00738         SccMap sccMap;
00739         sccMap.resize(numSccs, 0);
00740         enum SccFlag { seen_scc = 1u, is_scc_non_hcf = 128u };
00741         // replace disjunctions with shifted rules and non-hcf-disjunctions
00742         DisjList temp; temp.swap(disjunctions_);
00743         setFrozen(false);
00744         uint32 shifted = 0, added = 0;
00745         stats.nonHcfs  = uint32(nonHcfs_.size());
00746         for (uint32 i = 0, end = temp.size(); i != end; ++i) {
00747                 PrgDisj* d = temp[i];
00748                 Literal dx = d->inUpper() ? d->literal() : negLit(0);
00749                 PrgEdge e  = PrgEdge::newEdge(i, PrgEdge::CHOICE_EDGE, PrgEdge::DISJ_NODE);
00750                 d->resetId(i, true); // id changed during scc checking
00751                 // remove from program and 
00752                 // replace with shifted rules or component-shifted disjunction
00753                 head.clear(); supports.clear();
00754                 for (PrgDisj::atom_iterator it = d->begin(), end = d->end(); it != end; ++it) {
00755                         PrgAtom* at = getAtom(it->node());
00756                         at->removeSupport(e);
00757                         if (at->inUpper()) { 
00758                                 head.push_back(it->node()); 
00759                                 if (at->scc() != PrgNode::noScc) { sccMap[at->scc()] = seen_scc; }
00760                         }
00761                 }
00762                 EdgeVec temp;
00763                 d->clearSupports(temp);
00764                 for (EdgeVec::iterator it = temp.begin(), end = temp.end(); it != end; ++it) {
00765                         PrgBody* b = getBody(it->node());
00766                         if (b->relevant() && b->value() != value_false) { supports.push_back(b); }
00767                         b->removeHead(d, PrgEdge::NORMAL_EDGE);
00768                 }
00769                 d->destroy();
00770                 // create shortcut for supports to avoid duplications during shifting
00771                 Literal supportLit = dx != negLit(0) ? getEqAtomLit(dx, supports, p, sccMap) : dx;
00772                 // create shifted rules and split disjunctions into non-hcf components
00773                 for (VarVec::iterator hIt = head.begin(), hEnd = head.end(); hIt != hEnd; ++hIt) {
00774                         uint32 scc = getAtom(*hIt)->scc();
00775                         if (scc == PrgNode::noScc || (sccMap[scc] & seen_scc) != 0) {
00776                                 if (scc != PrgNode::noScc) { sccMap[scc] &= ~seen_scc; }
00777                                 else                       { scc = UINT32_MAX; }
00778                                 rule_.clear(); rule_.setType(DISJUNCTIVERULE);
00779                                 rule_.addHead(*hIt);
00780                                 if (supportLit.var() != 0) { rule_.addToBody(supportLit.var(), !supportLit.sign()); }
00781                                 else if (supportLit.sign()){ continue; }
00782                                 for (VarVec::iterator oIt = head.begin(); oIt != hEnd; ++oIt) {
00783                                         if (oIt != hIt) {
00784                                                 if (getAtom(*oIt)->scc() == scc) { rule_.addHead(*oIt); }
00785                                                 else                             { rule_.addToBody(*oIt, false); }
00786                                         }
00787                                 }
00788                                 RuleType t = simplifyRule(rule_, activeHead_, activeBody_);
00789                                 PrgBody* B = t != ENDRULE ? assignBodyFor(activeBody_, PrgEdge::NORMAL_EDGE, true) : 0;
00790                                 if (!B || B->value() == value_false) { continue; }
00791                                 if (t == BASICRULE) {
00792                                         ++shifted;
00793                                         B->addHead(getAtom(activeHead_[0]), PrgEdge::NORMAL_EDGE);
00794                                 }
00795                                 else if (t == DISJUNCTIVERULE) {
00796                                         PrgDisj* x = getDisjFor(activeHead_, 0);
00797                                         B->addHead(x, PrgEdge::NORMAL_EDGE);
00798                                         x->assignVar(*this, *x->supps_begin());
00799                                         x->setInUpper(true);
00800                                         x->markSeen(true);
00801                                         ++added;
00802                                         if ((sccMap[scc] & is_scc_non_hcf) == 0) {
00803                                                 sccMap[scc] |= is_scc_non_hcf;
00804                                                 nonHcfs_.add(scc);
00805                                         }
00806                                         if (!options().noGamma) {
00807                                                 rule_.setType(BASICRULE);
00808                                                 for (uint32 i = 1; i != rule_.heads.size(); ++i) { rule_.addToBody(rule_.heads[i], false); }
00809                                                 rule_.heads.resize(1);
00810                                                 WeightLitVec::iterator bIt = rule_.body.end();
00811                                                 for (uint32 i = x->size();;) {
00812                                                         t = simplifyRule(rule_, activeHead_, activeBody_);
00813                                                         B = t != ENDRULE ? assignBodyFor(activeBody_, PrgEdge::GAMMA_EDGE, true) : 0;
00814                                                         if (B && B->value() != value_false) { B->addHead(getAtom(activeHead_[0]), PrgEdge::GAMMA_EDGE); ++stats.gammas; }
00815                                                         if (--i == 0) { break; }
00816                                                         Var h          = rule_.heads[0];
00817                                                         rule_.heads[0] = (--bIt)->first.var();
00818                                                         *bIt           = WeightLiteral(negLit(h), 1);
00819                                                 }
00820                                         }
00821                                 }
00822                         }
00823                 }
00824         }
00825         stats.rules(DISJUNCTIVERULE).second = added;
00826         stats.rules(BASICRULE).second      += shifted;
00827         stats.nonHcfs  = uint32(nonHcfs_.size()) - stats.nonHcfs;
00828         setFrozen(true);
00829 }
00830 
00831 // optionally transform extended rules in sccs
00832 void LogicProgram::prepareComponents() {
00833         int trRec  = opts_.erMode == mode_transform_scc;
00834         // HACK: force transformation of extended rules in non-hcf components
00835         // REMOVE this once minimality check supports aggregates
00836         if (!disjunctions_.empty() && trRec != 1) {
00837                 trRec    = 2;
00838         }
00839         if (trRec != 0) {
00840                 BodyList ext;
00841                 EdgeVec  heads;
00842                 for (BodyList::const_iterator it = bodies_.begin(), end = bodies_.end(); it != end; ++it) {
00843                         if ((*it)->type() != BodyInfo::NORMAL_BODY && (*it)->hasVar() && (*it)->value() != value_false) {
00844                                 uint32 scc = (*it)->scc(*this);
00845                                 if (scc != PrgNode::noScc && (trRec == 1 || nonHcfs_.find(scc))) {
00846                                         ext.push_back(*it);
00847                                 }
00848                         }
00849                 }
00850                 if (ext.empty()) { return; }
00851                 struct Tr : public RuleTransform::ProgramAdapter {
00852                         Tr(LogicProgram* x) : self(x), scc(0) {}
00853                         Var newAtom() {
00854                                 Var x      = self->newAtom();
00855                                 PrgAtom* a = self->getAtom(x);
00856                                 self->sccAtoms_.push_back(a);
00857                                 a->setScc(scc);
00858                                 a->markSeen(true);
00859                                 atoms.push_back(x);
00860                                 return x;
00861                         }
00862                         void addRule(Rule& nr) {
00863                                 if (self->simplifyRule(nr, self->activeHead_, self->activeBody_) != ENDRULE) {
00864                                         PrgBody* B = self->assignBodyFor(self->activeBody_, PrgEdge::NORMAL_EDGE, false);
00865                                         if (B->value() != value_false) {
00866                                                 B->addHead(self->getAtom(self->activeHead_[0]), PrgEdge::NORMAL_EDGE);
00867                                         }
00868                                 }
00869                         }
00870                         LogicProgram* self;
00871                         uint32 scc;
00872                         VarVec atoms;
00873                 } tr(this);
00874                 RuleTransform trans;
00875                 setFrozen(false);
00876                 if (incData_) { incData_->startAux = (uint32)atoms_.size(); }
00877                 for (BodyList::const_iterator it = ext.begin(), end = ext.end(); it != end; ++it) {
00878                         uint32 scc = (*it)->scc(*this);
00879                         rule_.clear();
00880                         rule_.setType((*it)->type() == BodyInfo::COUNT_BODY ? CONSTRAINTRULE : WEIGHTRULE);
00881                         rule_.setBound((*it)->bound());
00882                         tr.scc = scc;
00883                         for (uint32 i = 0; i != (*it)->size(); ++i) {
00884                                 rule_.addToBody((*it)->goal(i).var(), (*it)->goal(i).sign() == false, (*it)->weight(i));
00885                         }
00886                         heads.assign((*it)->heads_begin(), (*it)->heads_end());
00887                         for (EdgeVec::const_iterator hIt = heads.begin(); hIt != heads.end(); ++hIt) {
00888                                 assert(hIt->isAtom());
00889                                 if (getAtom(hIt->node())->scc() == scc) {
00890                                         (*it)->removeHead(getAtom(hIt->node()), hIt->type());
00891                                         rule_.heads.assign(1, hIt->node());
00892                                         if (simplifyRule(rule_, activeHead_, activeBody_) != ENDRULE) {
00893                                                 trans.transform(tr, rule_);     
00894                                         }
00895                                 }
00896                         }
00897                 }
00898                 incTrAux(tr.atoms.size());
00899                 while (!tr.atoms.empty()) {
00900                         PrgAtom* ax = getAtom(tr.atoms.back());
00901                         tr.atoms.pop_back();
00902                         if (ax->supports()) {
00903                                 ax->setInUpper(true);
00904                                 ax->assignVar(*this, *ax->supps_begin());
00905                         }
00906                         else { assignValue(ax, value_false); }
00907                 }
00908                 setFrozen(true);
00909         }
00910 }
00911 
00912 // add (completion) nogoods
00913 bool LogicProgram::addConstraints() {
00914         ClauseCreator gc(ctx()->master());
00915         if (options().iters == 0) {
00916                 gc.addDefaultFlags(ClauseCreator::clause_force_simplify);
00917         }
00918         ctx()->startAddConstraints();
00919         ctx()->symbolTable().endInit();
00920         CLASP_ASSERT_CONTRACT(ctx()->symbolTable().curBegin() == ctx()->symbolTable().end() || startAtom() <= ctx()->symbolTable().curBegin()->first);
00921         // handle initial conflict, if any
00922         if (!ctx()->ok() || !ctx()->addUnary(getFalseAtom()->trueLit())) {
00923                 return false;
00924         }
00925         // add bodies from this step
00926         for (BodyList::const_iterator it = bodies_.begin(); it != bodies_.end(); ++it) {
00927                 if (!toConstraint((*it), *this, gc)) { return false; }
00928         }
00929         // add atoms thawed in this step
00930         for (VarIter it = unfreeze_begin(), end = unfreeze_end(); it != end; ++it) {
00931                 if (!toConstraint(getAtom(*it), *this, gc)) { return false; }
00932         }
00933         // add atoms from this step and finalize symbol table
00934         typedef SymbolTable::const_iterator symbol_iterator;
00935         const bool freezeAll   = incData_ && ctx()->satPrepro.get() != 0;
00936         symbol_iterator sym    = ctx()->symbolTable().lower_bound(ctx()->symbolTable().curBegin(), startAtom());
00937         symbol_iterator symEnd = ctx()->symbolTable().end();
00938         Var             atomId = startAtom();
00939         for (AtomList::const_iterator it = atoms_.begin()+atomId, end = atoms_.end(); it != end; ++it, ++atomId) {
00940                 if (!toConstraint(*it, *this, gc)) { return false; }
00941                 if (freezeAll && (*it)->hasVar())  { ctx()->setFrozen((*it)->var(), true); }
00942                 if (sym != symEnd && atomId == sym->first) {
00943                         sym->second.lit = atoms_[getEqAtom(atomId)]->literal();
00944                         ++sym;
00945                 }
00946         }
00947         if (!sccAtoms_.empty()) {
00948                 if (ctx()->sccGraph.get() == 0) {
00949                         ctx()->sccGraph = new SharedDependencyGraph(nonHcfCfg_);
00950                 }
00951                 uint32 oldNodes = ctx()->sccGraph->nodes();
00952                 ctx()->sccGraph->addSccs(*this, sccAtoms_, nonHcfs_);
00953                 stats.ufsNodes  = ctx()->sccGraph->nodes()-oldNodes;
00954                 sccAtoms_.clear();
00955         }
00956         return true;
00957 }
00958 #undef check_modular
00959 
00960 // misc/helper functions
00962 PrgAtom* LogicProgram::resize(Var atomId) {
00963         while (atoms_.size() <= AtomList::size_type(atomId)) {
00964                 newAtom();
00965         }
00966         return atoms_[getEqAtom(atomId)];
00967 }
00968 
00969 bool LogicProgram::propagate(bool backprop) {
00970         assert(frozen());
00971         bool oldB = opts_.backprop;
00972         opts_.backprop = backprop;
00973         for (VarVec::size_type i = 0; i != propQ_.size(); ++i) {
00974                 PrgAtom* a = getAtom(propQ_[i]);
00975                 if (!a->relevant()) { continue; }
00976                 if (!a->propagateValue(*this, backprop)) {
00977                         setConflict();
00978                         return false;
00979                 }
00980                 if (a->hasVar() && a->id() < startAtom() && !ctx()->addUnary(a->trueLit())) {
00981                         setConflict();
00982                         return false;
00983                 }
00984         }
00985         opts_.backprop = oldB;
00986         propQ_.clear();
00987         return true;
00988 }
00989 
00990 // Simplifies the rule's body:
00991 //   - removes duplicate literals: {a,b,a} -> {a, b}.
00992 //   - checks for contradictions : {a, not a}
00993 //   - removes literals with weight 0    : LB [a = 0, b = 2, c = 0, ...] -> LB [b = 2, ...]
00994 //   - reduces weights > bound() to bound:  2 [a=1, b=3] -> 2 [a=1, b=2]
00995 //   - merges duplicate literals         : LB [a=w1, b=w2, a=w3] -> LB [a=w1+w3, b=w2]
00996 //   - checks for contradiction, i.e.
00997 //     rule body contains both p and not p and both are needed
00998 //   - replaces weight constraint with cardinality constraint 
00999 //     if all body weights are equal
01000 //   - replaces weight/cardinality constraint with normal body 
01001 //     if sumW - minW < bound()
01002 RuleType LogicProgram::simplifyBody(const Rule& r, BodyInfo& info) {
01003         info.reset();
01004         WeightLitVec& sBody= info.lits;
01005         if (r.bodyHasBound() && r.bound() <= 0) {
01006                 return BASICRULE;
01007         }
01008         sBody.reserve(r.body.size());
01009         RuleType resType = r.type();
01010         weight_t w       = 0;
01011         weight_t BOUND   = r.bodyHasBound() ? r.bound() : std::numeric_limits<weight_t>::max();
01012         weight_t bound   = r.bodyHasBound() ? r.bound() : static_cast<weight_t>(r.body.size());
01013         uint32   pos     = 0;
01014         uint32   hash    = 0;
01015         bool     dirty   = r.bodyHasWeights();
01016         Literal lit;
01017         for (WeightLitVec::const_iterator it = r.body.begin(), bEnd = r.body.end(); it != bEnd; ++it) {
01018                 if (it->second == 0) continue; // skip irrelevant lits
01019                 CLASP_ASSERT_CONTRACT_MSG(it->second>0, "Positive weight expected!");
01020                 PrgAtom* a = resize(it->first.var());
01021                 lit        = Literal(a->id(), it->first.sign());// replace any eq atoms
01022                 w          = std::min(it->second, BOUND);       // reduce weights to bound
01023                 if (a->value() != value_free || !a->relevant()) {
01024                         bool vSign = a->value() == value_false || !a->relevant();
01025                         if (vSign != lit.sign()) {
01026                                 // literal is false - drop rule?
01027                                 if (r.bodyIsSet()) { resType = ENDRULE; break; }
01028                                 continue;
01029                         }
01030                         else if (a->value() != value_weak_true && r.type() != OPTIMIZERULE) {
01031                                 // literal is true - drop from rule
01032                                 if ((bound -= w) <= 0) {
01033                                         while (!sBody.empty()) { ruleState_.clear(sBody.back().first.var()); sBody.pop_back(); }
01034                                         pos = hash = 0;
01035                                         break;
01036                                 }
01037                                 continue;
01038                         }
01039                 }
01040                 if (!ruleState_.inBody(lit)) {  // literal not seen yet
01041                         ruleState_.addToBody(lit);    // add to simplified body
01042                         sBody.push_back(WeightLiteral(lit, w));
01043                         pos += !lit.sign();
01044                         hash+= !lit.sign() ? hashId(lit.var()) : hashId(-lit.var());
01045                 }
01046                 else if (!r.bodyIsSet()) {      // Merge duplicate lits
01047                         WeightLiteral& oldLit = info[info.findLit(lit)];
01048                         weight_t       oldW   = oldLit.second;
01049                         CLASP_ASSERT_CONTRACT_MSG((INT_MAX-oldW)>= w, "Integer overflow!");
01050                         w             = std::min(oldW + w, BOUND); // remember new weight
01051                         oldLit.second = w;
01052                         dirty         = true;
01053                         if (resType == CONSTRAINTRULE) {
01054                                 resType = WEIGHTRULE;
01055                         }
01056                 }
01057                 dirty |= ruleState_.inBody(~lit);
01058         }
01059         weight_t minW  = 1;
01060         weight_t maxW  = 1;
01061         wsum_t realSum = (wsum_t)sBody.size();
01062         wsum_t sumW    = (wsum_t)sBody.size();
01063         if (dirty) {
01064                 minW    = std::numeric_limits<weight_t>::max();
01065                 realSum = sumW = 0;
01066                 for (WeightLitVec::size_type i = 0; i != sBody.size(); ++i) {
01067                         lit = sBody[i].first; w = sBody[i].second;
01068                         minW = std::min(minW, w);
01069                         maxW = std::max(maxW, w);
01070                         sumW+= w;
01071                         if      (!ruleState_.inBody(~lit)) { realSum += w; }
01072                         else if (r.bodyIsSet())            { resType = ENDRULE; break; }
01073                         else if (lit.sign())               { 
01074                                 // body contains lit and ~lit: we can achieve at most max(weight(lit), weight(~lit))
01075                                 realSum += std::max(w, info[info.findLit(~lit)].second);
01076                         }
01077                 }
01078                 if (resType == OPTIMIZERULE) { bound = 0; }
01079         }
01080         if (resType != ENDRULE && r.bodyHasBound()) {
01081                 if      (bound <= 0)            { resType = BASICRULE; bound = 0; }
01082                 else if (realSum < bound)       { resType = ENDRULE;   }
01083                 else if ((sumW - minW) < bound) { resType = BASICRULE;      bound = (weight_t)sBody.size(); }
01084                 else if (minW == maxW)          { resType = CONSTRAINTRULE; bound = (bound+(minW-1))/minW;  }
01085         }
01086         info.init(resType, bound, hash, pos);
01087         return resType;
01088 }
01089 
01090 RuleType LogicProgram::simplifyRule(const Rule& r, VarVec& head, BodyInfo& body) {
01091         RuleType type = simplifyBody(r, body);
01092         head.clear();
01093         if (type != ENDRULE && type != OPTIMIZERULE) {
01094                 bool blocked = false, taut = false;
01095                 weight_t sum = -1;
01096                 for (VarVec::const_iterator it = r.heads.begin(), end = r.heads.end(); it != end; ++it) {
01097                         if (!ruleState_.isSet(*it, RuleState::any_flag)) {
01098                                 head.push_back(*it);
01099                                 ruleState_.addToHead(*it);
01100                         }
01101                         else if (!ruleState_.isSet(*it, RuleState::head_flag)) {
01102                                 weight_t wPos = ruleState_.inBody(posLit(*it)) ? body.weight(posLit(*it)) : 0;
01103                                 weight_t wNeg = ruleState_.inBody(negLit(*it)) ? body.weight(negLit(*it)) : 0;
01104                                 if (sum == -1) sum = body.sum();
01105                                 if ((sum - wPos) < body.bound()) {
01106                                         taut    = (type != CHOICERULE);
01107                                 }
01108                                 else if ((sum - wNeg) < body.bound()) {
01109                                         blocked = (type != CHOICERULE);
01110                                 }
01111                                 else {
01112                                         head.push_back(*it);
01113                                         ruleState_.addToHead(*it);
01114                                 }
01115                         }
01116                 }
01117                 for (VarVec::const_iterator it = head.begin(), end = head.end(); it != end; ++it) {
01118                         ruleState_.clear(*it);
01119                 }
01120                 if (blocked && type != DISJUNCTIVERULE) {
01121                         head.clear();
01122                         head.push_back(0);
01123                 }
01124                 else if (taut && (type == DISJUNCTIVERULE || head.empty())) {
01125                         head.clear();
01126                         type = ENDRULE;
01127                 }
01128                 else if (type == DISJUNCTIVERULE && head.size() == 1) {
01129                         type = BASICRULE;
01130                 }
01131                 else if (head.empty()) {
01132                         type = ENDRULE;
01133                 }
01134         }
01135         for (WeightLitVec::size_type i = 0; i != body.size(); ++i) {
01136                 ruleState_.clear(body[i].first.var());
01137         }
01138         return type;
01139 }
01140 
01141 // create new atom aux representing supports, i.e.
01142 // aux == S1 v ... v Sn
01143 Literal LogicProgram::getEqAtomLit(Literal lit, const BodyList& supports, Preprocessor& p, const SccMap& sccMap) {
01144         if (supports.empty() || lit == negLit(0)) { return negLit(0); }
01145         if (supports.size() == 1 && supports[0]->size() < 2) { 
01146                 return supports[0]->size() == 0 ? posLit(0) : supports[0]->goal(0); 
01147         }
01148         if (p.getRootAtom(lit) != varMax)         { return posLit(p.getRootAtom(lit)); }
01149         incTrAux(1);
01150         Var auxV     = newAtom();
01151         PrgAtom* aux = getAtom(auxV);
01152         uint32 scc   = PrgNode::noScc;
01153         aux->setLiteral(lit);
01154         aux->markSeen(true);
01155         p.setRootAtom(aux->literal(), auxV);
01156         for (BodyList::const_iterator sIt = supports.begin(); sIt != supports.end(); ++sIt) {
01157                 PrgBody* b = *sIt;
01158                 if (b->relevant() && b->value() != value_false) {
01159                         for (uint32 g = 0; scc == PrgNode::noScc && g != b->size() && !b->goal(g).sign(); ++g) {
01160                                 uint32 aScc = getAtom(b->goal(g).var())->scc();
01161                                 if (aScc != PrgNode::noScc && (sccMap[aScc] & 1u)) { scc = aScc; }
01162                         }
01163                         b->addHead(aux, PrgEdge::NORMAL_EDGE);
01164                         if (b->value() != aux->value()) { assignValue(aux, b->value()); }
01165                         aux->setInUpper(true);
01166                 }
01167         }
01168         if (!aux->inUpper()) {
01169                 aux->setValue(value_false);
01170                 return negLit(0);
01171         }
01172         else if (scc != PrgNode::noScc) {
01173                 aux->setScc(scc);
01174                 sccAtoms_.push_back(aux);
01175         }
01176         return posLit(auxV);
01177 }
01178 
01179 PrgBody* LogicProgram::getBodyFor(BodyInfo& body, bool addDeps) {
01180         uint32 bodyId = equalBody(bodyIndex_.equal_range(body.hash), body);
01181         if (bodyId != varMax) {
01182                 return getBody(bodyId);
01183         }
01184         // no corresponding body exists, create a new object
01185         bodyId        = (uint32)bodies_.size();
01186         PrgBody* b    = PrgBody::create(*this, bodyId, body, addDeps);
01187         bodyIndex_.insert(IndexMap::value_type(body.hash, bodyId));
01188         bodies_.push_back(b);
01189         if (b->isSupported()) {
01190                 initialSupp_.push_back(bodyId);
01191         }
01192         return b;
01193 }
01194 
01195 PrgBody* LogicProgram::assignBodyFor(BodyInfo& body, EdgeType depEdge, bool simpStrong) {
01196         PrgBody* b = getBodyFor(body, depEdge != PrgEdge::GAMMA_EDGE);
01197         if (!b->hasVar() && !b->seen()) {
01198                 uint32 eqId;
01199                 b->markDirty();
01200                 b->simplify(*this, simpStrong, &eqId);
01201                 if (eqId != b->id()) {
01202                         assert(b->id() == bodies_.size()-1);
01203                         removeBody(b, body.hash);
01204                         bodies_.pop_back();
01205                         if (depEdge != PrgEdge::GAMMA_EDGE) {
01206                                 for (uint32 i = 0; i != b->size(); ++i) {
01207                                         getAtom(b->goal(i).var())->removeDep(b->id(), !b->goal(i).sign());
01208                                 }
01209                         }
01210                         b->destroy();
01211                         b = bodies_[eqId];
01212                 }
01213         }
01214         b->markSeen(true);
01215         b->assignVar(*this);
01216         return b;
01217 }
01218 
01219 uint32 LogicProgram::equalBody(const IndexRange& range, BodyInfo& body) const {
01220         bool sorted = false;
01221         for (IndexIter it = range.first; it != range.second; ++it) {
01222                 PrgBody& o = *bodies_[it->second];
01223                 if (o.type() == body.type() && o.size() == body.size() && o.bound() == body.bound() && (body.posSize() == 0u || o.goal(body.posSize()-1).sign() == false)) {
01224                         // bodies are structurally equivalent - check if they contain the same literals
01225                         if ((o.relevant() || (o.eq() && getBody(o.id())->relevant())) && o.eqLits(body.lits, sorted)) {
01226                                 assert(o.id() == it->second || o.eq());
01227                                 return o.id();
01228                         }
01229                 }
01230         }
01231         return varMax;
01232 }
01233 uint32 LogicProgram::findEqBody(PrgBody* b, uint32 hash) {
01234         LogicProgram::IndexRange eqRange = bodyIndex_.equal_range(hash);
01235         // check for existing body
01236         if (eqRange.first != eqRange.second) {
01237                 activeBody_.reset();
01238                 WeightLitVec& lits = activeBody_.lits;
01239                 uint32 p = 0;
01240                 for (uint32 i = 0, end = b->size(); i != end; ++i) { 
01241                         lits.push_back(WeightLiteral(b->goal(i), b->weight(i))); 
01242                         p += !lits.back().first.sign(); 
01243                 }
01244                 activeBody_.init(b->type(), b->bound(), hash, p);
01245                 return equalBody(eqRange, activeBody_);
01246         }
01247         return varMax;
01248 }
01249 
01250 PrgDisj* LogicProgram::getDisjFor(const VarVec& heads, uint32 headHash) {
01251         PrgDisj* d = 0;
01252         if (headHash) {
01253                 LogicProgram::IndexRange eqRange = disjIndex_.equal_range(headHash);
01254                 for (; eqRange.first != eqRange.second; ++eqRange.first) {
01255                         PrgDisj& o = *disjunctions_[eqRange.first->second];
01256                         if (o.relevant() && o.size() == heads.size() && ruleState_.allMarked(heads, RuleState::head_flag)) {
01257                                 assert(o.id() == eqRange.first->second);
01258                                 d = &o;
01259                                 break;
01260                         }
01261                 }
01262                 for (VarVec::const_iterator it = heads.begin(), end = heads.end(); it != end; ++it) {
01263                         ruleState_.clear(*it);
01264                 }
01265         }
01266         if (!d) {
01267                 // no corresponding disjunction exists, create a new object
01268                 // and link it to all atoms
01269                 uint32 id    = disjunctions_.size();
01270                 d            = PrgDisj::create(id, heads);
01271                 disjunctions_.push_back(d);
01272                 PrgEdge edge = PrgEdge::newEdge(id, PrgEdge::CHOICE_EDGE, PrgEdge::DISJ_NODE);
01273                 for (VarVec::const_iterator it = heads.begin(), end = heads.end(); it != end; ++it) {
01274                         getAtom(*it)->addSupport(edge);
01275                 }
01276                 if (headHash) {
01277                         disjIndex_.insert(IndexMap::value_type(headHash, d->id()));
01278                 }
01279         }
01280         return d;
01281 }
01282 
01283 // body has changed - update index
01284 uint32 LogicProgram::update(PrgBody* body, uint32 oldHash, uint32 newHash) {
01285         uint32 id   = removeBody(body, oldHash);
01286         if (body->relevant()) {
01287                 uint32 eqId = findEqBody(body, newHash);
01288                 if (eqId == varMax) {
01289                         // No equivalent body found. 
01290                         // Add new entry to index
01291                         bodyIndex_.insert(IndexMap::value_type(newHash, id));
01292                 }
01293                 return eqId;
01294         }
01295         return varMax;
01296 }
01297 
01298 // body b has changed - remove old entry from body node index
01299 uint32 LogicProgram::removeBody(PrgBody* b, uint32 hash) {
01300         IndexRange ra = bodyIndex_.equal_range(hash);
01301         uint32 id     = b->id();
01302         for (; ra.first != ra.second; ++ra.first) {
01303                 if (bodies_[ra.first->second] == b) {
01304                         id = ra.first->second;
01305                         bodyIndex_.erase(ra.first);
01306                         break;
01307                 }
01308         }
01309         return id;
01310 }
01311 
01312 PrgAtom* LogicProgram::mergeEqAtoms(PrgAtom* a, Var rootId) {
01313         rootId        = getEqAtom(rootId);
01314         PrgAtom* root = getAtom(rootId);
01315         assert(!a->eq() && !root->eq());
01316         if (a->ignoreScc())       { root->setIgnoreScc(true); }
01317         if (a->frozen())          { root->setState(std::max(a->state(), root->state())); }
01318         if (!mergeValue(a, root)) { setConflict(); return 0;  }
01319         assert(a->value() == root->value() || (root->value() == value_true && a->value() == value_weak_true));
01320         a->setEq(rootId);
01321         incEqs(Var_t::atom_var);
01322         return root;
01323 }
01324 
01325 // returns whether posSize(root) <= posSize(body)
01326 bool LogicProgram::positiveLoopSafe(PrgBody* body, PrgBody* root) const {
01327         uint32 i = 0, end = std::min(body->size(), root->size());
01328         while (i != end && body->goal(i).sign() == root->goal(i).sign()) { ++i; }
01329         return i == root->size() || root->goal(i).sign();       
01330 }
01331 
01332 PrgBody* LogicProgram::mergeEqBodies(PrgBody* b, Var rootId, bool hashEq, bool atomsAssigned) {
01333         rootId        = getEqNode(bodies_, rootId);
01334         PrgBody* root = getBody(rootId);
01335         bool     bp   = options().backprop;
01336         if (b == root) { return root; }
01337         assert(!b->eq() && !root->eq() && (hashEq || b->literal() == root->literal()));
01338         if (!b->simplifyHeads(*this, atomsAssigned) || (b->value() != root->value() && (!mergeValue(b, root) || !root->propagateValue(*this, bp) || !b->propagateValue(*this, bp)))) {
01339                 setConflict(); 
01340                 return 0; 
01341         }
01342         assert(b->value() == root->value());
01343         if (hashEq || positiveLoopSafe(b, root)) {
01344                 b->setLiteral(root->literal());
01345                 if (!root->mergeHeads(*this, *b, atomsAssigned, !hashEq)) {
01346                         setConflict(); 
01347                         return 0;
01348                 }
01349                 incEqs(Var_t::body_var);
01350                 b->setEq(rootId);
01351                 return root;
01352         }
01353         return b;
01354 }
01355 
01356 uint32 LogicProgram::findLpFalseAtom() const {
01357         for (VarVec::size_type i = 1; i < atoms_.size(); ++i) {
01358                 if (!atoms_[i]->eq() && atoms_[i]->value() == value_false) {
01359                         return i;
01360                 }
01361         }
01362         return 0;
01363 }
01364 
01365 const char* LogicProgram::getAtomName(Var id) const {
01366         if (const SymbolTable::symbol_type* x = ctx()->symbolTable().find(id)) {
01367                 return x->name.c_str();
01368         }
01369         return "";
01370 }
01371 VarVec& LogicProgram::getSupportedBodies(bool sorted) {
01372         if (sorted) {
01373                 std::stable_sort(initialSupp_.begin(), initialSupp_.end(), LessBodySize(bodies_));
01374         }
01375         return initialSupp_;
01376 }
01377 
01378 bool LogicProgram::transform(const PrgBody& body, BodyInfo& out) const {
01379         out.reset();
01380         out.lits.reserve(body.size());
01381         uint32 p = 0, end = body.size();
01382         while (p != end && !body.goal(p).sign()) { ++p; }
01383         uint32 R[2][2] = { {p, end}, {0, p} };
01384         weight_t sw = 0, st = 0;
01385         for (uint32 range = 0; range != 2; ++range) {
01386                 for (uint32 x = R[range][0]; x != R[range][1]; ++x) {
01387                         WeightLiteral wl(body.goal(x), body.weight(x));
01388                         if (getAtom(wl.first.var())->hasVar()) {
01389                                 sw += wl.second;
01390                                 out.lits.push_back(wl);
01391                         }
01392                         else if (wl.first.sign()) {
01393                                 st += wl.second;
01394                         }
01395                 }
01396         }
01397         out.init(body.type(), std::max(body.bound() - st, weight_t(0)), 0, p);
01398         return sw >= out.bound();
01399 }
01400 
01401 void LogicProgram::transform(const MinimizeRule& body, BodyInfo& out) const {
01402         out.reset();
01403         uint32 pos = 0;
01404         for (WeightLitVec::const_iterator it = body.lits_.begin(), end = body.lits_.end(); it != end; ++it) {
01405                 if (it->first.sign() && getAtom(it->first.var())->hasVar()) {
01406                         out.lits.push_back(*it);
01407                 }
01408         }
01409         for (WeightLitVec::const_iterator it = body.lits_.begin(), end = body.lits_.end(); it != end; ++it) {
01410                 if (!it->first.sign() && getAtom(it->first.var())->hasVar()) {
01411                         out.lits.push_back(*it);
01412                         ++pos;
01413                 }
01414         }
01415         out.init(BodyInfo::SUM_BODY, -1, 0, pos);
01416 }
01417 
01418 void LogicProgram::writeBody(const BodyInfo& body, std::ostream& out) const {
01419         if (body.type() == BodyInfo::SUM_BODY && body.bound() != -1) { out << body.bound() << " "; }
01420         out << body.size() << " ";
01421         out << (body.size() - body.posSize()) << " ";
01422         if (body.type() == BodyInfo::COUNT_BODY) { out << body.bound() << " "; }
01423         for (WeightLitVec::const_iterator it = body.lits.begin(), end = body.lits.end(); it != end; ++it) {
01424                 out << it->first.var() << " ";
01425         }
01426         if (body.type() == BodyInfo::SUM_BODY) {
01427                 for (WeightLitVec::const_iterator it = body.lits.begin(), end = body.lits.end(); it != end; ++it) {
01428                         out << it->second << " ";
01429                 }
01430         }
01431 }
01432 
01433 } } // end namespace Asp
01434 


clasp
Author(s): Benjamin Kaufmann
autogenerated on Thu Aug 27 2015 12:41:39