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/cb_enumerator.h>
25 #include <clasp/solver.h>
26 #include <clasp/clause.h>
27 #if CLASP_HAS_THREADS
28 #include <clasp/mt/thread.h>
29 #define ACQUIRE_LOCK(m) while ( (m).exchange(1) != 0 ) Clasp::mt::this_thread::yield()
30 #define RELEASE_LOCK(m) (m) = 0
31 #else
32 #define ACQUIRE_LOCK(m)
33 #define RELEASE_LOCK(m)
34 #endif
35 namespace Clasp {
36 /////////////////////////////////////////////////////////////////////////////////////////
37 // CBConsequences::SharedConstraint
38 /////////////////////////////////////////////////////////////////////////////////////////
39 class CBConsequences::SharedConstraint {
40 public:
SharedConstraint()41 	SharedConstraint() : current(0) { mutex = 0; }
fetch_if_neq(SharedLiterals * last) const42 	SharedLiterals* fetch_if_neq(SharedLiterals* last) const {
43 		ACQUIRE_LOCK(mutex);
44 		SharedLiterals* ret = last != current ? current->share() : 0;
45 		RELEASE_LOCK(mutex);
46 		return ret;
47 	}
release(SharedLiterals * newLits)48 	void release(SharedLiterals* newLits) {
49 		ACQUIRE_LOCK(mutex);
50 		SharedLiterals* old = current;
51 		current = newLits;
52 		RELEASE_LOCK(mutex);
53 		if (old) { old->release(); }
54 	}
55 	SharedLiterals* current;
56 	typedef Clasp::Atomic_t<int>::type MutexType;
57 	mutable MutexType mutex;
58 };
59 #undef ACQUIRE_LOCK
60 #undef RELEASE_LOCK
61 /////////////////////////////////////////////////////////////////////////////////////////
62 // CBConsequences::CBFinder
63 /////////////////////////////////////////////////////////////////////////////////////////
64 class CBConsequences::CBFinder : public EnumerationConstraint {
65 public:
66 	typedef CBConsequences::SharedConstraint  SharedCon;
67 	typedef Solver::ConstraintDB              ConstraintDB;
68 	typedef SharedLiterals                    SharedLits;
CBFinder(SharedCon * sh)69 	explicit CBFinder(SharedCon* sh) : EnumerationConstraint(), shared(sh), last(0) {}
clone()70 	ConPtr clone() { return new CBFinder(shared); }
doCommitModel(Enumerator & ctx,Solver & s)71 	void   doCommitModel(Enumerator& ctx, Solver& s) { static_cast<CBConsequences&>(ctx).addCurrent(s, current, s.model, rootLevel()); }
72 	void   destroy(Solver* s, bool detach);
73 	bool   doUpdate(Solver& s);
74 	void   pushLocked(Solver& s, ClauseHead* h);
75 	LitVec       current;
76 	SharedCon*   shared;
77 	SharedLits*  last;
78 	ConstraintDB locked;
79 };
80 /////////////////////////////////////////////////////////////////////////////////////////
81 // CBConsequences::QueryFinder
82 /////////////////////////////////////////////////////////////////////////////////////////
83 class CBConsequences::QueryFinder : public EnumerationConstraint{
84 public:
85 	class State {
86 	public:
State(Model & m,uint32 nVars)87 		State(Model& m, uint32 nVars) : model_(&m) {
88 			refs_  = 1;
89 			size_  = nVars;
90 			value_ = new ValueType[nVars];
91 			for (uint32 i = 0; i != nVars; ++i) { value_[i] = 0; }
92 		}
share()93 		State* share()    { ++refs_; return this; }
release()94 		void   release()  { if (--refs_ == 0) delete this; }
size() const95 		uint32 size()          const { return size_; }
open(Literal p) const96 		bool   open(Literal p) const { return (value_[p.var()] & Model::estMask(p)) != 0; }
setModel(Clasp::ValueVec & m,bool update)97 		void   setModel(Clasp::ValueVec& m, bool update) {
98 			m.assign(value_, value_ + size_);
99 			if (update) { model_->values = &m; model_->up = 1; }
100 		}
push(Literal p)101 		void   push(Literal p) { value_[p.var()] = Model::estMask(p)|trueValue(p);}
pop(Literal p)102 		void   pop(Literal p)  { value_[p.var()] = 0; }
fix(Literal p)103 		void   fix(Literal p)  { value_[p.var()] = trueValue(p); }
104 	private:
~State()105 		~State() { delete [] value_; }
106 		typedef Clasp::Atomic_t<uint8>::type  ValueType;
107 		typedef Clasp::Atomic_t<uint32>::type SizeType;
108 		typedef ValueType* ValueVec;
109 		ValueVec value_;
110 		Model*   model_;
111 		uint32   size_;
112 		SizeType refs_;
113 	};
QueryFinder(const LitVec & c,Model & m,uint32 nVars)114 	explicit QueryFinder(const LitVec& c, Model& m, uint32 nVars) : EnumerationConstraint(), open_(c), state_(new State(m, nVars)), query_(lit_false()), level_(0), dirty_(false) {
115 		state_->push(query_);
116 	}
QueryFinder(const LitVec & c,State * st)117 	explicit QueryFinder(const LitVec& c, State* st) : EnumerationConstraint(), open_(c), state_(st), query_(lit_false()), level_(0), dirty_(false) {
118 	}
~QueryFinder()119 	~QueryFinder() { state_->release(); }
clone()120 	ConPtr clone() { return new QueryFinder(open_, state_->share()); }
hasQuery() const121 	bool    hasQuery() const { return query_ != lit_false(); }
122 	bool    doUpdate(Solver& s);
123 	void    doCommitModel(Enumerator&, Solver&);
124 	void    doCommitUnsat(Enumerator&, Solver&);
125 	void    updateUpper(Solver& s, uint32 rl, ValueVec& mOut);
126 	void    updateLower(Solver& s, uint32 rl, ValueVec& mOut);
127 	bool    selectOpen(Solver& s, Literal& q);
reason(Solver & s,Literal p,LitVec & out)128 	void    reason(Solver& s, Literal p, LitVec& out) {
129 		for (uint32 i = 1, end = s.level(p.var()); i <= end; ++i) {
130 			Literal q = s.decision(i);
131 			if (q != p) { out.push_back(q); }
132 		}
133 	}
popQuery(Solver & s)134 	bool    popQuery(Solver& s) {
135 		if (!hasQuery() || s.rootLevel() == level_ || s.value(query_.var()) == value_free) {
136 			return s.popRootLevel(0);
137 		}
138 		return s.popRootLevel((s.rootLevel() - level_) + 1);
139 	}
140 	LitVec  open_;
141 	State*  state_;
142 	Literal query_;
143 	uint32  level_;
144 	bool    dirty_;
145 };
146 // Reduce the overestimate by computing c = c \cap M,
147 // where M is the current model stored in s.
updateUpper(Solver & s,uint32 root,ValueVec & mOut)148 void CBConsequences::QueryFinder::updateUpper(Solver& s, uint32 root, ValueVec& mOut) {
149 	LitVec::iterator j = open_.begin();
150 	for (LitVec::iterator it = j, end = open_.end(); it != end; ++it) {
151 		if      (!state_->open(*it))        { continue; }
152 		else if (!s.isTrue(*it))            { state_->pop(*it); }
153 		else if (s.level(it->var()) > root) { *j++ = *it; }
154 		else                                { state_->fix(*it); }
155 	}
156 	open_.erase(j, open_.end());
157 	state_->setModel(mOut, dirty_ = false);
158 }
159 // Adds facts to (under) estimate.
updateLower(Solver & s,uint32 rl,ValueVec & mOut)160 void CBConsequences::QueryFinder::updateLower(Solver& s, uint32 rl, ValueVec& mOut) {
161 	LitVec::iterator j = open_.begin();
162 	for (LitVec::iterator it = j, end = open_.end(); it != end; ++it) {
163 		ValueRep val = s.value(it->var());
164 		if (val != value_free && s.level(it->var()) > rl) {
165 			val = value_free;
166 		}
167 		if      (!state_->open(*it)) { continue; }
168 		else if (val == value_free)  { *j++ = *it; }
169 		else if (s.isTrue(*it))      { state_->fix(*it); }
170 		else                         { state_->pop(*it); }
171 	}
172 	if (j != open_.end()) { dirty_ = true; }
173 	open_.erase(j, open_.end());
174 	state_->setModel(mOut, dirty_);
175 	dirty_ = false;
176 }
selectOpen(Solver & s,Literal & q)177 bool CBConsequences::QueryFinder::selectOpen(Solver& s, Literal& q) {
178 	for (LitVec::size_type i = 0, end = open_.size();; --end, open_.pop_back()) {
179 		for (; i != end && s.value(open_[i].var()) == value_free && state_->open(open_[i]); ++i) { ; }
180 		if (i == end) { break; }
181 		q = open_[i];
182 		open_[i] = open_.back();
183 		if (s.isTrue(q)) { state_->fix(q); }
184 		else             { state_->pop(q); }
185 		dirty_ = true;
186 	}
187 	if (open_.empty()) { return false; }
188 	q = s.heuristic()->selectRange(s, &open_[0], &open_[0] + open_.size());
189 	return true;
190 }
191 // solve(~query) produced a model - query is not a cautious consequence, update overstimate
doCommitModel(Enumerator &,Solver & s)192 void CBConsequences::QueryFinder::doCommitModel(Enumerator&, Solver& s) {
193 	if (!hasQuery() && state_->open(query_)) {
194 		// init state to first model
195 		for (LitVec::iterator it = open_.begin(), end = open_.end(); it != end; ++it) {
196 			if (s.isTrue(*it)) { state_->push(*it); }
197 		}
198 	}
199 	state_->pop(query_);
200 	updateUpper(s, level_, s.model);
201 	query_.flag();
202 }
203 // solve(~query) failed - query is a cautious consequence
doCommitUnsat(Enumerator &,Solver & s)204 void CBConsequences::QueryFinder::doCommitUnsat(Enumerator&, Solver& s) {
205 	bool commit = !disjointPath() && s.hasConflict() && !s.hasStopConflict() && hasQuery();
206 	popQuery(s);
207 	if (commit) {
208 		state_->fix(query_);
209 		query_.flag();
210 	}
211 	updateLower(s, level_, s.model);
212 }
doUpdate(Solver & s)213 bool CBConsequences::QueryFinder::doUpdate(Solver& s) {
214 	bool newQ = query_.flagged() || !state_->open(query_);
215 	if (newQ || s.value(query_.var()) == value_free) { // query was SAT/UNSAT or solved by other thread
216 		if (!popQuery(s)) { return false; }
217 		assert(s.decisionLevel() == s.rootLevel());
218 		level_ = s.rootLevel();
219 		return newQ && !selectOpen(s, query_) ? s.force(query_ = lit_false(), this) : s.pushRoot(~query_);
220 	}
221 	return true;
222 }
223 /////////////////////////////////////////////////////////////////////////////////////////
224 // CBConsequences
225 /////////////////////////////////////////////////////////////////////////////////////////
CBConsequences(Type type,Algo algo)226 CBConsequences::CBConsequences(Type type, Algo algo)
227 	: Enumerator()
228 	, shared_(0)
229 	, type_(type)
230 	, algo_(algo) {
231 	if (type_ != Cautious) { algo_ = Default; }
232 }
233 
createConsEnumerator(const EnumOptions & opts)234 Enumerator* EnumOptions::createConsEnumerator(const EnumOptions& opts) {
235 	return new CBConsequences(opts.enumMode == enum_brave ? CBConsequences::Brave : CBConsequences::Cautious, opts.enumMode != enum_query ? CBConsequences::Default : CBConsequences::Query);
236 }
~CBConsequences()237 CBConsequences::~CBConsequences() {
238 	delete shared_;
239 }
supportsSplitting(const SharedContext & problem) const240 bool CBConsequences::supportsSplitting(const SharedContext& problem) const {
241 	return algo_ == Default && Enumerator::supportsSplitting(problem);
242 }
unsatType() const243 int CBConsequences::unsatType() const {
244 	return algo_ == Default ? Enumerator::unsatType() : Enumerator::unsat_sync;
245 }
doInit(SharedContext & ctx,SharedMinimizeData * m,int)246 EnumerationConstraint* CBConsequences::doInit(SharedContext& ctx, SharedMinimizeData* m, int) {
247 	cons_.clear();
248 	const OutputTable& out = ctx.output;
249 	if (out.projectMode() == ProjectMode_t::Output) {
250 		for (OutputTable::pred_iterator it = out.pred_begin(), end = out.pred_end(); it != end; ++it) {
251 			addLit(ctx, it->cond);
252 		}
253 		for (OutputTable::range_iterator it = out.vars_begin(), end = out.vars_end(); it != end; ++it) {
254 			addLit(ctx, posLit(*it));
255 		}
256 	}
257 	else {
258 		for (OutputTable::lit_iterator it = out.proj_begin(), end = out.proj_end(); it != end; ++it) {
259 			addLit(ctx, *it);
260 		}
261 	}
262 	// init M to either cons or {} depending on whether we compute cautious or brave cons.
263 	const uint32 fMask = (type_ == Cautious && algo_ != Query);
264 	const uint32 vMask = (type_ == Cautious) ? 3u : 0u;
265 	for (LitVec::iterator it = cons_.begin(), end = cons_.end(); it != end; ++it) {
266 		it->rep() |= fMask;
267 		ctx.unmark(it->var());
268 		if (!ctx.varInfo(it->var()).nant()) {
269 			ctx.master()->setPref(it->var(), ValueSet::def_value, static_cast<ValueRep>(trueValue(*it) ^ vMask));
270 		}
271 	}
272 	delete shared_; shared_ = 0;
273 	setIgnoreSymmetric(true);
274 	if (m && m->optimize() && algo_ == Query) {
275 		ctx.warn("Query algorithm does not support optimization!");
276 		algo_ = Default;
277 	}
278 	if (type_ != Cautious || algo_ != Query) {
279 		shared_ = ctx.concurrency() > 1 ? new SharedConstraint() : 0;
280 		return new CBFinder(shared_);
281 	}
282 	return new QueryFinder(cons_, model(), ctx.numVars() + 1);
283 }
addLit(SharedContext & ctx,Literal p)284 void CBConsequences::addLit(SharedContext& ctx, Literal p) {
285 	if (!ctx.marked(p) && !ctx.eliminated(p.var())) {
286 		cons_.push_back(p);
287 		ctx.setFrozen(p.var(), true);
288 		ctx.mark(p);
289 	}
290 }
addCurrent(Solver & s,LitVec & con,ValueVec & m,uint32 root)291 void CBConsequences::addCurrent(Solver& s, LitVec& con, ValueVec& m, uint32 root) {
292 	con.assign(1, ~s.sharedContext()->stepLiteral());
293 	// reset state of relevant variables
294 	for (LitVec::iterator it = cons_.begin(), end = cons_.end(); it != end; ++it) {
295 		m[it->var()] = 0;
296 	}
297 	// let M be all lits p with p.watch() == true
298 	for (LitVec::iterator it = cons_.begin(), end = cons_.end(); it != end; ++it) {
299 		Literal& p = *it;
300 		uint32  dl = s.level(p.var());
301 		uint32 ost = dl > root ? Model::estMask(p) : 0;
302 		if (type_ == Brave) {
303 			// brave: extend M with true literals and force a literal not in M to true
304 			if      (p.flagged() || s.isTrue(p)) { p.flag(); ost = 0; }
305 			else if (dl)                         { con.push_back(p); }
306 		}
307 		else if (type_ == Cautious) {
308 			// cautious: intersect M with true literals and force a literal in M to false
309 			if      (!p.flagged() || s.isFalse(p)) { p.unflag(); ost = 0; }
310 			else if (dl)                           { con.push_back(~p); }
311 		}
312 		// set output state
313 		if (p.flagged()) { ost |= trueValue(p); }
314 		m[p.var()] |= ost;
315 	}
316 	if (shared_) {
317 		shared_->release(SharedLiterals::newShareable(con, Constraint_t::Other, 1));
318 	}
319 }
320 /////////////////////////////////////////////////////////////////////////////////////////
321 // CBConsequences::CBFinder implementation
322 /////////////////////////////////////////////////////////////////////////////////////////
destroy(Solver * s,bool detach)323 void CBConsequences::CBFinder::destroy(Solver* s, bool detach) {
324 	Clasp::destroyDB(locked, s, detach);
325 	if (last) {
326 		last->release();
327 	}
328 	EnumerationConstraint::destroy(s, detach);
329 }
pushLocked(Solver & s,ClauseHead * c)330 void CBConsequences::CBFinder::pushLocked(Solver& s, ClauseHead* c) {
331 	for (ClauseHead* h; !locked.empty() && !(h = static_cast<ClauseHead*>(locked.back()))->locked(s);) {
332 		h->destroy(&s, true);
333 		locked.pop_back();
334 	}
335 	locked.push_back(c);
336 }
doUpdate(Solver & s)337 bool CBConsequences::CBFinder::doUpdate(Solver& s) {
338 	ClauseCreator::Result ret;
339 	uint32 flags = ClauseCreator::clause_explicit|ClauseCreator::clause_no_add;
340 	if (!shared) {
341 		ret  = !current.empty() ? ClauseCreator::create(s, current, flags, ConstraintInfo(Constraint_t::Other)) : ClauseCreator::Result();
342 	}
343 	else if (SharedLiterals* x = shared->fetch_if_neq(last)) {
344 		if (last) { last->release(); }
345 		last = x;
346 		ret  = ClauseCreator::integrate(s, x, flags | ClauseCreator::clause_no_release);
347 	}
348 	if (ret.local) { pushLocked(s, ret.local); }
349 	current.clear();
350 	return ret.ok();
351 }
352 }
353