1 //
2 // Copyright (c) 2006-2017 Benjamin Kaufmann
3 //
4 // This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to
8 // deal in the Software without restriction, including without limitation the
9 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 // sell copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 // IN THE SOFTWARE.
23 //
24 #include <clasp/enumerator.h>
25 #include <clasp/solver.h>
26 #include <clasp/util/multi_queue.h>
27 #include <clasp/clause.h>
28 namespace Clasp {
29 /////////////////////////////////////////////////////////////////////////////////////////
30 // Enumerator - Shared Queue / Thread Queue
31 /////////////////////////////////////////////////////////////////////////////////////////
32 class Enumerator::SharedQueue : public mt::MultiQueue<SharedLiterals*, void (*)(SharedLiterals*)> {
33 public:
34 typedef mt::MultiQueue<SharedLiterals*, void (*)(SharedLiterals*)> BaseType;
SharedQueue(uint32 m)35 explicit SharedQueue(uint32 m) : BaseType(m, releaseLits) { reserve(m + 1); }
pushRelaxed(SharedLiterals * clause)36 bool pushRelaxed(SharedLiterals* clause) { unsafePublish(clause); return true; }
releaseLits(SharedLiterals * x)37 static void releaseLits(SharedLiterals* x){ x->release(); }
38 };
39 class Enumerator::ThreadQueue {
40 public:
ThreadQueue(SharedQueue & q)41 explicit ThreadQueue(SharedQueue& q) : queue_(&q) { tail_ = q.addThread(); }
pop(SharedLiterals * & out)42 bool pop(SharedLiterals*& out){ return queue_->tryConsume(tail_, out); }
clone()43 ThreadQueue* clone() { return new ThreadQueue(*queue_); }
44 private:
45 Enumerator::SharedQueue* queue_;
46 Enumerator::SharedQueue::ThreadId tail_;
47 };
48 /////////////////////////////////////////////////////////////////////////////////////////
49 // EnumerationConstraint
50 /////////////////////////////////////////////////////////////////////////////////////////
EnumerationConstraint()51 EnumerationConstraint::EnumerationConstraint() : mini_(0), root_(0), state_(0), upMode_(0), heuristic_(0), disjoint_(false) {
52 setDisjoint(false);
53 }
~EnumerationConstraint()54 EnumerationConstraint::~EnumerationConstraint() { }
init(Solver & s,SharedMinimizeData * m,QueuePtr p)55 void EnumerationConstraint::init(Solver& s, SharedMinimizeData* m, QueuePtr p) {
56 mini_ = 0;
57 queue_ = p;
58 upMode_ = value_false;
59 heuristic_ = 0;
60 if (m) {
61 OptParams opt = s.sharedContext()->configuration()->solver(s.id()).opt;
62 mini_ = m->attach(s, opt);
63 if (optimize()) {
64 if (opt.type != OptParams::type_bb) { upMode_ |= value_true; }
65 else { heuristic_ |= 1; }
66 }
67 if (opt.hasOption(OptParams::heu_sign)) {
68 for (const WeightLiteral* it = m->lits; !isSentinel(it->first); ++it) {
69 s.setPref(it->first.var(), ValueSet::pref_value, falseValue(it->first));
70 }
71 }
72 if (opt.hasOption(OptParams::heu_model)) { heuristic_ |= 2; }
73 }
74 }
valid(Solver & s)75 bool EnumerationConstraint::valid(Solver& s) { return !optimize() || mini_->valid(s); }
add(Constraint * c)76 void EnumerationConstraint::add(Constraint* c) { if (c) { nogoods_.push_back(c); } }
integrateBound(Solver & s)77 bool EnumerationConstraint::integrateBound(Solver& s){ return !mini_ || mini_->integrate(s); }
optimize() const78 bool EnumerationConstraint::optimize() const { return mini_ && mini_->shared()->optimize(); }
setDisjoint(bool x)79 void EnumerationConstraint::setDisjoint(bool x) { disjoint_ = x; }
cloneAttach(Solver & s)80 Constraint* EnumerationConstraint::cloneAttach(Solver& s) {
81 EnumerationConstraint* c = clone();
82 POTASSCO_REQUIRE(c != 0, "Cloning not supported by Enumerator");
83 c->init(s, mini_ ? const_cast<SharedMinimizeData*>(mini_->shared()) : 0, queue_.get() ? queue_->clone() : 0);
84 return c;
85 }
end(Solver & s)86 void EnumerationConstraint::end(Solver& s) {
87 if (mini_) { mini_->relax(s, disjointPath()); }
88 state_ = 0;
89 setDisjoint(false);
90 next_.clear();
91 if (s.rootLevel() > root_) { s.popRootLevel(s.rootLevel() - root_); }
92 }
start(Solver & s,const LitVec & path,bool disjoint)93 bool EnumerationConstraint::start(Solver& s, const LitVec& path, bool disjoint) {
94 state_ = 0;
95 root_ = s.rootLevel();
96 setDisjoint(disjoint);
97 if (s.pushRoot(s.sharedContext()->stepLiteral()) && s.pushRoot(path)) {
98 integrateBound(s);
99 integrateNogoods(s);
100 return true;
101 }
102 return false;
103 }
update(Solver & s)104 bool EnumerationConstraint::update(Solver& s) {
105 ValueRep st = state();
106 if (st == value_true) {
107 if (s.restartOnModel()) { s.undoUntil(0); }
108 if (optimize()) { s.strengthenConditional(); }
109 }
110 else if (st == value_false && !s.pushRoot(next_)) {
111 if (!s.hasConflict()) { s.setStopConflict(); }
112 return false;
113 }
114 state_ = 0;
115 next_.clear();
116 do {
117 if (!s.hasConflict() && doUpdate(s) && integrateBound(s) && integrateNogoods(s)) {
118 if (st == value_true) { modelHeuristic(s); }
119 return true;
120 }
121 } while (st != value_free && s.hasConflict() && s.resolveConflict());
122 return false;
123 }
integrateNogoods(Solver & s)124 bool EnumerationConstraint::integrateNogoods(Solver& s) {
125 if (!queue_.get() || s.hasConflict()) { return !s.hasConflict(); }
126 const uint32 f = ClauseCreator::clause_no_add | ClauseCreator::clause_no_release | ClauseCreator::clause_explicit;
127 for (SharedLiterals* clause; queue_->pop(clause); ) {
128 ClauseCreator::Result res = ClauseCreator::integrate(s, clause, f);
129 if (res.local) { add(res.local);}
130 if (!res.ok()) { return false; }
131 }
132 return true;
133 }
destroy(Solver * s,bool x)134 void EnumerationConstraint::destroy(Solver* s, bool x) {
135 if (mini_) { mini_->destroy(s, x); mini_ = 0; }
136 queue_ = 0;
137 Clasp::destroyDB(nogoods_, s, x);
138 Constraint::destroy(s, x);
139 }
simplify(Solver & s,bool reinit)140 bool EnumerationConstraint::simplify(Solver& s, bool reinit) {
141 if (mini_) { mini_->simplify(s, reinit); }
142 simplifyDB(s, nogoods_, reinit);
143 return false;
144 }
145
commitModel(Enumerator & ctx,Solver & s)146 bool EnumerationConstraint::commitModel(Enumerator& ctx, Solver& s) {
147 if (state() == value_true) { return !next_.empty() && (s.satPrepro()->extendModel(s.model, next_), true); }
148 if (mini_ && !mini_->handleModel(s)){ return false; }
149 if (!ctx.tentative()) { doCommitModel(ctx, s); }
150 next_ = s.symmetric();
151 state_ |= value_true;
152 return true;
153 }
commitUnsat(Enumerator & ctx,Solver & s)154 bool EnumerationConstraint::commitUnsat(Enumerator& ctx, Solver& s) {
155 next_.clear();
156 state_ |= value_false;
157 if (mini_) {
158 mini_->handleUnsat(s, !disjointPath(), next_);
159 }
160 if (!ctx.tentative()) {
161 doCommitUnsat(ctx, s);
162 }
163 return !s.hasConflict() || s.decisionLevel() != s.rootLevel();
164 }
modelHeuristic(Solver & s)165 void EnumerationConstraint::modelHeuristic(Solver& s) {
166 const bool full = heuristic_ > 1;
167 const bool heuristic = full || (heuristic_ == 1 && s.queueSize() == 0 && s.decisionLevel() == s.rootLevel());
168 if (optimize() && heuristic && s.propagate()) {
169 for (const WeightLiteral* w = mini_->shared()->lits; !isSentinel(w->first); ++w) {
170 if (s.value(w->first.var()) == value_free) {
171 s.assume(~w->first);
172 if (!full || !s.propagate()) { break; }
173 }
174 }
175 }
176 }
177 /////////////////////////////////////////////////////////////////////////////////////////
178 // Enumerator
179 /////////////////////////////////////////////////////////////////////////////////////////
reset()180 void Model::reset() { std::memset(this, 0, sizeof(Model)); }
Enumerator()181 Enumerator::Enumerator() : mini_(0), queue_(0) { model_.reset(); }
~Enumerator()182 Enumerator::~Enumerator() { delete queue_; }
setDisjoint(Solver & s,bool b) const183 void Enumerator::setDisjoint(Solver& s, bool b)const { constraintRef(s).setDisjoint(b); }
setIgnoreSymmetric(bool b)184 void Enumerator::setIgnoreSymmetric(bool b) { model_.sym = static_cast<uint32>(b == false); }
end(Solver & s) const185 void Enumerator::end(Solver& s) const { constraintRef(s).end(s); }
doReset()186 void Enumerator::doReset() {}
reset()187 void Enumerator::reset() {
188 if (mini_) { mini_ = 0; }
189 if (queue_){ delete queue_; queue_ = 0; }
190 model_.reset();
191 model_.ctx = this;
192 model_.sym = 1;
193 model_.type = uint32(modelType());
194 model_.sId = 0;
195 doReset();
196 }
init(SharedContext & ctx,OptMode oMode,int limit)197 int Enumerator::init(SharedContext& ctx, OptMode oMode, int limit) {
198 ctx.master()->setEnumerationConstraint(0);
199 reset();
200 if (oMode != MinimizeMode_t::ignore){ mini_ = ctx.minimize(); }
201 limit = limit >= 0 ? limit : 1 - int(exhaustive());
202 if (limit != 1) { ctx.setPreserveModels(true); }
203 queue_ = new SharedQueue(ctx.concurrency());
204 ConPtr c = doInit(ctx, mini_, limit);
205 bool cons = model_.consequences();
206 if (tentative()) { model_.type = Model::Sat; }
207 else if (cons && optimize()) { ctx.warn("Optimization: Consequences may depend on enumeration order."); }
208 c->init(*ctx.master(), mini_, new ThreadQueue(*queue_));
209 ctx.master()->setEnumerationConstraint(c);
210 return limit;
211 }
constraintRef(const Solver & s) const212 Enumerator::ConRef Enumerator::constraintRef(const Solver& s) const {
213 POTASSCO_ASSERT(s.enumerationConstraint(), "Solver not attached");
214 return static_cast<ConRef>(*s.enumerationConstraint());
215 }
constraint(const Solver & s) const216 Enumerator::ConPtr Enumerator::constraint(const Solver& s) const {
217 return static_cast<ConPtr>(s.enumerationConstraint());
218 }
start(Solver & s,const LitVec & path,bool disjointPath) const219 bool Enumerator::start(Solver& s, const LitVec& path, bool disjointPath) const {
220 return constraintRef(s).start(s, path, disjointPath);
221 }
commit(Solver & s)222 ValueRep Enumerator::commit(Solver& s) {
223 if (s.hasConflict() && s.decisionLevel() == s.rootLevel()) { return commitUnsat(s) ? value_free : value_false; }
224 else if (s.numFreeVars() == 0 && s.queueSize() == 0 && !s.hasConflict()){ return commitModel(s) ? value_true : value_free; }
225 return value_free;
226 }
commitModel(Solver & s)227 bool Enumerator::commitModel(Solver& s) {
228 assert(s.numFreeVars() == 0 && !s.hasConflict() && s.queueSize() == 0);
229 if (constraintRef(s).commitModel(*this, s)) {
230 s.stats.addModel(s.decisionLevel());
231 ++model_.num;
232 model_.sId = s.id();
233 model_.values = &s.model;
234 model_.costs = 0;
235 model_.up = 0;
236 if (minimizer()) {
237 costs_.resize(minimizer()->numRules());
238 std::transform(minimizer()->adjust(), minimizer()->adjust()+costs_.size(), minimizer()->sum(), costs_.begin(), std::plus<wsum_t>());
239 model_.costs = &costs_;
240 }
241 return true;
242 }
243 return false;
244 }
commitSymmetric(Solver & s)245 bool Enumerator::commitSymmetric(Solver& s){ return model_.sym && !optimize() && commitModel(s); }
commitUnsat(Solver & s)246 bool Enumerator::commitUnsat(Solver& s) { return constraintRef(s).commitUnsat(*this, s); }
commitClause(const LitVec & clause) const247 bool Enumerator::commitClause(const LitVec& clause) const {
248 return queue_ && queue_->pushRelaxed(SharedLiterals::newShareable(clause, Constraint_t::Other));
249 }
commitComplete()250 bool Enumerator::commitComplete() {
251 if (enumerated()) {
252 if (tentative()) {
253 mini_->markOptimal();
254 model_.opt = 1;
255 model_.num = 0;
256 model_.type= uint32(modelType());
257 return false;
258 }
259 else if (model_.consequences() || (!model_.opt && optimize())) {
260 model_.opt = uint32(optimize());
261 model_.def = uint32(model_.consequences());
262 model_.num = 1;
263 }
264 }
265 return true;
266 }
update(Solver & s) const267 bool Enumerator::update(Solver& s) const {
268 return constraintRef(s).update(s);
269 }
supportsSplitting(const SharedContext & ctx) const270 bool Enumerator::supportsSplitting(const SharedContext& ctx) const {
271 if (!optimize()) { return true; }
272 const Configuration* config = ctx.configuration();
273 bool ok = true;
274 for (uint32 i = 0; i != ctx.concurrency() && ok; ++i) {
275 if (ctx.hasSolver(i) && constraint(*ctx.solver(i))){ ok = constraint(*ctx.solver(i))->minimizer()->supportsSplitting(); }
276 else if (config && i < config->numSolver()) { ok = config->solver(i).opt.supportsSplitting(); }
277 }
278 return ok;
279 }
unsatType() const280 int Enumerator::unsatType() const {
281 return !optimize() ? unsat_stop : unsat_cont;
282 }
model()283 Model& Enumerator::model() {
284 return model_;
285 }
286 /////////////////////////////////////////////////////////////////////////////////////////
287 // EnumOptions
288 /////////////////////////////////////////////////////////////////////////////////////////
createEnumerator(const EnumOptions & opts)289 Enumerator* EnumOptions::createEnumerator(const EnumOptions& opts) {
290 if (opts.models()) { return createModelEnumerator(opts);}
291 else if (opts.consequences()){ return createConsEnumerator(opts); }
292 else { return nullEnumerator(); }
293 }
nullEnumerator()294 Enumerator* EnumOptions::nullEnumerator() {
295 struct NullEnum : Enumerator {
296 ConPtr doInit(SharedContext&, SharedMinimizeData*, int) {
297 struct Constraint : public EnumerationConstraint {
298 Constraint() : EnumerationConstraint() {}
299 ConPtr clone() { return new Constraint(); }
300 bool doUpdate(Solver&){ return true; }
301 };
302 return new Constraint();
303 }
304 };
305 return new NullEnum;
306 }
307
modelType(const Model & m)308 const char* modelType(const Model& m) {
309 switch (m.type) {
310 case Model::Sat : return "Model";
311 case Model::Brave : return "Brave";
312 case Model::Cautious: return "Cautious";
313 case Model::User : return "User";
314 default: return 0;
315 }
316 }
317
318 }
319