EnumerationConstraint* ModelEnumerator::doInit(SharedContext& ctx, SharedMinimizeData* opt, int numModels) { initProjection(ctx); uint32 st = strategy(); if (detectStrategy() || (ctx.concurrency() > 1 && !ModelEnumerator::supportsParallel())) { st = 0; } bool optOne = opt && opt->mode() == MinimizeMode_t::optimize; bool trivial = optOne || std::abs(numModels) == 1; if (optOne && projectionEnabled()) { for (const WeightLiteral* it = minimizer()->lits; !isSentinel(it->first) && trivial; ++it) { trivial = ctx.varInfo(it->first.var()).project(); } if (!trivial) { ctx.report(warning(Event::subsystem_prepare, "Projection: Optimization may depend on enumeration order.")); } } if (st == strategy_auto) { st = trivial || (projectionEnabled() && ctx.concurrency() > 1) ? strategy_record : strategy_backtrack; } if (trivial) { st |= trivial_flag; } options_ &= ~uint32(strategy_opts_mask); options_ |= st; const LitVec* dom = (projectOpts() & project_dom_lits) != 0 ? (ctx.heuristic.domRec = &domRec_) : 0; EnumerationConstraint* c = st == strategy_backtrack ? static_cast<ConPtr>(new BacktrackFinder(projectOpts())) : static_cast<ConPtr>(new RecordFinder(dom)); if (projectionEnabled()) { setIgnoreSymmetric(true); } return c; }
EnumerationConstraint* ModelEnumerator::doInit(SharedContext& ctx, MinimizeConstraint* min, int numModels) { delete queue_; queue_ = 0; initProjection(ctx); uint32 st = strategy(); if (detectStrategy() || (ctx.concurrency() > 1 && !ModelEnumerator::supportsParallel())) { st = 0; } bool optOne = minimizer() && minimizer()->mode() == MinimizeMode_t::optimize; bool trivial = optOne || std::abs(numModels) == 1; if (optOne && project_) { const SharedMinimizeData* min = minimizer(); for (const WeightLiteral* it = min->lits; !isSentinel(it->first) && trivial; ++it) { trivial = ctx.varInfo(it->first.var()).project(); } if (!trivial) { ctx.report(warning(Event::subsystem_prepare, "Projection: Optimization may depend on enumeration order.")); } } if (st == strategy_auto) { st = trivial || (project_ && ctx.concurrency() > 1) ? strategy_record : strategy_backtrack; } if (trivial) { st |= trivial_flag; } if (ctx.concurrency() > 1 && !trivial && st != strategy_backtrack) { queue_ = new SolutionQueue(ctx.concurrency()); queue_->reserve(ctx.concurrency() + 1); } options_ &= ~uint32(strategy_opts_mask); options_ |= st; Solver& s = *ctx.master(); EnumerationConstraint* c = st == strategy_backtrack ? static_cast<ConPtr>(new BacktrackFinder(s, min, project_, projectOpts())) : static_cast<ConPtr>(new RecordFinder(s, min, project_, queue_)); if (projectionEnabled()) { setIgnoreSymmetric(true); } return c; }
bool RecordEnumerator::doBacktrack(Solver& s, uint32 bl) { assert(bl >= s.rootLevel()); if (!projectionEnabled()) { addSolution(s); } bl = std::min(bl, assertionLevel(s)); if (s.backtrackLevel() > 0) { // must clear backtracking level; // not needed to guarantee redundancy-freeness // and may inadvertently bound undoUntil() s.setBacktrackLevel(0); } s.undoUntil(bl); if (solution_.empty()) { assert(minimize() && minimize()->mode() == MinimizeConstraint::compare_less); return true; } bool r = true; if (solution_.size() < 4) { r = solution_.end(); } else { Literal x; if (s.isFalse(solution_[solution_.secondWatch()])) { x = solution_[0]; } LearntConstraint* c = Clause::newLearntClause(s, solution_.lits(), Constraint_t::learnt_conflict, solution_.secondWatch()); nogoods_.push_back((Clause*)c); r = s.force(x, c); } return r || s.resolveConflict(); }
bool ModelEnumerator::backtrack(Solver& s) { uint32 bl = getHighestActiveLevel(); if (projectionEnabled()) { bl = std::min(bl, getProjectLevel(s)); } if (bl <= s.rootLevel()) { return false; } return doBacktrack(s, bl-1); }
uint32 BacktrackEnumerator::getHighestBacktrackLevel(const Solver& s, uint32 bl) const { if (!projectionEnabled() || (projectOpts_ & MINIMIZE_BACKJUMPING) == 0) { return s.backtrackLevel(); } uint32 res = s.backtrackLevel(); for (uint32 r = res+1; r <= bl; ++r) { if (!s.project(s.decision(r).var())) { return res; } ++res; } return res; }
void ModelEnumerator::initProjection(SharedContext& ctx) { if (!projectionEnabled()) { return; } const OutputTable& out = ctx.output; char const filter = static_cast<char>(options_ >> 24); // Make sure that nogoods are tagged with step literal. addProject(ctx, ctx.stepLiteral().var()); if (out.projectMode() == OutputTable::project_output) { // Mark all relevant output variables. for (OutputTable::pred_iterator it = out.pred_begin(), end = out.pred_end(); it != end; ++it) { if (*it->name != filter) { addProject(ctx, it->cond.var()); } } for (OutputTable::range_iterator it = out.vars_begin(), end = out.vars_end(); it != end; ++it) { for (Var v = it->lo; v != it->hi; ++v) { addProject(ctx, v); } } } else { // Mark explicitly requested variables only. for (OutputTable::lit_iterator it = out.proj_begin(), end = out.proj_end(); it != end; ++it) { addProject(ctx, it->var()); } } domRec_.clear(); }
bool ModelEnumerator::ignoreSymmetric() const { return projectionEnabled() || Enumerator::ignoreSymmetric(); }
bool BacktrackEnumerator::doBacktrack(Solver& s, uint32 bl) { // bl is the decision level on which search should proceed. // bl + 1 the minimum of: // a) the highest DL on which one of the projected vars was assigned // b) the highest DL on which one of the vars in a minimize statement was assigned // c) the current decision level assert(bl >= s.rootLevel()); ++bl; assert(bl <= s.decisionLevel()); uint32 btLevel = getHighestBacktrackLevel(s, bl); if (!projectionEnabled() || bl <= btLevel) { // If we do not project or one of the projection vars is already on the backtracking level // proceed with simple backtracking. while (!s.backtrack() || s.decisionLevel() >= bl) { if (s.decisionLevel() == s.rootLevel()) { return false; } } return true; } else if (numProjectionVars() == 1u) { Literal x = s.trueLit(projectVar(0)); s.undoUntil(0); s.force(~x, 0); // force the complement of x s.setBacktrackLevel(s.decisionLevel()); } else { // Store the current projected assignment as a nogood // and attach it to the current decision level. // Once the solver goes above that level, the nogood is automatically // destroyed. Hence, the number of projection nogoods is linear in the // number of (projection) atoms. if ( (projectOpts_ & ENABLE_PROGRESS_SAVING) != 0 ) { s.strategies().saveProgress = 1; } projAssign_.clear(); projAssign_.resize(numProjectionVars()); LitVec::size_type front = 0; LitVec::size_type back = numProjectionVars(); for (uint32 i = 0; i != numProjectionVars(); ++i) { Literal x = ~s.trueLit(projectVar(i)); // Note: complement because we store the nogood as a clause! if (s.level(x.var()) > btLevel) { projAssign_[front++] = x; } else { projAssign_[--back] = x; } } s.undoUntil( btLevel ); Literal x = projAssign_[0]; LearntConstraint* c; if (front == 1) { // The projection nogood is unit. Force the single remaining literal // from the current DL. ++back; // so that the active part of the nogood contains at least two literals c = Clause::newContractedClause(s, projAssign_, back-1, back); s.force(x, c); } else { // Shorten the projection nogood by assuming one of its literals... if ( (projectOpts_ & ENABLE_HEURISTIC_SELECT) != 0 ) { x = s.strategies().heuristic->selectRange(s, &projAssign_[0], &projAssign_[0]+back); } c = Clause::newContractedClause(s, projAssign_, back-1, back); // to false. s.assume(~x); } if (s.backtrackLevel() < s.decisionLevel()) { // Remember that we must backtrack the current decision // level in order to guarantee a different projected solution. s.setBacktrackLevel(s.decisionLevel()); } if (s.decisionLevel() != 0) { // Attach nogood to the current decision literal. // Once the solver goes above that level, the nogood (which is then satisfied) is destroyed. s.addUndoWatch(s.decisionLevel(), this); } nogoods_.push_back( NogoodPair(c, s.decisionLevel()) ); assert(s.backtrackLevel() == s.decisionLevel()); } return true; }