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