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