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/clasp_facade.h>
25 #include <clasp/lookahead.h>
26 #include <clasp/dependency_graph.h>
27 #include <clasp/minimize_constraint.h>
28 #include <clasp/unfounded_check.h>
29 #include <clasp/parser.h>
30 #include <clasp/clingo.h>
31 #include <clasp/util/timer.h>
32 #include <potassco/string_convert.h>
33 #include <cstdio>
34 #include <cstdlib>
35 #include <signal.h>
36 #include <climits>
37 #include <utility>
38 #if CLASP_HAS_THREADS
39 #include <clasp/mt/mutex.h>
40 #include <clasp/mt/thread.h>
41 #endif
42 namespace Clasp {
43 /////////////////////////////////////////////////////////////////////////////////////////
44 // ClaspConfig
45 /////////////////////////////////////////////////////////////////////////////////////////
46 struct ClaspConfig::Impl {
47 	struct ConfiguratorProxy {
48 		enum { OnceBit = 62, AcquireBit = 61 };
ConfiguratorProxyClasp::ClaspConfig::Impl::ConfiguratorProxy49 		ConfiguratorProxy(Configurator* c, Ownership_t::Type own, bool once) : cfg(reinterpret_cast<uintp>(c)), set(0) {
50 			if (once) { store_set_bit(cfg, OnceBit); }
51 			if (own == Ownership_t::Acquire) { store_set_bit(cfg, AcquireBit); }
52 			assert(ptr() == c);
53 		}
applyConfigClasp::ClaspConfig::Impl::ConfiguratorProxy54 		bool applyConfig(Solver& s) {
55 			POTASSCO_ASSERT(s.id() < 64, "invalid solver id!");
56 			if (test_bit(set, s.id()))  { return true;  }
57 			if (test_bit(cfg, OnceBit)) { store_set_bit(set, s.id()); }
58 			return this->ptr()->applyConfig(s);
59 		}
prepareClasp::ClaspConfig::Impl::ConfiguratorProxy60 		void prepare(SharedContext& ctx) {
61 			if (ctx.concurrency() < 64) {
62 				set &= (bit_mask<uint64>(ctx.concurrency()) - 1);
63 			}
64 			ptr()->prepare(ctx);
65 		}
unfreezeClasp::ClaspConfig::Impl::ConfiguratorProxy66 		void unfreeze(SharedContext& ctx) {
67 			ptr()->unfreeze(ctx);
68 		}
destroyClasp::ClaspConfig::Impl::ConfiguratorProxy69 		void destroy() { if (test_bit(cfg, AcquireBit)) { delete ptr(); } }
ptrClasp::ClaspConfig::Impl::ConfiguratorProxy70 		Configurator* ptr() const {
71 			static const uint64 ptrMask = ~(bit_mask<uint64>(OnceBit) | bit_mask<uint64>(AcquireBit));
72 			return reinterpret_cast<Configurator*>(static_cast<uintp>(cfg & ptrMask));
73 		}
74 		uint64 cfg;
75 		uint64 set;
76 	};
77 	typedef PodVector<ConfiguratorProxy>::type PPVec;
ImplClasp::ClaspConfig::Impl78 	Impl()  { acycSet = 0; }
~ImplClasp::ClaspConfig::Impl79 	~Impl() { reset(); }
80 	void reset();
81 	void prepare(SharedContext& ctx);
82 	bool addPost(Solver& s, const SolverParams& opts);
addClasp::ClaspConfig::Impl83 	void add(Configurator* c, Ownership_t::Type t, bool once) { pp.push_back(ConfiguratorProxy(c, t, once)); }
84 	void unfreeze(SharedContext& ctx);
85 	PPVec   pp;
86 	uint64  acycSet;
87 #if CLASP_HAS_THREADS
88 	Clasp::mt::mutex mutex;
89 #endif
90 };
reset()91 void ClaspConfig::Impl::reset() {
92 	for (; !pp.empty(); pp.pop_back()) { pp.back().destroy(); }
93 }
94 
prepare(SharedContext & ctx)95 void ClaspConfig::Impl::prepare(SharedContext& ctx) {
96 	if (ctx.concurrency() < 64) {
97 		acycSet &= (bit_mask<uint64>(ctx.concurrency()) - 1);
98 	}
99 	for (PPVec::iterator it = pp.begin(), end = pp.end(); it != end; ++it) {
100 		it->prepare(ctx);
101 	}
102 }
addPost(Solver & s,const SolverParams & opts)103 bool ClaspConfig::Impl::addPost(Solver& s, const SolverParams& opts) {
104 #if CLASP_HAS_THREADS
105 #define LOCKED() for (Clasp::mt::unique_lock<Clasp::mt::mutex> lock(mutex); lock.owns_lock(); lock.unlock())
106 #else
107 #define LOCKED()
108 #endif
109 	POTASSCO_ASSERT(s.sharedContext() != 0, "Solver not attached!");
110 	if (s.sharedContext()->sccGraph.get()) {
111 		if (DefaultUnfoundedCheck* ufs = static_cast<DefaultUnfoundedCheck*>(s.getPost(PostPropagator::priority_reserved_ufs))) {
112 			ufs->setReasonStrategy(static_cast<DefaultUnfoundedCheck::ReasonStrategy>(opts.loopRep));
113 		}
114 		else if (!s.addPost(new DefaultUnfoundedCheck(*s.sharedContext()->sccGraph, static_cast<DefaultUnfoundedCheck::ReasonStrategy>(opts.loopRep)))) {
115 			return false;
116 		}
117 	}
118 	if (s.sharedContext()->extGraph.get()) {
119 		bool addAcyc = false;
120 		// protect access to acycSet
121 		LOCKED() { addAcyc = !test_bit(acycSet, s.id()) && store_set_bit(acycSet, s.id()); }
122 		if (addAcyc && !s.addPost(new AcyclicityCheck(s.sharedContext()->extGraph.get()))) {
123 			return false;
124 		}
125 	}
126 	for (PPVec::iterator it = pp.begin(), end = pp.end(); it != end; ++it) {
127 		// protect call to user code
128 		LOCKED() { if (!it->applyConfig(s)) { return false; } }
129 	}
130 	return true;
131 #undef LOCKED
132 }
unfreeze(SharedContext & ctx)133 void ClaspConfig::Impl::unfreeze(SharedContext& ctx) {
134 	for (PPVec::iterator it = pp.begin(), end = pp.end(); it != end; ++it) {
135 		it->unfreeze(ctx);
136 	}
137 }
138 
ClaspConfig()139 ClaspConfig::ClaspConfig() : tester_(0), impl_(new Impl()) {}
~ClaspConfig()140 ClaspConfig::~ClaspConfig() {
141 	delete impl_;
142 	delete tester_;
143 }
144 
reset()145 void ClaspConfig::reset() {
146 	if (tester_) { tester_->reset(); }
147 	impl_->reset();
148 	BasicSatConfig::reset();
149 	solve = SolveOptions();
150 	asp   = AspOptions();
151 }
152 
addTesterConfig()153 BasicSatConfig* ClaspConfig::addTesterConfig() {
154 	if (!tester_) { tester_ = new BasicSatConfig(); }
155 	return tester_;
156 }
157 
prepare(SharedContext & ctx)158 void ClaspConfig::prepare(SharedContext& ctx) {
159 	BasicSatConfig::prepare(ctx);
160 	uint32 numS = solve.numSolver();
161 	if (numS > solve.supportedSolvers()) {
162 		ctx.warn("Too many solvers.");
163 		numS = solve.supportedSolvers();
164 	}
165 	if (numS > solve.recommendedSolvers()) {
166 		ctx.warn(POTASSCO_FORMAT("Oversubscription: #Threads=%u exceeds logical CPUs=%u.", numS, solve.recommendedSolvers()));
167 	}
168 	for (uint32 i = 0; i != numS; ++i) {
169 		if (solver(i).heuId == Heuristic_t::Domain) {
170 			parse.enableHeuristic();
171 			break;
172 		}
173 	}
174 	solve.setSolvers(numS);
175 	if (std::abs(static_cast<int>(solve.numModels)) != 1 || !solve.models()) {
176 		ctx.setPreserveModels(true);
177 	}
178 	ctx.setConcurrency(solve.numSolver(), SharedContext::resize_resize);
179 	impl_->prepare(ctx);
180 }
181 
config(const char * n)182 Configuration* ClaspConfig::config(const char* n) {
183 	return (n && std::strcmp(n, "tester") == 0) ? testerConfig() : BasicSatConfig::config(n);
184 }
185 
addConfigurator(Configurator * c,Ownership_t::Type t,bool once)186 void ClaspConfig::addConfigurator(Configurator* c, Ownership_t::Type t, bool once) {
187 	impl_->add(c, t, once);
188 }
189 
addPost(Solver & s) const190 bool ClaspConfig::addPost(Solver& s) const {
191 	return impl_->addPost(s, solver(s.id())) && BasicSatConfig::addPost(s);
192 }
193 
unfreeze(SharedContext & ctx)194 void ClaspConfig::unfreeze(SharedContext& ctx) {
195 	impl_->unfreeze(ctx);
196 }
197 
~Configurator()198 ClaspConfig::Configurator::~Configurator() {}
prepare(SharedContext &)199 void ClaspConfig::Configurator::prepare(SharedContext&) {}
unfreeze(SharedContext &)200 void ClaspConfig::Configurator::unfreeze(SharedContext&){}
201 /////////////////////////////////////////////////////////////////////////////////////////
202 // ClaspFacade::SolveStrategy
203 /////////////////////////////////////////////////////////////////////////////////////////
204 struct ClaspFacade::SolveStrategy {
205 public:
206 	enum { SIGCANCEL = 9, SIGERROR = 128};
207 	enum State { state_run = 1u, state_model = 2u, state_done = 4u };
208 	enum Event { event_attach, event_model, event_resume, event_detach };
~SolveStrategyClasp::ClaspFacade::SolveStrategy209 	virtual ~SolveStrategy() {}
210 	static SolveStrategy* create(SolveMode_t m, ClaspFacade& f, SolveAlgorithm& algo);
211 	void start(EventHandler* h, const LitVec& a);
runningClasp::ClaspFacade::SolveStrategy212 	bool running()  const { return (state_ & uint32(state_done-1)) != 0u; }
errorClasp::ClaspFacade::SolveStrategy213 	bool error()    const { return signal_ == SIGERROR; }
readyClasp::ClaspFacade::SolveStrategy214 	bool ready()    const { return state_ != uint32(state_run); }
signalClasp::ClaspFacade::SolveStrategy215 	int  signal()   const { return static_cast<int>(signal_); }
interruptClasp::ClaspFacade::SolveStrategy216 	bool interrupt(int sig) {
217 		bool stopped = running() && compare_and_swap(signal_, uint32(0), uint32(sig)) == 0u && algo_->interrupt();
218 		if (sig == SIGCANCEL) { wait(-1.0); }
219 		return stopped;
220 	}
waitClasp::ClaspFacade::SolveStrategy221 	bool wait(double s) { return doWait(s); }
resumeClasp::ClaspFacade::SolveStrategy222 	void resume() { doNotify(event_resume); }
setModelClasp::ClaspFacade::SolveStrategy223 	bool setModel(const Solver& s, const Model& m) {
224 		result_.flags |= SolveResult::SAT;
225 		bool ok = !handler_ || handler_->onModel(s, m);
226 		if ((mode_ & SolveMode_t::Yield) != 0) { doNotify(event_model); }
227 		return ok && !signal();
228 	}
resultClasp::ClaspFacade::SolveStrategy229 	Result result() {
230 		wait(-1.0);
231 		if (error()) { throw std::runtime_error(error_.c_str()); }
232 		return result_;
233 	}
modelClasp::ClaspFacade::SolveStrategy234 	const Model* model() {
235 		return state_ == state_model || (result().sat() && state_ == state_model)
236 			? &algo_->model()
237 			: 0;
238 	}
nextClasp::ClaspFacade::SolveStrategy239 	bool next() {
240 		return running() && (state_ != state_model || (resume(), true)) && model() != 0;
241 	}
releaseClasp::ClaspFacade::SolveStrategy242 	void release() {
243 		if      (--nrefs_ == 1) { interrupt(SIGCANCEL); }
244 		else if (nrefs_   == 0) { delete this; }
245 	}
shareClasp::ClaspFacade::SolveStrategy246 	SolveStrategy* share() { ++nrefs_; return this; }
247 protected:
248 	SolveStrategy(SolveMode_t m, ClaspFacade& f, SolveAlgorithm* algo);
249 	ClaspFacade*    facade_;
250 	SolveAlgorithm* algo_;
251 	void startAlgo(SolveMode_t m);
continueAlgoClasp::ClaspFacade::SolveStrategy252 	void continueAlgo() {
253 		bool detach = true;
254 		try {
255 			detach = (signal() && running()) || (state_ == state_run && !algo_->next());
256 			if (detach) { detach = false; detachAlgo(algo_->more(), 0); }
257 		}
258 		catch (...) {
259 			if (detach) { detachAlgo(algo_->more(), 1); }
260 			else        { throw; }
261 		}
262 	}
263 	void detachAlgo(bool more, int nException, int state = 0);
264 private:
265 	struct Async;
doStartClasp::ClaspFacade::SolveStrategy266 	virtual void doStart() { startAlgo(mode_);  }
doWaitClasp::ClaspFacade::SolveStrategy267 	virtual bool doWait(double maxTime) {
268 		POTASSCO_REQUIRE(maxTime < 0.0, "Timed wait not supported!");
269 		if (mode_ == SolveMode_t::Yield) { continueAlgo(); }
270 		return true;
271 	}
doNotifyClasp::ClaspFacade::SolveStrategy272 	virtual void doNotify(Event event) {
273 		switch (event) {
274 			case event_attach: state_ = state_run;   break;
275 			case event_model : state_ = state_model; break;
276 			case event_resume: compare_and_swap(state_, uint32(state_model), uint32(state_run)); break;
277 			case event_detach: state_ = state_done; break;
278 		};
279 	}
280 	typedef Clasp::Atomic_t<uint32>::type SafeIntType;
281 	std::string   error_;
282 	EventHandler* handler_;
283 	SafeIntType   nrefs_;   // Facade + #Handle objects
284 	SafeIntType   state_;
285 	SafeIntType   signal_;
286 	Result        result_;
287 	SolveMode_t   mode_;
288 	uint32        aTop_;
289 };
SolveStrategy(SolveMode_t m,ClaspFacade & f,SolveAlgorithm * algo)290 ClaspFacade::SolveStrategy::SolveStrategy(SolveMode_t m, ClaspFacade& f, SolveAlgorithm* algo)
291 	: facade_(&f)
292 	, algo_(algo)
293 	, handler_(0)
294 	, mode_(m) {
295 	nrefs_ = 1;
296 	state_ = signal_ = 0;
297 }
start(EventHandler * h,const LitVec & a)298 void ClaspFacade::SolveStrategy::start(EventHandler* h, const LitVec& a) {
299 	ClaspFacade& f = *facade_;
300 	aTop_ = (uint32)f.assume_.size();
301 	f.assume_.insert(f.assume_.end(), a.begin(), a.end());
302 	if (!isSentinel(f.ctx.stepLiteral())) {
303 		f.assume_.push_back(f.ctx.stepLiteral());
304 	}
305 	handler_ = h;
306 	std::memset(&result_, 0, sizeof(SolveResult));
307 	doStart();
308 	assert(running() || ready());
309 }
startAlgo(SolveMode_t m)310 void ClaspFacade::SolveStrategy::startAlgo(SolveMode_t m) {
311 	bool more = true, detach = true;
312 	doNotify(event_attach);
313 	try {
314 		facade_->interrupt(0); // handle pending interrupts
315 		if (!signal_ && !facade_->ctx.master()->hasConflict()) {
316 			facade_->step_.solveTime = facade_->step_.unsatTime = RealTime::getTime();
317 			if ((m & SolveMode_t::Yield) == 0) {
318 				more = algo_->solve(facade_->ctx, facade_->assume_, facade_);
319 			}
320 			else {
321 				algo_->start(facade_->ctx, facade_->assume_, facade_);
322 				detach = false;
323 			}
324 		}
325 		else {
326 			facade_->ctx.report(Clasp::Event::subsystem_solve);
327 			more = facade_->ctx.ok();
328 		}
329 		if (detach) { detach = false; detachAlgo(more, 0); }
330 	}
331 	catch (...) {
332 		if (detach) { detachAlgo(more, 1); }
333 		else        { throw; }
334 	}
335 }
detachAlgo(bool more,int nException,int state)336 void ClaspFacade::SolveStrategy::detachAlgo(bool more, int nException, int state) {
337 #define PROTECT(ec, X) if ((ec)) try { X; } catch (...) {} else X
338 	try {
339 		if (nException == 1) { throw; }
340 		switch (state) {
341 			case 0: ++state; PROTECT(nException, algo_->stop());  // FALLTHRU
342 			case 1: ++state; PROTECT(nException, facade_->stopStep(signal_, !more));  // FALLTHRU
343 			case 2: ++state; if (handler_) { PROTECT(nException, handler_->onEvent(StepReady(facade_->summary()))); }   // FALLTHRU
344 			case 3: state = -1;
345 				result_ = facade_->result();
346 				facade_->assume_.resize(aTop_);
347 				doNotify(event_detach);
348 			default:	break;
349 		}
350 	}
351 	catch (...) {
352 		error_ = "Operation failed: ";
353 		if (!signal_)    { signal_ = SIGERROR; }
354 		if (state != -1) { detachAlgo(more, 2, state); }
355 		if ((mode_ & SolveMode_t::Async) == 0) {
356 			error_ += "exception thrown";
357 			throw;
358 		}
359 		try { throw; }
360 		catch (const std::exception& e) { error_ = e.what(); }
361 		catch (...)                     { error_ = "unknown error"; }
362 	}
363 }
364 #if CLASP_HAS_THREADS
365 struct ClaspFacade::SolveStrategy::Async : public ClaspFacade::SolveStrategy {
366 	enum { state_async = (state_done << 1), state_next = state_model | state_async, state_join = state_done | state_async };
AsyncClasp::ClaspFacade::SolveStrategy::Async367 	Async(SolveMode_t m, ClaspFacade& f, SolveAlgorithm* algo) : SolveStrategy(m, f, algo) {}
doStartClasp::ClaspFacade::SolveStrategy::Async368 	virtual void doStart() {
369 		algo_->enableInterrupts();
370 		Clasp::mt::thread(std::mem_fun(&SolveStrategy::startAlgo), this, SolveMode_t::Async).swap(task_);
371 		for (mt::unique_lock<Clasp::mt::mutex> lock(mqMutex_); state_ == 0u;) {
372 			mqCond_.wait(lock);
373 		}
374 	}
doWaitClasp::ClaspFacade::SolveStrategy::Async375 	virtual bool doWait(double t) {
376 		for (mt::unique_lock<Clasp::mt::mutex> lock(mqMutex_);;) {
377 			if (signal() && running()) { // propagate signal to async thread and force wait
378 				mqCond_.notify_all();
379 				mqCond_.wait(lock);
380 			}
381 			else if (ready()) { break; }
382 			else if (t < 0.0) { mqCond_.wait(lock); }
383 			else if (t > 0.0) { mqCond_.wait_for(lock, t); t = 0.0; }
384 			else              { return false; }
385 		}
386 		assert(ready());
387 		// acknowledge current model or join if first to see done
388 		if (compare_and_swap(state_, uint32(state_next), uint32(state_model)) == state_done
389 			&& compare_and_swap(state_, uint32(state_done), uint32(state_join)) == state_done) {
390 			task_.join();
391 		}
392 		return true;
393 	}
doNotifyClasp::ClaspFacade::SolveStrategy::Async394 	virtual void doNotify(Event event) {
395 		mt::unique_lock<Clasp::mt::mutex> lock(mqMutex_);
396 		switch (event) {
397 			case event_attach: state_ = state_run;  break;
398 			case event_model : state_ = state_next; break;
399 			case event_resume: if (state_ == state_model) { state_ = state_run; break; } else { return; }
400 			case event_detach: state_ = state_done;  break;
401 		};
402 		lock.unlock(); // synchronize-with other threads but no need to notify under lock
403 		mqCond_.notify_all();
404 		if (event == event_model) {
405 			for (lock.lock(); state_ != state_run && !signal();) {
406 				mqCond_.wait(lock);
407 			}
408 		}
409 	}
410 	typedef Clasp::mt::condition_variable ConditionVar;
411 	Clasp::mt::thread task_;   // async solving thread
412 	Clasp::mt::mutex  mqMutex_;// protects mqCond
413 	ConditionVar      mqCond_; // for iterating over models one by one
414 };
415 #endif
create(SolveMode_t m,ClaspFacade & f,SolveAlgorithm & algo)416 ClaspFacade::SolveStrategy* ClaspFacade::SolveStrategy::create(SolveMode_t m, ClaspFacade& f, SolveAlgorithm& algo) {
417 	if ((m & SolveMode_t::Async) == 0) { return new SolveStrategy(m, f, &algo); }
418 #if CLASP_HAS_THREADS
419 	return new SolveStrategy::Async(m, f, &algo);
420 #else
421 	throw std::logic_error("Solve mode not supported!");
422 #endif
423 }
424 /////////////////////////////////////////////////////////////////////////////////////////
425 // ClaspFacade::SolveData
426 /////////////////////////////////////////////////////////////////////////////////////////
427 struct ClaspFacade::SolveData {
428 	struct CostArray {
CostArrayClasp::ClaspFacade::SolveData::CostArray429 		CostArray() : data(0) {}
~CostArrayClasp::ClaspFacade::SolveData::CostArray430 		~CostArray() { while (!refs.empty()) { delete refs.back(); refs.pop_back(); } }
431 		struct LevelRef {
LevelRefClasp::ClaspFacade::SolveData::CostArray::LevelRef432 			LevelRef(const CostArray* a, uint32 l) : arr(a), at(l) {}
valueClasp::ClaspFacade::SolveData::CostArray::LevelRef433 			static double value(const LevelRef* ref) {
434 				POTASSCO_REQUIRE(ref->at < ref->arr->size(), "expired key");
435 				return static_cast<double>(ref->arr->data->costs->at(ref->at));
436 			}
437 			const CostArray* arr;
438 			uint32           at;
439 		};
440 		typedef PodVector<LevelRef*>::type ElemVec;
sizeClasp::ClaspFacade::SolveData::CostArray441 		uint32 size() const {
442 			return data && data->costs ? sizeVec(*data->costs) : 0;
443 		}
atClasp::ClaspFacade::SolveData::CostArray444 		StatisticObject at(uint32 i) const {
445 			POTASSCO_REQUIRE(i < size(), "invalid key");
446 			while (i >= refs.size()) { refs.push_back(new LevelRef(this, sizeVec(refs))); }
447 			return StatisticObject::value<LevelRef, &LevelRef::value>(refs[i]);
448 		}
449 		const Model*    data;
450 		mutable ElemVec refs;
451 	};
452 	typedef SingleOwnerPtr<SolveAlgorithm> AlgoPtr;
453 	typedef SingleOwnerPtr<Enumerator>     EnumPtr;
454 	typedef Clasp::Atomic_t<int>::type     SafeIntType;
455 	typedef const SharedMinimizeData*      MinPtr;
SolveDataClasp::ClaspFacade::SolveData456 	SolveData() : en(0), algo(0), active(0), prepared(false), solved(false), interruptible(false) { qSig = 0; }
~SolveDataClasp::ClaspFacade::SolveData457 	~SolveData() { reset(); }
458 	void init(SolveAlgorithm* algo, Enumerator* en);
459 	void reset();
460 	void prepareEnum(SharedContext& ctx, int64 numM, EnumOptions::OptMode opt, EnumMode mode, ProjectMode prj);
interruptClasp::ClaspFacade::SolveData461 	bool interrupt(int sig) {
462 		if (solving()) { return active->interrupt(sig); }
463 		if (!qSig && sig != SolveStrategy::SIGCANCEL) { qSig = sig; }
464 		return false;
465 	}
onModelClasp::ClaspFacade::SolveData466 	bool onModel(const Solver& s, const Model& m) {
467 		return !active || active->setModel(s, m);
468 	}
solvingClasp::ClaspFacade::SolveData469 	bool         solving()   const { return active && active->running(); }
lastModelClasp::ClaspFacade::SolveData470 	const Model* lastModel() const { return en.get() ? &en->lastModel() : 0; }
minimizerClasp::ClaspFacade::SolveData471 	MinPtr       minimizer() const { return en.get() ? en->minimizer() : 0; }
enumeratorClasp::ClaspFacade::SolveData472 	Enumerator*  enumerator()const { return en.get(); }
modelTypeClasp::ClaspFacade::SolveData473 	int          modelType() const { return en.get() ? en->modelType() : 0; }
signalClasp::ClaspFacade::SolveData474 	int          signal()    const { return solving() ? active->signal() : static_cast<int>(qSig); }
475 	EnumPtr        en;
476 	AlgoPtr        algo;
477 	SolveStrategy* active;
478 	CostArray      costs;
479 	SafeIntType    qSig;
480 	bool           prepared;
481 	bool           solved;
482 	bool           interruptible;
483 };
init(SolveAlgorithm * a,Enumerator * e)484 void ClaspFacade::SolveData::init(SolveAlgorithm* a, Enumerator* e) {
485 	en = e;
486 	algo = a;
487 	costs.data = 0;
488 	algo->setEnumerator(*en);
489 	if (interruptible) {
490 		this->algo->enableInterrupts();
491 	}
492 }
reset()493 void ClaspFacade::SolveData::reset() {
494 	if (active)     { active->interrupt(SolveStrategy::SIGCANCEL); active->release(); active = 0; }
495 	if (algo.get()) { algo->resetSolve(); }
496 	if (en.get())   { en->reset(); }
497 	prepared = solved = false;
498 }
prepareEnum(SharedContext & ctx,int64 numM,EnumOptions::OptMode opt,EnumMode mode,ProjectMode proj)499 void ClaspFacade::SolveData::prepareEnum(SharedContext& ctx, int64 numM, EnumOptions::OptMode opt, EnumMode mode, ProjectMode proj) {
500 	POTASSCO_REQUIRE(!active, "Solve operation still active");
501 	if (ctx.ok() && !ctx.frozen() && !prepared) {
502 		if (mode == enum_volatile && ctx.solveMode() == SharedContext::solve_multi) {
503 			ctx.requestStepVar();
504 		}
505 		ctx.output.setProjectMode(proj);
506 		int lim = en->init(ctx, opt, (int)Range<int64>(-1, INT_MAX).clamp(numM));
507 		if (lim == 0 || numM < 0) {
508 			numM = lim;
509 		}
510 		algo->setEnumLimit(numM ? static_cast<uint64>(numM) : UINT64_MAX);
511 		costs.data = lastModel();
512 		prepared = true;
513 	}
514 }
SolveHandle(SolveStrategy * s)515 ClaspFacade::SolveHandle::SolveHandle(SolveStrategy* s) : strat_(s->share()) {}
~SolveHandle()516 ClaspFacade::SolveHandle::~SolveHandle() { strat_->release(); }
SolveHandle(const SolveHandle & o)517 ClaspFacade::SolveHandle::SolveHandle(const SolveHandle& o) : strat_(o.strat_->share()) {}
interrupted() const518 int  ClaspFacade::SolveHandle::interrupted()     const { return strat_->signal(); }
error() const519 bool ClaspFacade::SolveHandle::error()           const { return ready() && strat_->error(); }
ready() const520 bool ClaspFacade::SolveHandle::ready()           const { return strat_->ready(); }
running() const521 bool ClaspFacade::SolveHandle::running()         const { return strat_->running(); }
cancel() const522 void ClaspFacade::SolveHandle::cancel()          const { strat_->interrupt(SolveStrategy::SIGCANCEL); }
wait() const523 void ClaspFacade::SolveHandle::wait()            const { strat_->wait(-1.0); }
waitFor(double s) const524 bool ClaspFacade::SolveHandle::waitFor(double s) const { return strat_->wait(s); }
resume() const525 void ClaspFacade::SolveHandle::resume()          const { strat_->resume(); }
get() const526 SolveResult ClaspFacade::SolveHandle::get()      const { return strat_->result(); }
model() const527 const Model* ClaspFacade::SolveHandle::model()   const { return strat_->model(); }
next() const528 bool ClaspFacade::SolveHandle::next()            const { return strat_->next(); }
529 /////////////////////////////////////////////////////////////////////////////////////////
530 // ClaspFacade::Statistics
531 /////////////////////////////////////////////////////////////////////////////////////////
532 namespace {
533 struct KV { const char* key; StatisticObject(*get)(const ClaspFacade::Summary*); };
534 template <double ClaspFacade::Summary::*time>
_getT(const ClaspFacade::Summary * x)535 StatisticObject _getT(const ClaspFacade::Summary* x) { return StatisticObject::value(&static_cast<const double&>((x->*time))); }
536 template <uint64 ClaspFacade::Summary::*model>
_getM(const ClaspFacade::Summary * x)537 StatisticObject _getM(const ClaspFacade::Summary* x) { return StatisticObject::value(&static_cast<const uint64&>((x->*model))); }
538 static const KV sumKeys_s[] = {
539 	{"total"     , _getT<&ClaspFacade::Summary::totalTime>},
540 	{"cpu"       , _getT<&ClaspFacade::Summary::cpuTime>},
541 	{"solve"     , _getT<&ClaspFacade::Summary::solveTime>},
542 	{"unsat"     , _getT<&ClaspFacade::Summary::unsatTime>},
543 	{"sat"       , _getT<&ClaspFacade::Summary::satTime>},
544 	{"enumerated", _getM<&ClaspFacade::Summary::numEnum>},
545 	{"optimal"   , _getM<&ClaspFacade::Summary::numOptimal>},
546 };
547 struct SummaryStats {
SummaryStatsClasp::__anon3da370190411::SummaryStats548 	SummaryStats() : stats_(0), range_(0,0) {}
bindClasp::__anon3da370190411::SummaryStats549 	void bind(const ClaspFacade::Summary& x, Range32 r) {
550 		stats_ = &x;
551 		range_ = r;
552 	}
sizeClasp::__anon3da370190411::SummaryStats553 	uint32          size()        const { return range_.hi - range_.lo; }
keyClasp::__anon3da370190411::SummaryStats554 	const char*     key(uint32 i) const { return i < size() ? sumKeys_s[i + range_.lo].key : throw std::out_of_range(POTASSCO_FUNC_NAME); }
atClasp::__anon3da370190411::SummaryStats555 	StatisticObject at(const char* key) const {
556 		for (const KV* x = sumKeys_s + range_.lo, *end = sumKeys_s + range_.hi; x != end; ++x) {
557 			if (std::strcmp(x->key, key) == 0) { return x->get(stats_); }
558 		}
559 		throw std::out_of_range(POTASSCO_FUNC_NAME);
560 	}
toStatsClasp::__anon3da370190411::SummaryStats561 	StatisticObject toStats() const { return StatisticObject::map(this); }
562 	const ClaspFacade::Summary* stats_;
563 	Range32 range_;
564 };
565 
_getConcurrency(const SharedContext * ctx)566 double _getConcurrency(const SharedContext* ctx) { return ctx->concurrency(); }
_getWinner(const SharedContext * ctx)567 double _getWinner(const SharedContext* ctx) { return ctx->winner(); }
_getResult(const SolveResult * r)568 double _getResult(const SolveResult* r) { return static_cast<double>(r->operator Clasp::SolveResult::Base()); }
_getSignal(const SolveResult * r)569 double _getSignal(const SolveResult* r) { return static_cast<double>(r->signal); }
_getExhausted(const SolveResult * r)570 double _getExhausted(const SolveResult* r) { return static_cast<double>(r->exhausted()); }
571 }
572 
573 struct ClaspFacade::Statistics {
StatisticsClasp::ClaspFacade::Statistics574 	Statistics(ClaspFacade& f) : self_(&f), tester_(0), level_(0), clingo_(0) {}
~StatisticsClasp::ClaspFacade::Statistics575 	~Statistics() { delete clingo_; delete solvers_.multi; }
576 	void start(uint32 level);
577 	void initLevel(uint32 level);
578 	void end();
579 	void addTo(StatsMap& solving, StatsMap* accu) const;
580 	void accept(StatsVisitor& out, bool final) const;
incrementalClasp::ClaspFacade::Statistics581 	bool incremental() const { return self_->incremental(); }
582 	typedef StatsVec<SolverStats>        SolverVec;
583 	typedef SingleOwnerPtr<Asp::LpStats> LpStatsPtr;
584 	typedef PrgDepGraph::NonHcfStats     TesterStats;
585 	ClaspFacade* self_;
586 	LpStatsPtr   lp_;      // level 0 and asp
587 	SolverStats  solvers_; // level 0
588 	SolverVec    solver_;  // level > 1
589 	SolverVec    accu_;    // level > 1 and incremental
590 	TesterStats* tester_;  // level > 0 and nonhcfs
591 	uint32       level_;   // active stats level
592 	// For clingo stats interface
593 	class ClingoView : public ClaspStatistics {
594 	public:
595 		explicit ClingoView(const ClaspFacade& f);
596 		void update(const Statistics& s);
597 		Key_t user(bool final) const;
598 	private:
599 		struct StepStats {
600 			SummaryStats times;
601 			SummaryStats models;
bindClasp::ClaspFacade::Statistics::ClingoView::StepStats602 			void bind(const ClaspFacade::Summary& x) {
603 				times.bind(x, Range32(0, 5));
604 				models.bind(x, Range32(5, 7));
605 			}
addToClasp::ClaspFacade::Statistics::ClingoView::StepStats606 			void addTo(StatsMap& summary) {
607 				summary.add("times", times.toStats());
608 				summary.add("models", models.toStats());
609 			}
610 		};
611 		StatsMap*   keys_;
612 		StatsMap    problem_;
613 		StatsMap    solving_;
614 		struct Summary : StatsMap { StepStats step; } summary_;
615 		struct Accu    : StatsMap { StepStats step; StatsMap solving_; };
616 		typedef SingleOwnerPtr<Accu> AccuPtr;
617 		AccuPtr accu_;
618 	}* clingo_; // new clingo stats interface
619 	ClingoView* getClingo();
620 };
initLevel(uint32 level)621 void ClaspFacade::Statistics::initLevel(uint32 level) {
622 	if (level_ < level) {
623 		if (incremental() && !solvers_.multi) { solvers_.multi = new SolverStats(); }
624 		level_ = level;
625 	}
626 	if (self_->ctx.sccGraph.get() && self_->ctx.sccGraph->numNonHcfs() && !tester_) {
627 		tester_ = self_->ctx.sccGraph->nonHcfStats();
628 	}
629 }
630 
start(uint32 level)631 void ClaspFacade::Statistics::start(uint32 level) {
632 	// cleanup previous state
633 	solvers_.reset();
634 	solver_.reset();
635 	if (tester_) { tester_->startStep(self_->config()->testerConfig() ? self_->config()->testerConfig()->context().stats : 0); }
636 	// init next step
637 	initLevel(level);
638 	if (lp_.get() && self_->step_.lpStep()) {
639 		lp_->accu(*self_->step_.lpStep());
640 	}
641 	if (level > 1 && solver_.size() < self_->ctx.concurrency()) {
642 		uint32 sz = sizeVec(solver_);
643 		solver_.growTo(self_->ctx.concurrency());
644 		for (const bool inc = incremental() && (accu_.growTo(sizeVec(solver_)), true); sz != sizeVec(solver_); ++sz) {
645 			if (!inc) { solver_[sz] = &self_->ctx.solverStats(sz); }
646 			else      { (solver_[sz] = new SolverStats())->multi = (accu_[sz] = new SolverStats()); }
647 		}
648 		if (!incremental()) { solver_.release(); }
649 	}
650 }
end()651 void ClaspFacade::Statistics::end() {
652 	self_->ctx.accuStats(solvers_); // compute solvers = sum(solver[1], ... , solver[n])
653 	solvers_.flush();
654 	for (uint32 i = incremental() ? 0 : sizeVec(solver_), end = sizeVec(solver_); i != end && self_->ctx.hasSolver(i); ++i) {
655 		solver_[i]->accu(self_->ctx.solverStats(i), true);
656 		solver_[i]->flush();
657 	}
658 	if (tester_) { tester_->endStep(); }
659 	if (clingo_) { clingo_->update(*this); }
660 }
addTo(StatsMap & solving,StatsMap * accu) const661 void ClaspFacade::Statistics::addTo(StatsMap& solving, StatsMap* accu) const {
662 	solvers_.addTo("solvers", solving, accu);
663 	if (solver_.size())       { solving.add("solver", solver_.toStats()); }
664 	if (accu && accu_.size()) { accu->add("solver", accu_.toStats()); }
665 }
accept(StatsVisitor & out,bool final) const666 void ClaspFacade::Statistics::accept(StatsVisitor& out, bool final) const {
667 	final = final && solvers_.multi;
668 	if (out.visitGenerator(StatsVisitor::Enter)) {
669 		out.visitSolverStats(final ? *solvers_.multi : solvers_);
670 		if (lp_.get()) { out.visitLogicProgramStats(*lp_); }
671 		out.visitProblemStats(self_->ctx.stats());
672 		const SolverVec& solver = final ? accu_ : solver_;
673 		const uint32 nThreads = final ? (uint32)accu_.size() : self_->ctx.concurrency();
674 		const uint32 nSolver  = (uint32)solver.size();
675 		if (const AbstractStatistics::Key_t userKey = clingo_ ? clingo_->user(final) : 0) {
676 			out.visitExternalStats(clingo_->getObject(userKey));
677 		}
678 		if (nThreads > 1 && nSolver > 1 && out.visitThreads(StatsVisitor::Enter)) {
679 			for (uint32 i = 0, end = std::min(nSolver, nThreads); i != end; ++i) {
680 				out.visitThread(i, *solver[i]);
681 			}
682 			out.visitThreads(StatsVisitor::Leave);
683 		}
684 		out.visitGenerator(StatsVisitor::Leave);
685 	}
686 	if (tester_ && out.visitTester(StatsVisitor::Enter)) {
687 		tester_->accept(out, final);
688 		out.visitTester(StatsVisitor::Leave);
689 	}
690 }
getClingo()691 ClaspFacade::Statistics::ClingoView* ClaspFacade::Statistics::getClingo() {
692 	if (!clingo_) {
693 		clingo_ = new ClingoView(*this->self_);
694 		clingo_->update(*this);
695 	}
696 	return clingo_;
697 }
ClingoView(const ClaspFacade & f)698 ClaspFacade::Statistics::ClingoView::ClingoView(const ClaspFacade& f) {
699 	keys_ = makeRoot();
700 	summary_.add("call"       , StatisticObject::value(&f.step_.step));
701 	summary_.add("result"     , StatisticObject::value<SolveResult, _getResult>(&f.step_.result));
702 	summary_.add("signal"     , StatisticObject::value<SolveResult, _getSignal>(&f.step_.result));
703 	summary_.add("exhausted"  , StatisticObject::value<SolveResult, _getExhausted>(&f.step_.result));
704 	summary_.add("costs"      , StatisticObject::array(&f.solve_->costs));
705 	summary_.add("concurrency", StatisticObject::value<SharedContext, _getConcurrency>(&f.ctx));
706 	summary_.add("winner"     , StatisticObject::value<SharedContext, _getWinner>(&f.ctx));
707 	summary_.step.bind(f.step_);
708 	summary_.step.addTo(summary_);
709 	if (f.step_.lpStats()) {
710 		problem_.add("lp", StatisticObject::map(f.step_.lpStats()));
711 		if (f.incremental()) { problem_.add("lpStep", StatisticObject::map(f.step_.lpStep())); }
712 	}
713 	problem_.add("generator", StatisticObject::map(&f.ctx.stats()));
714 	keys_->add("problem", problem_.toStats());
715 	keys_->add("solving", solving_.toStats());
716 	keys_->add("summary", summary_.toStats());
717 
718 	if (f.incremental()) {
719 		accu_ = new Accu();
720 		accu_->step.bind(*f.accu_.get());
721 	}
722 }
user(bool final) const723 Potassco::AbstractStatistics::Key_t ClaspFacade::Statistics::ClingoView::user(bool final) const {
724 	Key_t key = 0;
725 	find(root(), final ? "user_accu" : "user_step", &key);
726 	return key;
727 }
update(const ClaspFacade::Statistics & stats)728 void ClaspFacade::Statistics::ClingoView::update(const ClaspFacade::Statistics& stats) {
729 	if (stats.level_ > 0 && accu_.get() && keys_->add("accu", accu_->toStats())) {
730 		accu_->step.addTo(*accu_);
731 		accu_->add("solving", accu_->solving_.toStats());
732 	}
733 	stats.addTo(solving_, stats.level_ > 0 && accu_.get() ? &accu_->solving_ : 0);
734 	if (stats.tester_) {
735 		stats.tester_->addTo(problem_, solving_, stats.level_ > 0 && accu_.get() ? &accu_->solving_ : 0);
736 	}
737 }
738 /////////////////////////////////////////////////////////////////////////////////////////
739 // ClaspFacade
740 /////////////////////////////////////////////////////////////////////////////////////////
ClaspFacade()741 ClaspFacade::ClaspFacade() : config_(0) { step_.init(*this); }
~ClaspFacade()742 ClaspFacade::~ClaspFacade() {}
prepared() const743 bool ClaspFacade::prepared() const {
744 	return solve_.get() && solve_->prepared;
745 }
solving() const746 bool ClaspFacade::solving() const {
747 	return solve_.get() && solve_->solving();
748 }
solved() const749 bool ClaspFacade::solved() const {
750 	return solve_.get() && solve_->solved;
751 }
interrupted() const752 bool ClaspFacade::interrupted() const {
753 	return result().interrupted();
754 }
incremental() const755 bool ClaspFacade::incremental() const {
756 	return accu_.get() != 0;
757 }
detectProblemType(std::istream & str)758 ProblemType ClaspFacade::detectProblemType(std::istream& str) {
759 	return Clasp::detectProblemType(str);
760 }
summary(bool accu) const761 const ClaspFacade::Summary&  ClaspFacade::summary(bool accu) const {
762 	return accu && accu_.get() ? *accu_ : step_;
763 }
764 
discardProblem()765 void ClaspFacade::discardProblem() {
766 	config_  = 0;
767 	builder_ = 0;
768 	stats_   = 0;
769 	solve_   = 0;
770 	accu_    = 0;
771 	step_.init(*this);
772 	if (ctx.frozen() || ctx.numVars()) { ctx.reset(); }
773 }
init(ClaspConfig & config,bool discard)774 void ClaspFacade::init(ClaspConfig& config, bool discard) {
775 	if (discard) { discardProblem(); }
776 	ctx.setConfiguration(0, Ownership_t::Retain); // force reload of configuration once done
777 	config_ = &config;
778 	if (config_->solve.enumMode == EnumOptions::enum_dom_record && config_->solver(0).heuId != Heuristic_t::Domain) {
779 		ctx.warn("Reasoning mode requires domain heuristic and is ignored.");
780 		config_->solve.enumMode = EnumOptions::enum_auto;
781 	}
782 	SolveData::EnumPtr e(config.solve.createEnumerator(config.solve));
783 	if (e.get() == 0) { e = EnumOptions::nullEnumerator(); }
784 	if (config.solve.numSolver() > 1 && !e->supportsParallel()) {
785 		ctx.warn("Selected reasoning mode implies #Threads=1.");
786 		config.solve.setSolvers(1);
787 	}
788 	ctx.setConfiguration(&config, Ownership_t::Retain); // prepare and apply config
789 	if (program() && type_ == Problem_t::Asp) {
790 		Asp::LogicProgram* p = static_cast<Asp::LogicProgram*>(program());
791 		p->setOptions(config.asp);
792 		p->setNonHcfConfiguration(config.testerConfig());
793 	}
794 	if (!solve_.get()) { solve_ = new SolveData(); }
795 	SolveData::AlgoPtr a(config.solve.createSolveObject());
796 	solve_->init(a.release(), e.release());
797 	if (discard) { startStep(0); }
798 }
799 
initBuilder(ProgramBuilder * in)800 void ClaspFacade::initBuilder(ProgramBuilder* in) {
801 	builder_ = in;
802 	assume_.clear();
803 	builder_->startProgram(ctx);
804 }
start(ClaspConfig & config,ProblemType t)805 ProgramBuilder& ClaspFacade::start(ClaspConfig& config, ProblemType t) {
806 	if      (t == Problem_t::Sat) { return startSat(config); }
807 	else if (t == Problem_t::Pb)  { return startPB(config);  }
808 	else if (t == Problem_t::Asp) { return startAsp(config); }
809 	else                          { throw std::domain_error("Unknown problem type!"); }
810 }
811 
start(ClaspConfig & config,std::istream & str)812 ProgramBuilder& ClaspFacade::start(ClaspConfig& config, std::istream& str) {
813 	ProgramParser& p = start(config, detectProblemType(str)).parser();
814 	POTASSCO_REQUIRE(p.accept(str, config_->parse), "Auto detection failed!");
815 	if (p.incremental()) { enableProgramUpdates(); }
816 	return *program();
817 }
818 
startSat(ClaspConfig & config)819 SatBuilder& ClaspFacade::startSat(ClaspConfig& config) {
820 	init(config, true);
821 	initBuilder(new SatBuilder());
822 	type_ = Problem_t::Sat;
823 	return static_cast<SatBuilder&>(*builder_.get());
824 }
825 
startPB(ClaspConfig & config)826 PBBuilder& ClaspFacade::startPB(ClaspConfig& config) {
827 	init(config, true);
828 	initBuilder(new PBBuilder());
829 	type_ = Problem_t::Sat;
830 	return static_cast<PBBuilder&>(*builder_.get());
831 }
832 
startAsp(ClaspConfig & config,bool enableUpdates)833 Asp::LogicProgram& ClaspFacade::startAsp(ClaspConfig& config, bool enableUpdates) {
834 	init(config, true);
835 	Asp::LogicProgram* p = new Asp::LogicProgram();
836 	initBuilder(p);
837 	p->setOptions(config.asp);
838 	p->setNonHcfConfiguration(config.testerConfig());
839 	type_ = Problem_t::Asp;
840 	stats_->lp_ = new Asp::LpStats();
841 	if (enableUpdates) { enableProgramUpdates(); }
842 	return *p;
843 }
844 
enableProgramUpdates()845 bool ClaspFacade::enableProgramUpdates() {
846 	POTASSCO_REQUIRE(program(), "Program was already released!");
847 	POTASSCO_REQUIRE(!solving() && !program()->frozen());
848 	if (!accu_.get()) {
849 		builder_->updateProgram();
850 		ctx.setSolveMode(SharedContext::solve_multi);
851 		enableSolveInterrupts();
852 		accu_ = new Summary();
853 		accu_->init(*this);
854 		accu_->step = UINT32_MAX;
855 	}
856 	return isAsp(); // currently only ASP supports program updates
857 }
enableSolveInterrupts()858 void ClaspFacade::enableSolveInterrupts() {
859 	POTASSCO_REQUIRE(!solving(), "Solving is already active!");
860 	POTASSCO_ASSERT(solve_.get(), "Active program required!");
861 	if (!solve_->interruptible) {
862 		solve_->interruptible = true;
863 		solve_->algo->enableInterrupts();
864 	}
865 }
866 
startStep(uint32 n)867 void ClaspFacade::startStep(uint32 n) {
868 	step_.init(*this);
869 	step_.totalTime = RealTime::getTime();
870 	step_.cpuTime   = ProcessTime::getTime();
871 	step_.step      = n;
872 	solve_->solved  = false;
873 	if (!stats_.get()) { stats_ = new Statistics(*this); }
874 	ctx.report(StepStart(*this));
875 }
876 
stopStep(int signal,bool complete)877 ClaspFacade::Result ClaspFacade::stopStep(int signal, bool complete) {
878 	if (!solved()) {
879 		double t = RealTime::getTime();
880 		solve_->solved  = true;
881 		step_.totalTime = diffTime(t, step_.totalTime);
882 		step_.cpuTime   = diffTime(ProcessTime::getTime(), step_.cpuTime);
883 		if (step_.solveTime) {
884 			step_.solveTime = diffTime(t, step_.solveTime);
885 			step_.unsatTime = complete ? diffTime(t, step_.unsatTime) : 0;
886 		}
887 		Result res = {uint8(0), uint8(signal)};
888 		if (complete) { res.flags = uint8(step_.numEnum ? Result::SAT : Result::UNSAT) | Result::EXT_EXHAUST; }
889 		else          { res.flags = uint8(step_.numEnum ? Result::SAT : Result::UNKNOWN); }
890 		if (signal)   { res.flags|= uint8(Result::EXT_INTERRUPT); }
891 		step_.result = res;
892 		if (res.sat() && step_.model()->opt && !step_.numOptimal) {
893 			step_.numOptimal = 1;
894 		}
895 		updateStats();
896 		ctx.report(StepReady(step_));
897 		ctx.report(Event::subsystem_facade);
898 	}
899 	return result();
900 }
901 
updateStats()902 void ClaspFacade::updateStats() {
903 	if (stats_.get()) {
904 		stats_->end();
905 	}
906 	if (accu_.get() && accu_->step != step_.step) {
907 		accu_->totalTime  += step_.totalTime;
908 		accu_->cpuTime    += step_.cpuTime;
909 		accu_->solveTime  += step_.solveTime;
910 		accu_->unsatTime  += step_.unsatTime;
911 		accu_->satTime    += step_.satTime;
912 		accu_->numEnum    += step_.numEnum;
913 		accu_->numOptimal += step_.numOptimal;
914 		// no aggregation
915 		accu_->step   = step_.step;
916 		accu_->result = step_.result;
917 	}
918 }
919 
interrupt(int signal)920 bool ClaspFacade::interrupt(int signal) {
921 	return solve_.get() && (signal || (signal = solve_->qSig.exchange(0)) != 0) && solve_->interrupt(signal);
922 }
923 
shutdown()924 const ClaspFacade::Summary& ClaspFacade::shutdown() {
925 	if (solve_.get()) {
926 		solve_->interrupt(SolveStrategy::SIGCANCEL);
927 		stopStep(solve_->signal(), !ok());
928 	}
929 	return summary(true);
930 }
931 
read()932 bool ClaspFacade::read() {
933 	POTASSCO_REQUIRE(solve_.get());
934 	if (!program() || interrupted()) { return false; }
935 	ProgramParser& p = program()->parser();
936 	if (!p.isOpen() || (solved() && !update().ok())) { return false; }
937 	POTASSCO_REQUIRE(p.parse(), "Invalid input stream!");
938 	if (!p.more()) { p.reset(); }
939 	return true;
940 }
941 
prepare(EnumMode enumMode)942 void ClaspFacade::prepare(EnumMode enumMode) {
943 	POTASSCO_REQUIRE(solve_.get() && !solving());
944 	EnumOptions& en = config_->solve;
945 	if (solved()) {
946 		doUpdate(0, false, SIG_DFL);
947 		solve_->prepareEnum(ctx, en.numModels, en.optMode, enumMode, en.proMode);
948 		ctx.endInit();
949 	}
950 	if (prepared()) { return; }
951 	SharedMinimizeData* m = 0;
952 	ProgramBuilder*   prg = program();
953 	if (prg && prg->endProgram()) {
954 		assume_.clear();
955 		prg->getAssumptions(assume_);
956 		prg->getWeakBounds(en.optBound);
957 	}
958 	stats_->start(uint32(config_->context().stats));
959 	if (ctx.ok() && en.optMode != MinimizeMode_t::ignore && (m = ctx.minimize()) != 0) {
960 		if (!m->setMode(en.optMode, en.optBound)) {
961 			assume_.push_back(lit_false());
962 		}
963 		if (en.optMode == MinimizeMode_t::enumerate && en.optBound.empty()) {
964 			ctx.warn("opt-mode=enum: No bound given, optimize statement ignored.");
965 		}
966 	}
967 	POTASSCO_REQUIRE(!ctx.ok() || !ctx.frozen());
968 	solve_->prepareEnum(ctx, en.numModels, en.optMode, enumMode, en.proMode);
969 	if      (!accu_.get()) { builder_ = 0; }
970 	else if (isAsp())      { static_cast<Asp::LogicProgram*>(builder_.get())->dispose(false); }
971 	if (!builder_.get() && !ctx.heuristic.empty()) {
972 		bool keepDom = false;
973 		for (uint32 i = 0; i != config_->solve.numSolver() && !keepDom; ++i) {
974 			keepDom = config_->solver(i).heuId == Heuristic_t::Domain;
975 		}
976 		if (!keepDom) { ctx.heuristic.reset(); }
977 	}
978 	if (ctx.ok()) { ctx.endInit(); }
979 }
980 
solve(SolveMode_t p,const LitVec & a,EventHandler * eh)981 ClaspFacade::SolveHandle ClaspFacade::solve(SolveMode_t p, const LitVec& a, EventHandler* eh) {
982 	prepare();
983 	solve_->active = SolveStrategy::create(p, *this, *solve_->algo.get());
984 	solve_->active->start(eh, a);
985 	return SolveHandle(solve_->active);
986 }
solve(const LitVec & a,EventHandler * handler)987 ClaspFacade::Result ClaspFacade::solve(const LitVec& a, EventHandler* handler) {
988 	return solve(SolveMode_t::Default, a, handler).get();
989 }
990 
update(bool updateConfig,void (* sigAct)(int))991 ProgramBuilder& ClaspFacade::update(bool updateConfig, void (*sigAct)(int)) {
992 	POTASSCO_REQUIRE(config_ && program() && !solving());
993 	doUpdate(program(), updateConfig, sigAct);
994 	return *program();
995 }
update(bool updateConfig)996 ProgramBuilder& ClaspFacade::update(bool updateConfig) {
997 	return update(updateConfig, SIG_DFL);
998 }
999 
doUpdate(ProgramBuilder * p,bool updateConfig,void (* sigAct)(int))1000 void ClaspFacade::doUpdate(ProgramBuilder* p, bool updateConfig, void(*sigAct)(int)) {
1001 	if (updateConfig) {
1002 		init(*config_, false);
1003 	}
1004 	if (solved()) {
1005 		startStep(step() + 1);
1006 	}
1007 	if (p && p->frozen()) {
1008 		p->updateProgram();
1009 	}
1010 	if (ctx.frozen()) {
1011 		ctx.unfreeze();
1012 	}
1013 	solve_->reset();
1014 	config_->unfreeze(ctx);
1015 	int sig = sigAct == SIG_DFL ? 0 : solve_->qSig.exchange(0);
1016 	if (sig && sigAct != SIG_IGN) { sigAct(sig); }
1017 }
1018 
onModel(const Solver & s,const Model & m)1019 bool ClaspFacade::onModel(const Solver& s, const Model& m) {
1020 	step_.unsatTime = RealTime::getTime();
1021 	if (++step_.numEnum == 1) { step_.satTime = diffTime(step_.unsatTime, step_.solveTime); }
1022 	if (m.opt) { ++step_.numOptimal; }
1023 	return solve_->onModel(s, m);
1024 }
enumerator() const1025 Enumerator* ClaspFacade::enumerator() const { return solve_.get() ? solve_->enumerator() : 0; }
getStats() const1026 Potassco::AbstractStatistics* ClaspFacade::getStats() const {
1027 	POTASSCO_REQUIRE(stats_.get() && !solving(), "statistics not (yet) available");
1028 	return stats_->getClingo();
1029 }
1030 /////////////////////////////////////////////////////////////////////////////////////////
1031 // ClaspFacade::Summary
1032 /////////////////////////////////////////////////////////////////////////////////////////
init(ClaspFacade & f)1033 void ClaspFacade::Summary::init(ClaspFacade& f)  { std::memset(this, 0, sizeof(Summary)); facade = &f;}
model() const1034 const Model* ClaspFacade::Summary::model() const { return facade->solve_.get() ? facade->solve_->lastModel() : 0; }
costs() const1035 const SumVec* ClaspFacade::Summary::costs()const { return model() ? model()->costs : 0; }
optimal() const1036 uint64  ClaspFacade::Summary::optimal()    const { return facade->step_.numOptimal; }
optimize() const1037 bool ClaspFacade::Summary::optimize()      const {
1038 	if (const Enumerator* e = facade->enumerator()){
1039 		return e->optimize() || e->lastModel().opt;
1040 	}
1041 	return false;
1042 }
lpStep() const1043 const Asp::LpStats* ClaspFacade::Summary::lpStep() const {
1044 	return facade->isAsp() ? &static_cast<const Asp::LogicProgram*>(facade->program())->stats : 0;
1045 }
lpStats() const1046 const Asp::LpStats* ClaspFacade::Summary::lpStats() const {
1047 	return facade->stats_.get() ? facade->stats_->lp_.get() : lpStep();
1048 }
consequences() const1049 const char* ClaspFacade::Summary::consequences() const {
1050 	const Model* m = model();
1051 	return m && m->consequences() ? modelType(*m) : 0;
1052 }
1053 
hasLower() const1054 bool ClaspFacade::Summary::hasLower() const {
1055 	const SharedMinimizeData* m = optimize() ? facade->enumerator()->minimizer() : 0;
1056 	return m && m->lower(0) != 0;
1057 }
lower() const1058 SumVec ClaspFacade::Summary::lower() const {
1059 	if (hasLower()) {
1060 		const SharedMinimizeData* m = facade->enumerator()->minimizer();
1061 		SumVec ret(m->numRules());
1062 		for (uint32 i = 0; i != m->numRules(); ++i) {
1063 			ret[i] = m->lower(i) + m->adjust(i);
1064 		}
1065 		return ret;
1066 	}
1067 	return SumVec();
1068 }
accept(StatsVisitor & out) const1069 void ClaspFacade::Summary::accept(StatsVisitor& out) const {
1070 	if (facade->solved()) { facade->stats_->accept(out, this == facade->accu_.get()); }
1071 }
1072 
1073 }
1074 
1075