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