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/program_builder.h>
25 #include <clasp/shared_context.h>
26 #include <clasp/solver.h>
27 #include <clasp/clause.h>
28 #include <clasp/weight_constraint.h>
29 #include <clasp/parser.h>
30 #include <limits>
31 namespace Clasp {
32
33 /////////////////////////////////////////////////////////////////////////////////////////
34 // class ProgramBuilder
35 /////////////////////////////////////////////////////////////////////////////////////////
ProgramBuilder()36 ProgramBuilder::ProgramBuilder() : ctx_(0), frozen_(true) {}
~ProgramBuilder()37 ProgramBuilder::~ProgramBuilder() {}
ok() const38 bool ProgramBuilder::ok() const { return ctx_ && ctx_->ok(); }
startProgram(SharedContext & ctx)39 bool ProgramBuilder::startProgram(SharedContext& ctx) {
40 ctx.report(Event::subsystem_load);
41 ctx_ = &ctx;
42 frozen_ = ctx.frozen();
43 return ctx_->ok() && doStartProgram();
44 }
updateProgram()45 bool ProgramBuilder::updateProgram() {
46 POTASSCO_REQUIRE(ctx_, "startProgram() not called!");
47 bool up = frozen();
48 bool ok = ctx_->ok() && ctx_->unfreeze() && doUpdateProgram() && (ctx_->setSolveMode(SharedContext::solve_multi), true);
49 frozen_ = ctx_->frozen();
50 if (up && !frozen()){ ctx_->report(Event::subsystem_load); }
51 return ok;
52 }
endProgram()53 bool ProgramBuilder::endProgram() {
54 POTASSCO_REQUIRE(ctx_, "startProgram() not called!");
55 bool ok = ctx_->ok();
56 if (ok && !frozen_) {
57 ctx_->report(Event::subsystem_prepare);
58 ok = doEndProgram();
59 frozen_ = true;
60 }
61 return ok;
62 }
getAssumptions(LitVec & out) const63 void ProgramBuilder::getAssumptions(LitVec& out) const {
64 POTASSCO_REQUIRE(ctx_ && frozen());
65 doGetAssumptions(out);
66 }
getWeakBounds(SumVec & out) const67 void ProgramBuilder::getWeakBounds(SumVec& out) const {
68 POTASSCO_REQUIRE(ctx_ && frozen());
69 doGetWeakBounds(out);
70 }
parser()71 ProgramParser& ProgramBuilder::parser() {
72 if (!parser_.get()) {
73 parser_.reset(doCreateParser());
74 }
75 return *parser_;
76 }
parseProgram(std::istream & input)77 bool ProgramBuilder::parseProgram(std::istream& input) {
78 POTASSCO_REQUIRE(ctx_ && !frozen());
79 ProgramParser& p = parser();
80 POTASSCO_REQUIRE(p.accept(input), "unrecognized input format");
81 return p.parse();
82 }
addMinLit(weight_t prio,WeightLiteral x)83 void ProgramBuilder::addMinLit(weight_t prio, WeightLiteral x) {
84 ctx_->addMinimize(x, prio);
85 }
markOutputVariables() const86 void ProgramBuilder::markOutputVariables() const {
87 const OutputTable& out = ctx_->output;
88 for (OutputTable::range_iterator it = out.vars_begin(), end = out.vars_end(); it != end; ++it) {
89 ctx_->setOutput(*it, true);
90 }
91 for (OutputTable::pred_iterator it = out.pred_begin(), end = out.pred_end(); it != end; ++it) {
92 ctx_->setOutput(it->cond.var(), true);
93 }
94 }
doGetWeakBounds(SumVec &) const95 void ProgramBuilder::doGetWeakBounds(SumVec&) const {}
96 /////////////////////////////////////////////////////////////////////////////////////////
97 // class SatBuilder
98 /////////////////////////////////////////////////////////////////////////////////////////
SatBuilder()99 SatBuilder::SatBuilder() : ProgramBuilder(), hardWeight_(0), vars_(0), pos_(0) {}
markAssigned()100 bool SatBuilder::markAssigned() {
101 if (pos_ == ctx()->master()->trail().size()) { return true; }
102 bool ok = ctx()->ok() && ctx()->master()->propagate();
103 for (const LitVec& trail = ctx()->master()->trail(); pos_ < trail.size(); ++pos_) {
104 markLit(~trail[pos_]);
105 }
106 return ok;
107 }
prepareProblem(uint32 numVars,wsum_t cw,uint32 clauseHint)108 void SatBuilder::prepareProblem(uint32 numVars, wsum_t cw, uint32 clauseHint) {
109 POTASSCO_REQUIRE(ctx(), "startProgram() not called!");
110 Var start = ctx()->addVars(numVars, Var_t::Atom, VarInfo::Input | VarInfo::Nant);
111 ctx()->output.setVarRange(Range32(start, start + numVars));
112 ctx()->startAddConstraints(std::min(clauseHint, uint32(10000)));
113 varState_.resize(start + numVars);
114 vars_ = ctx()->numVars();
115 hardWeight_ = cw;
116 markAssigned();
117 }
addObjective(const WeightLitVec & min)118 bool SatBuilder::addObjective(const WeightLitVec& min) {
119 for (WeightLitVec::const_iterator it = min.begin(), end = min.end(); it != end; ++it) {
120 addMinLit(0, *it);
121 varState_[it->first.var()] |= (falseValue(it->first) << 2u);
122 }
123 return ctx()->ok();
124 }
addProject(Var v)125 void SatBuilder::addProject(Var v) {
126 ctx()->output.addProject(posLit(v));
127 }
addAssumption(Literal x)128 void SatBuilder::addAssumption(Literal x) {
129 assume_.push_back(x);
130 }
addClause(LitVec & clause,wsum_t cw)131 bool SatBuilder::addClause(LitVec& clause, wsum_t cw) {
132 if (!ctx()->ok() || satisfied(clause)) { return ctx()->ok(); }
133 POTASSCO_REQUIRE(cw >= 0 && (cw <= std::numeric_limits<weight_t>::max() || cw == hardWeight_), "Clause weight out of bounds");
134 if (cw == hardWeight_) {
135 return ClauseCreator::create(*ctx()->master(), clause, Constraint_t::Static).ok() && markAssigned();
136 }
137 else {
138 // Store weight, relaxtion var, and (optionally) clause
139 softClauses_.push_back(Literal::fromRep((uint32)cw));
140 if (clause.size() > 1){ softClauses_.push_back(posLit(++vars_)); softClauses_.insert(softClauses_.end(), clause.begin(), clause.end()); }
141 else if (!clause.empty()) { softClauses_.push_back(~clause.back()); }
142 else { softClauses_.push_back(lit_true()); }
143 softClauses_.back().flag(); // mark end of clause
144 }
145 return true;
146 }
satisfied(LitVec & cc)147 bool SatBuilder::satisfied(LitVec& cc) {
148 bool sat = false;
149 LitVec::iterator j = cc.begin();
150 for (LitVec::const_iterator it = cc.begin(), end = cc.end(); it != end; ++it) {
151 Literal x = *it;
152 uint32 m = 1+x.sign();
153 uint32 n = uint32(varState_[it->var()] & 3u) + m;
154 if (n == m) { varState_[it->var()] |= m; x.unflag(); *j++ = x; }
155 else if (n == 3u){ sat = true; break; }
156 }
157 cc.erase(j, cc.end());
158 for (LitVec::const_iterator it = cc.begin(), end = cc.end(); it != end; ++it) {
159 if (!sat) { varState_[it->var()] |= (varState_[it->var()] & 3u) << 2; }
160 varState_[it->var()] &= ~3u;
161 }
162 return sat;
163 }
addConstraint(WeightLitVec & lits,weight_t bound)164 bool SatBuilder::addConstraint(WeightLitVec& lits, weight_t bound) {
165 if (!ctx()->ok()) { return false; }
166 WeightLitsRep rep = WeightLitsRep::create(*ctx()->master(), lits, bound);
167 if (rep.open()) {
168 for (const WeightLiteral* x = rep.lits, *end = rep.lits + rep.size; x != end; ++x) {
169 varState_[x->first.var()] |= (trueValue(x->first) << 2);
170 }
171 }
172 return WeightConstraint::create(*ctx()->master(), lit_true(), rep, 0u).ok();
173 }
doStartProgram()174 bool SatBuilder::doStartProgram() {
175 vars_ = ctx()->numVars();
176 pos_ = 0;
177 assume_.clear();
178 return markAssigned();
179 }
doCreateParser()180 ProgramParser* SatBuilder::doCreateParser() {
181 return new SatParser(*this);
182 }
doEndProgram()183 bool SatBuilder::doEndProgram() {
184 bool ok = ctx()->ok();
185 if (!softClauses_.empty() && ok) {
186 ctx()->setPreserveModels(true);
187 uint32 softVars = vars_ - ctx()->numVars();
188 ctx()->addVars(softVars, Var_t::Atom, VarInfo::Nant);
189 ctx()->startAddConstraints();
190 LitVec cc;
191 for (LitVec::const_iterator it = softClauses_.begin(), end = softClauses_.end(); it != end && ok; ++it) {
192 weight_t w = (weight_t)it->rep();
193 Literal relax = *++it;
194 if (!relax.flagged()) {
195 cc.assign(1, relax);
196 do { cc.push_back(*++it); } while (!cc.back().flagged());
197 cc.back().unflag();
198 ok = ClauseCreator::create(*ctx()->master(), cc, Constraint_t::Static).ok();
199 }
200 addMinLit(0, WeightLiteral(relax.unflag(), w));
201 }
202 LitVec().swap(softClauses_);
203 }
204 if (ok) {
205 const uint32 seen = 12;
206 const bool elim = !ctx()->preserveModels();
207 for (Var v = 1; v != (Var)varState_.size() && ok; ++v) {
208 uint32 m = varState_[v];
209 if ( (m & seen) != seen ) {
210 if (m) { ctx()->setNant(v, false); ctx()->master()->setPref(v, ValueSet::def_value, ValueRep(m >> 2)); }
211 else if (elim){ ctx()->eliminate(v); }
212 }
213 }
214 markOutputVariables();
215 }
216 return ok;
217 }
218 /////////////////////////////////////////////////////////////////////////////////////////
219 // class PBBuilder
220 /////////////////////////////////////////////////////////////////////////////////////////
PBBuilder()221 PBBuilder::PBBuilder() : auxVar_(1) {}
prepareProblem(uint32 numVars,uint32 numProd,uint32 numSoft,uint32 numCons)222 void PBBuilder::prepareProblem(uint32 numVars, uint32 numProd, uint32 numSoft, uint32 numCons) {
223 POTASSCO_REQUIRE(ctx(), "startProgram() not called!");
224 Var out = ctx()->addVars(numVars, Var_t::Atom, VarInfo::Nant | VarInfo::Input);
225 auxVar_ = ctx()->addVars(numProd + numSoft, Var_t::Atom, VarInfo::Nant);
226 endVar_ = auxVar_ + numProd + numSoft;
227 ctx()->output.setVarRange(Range32(out, out + numVars));
228 ctx()->startAddConstraints(numCons);
229 }
getAuxVar()230 uint32 PBBuilder::getAuxVar() {
231 POTASSCO_REQUIRE(ctx()->validVar(auxVar_), "Variables out of bounds");
232 return auxVar_++;
233 }
addConstraint(WeightLitVec & lits,weight_t bound,bool eq,weight_t cw)234 bool PBBuilder::addConstraint(WeightLitVec& lits, weight_t bound, bool eq, weight_t cw) {
235 if (!ctx()->ok()) { return false; }
236 Var eqVar = 0;
237 if (cw > 0) { // soft constraint
238 if (lits.size() != 1) {
239 eqVar = getAuxVar();
240 addMinLit(0, WeightLiteral(negLit(eqVar), cw));
241 }
242 else {
243 if (lits[0].second < 0) { bound += (lits[0].second = -lits[0].second); lits[0].first = ~lits[0].first; }
244 if (lits[0].second < bound){ lits[0].first = lit_false(); }
245 addMinLit(0, WeightLiteral(~lits[0].first, cw));
246 return true;
247 }
248 }
249 return WeightConstraint::create(*ctx()->master(), posLit(eqVar), lits, bound, !eq ? 0 : WeightConstraint::create_eq_bound).ok();
250 }
251
addObjective(const WeightLitVec & min)252 bool PBBuilder::addObjective(const WeightLitVec& min) {
253 for (WeightLitVec::const_iterator it = min.begin(), end = min.end(); it != end; ++it) {
254 addMinLit(0, *it);
255 }
256 return ctx()->ok();
257 }
addProject(Var v)258 void PBBuilder::addProject(Var v) {
259 ctx()->output.addProject(posLit(v));
260 }
addAssumption(Literal x)261 void PBBuilder::addAssumption(Literal x) {
262 assume_.push_back(x);
263 }
setSoftBound(wsum_t b)264 bool PBBuilder::setSoftBound(wsum_t b) {
265 if (b > 0) { soft_ = b-1; }
266 return true;
267 }
268
doGetWeakBounds(SumVec & out) const269 void PBBuilder::doGetWeakBounds(SumVec& out) const {
270 if (soft_ != std::numeric_limits<wsum_t>::max()) {
271 if (out.empty()) { out.push_back(soft_); }
272 else if (out[0] > soft_){ out[0] = soft_; }
273 }
274 }
275
addProduct(LitVec & lits)276 Literal PBBuilder::addProduct(LitVec& lits) {
277 if (!ctx()->ok()) { return lit_false(); }
278 prod_.lits.reserve(lits.size() + 1);
279 if (productSubsumed(lits, prod_)){
280 return lits[0];
281 }
282 Literal& eq = products_[prod_];
283 if (eq != lit_true()) {
284 return eq;
285 }
286 eq = posLit(getAuxVar());
287 addProductConstraints(eq, lits);
288 return eq;
289 }
productSubsumed(LitVec & lits,PKey & prod)290 bool PBBuilder::productSubsumed(LitVec& lits, PKey& prod) {
291 Literal last = lit_true();
292 LitVec::iterator j = lits.begin();
293 Solver& s = *ctx()->master();
294 uint32 abst = 0;
295 prod.lits.assign(1, lit_true()); // room for abst
296 for (LitVec::const_iterator it = lits.begin(), end = lits.end(); it != end; ++it) {
297 if (s.isFalse(*it) || ~*it == last) { // product is always false
298 lits.assign(1, lit_false());
299 return true;
300 }
301 else if (last.var() > it->var()) { // not sorted - redo with sorted product
302 std::sort(lits.begin(), lits.end());
303 return productSubsumed(lits, prod);
304 }
305 else if (!s.isTrue(*it) && last != *it) {
306 prod.lits.push_back(*it);
307 abst += hashLit(*it);
308 last = *it;
309 *j++ = last;
310 }
311 }
312 prod.lits[0].rep() = abst;
313 lits.erase(j, lits.end());
314 if (lits.empty()) { lits.assign(1, lit_true()); }
315 return lits.size() < 2;
316 }
addProductConstraints(Literal eqLit,LitVec & lits)317 void PBBuilder::addProductConstraints(Literal eqLit, LitVec& lits) {
318 Solver& s = *ctx()->master();
319 assert(s.value(eqLit.var()) == value_free);
320 bool ok = ctx()->ok();
321 for (LitVec::iterator it = lits.begin(), end = lits.end(); it != end && ok; ++it) {
322 assert(s.value(it->var()) == value_free);
323 ok = ctx()->addBinary(~eqLit, *it);
324 *it = ~*it;
325 }
326 lits.push_back(eqLit);
327 if (ok) { ClauseCreator::create(s, lits, ClauseCreator::clause_no_prepare); }
328 }
329
doStartProgram()330 bool PBBuilder::doStartProgram() {
331 auxVar_ = ctx()->numVars() + 1;
332 soft_ = std::numeric_limits<wsum_t>::max();
333 assume_.clear();
334 return true;
335 }
doEndProgram()336 bool PBBuilder::doEndProgram() {
337 while (auxVar_ != endVar_) {
338 if (!ctx()->addUnary(negLit(getAuxVar()))) { return false; }
339 }
340 markOutputVariables();
341 return true;
342 }
doCreateParser()343 ProgramParser* PBBuilder::doCreateParser() {
344 return new SatParser(*this);
345 }
346 /////////////////////////////////////////////////////////////////////////////////////////
347 // class BasicProgramAdapter
348 /////////////////////////////////////////////////////////////////////////////////////////
BasicProgramAdapter(ProgramBuilder & prg)349 BasicProgramAdapter::BasicProgramAdapter(ProgramBuilder& prg) : prg_(&prg), inc_(false) {
350 int t = prg_->type();
351 POTASSCO_REQUIRE(t == Problem_t::Sat || t == Problem_t::Pb, "unknown program type");
352 }
initProgram(bool inc)353 void BasicProgramAdapter::initProgram(bool inc) { inc_ = inc; }
beginStep()354 void BasicProgramAdapter::beginStep() { if (inc_ || prg_->frozen()) { prg_->updateProgram(); } }
355
rule(Potassco::Head_t,const Potassco::AtomSpan & head,const Potassco::LitSpan & body)356 void BasicProgramAdapter::rule(Potassco::Head_t, const Potassco::AtomSpan& head, const Potassco::LitSpan& body) {
357 using namespace Potassco;
358 POTASSCO_REQUIRE(empty(head), "unsupported rule type");
359 if (prg_->type() == Problem_t::Sat) {
360 clause_.clear();
361 for (LitSpan::iterator it = begin(body), end = Potassco::end(body); it != end; ++it) { clause_.push_back(~toLit(*it)); }
362 static_cast<SatBuilder&>(*prg_).addClause(clause_);
363 }
364 else {
365 constraint_.clear();
366 for (LitSpan::iterator it = begin(body), end = Potassco::end(body); it != end; ++it) { constraint_.push_back(WeightLiteral(~toLit(*it), 1)); }
367 static_cast<PBBuilder&>(*prg_).addConstraint(constraint_, 1);
368 }
369 }
rule(Potassco::Head_t,const Potassco::AtomSpan & head,Potassco::Weight_t bound,const Potassco::WeightLitSpan & body)370 void BasicProgramAdapter::rule(Potassco::Head_t, const Potassco::AtomSpan& head, Potassco::Weight_t bound, const Potassco::WeightLitSpan& body) {
371 using namespace Potassco;
372 POTASSCO_REQUIRE(empty(head), "unsupported rule type");
373 constraint_.clear();
374 Potassco::Weight_t sum = 0;
375 for (WeightLitSpan::iterator it = begin(body), end = Potassco::end(body); it != end; ++it) {
376 constraint_.push_back(WeightLiteral(~toLit(it->lit), it->weight));
377 sum += it->weight;
378 }
379 if (prg_->type() == Problem_t::Sat) {
380 static_cast<SatBuilder&>(*prg_).addConstraint(constraint_, (sum - bound) + 1);
381 }
382 else {
383 static_cast<PBBuilder&>(*prg_).addConstraint(constraint_, (sum - bound) + 1);
384 }
385 }
minimize(Potassco::Weight_t prio,const Potassco::WeightLitSpan & lits)386 void BasicProgramAdapter::minimize(Potassco::Weight_t prio, const Potassco::WeightLitSpan& lits) {
387 POTASSCO_REQUIRE(prio == 0, "unsupported rule type");
388 using namespace Potassco;
389 constraint_.clear();
390 for (WeightLitSpan::iterator it = begin(lits), end = Potassco::end(lits); it != end; ++it) { constraint_.push_back(WeightLiteral(toLit(it->lit), it->weight)); }
391 if (prg_->type() == Problem_t::Sat) {
392 static_cast<SatBuilder&>(*prg_).addObjective(constraint_);
393 }
394 else {
395 static_cast<PBBuilder&>(*prg_).addObjective(constraint_);
396 }
397 }
398 }
399