model_enumerators.cpp
Go to the documentation of this file.
00001 // 
00002 // Copyright (c) 2006-2011, 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 
00021 #include <clasp/model_enumerators.h>
00022 #include <clasp/solver.h>
00023 #include <clasp/minimize_constraint.h>
00024 #include <clasp/util/multi_queue.h>
00025 #include <algorithm>
00026 #include <cstdlib>
00027 namespace Clasp {
00028 class ModelEnumerator::ModelFinder : public EnumerationConstraint {
00029 protected:
00030         ModelFinder(Solver& s, MinimizeConstraint* min, VarVec* p) : EnumerationConstraint(s,min), project(p) {}
00031         void destroy(Solver* s, bool detach) {
00032                 while (!nogoods.empty()) {
00033                         if (nogoods.back()) { nogoods.back()->destroy(s, detach); }
00034                         nogoods.pop_back();
00035                 }
00036                 if (project && s && s->sharedContext()->master() == s) {
00037                         SharedContext& problem = const_cast<SharedContext&>(*s->sharedContext());
00038                         while (!project->empty()) {
00039                                 problem.setProject(project->back(), false);
00040                                 project->pop_back();
00041                         }
00042                 }
00043                 EnumerationConstraint::destroy(s, detach);
00044         }
00045         bool simplify(Solver& s, bool) { EnumerationConstraint::simplify(s, false); simplifyDB(s, nogoods, false); return false; }
00046         typedef Solver::ConstraintDB ConstraintDB;
00047         VarVec*      project;
00048         ConstraintDB nogoods;
00049 };
00051 // strategy_record
00053 class ModelEnumerator::SolutionQueue : public mt::MultiQueue<SharedLiterals*, void (*)(SharedLiterals*)> {
00054 public:
00055         typedef SharedLiterals SL;
00056         typedef mt::MultiQueue<SL*, void (*)(SL*)> base_type;
00057         SolutionQueue(uint32 m) : base_type(m, releaseLits) {}
00058         void addSolution(SL* solution, const ThreadId& id) {
00059                 publish(solution, id);
00060         }
00061         static void releaseLits(SL* x) { x->release(); }
00062 };
00063 
00064 class ModelEnumerator::RecordFinder : public ModelFinder {
00065 public:
00066         typedef ModelEnumerator::QPtr   QPtr;
00067         typedef SolutionQueue::ThreadId ThreadId;
00068         typedef SolutionQueue::SL       SL;
00069         typedef ClauseCreator::Result   Result;
00070         RecordFinder(Solver& s, MinimizeConstraint* min, VarVec* project, SolutionQueue* q) : ModelFinder(s, min, project), queue(q) {
00071                 if (q) { id = q->addThread(); }
00072         }
00073         Constraint* cloneAttach(Solver& s) { return new RecordFinder(s, cloneMinimizer(s), project, queue); }
00074         void doCommitModel(Enumerator& ctx, Solver& s);
00075         bool doUpdate(Solver& s);
00076         QPtr     queue;
00077         ThreadId id;
00078         LitVec   solution;
00079 };
00080 
00081 bool ModelEnumerator::RecordFinder::doUpdate(Solver& s) {
00082         if (queue) {
00083                 uint32 f = ClauseCreator::clause_no_add | ClauseCreator::clause_no_release | ClauseCreator::clause_explicit;
00084                 for (SL* clause; queue->tryConsume(id, clause); ) {
00085                         ClauseCreator::Result res = ClauseCreator::integrate(s, clause, f);     
00086                         if (res.local) { nogoods.push_back(res.local); }
00087                         if (!res.ok()) { return false; }
00088                 }
00089         }
00090         else if (!solution.empty()) {
00091                 ClauseInfo e(Constraint_t::learnt_other);
00092                 ClauseCreator::Result ret = ClauseCreator::create(s, solution, ClauseCreator::clause_no_add, e);
00093                 solution.clear();
00094                 if (ret.local) { nogoods.push_back(ret.local); }
00095                 return ret.ok();
00096         }
00097         return true;
00098 }
00099 
00100 void ModelEnumerator::RecordFinder::doCommitModel(Enumerator& x, Solver& s) {
00101         ModelEnumerator& ctx = static_cast<ModelEnumerator&>(x);
00102         if (ctx.trivial()) { return; }
00103         assert(solution.empty() && "Update not called!");
00104         solution.clear();
00105         if (!ctx.projectionEnabled()) {
00106                 for (uint32 x = s.decisionLevel(); x != 0; --x) {
00107                         Literal d = s.decision(x);
00108                         if      (!s.auxVar(d.var()))  { solution.push_back(~d); }
00109                         else if (d != s.tagLiteral()) {
00110                                 // Todo: set of vars could be reduced to those having the aux var in their reason set.
00111                                 const LitVec& tr = s.trail();
00112                                 const uint32  end= x != s.decisionLevel() ? s.levelStart(x+1) : (uint32)tr.size();
00113                                 for (uint32 n = s.levelStart(x)+1; n != end; ++n) {
00114                                         if (!s.auxVar(tr[n].var())) { solution.push_back(~tr[n]); }
00115                                 }
00116                         }
00117                 }
00118         }
00119         else {
00120                 for (uint32 i = 0, end = ctx.numProjectionVars(); i != end; ++i) {
00121                         solution.push_back(~s.trueLit(ctx.projectVar(i)));
00122                 }
00123         }
00124         if (queue) {
00125                 assert(!queue->hasItems(id));
00126                 // parallel solving active - share solution nogood with other solvers
00127                 SL* shared = SL::newShareable(solution, Constraint_t::learnt_other);
00128                 queue->addSolution(shared, id);
00129                 solution.clear();
00130         }
00131         else if (solution.empty()) { 
00132                 solution.push_back(negLit(0)); 
00133         }
00134 }
00136 // strategy_backtrack
00138 class ModelEnumerator::BacktrackFinder : public ModelFinder {
00139 public:
00140         BacktrackFinder(Solver& s, MinimizeConstraint* min, VarVec* project, uint32 projOpts) : ModelFinder(s, min, project), opts(projOpts) {}
00141         bool hasModel() const { return !solution.empty(); }
00142         
00143         // EnumerationConstraint interface
00144         void doCommitModel(Enumerator& ctx, Solver& s);
00145         bool doUpdate(Solver& s);
00146         // Constraint interface
00147         Constraint* cloneAttach(Solver& s){ return new BacktrackFinder(s, cloneMinimizer(s), project, opts); }
00148         PropResult  propagate(Solver&, Literal, uint32&);
00149         void        reason(Solver& s, Literal p, LitVec& x){
00150                 for (uint32 i = 1, end = s.level(p.var()); i <= end; ++i) { 
00151                         x.push_back(s.decision(i)); 
00152                 }
00153         }
00154         LitVec solution;
00155         uint32 opts;
00156 };
00157 
00158 Constraint::PropResult ModelEnumerator::BacktrackFinder::propagate(Solver& s, Literal, uint32& pos) {
00159         assert(pos < nogoods.size() && nogoods[pos] != 0);
00160         ClauseHead* c = static_cast<ClauseHead*>(nogoods[pos]);
00161         if (!c->locked(s)) {
00162                 c->destroy(&s, true);
00163                 nogoods[pos] = (c = 0);
00164                 while (!nogoods.empty() && !nogoods.back()) {
00165                         nogoods.pop_back();
00166                 }
00167         }
00168         return PropResult(true, c != 0);
00169 }
00170 bool ModelEnumerator::BacktrackFinder::doUpdate(Solver& s) {
00171         if (hasModel()) {
00172                 bool   ok = true;
00173                 uint32 sp = s.strategy.saveProgress;
00174                 if ((opts & ModelEnumerator::project_save_progress) != 0 ) { s.strategy.saveProgress = 1; }
00175                 s.undoUntil(s.backtrackLevel());
00176                 s.strategy.saveProgress = sp;
00177                 ClauseRep rep = ClauseCreator::prepare(s, solution, 0, Constraint_t::learnt_conflict);
00178                 if (rep.size == 0 || s.isFalse(rep.lits[0])) { // The decision stack is already ordered.
00179                         ok = s.backtrack();
00180                 }
00181                 else if (rep.size == 1 || s.isFalse(rep.lits[1])) { // The projection nogood is unit. Force the single remaining literal from the current DL.
00182                         ok = s.force(rep.lits[0], this);
00183                 }
00184                 else if (!s.isTrue(rep.lits[0])) { // Shorten the projection nogood by assuming one of its literals to false.
00185                         uint32  f = static_cast<uint32>(std::stable_partition(rep.lits+2, rep.lits+rep.size, std::not1(std::bind1st(std::mem_fun(&Solver::isFalse), &s))) - rep.lits);
00186                         Literal x = (opts & ModelEnumerator::project_use_heuristic) != 0 ? s.heuristic()->selectRange(s, rep.lits, rep.lits+f) : rep.lits[0];
00187                         LearntConstraint* c = Clause::newContractedClause(s, rep, f, true);
00188                         CLASP_FAIL_IF(!c, "Invalid constraint!");
00189                         s.assume(~x);
00190                         // Remember that we must backtrack the current decision
00191                         // level in order to guarantee a different projected solution.
00192                         s.setBacktrackLevel(s.decisionLevel());
00193                         // Attach nogood to the current decision literal. 
00194                         // Once we backtrack to x, the then obsolete nogood is destroyed 
00195                         // keeping the number of projection nogoods linear in the number of (projection) atoms.
00196                         s.addWatch(x, this, (uint32)nogoods.size());
00197                         nogoods.push_back(c);
00198                         ok = true;
00199                 }
00200                 solution.clear();
00201                 return ok;
00202         }
00203         if (optimize() || s.sharedContext()->concurrency() == 1 || disjointPath()) {
00204                 return true;
00205         }
00206         s.setStopConflict();
00207         return false;
00208 }
00209 
00210 void ModelEnumerator::BacktrackFinder::doCommitModel(Enumerator& ctx, Solver& s) {
00211         ModelEnumerator& en = static_cast<ModelEnumerator&>(ctx);
00212         uint32           dl = s.decisionLevel();
00213         solution.assign(1, dl ? ~s.decision(dl) : negLit(0));
00214         if (en.projectionEnabled()) {
00215                 // Remember the current projected assignment as a nogood.
00216                 solution.clear();
00217                 for (uint32 i = 0, end = en.numProjectionVars(); i != end; ++i) {
00218                         solution.push_back(~s.trueLit(en.projectVar(i)));
00219                 }
00220                 // Remember initial decisions that are projection vars.
00221                 for (dl = s.backtrackLevel(); dl < s.decisionLevel(); ++dl) {
00222                         if (!s.varInfo(s.decision(dl+1).var()).project()) { break; }
00223                 }
00224         }
00225         s.setBacktrackLevel(dl);
00226 }
00228 // class ModelEnumerator
00230 ModelEnumerator::ModelEnumerator(Strategy st)
00231         : Enumerator()
00232         , queue_(0)
00233         , project_(0)
00234         , options_(st) {
00235 }
00236 
00237 Enumerator* EnumOptions::createModelEnumerator(const EnumOptions& opts) {
00238         ModelEnumerator*          e = new ModelEnumerator();
00239         ModelEnumerator::Strategy s = ModelEnumerator::strategy_auto;
00240         if (opts.type > (int)ModelEnumerator::strategy_auto && opts.type <= (int)ModelEnumerator::strategy_record) {
00241                 s = static_cast<ModelEnumerator::Strategy>(opts.type);
00242         }
00243         e->setStrategy(s, opts.project);
00244         return e;
00245 }
00246 
00247 ModelEnumerator::~ModelEnumerator() {
00248         delete project_;
00249         delete queue_;
00250 }
00251 
00252 void ModelEnumerator::setStrategy(Strategy st, uint32 projection) {
00253         delete project_;
00254         options_ = st;
00255         project_ = 0;
00256         if (projection) { 
00257                 options_ |= (((projection|1u) & 7u) << 4u);
00258                 project_  = new VarVec();
00259         }
00260         if (st == strategy_auto) {
00261                 options_ |= detect_strategy_flag;
00262         }
00263 }
00264 
00265 EnumerationConstraint* ModelEnumerator::doInit(SharedContext& ctx, MinimizeConstraint* min, int numModels) {
00266         delete queue_;
00267         queue_ = 0;
00268         initProjection(ctx); 
00269         uint32 st = strategy();
00270         if (detectStrategy() || (ctx.concurrency() > 1 && !ModelEnumerator::supportsParallel())) {
00271                 st = 0;
00272         }
00273         bool optOne  = minimizer() && minimizer()->mode() == MinimizeMode_t::optimize;
00274         bool trivial = optOne || std::abs(numModels) == 1;
00275         if (optOne && project_) {
00276                 const SharedMinimizeData* min = minimizer();
00277                 for (const WeightLiteral* it = min->lits; !isSentinel(it->first) && trivial; ++it) {
00278                         trivial = ctx.varInfo(it->first.var()).project();
00279                 }
00280                 if (!trivial) { ctx.report(warning(Event::subsystem_prepare, "Projection: Optimization may depend on enumeration order.")); }
00281         }
00282         if (st == strategy_auto) { st  = trivial || (project_ && ctx.concurrency() > 1) ? strategy_record : strategy_backtrack; }
00283         if (trivial)             { st |= trivial_flag; }
00284         if (ctx.concurrency() > 1 && !trivial && st != strategy_backtrack) {
00285                 queue_ = new SolutionQueue(ctx.concurrency()); 
00286                 queue_->reserve(ctx.concurrency() + 1);
00287         }
00288         options_ &= ~uint32(strategy_opts_mask);
00289         options_ |= st;
00290         Solver& s = *ctx.master();
00291         EnumerationConstraint* c = st == strategy_backtrack 
00292           ? static_cast<ConPtr>(new BacktrackFinder(s, min, project_, projectOpts()))
00293           : static_cast<ConPtr>(new RecordFinder(s, min, project_, queue_));
00294         if (projectionEnabled()) { setIgnoreSymmetric(true); }
00295         return c;
00296 }
00297 
00298 void ModelEnumerator::initProjection(SharedContext& ctx) {
00299         if (!project_) { return; }
00300         project_->clear();
00301         const SymbolTable& index = ctx.symbolTable();
00302         if (index.type() == SymbolTable::map_indirect) {
00303                 for (SymbolTable::const_iterator it = index.begin(); it != index.end(); ++it) {
00304                         if (!it->second.name.empty() && it->second.name[0] != '_') {
00305                                 addProjectVar(ctx, it->second.lit.var(), true);
00306                         }
00307                 }
00308                 for (VarVec::const_iterator it = project_->begin(), end = project_->end(); it != end; ++it) {
00309                         ctx.unmark(*it);
00310                 }
00311         }
00312         else {
00313                 for (Var v = 1; v < index.size(); ++v) { addProjectVar(ctx, v, false); }
00314         }
00315         // tag projection nogoods with step literal (if any).
00316         addProjectVar(ctx, ctx.stepLiteral().var(), false);
00317         if (project_->empty()) { 
00318                 // We project to the empty set. Add true-var so that 
00319                 // we can distinguish this case from unprojected search
00320                 project_->push_back(0);
00321         }
00322 }
00323 
00324 void ModelEnumerator::addProjectVar(SharedContext& ctx, Var v, bool tag) {
00325         if (ctx.master()->value(v) == value_free && (!tag || !ctx.marked(posLit(v)))) {
00326                 project_->push_back(v);
00327                 ctx.setFrozen(v, true);
00328                 ctx.setProject(v, true);
00329                 if (tag) { ctx.mark(posLit(v)); ctx.mark(negLit(v)); }
00330         }
00331 }
00332 
00333 }


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