00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
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
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
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
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
00144 void doCommitModel(Enumerator& ctx, Solver& s);
00145 bool doUpdate(Solver& s);
00146
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])) {
00179 ok = s.backtrack();
00180 }
00181 else if (rep.size == 1 || s.isFalse(rep.lits[1])) {
00182 ok = s.force(rep.lits[0], this);
00183 }
00184 else if (!s.isTrue(rep.lits[0])) {
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
00191
00192 s.setBacktrackLevel(s.decisionLevel());
00193
00194
00195
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
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
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
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
00316 addProjectVar(ctx, ctx.stepLiteral().var(), false);
00317 if (project_->empty()) {
00318
00319
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 }