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_app.h"
25 #include <clasp/solver.h>
26 #include <clasp/dependency_graph.h>
27 #include <clasp/parser.h>
28 #include <clasp/clause.h>
29 #include <potassco/aspif.h>
30 #include <potassco/string_convert.h>
31 #include <iostream>
32 #include <fstream>
33 #include <cstdlib>
34 #include <climits>
35 #include <signal.h>
36 #ifdef _MSC_VER
37 #pragma warning (disable : 4996)
38 #endif
39 #if defined(__GLIBC__) || defined(__GNU_LIBRARY__)
40 #include <fpu_control.h>
41 #if defined(_FPU_EXTENDED) && defined(_FPU_SINGLE) && defined(_FPU_DOUBLE) && defined(_FPU_GETCW) && defined(_FPU_SETCW)
42 #define CLASP_HAS_FPU_CONTROL
fpuReset(unsigned m)43 inline unsigned fpuReset(unsigned m) { _FPU_SETCW(m); return m; }
fpuInit()44 inline unsigned fpuInit()            { unsigned r; _FPU_GETCW(r); fpuReset((r & ~_FPU_EXTENDED & ~_FPU_SINGLE) | _FPU_DOUBLE); return r; }
45 #endif
46 #elif defined (_MSC_VER) && !defined(_WIN64)
47 #include <float.h>
48 #define CLASP_HAS_FPU_CONTROL
fpuReset(unsigned m)49 inline unsigned fpuReset(unsigned m) { _controlfp(m, _MCW_PC); return m; }
fpuInit()50 inline unsigned fpuInit()            { unsigned r = _controlfp(0, 0);  fpuReset(_PC_53); return r; }
51 #pragma fenv_access (on)
52 #endif
53 #if !defined(CLASP_HAS_FPU_CONTROL)
fpuReset(unsigned)54 inline unsigned fpuReset(unsigned) { return 0u; }
fpuInit()55 inline unsigned fpuInit()          { return 0u; }
56 #endif
setFpuMode()57 inline bool setFpuMode() { return (sizeof(void*)*CHAR_BIT) < size_t(64u); }
58 namespace Clasp {
59 /////////////////////////////////////////////////////////////////////////////////////////
60 // Some helpers
61 /////////////////////////////////////////////////////////////////////////////////////////
62 static double shutdownTime_g;
63 static const std::string stdinStr  = "stdin";
64 static const std::string stdoutStr = "stdout";
isStdIn(const std::string & in)65 inline bool isStdIn(const std::string& in)   { return in == "-" || in == stdinStr; }
isStdOut(const std::string & out)66 inline bool isStdOut(const std::string& out) { return out == "-" || out == stdoutStr; }
67 /////////////////////////////////////////////////////////////////////////////////////////
68 // ClaspAppOptions
69 /////////////////////////////////////////////////////////////////////////////////////////
70 namespace Cli {
ClaspAppOptions()71 ClaspAppOptions::ClaspAppOptions() : outf(0), compute(0), ifs(' '), hideAux(false), onlyPre(0), printPort(false) {
72 	quiet[0] = quiet[1] = quiet[2] = static_cast<uint8>(UCHAR_MAX);
73 }
initOptions(Potassco::ProgramOptions::OptionContext & root)74 void ClaspAppOptions::initOptions(Potassco::ProgramOptions::OptionContext& root) {
75 	using namespace Potassco::ProgramOptions;
76 	OptionGroup basic("Basic Options");
77 	basic.addOptions()
78 		("print-portfolio,@1", flag(printPort), "Print default portfolio and exit")
79 		("quiet,q"    , notify(this, &ClaspAppOptions::mappedOpts)->implicit("2,2,2")->arg("<levels>"),
80 		 "Configure printing of models, costs, and calls\n"
81 		 "      %A: <mod>[,<cost>][,<call>]\n"
82 		 "        <mod> : print {0=all|1=last|2=no} models\n"
83 		 "        <cost>: print {0=all|1=last|2=no} optimize values [<mod>]\n"
84 		 "        <call>: print {0=all|1=last|2=no} call steps      [2]")
85 		("pre", notify(this, &ClaspAppOptions::mappedOpts)->arg("<fmt>")->implicit("aspif"), "Print simplified program and exit\n"
86 		 "      %A: Set output format to {aspif|smodels} (implicit: %I)")
87 		("outf,@1", storeTo(outf)->arg("<n>"), "Use {0=default|1=competition|2=JSON|3=no} output")
88 		("out-atomf,@2" , storeTo(outAtom), "Set atom format string (<Pre>?%%0<Post>?)")
89 		("out-ifs,@2"   , notify(this, &ClaspAppOptions::mappedOpts), "Set internal field separator")
90 		("out-hide-aux,@1" , flag(hideAux), "Hide auxiliary atoms in answers")
91 		("lemma-in,@1"     , storeTo(lemmaIn)->arg("<file>"), "Read additional lemmas from %A")
92 		("lemma-out,@1"    , storeTo(lemmaLog)->arg("<file>"), "Log learnt lemmas to %A")
93 		("lemma-out-lbd,@2", storeTo(lemma.lbdMax)->arg("<n>"), "Only log lemmas with lbd <= %A")
94 		("lemma-out-max,@2", storeTo(lemma.logMax)->arg("<n>"), "Stop logging after %A lemmas")
95 		("lemma-out-dom,@2", notify(this, &ClaspAppOptions::mappedOpts), "Log lemmas over <arg {input|output}> variables")
96 		("lemma-out-txt,@2", flag(lemma.logText), "Log lemmas as ground integrity constraints")
97 		("hcc-out,@2", storeTo(hccOut)->arg("<file>"), "Write non-hcf programs to %A.#scc")
98 		("file,f,@3" , storeTo(input)->composing(), "Input files")
99 		("compute,@2", storeTo(compute)->arg("<lit>"), "Force given literal to true")
100 	;
101 	root.add(basic);
102 }
mappedOpts(ClaspAppOptions * this_,const std::string & name,const std::string & value)103 bool ClaspAppOptions::mappedOpts(ClaspAppOptions* this_, const std::string& name, const std::string& value) {
104 	if (name == "quiet") {
105 		const char* err = 0;
106 		uint32      q[3]= {uint32(UCHAR_MAX),uint32(UCHAR_MAX),uint32(UCHAR_MAX)};
107 		int      parsed = Potassco::xconvert(value.c_str(), q, &err);
108 		for (int i = 0; i != parsed; ++i) { this_->quiet[i] = static_cast<uint8>(q[i]); }
109 		return parsed && *err == 0;
110 	}
111 	else if (name == "out-ifs") {
112 		if (value.empty() || value.size() > 2) { return false;}
113 		if (value.size() == 1) { this_->ifs = value[0]; return true; }
114 		if (value[1] == 't')   { this_->ifs = '\t'; return true; }
115 		if (value[1] == 'n')   { this_->ifs = '\n'; return true; }
116 		if (value[1] == 'v')   { this_->ifs = '\v'; return true; }
117 		if (value[1] == '\\')  { this_->ifs = '\\'; return true; }
118 	}
119 	else if (name == "lemma-out-dom") {
120 		return (this_->lemma.domOut = (strcasecmp(value.c_str(), "output") == 0)) == true || strcasecmp(value.c_str(), "input") == 0;
121 	}
122 	else if (name == "pre") {
123 		if      (strcasecmp(value.c_str(), "aspif")   == 0) { this_->onlyPre = (int8)AspParser::format_aspif; return true; }
124 		else if (strcasecmp(value.c_str(), "smodels") == 0) { this_->onlyPre = (int8)AspParser::format_smodels; return true; }
125 	}
126 	return false;
127 }
validateOptions(const Potassco::ProgramOptions::ParsedOptions &)128 bool ClaspAppOptions::validateOptions(const Potassco::ProgramOptions::ParsedOptions&) {
129 	if (quiet[1] == static_cast<uint8>(UCHAR_MAX)) { quiet[1] = quiet[0]; }
130 	return true;
131 }
132 /////////////////////////////////////////////////////////////////////////////////////////
133 // ClaspAppBase
134 /////////////////////////////////////////////////////////////////////////////////////////
ClaspAppBase()135 ClaspAppBase::ClaspAppBase() { }
~ClaspAppBase()136 ClaspAppBase::~ClaspAppBase(){ }
getSignals() const137 const int* ClaspAppBase::getSignals() const {
138 	static const int signals[] = {
139 		SIGINT, SIGTERM
140 #if !defined (_WIN32)
141 		, SIGUSR1, SIGUSR2, SIGQUIT, SIGHUP, SIGXCPU, SIGXFSZ
142 #endif
143 		, 0};
144 		return signals;
145 }
parsePositional(const std::string & t,std::string & out)146 bool ClaspAppBase::parsePositional(const std::string& t, std::string& out) {
147 	int num;
148 	if (Potassco::string_cast(t, num)) { out = "number"; }
149 	else                               { out = "file";   }
150 	return true;
151 }
initOptions(Potassco::ProgramOptions::OptionContext & root)152 void ClaspAppBase::initOptions(Potassco::ProgramOptions::OptionContext& root) {
153 	claspConfig_.addOptions(root);
154 	claspAppOpts_.initOptions(root);
155 	root.find("verbose")->get()->value()->defaultsTo("1");
156 }
157 
validateOptions(const Potassco::ProgramOptions::OptionContext &,const Potassco::ProgramOptions::ParsedOptions & parsed,const Potassco::ProgramOptions::ParsedValues & values)158 void ClaspAppBase::validateOptions(const Potassco::ProgramOptions::OptionContext&, const Potassco::ProgramOptions::ParsedOptions& parsed, const Potassco::ProgramOptions::ParsedValues& values) {
159 	if (claspAppOpts_.printPort) {
160 		printTemplate();
161 		exit(E_UNKNOWN);
162 	}
163 	setExitCode(E_NO_RUN);
164 	ProblemType pt = getProblemType();
165 	POTASSCO_REQUIRE(claspAppOpts_.validateOptions(parsed) && claspConfig_.finalize(parsed, pt, true), "command-line error!");
166 	ClaspAppOptions& app = claspAppOpts_;
167 	POTASSCO_REQUIRE(app.lemmaLog.empty() || isStdOut(app.lemmaLog) || (std::find(app.input.begin(), app.input.end(), app.lemmaLog) == app.input.end() && app.lemmaIn != app.lemmaLog),
168 		"'lemma-out': cowardly refusing to overwrite input file!");
169 	POTASSCO_REQUIRE(app.lemmaIn.empty() || isStdIn(app.lemmaIn) || std::ifstream(app.lemmaIn.c_str()).is_open(),
170 		"'lemma-in': could not open file!");
171 	for (std::size_t i = 1; i < app.input.size(); ++i) {
172 		POTASSCO_EXPECT(isStdIn(app.input[i]) || std::ifstream(app.input[i].c_str()).is_open(),
173 			"'%s': could not open input file!", app.input[i].c_str());
174 	}
175 	POTASSCO_REQUIRE(!app.onlyPre || pt == Problem_t::Asp, "Option '--pre' only supported for ASP!");
176 	setExitCode(0);
177 	storeCommandArgs(values);
178 }
setup()179 void ClaspAppBase::setup() {
180 	ProblemType pt = getProblemType();
181 	clasp_         = new ClaspFacade();
182 	if (setFpuMode()) { fpuMode_ = fpuInit(); }
183 	if (!claspAppOpts_.onlyPre) {
184 		out_ = createOutput(pt);
185 		Event::Verbosity verb	= (Event::Verbosity)std::min(verbose(), (uint32)Event::verbosity_max);
186 		if (out_.get() && out_->verbosity() < (uint32)verb) { verb = (Event::Verbosity)out_->verbosity(); }
187 		if (!claspAppOpts_.lemmaLog.empty()) {
188 			logger_ = new LemmaLogger(claspAppOpts_.lemmaLog.c_str(), claspAppOpts_.lemma);
189 		}
190 		EventHandler::setVerbosity(Event::subsystem_facade , verb);
191 		EventHandler::setVerbosity(Event::subsystem_load   , verb);
192 		EventHandler::setVerbosity(Event::subsystem_prepare, verb);
193 		EventHandler::setVerbosity(Event::subsystem_solve  , verb);
194 		clasp_->ctx.setEventHandler(this, logger_.get() == 0 ? SharedContext::report_default : SharedContext::report_conflict);
195 	}
196 }
197 
shutdown()198 void ClaspAppBase::shutdown() {
199 	if (!clasp_.get()) { return; }
200 	if (logger_.get()) { logger_->close(); }
201 	lemmaIn_ = 0;
202 	const ClaspFacade::Summary& result = clasp_->shutdown();
203 	if (shutdownTime_g) {
204 		shutdownTime_g += RealTime::getTime();
205 		info(POTASSCO_FORMAT("Shutdown completed in %.3f seconds", shutdownTime_g));
206 	}
207 	if (out_.get())  { out_->shutdown(result); }
208 	setExitCode(getExitCode() | exitCode(result));
209 	if (setFpuMode()){ fpuReset(fpuMode_); }
210 }
211 
run()212 void ClaspAppBase::run() {
213 	if (out_.get()) {
214 		Potassco::Span<std::string> in = !claspAppOpts_.input.empty() ? Potassco::toSpan(claspAppOpts_.input) : Potassco::toSpan(&stdinStr, 1);
215 		out_->run(getName(), getVersion(), Potassco::begin(in), Potassco::end(in));
216 	}
217 	try        { run(*clasp_); }
218 	catch(...) {
219 		try { blockSignals(); setExitCode(E_ERROR); throw; }
220 		catch (const std::bad_alloc&  ) { setExitCode(E_MEMORY); error("std::bad_alloc"); }
221 		catch (const std::exception& e) { error(e.what()); }
222 		catch (...)                     { ; }
223 	}
224 }
225 
onSignal(int sig)226 bool ClaspAppBase::onSignal(int sig) {
227 	if (!clasp_.get() || !clasp_->interrupt(sig)) {
228 		info("INTERRUPTED by signal!");
229 		setExitCode(E_INTERRUPT);
230 		shutdown();
231 		exit(getExitCode());
232 	}
233 	else {
234 		// multiple threads are active - shutdown was initiated
235 		shutdownTime_g = -RealTime::getTime();
236 		info("Sending shutdown signal...");
237 	}
238 	return false; // ignore all future signals
239 }
240 
onEvent(const Event & ev)241 void ClaspAppBase::onEvent(const Event& ev) {
242 	const LogEvent* log = event_cast<LogEvent>(ev);
243 	if (log && log->isWarning()) {
244 		warn(log->msg);
245 		return;
246 	}
247 	else if (const NewConflictEvent* cfl = event_cast<NewConflictEvent>(ev)) {
248 		if (logger_.get()) { logger_->add(*cfl->solver, *cfl->learnt, cfl->info); }
249 		return;
250 	}
251 	if (out_.get()) {
252 		blockSignals();
253 		out_->onEvent(ev);
254 		unblockSignals(true);
255 	}
256 }
257 
onModel(const Solver & s,const Model & m)258 bool ClaspAppBase::onModel(const Solver& s, const Model& m) {
259 	bool ret = true;
260 	if (out_.get() && !out_->quiet()) {
261 		blockSignals();
262 		ret = out_->onModel(s, m);
263 		unblockSignals(true);
264 	}
265 	return ret;
266 }
onUnsat(const Solver & s,const Model & m)267 bool ClaspAppBase::onUnsat(const Solver& s, const Model& m) {
268 	bool ret = true;
269 	if (out_.get() && !out_->quiet()) {
270 		blockSignals();
271 		ret = out_->onUnsat(s, m);
272 		unblockSignals(true);
273 	}
274 	return ret;
275 }
276 
exitCode(const RunSummary & run) const277 int ClaspAppBase::exitCode(const RunSummary& run) const {
278 	int ec = 0;
279 	if (run.sat())               { ec |= E_SAT;       }
280 	if (run.complete())          { ec |= E_EXHAUST;   }
281 	if (run.result.interrupted()){ ec |= E_INTERRUPT; }
282 	return ec;
283 }
284 
printTemplate() const285 void ClaspAppBase::printTemplate() const {
286 	printf(
287 		"# clasp %s configuration file\n"
288 		"# A configuration file contains a (possibly empty) list of configurations.\n"
289 		"# Each of which must have the following format:\n"
290 		"#   <name>[(<config>)]: <cmd>\n"
291 		"# where <name> is a string that must not contain ':',\n"
292 		"# <config> is one of clasp's default configs (and optional)\n"
293 		"# and   <cmd>  is a command-line string of clasp options in long-format, e.g.\n"
294 		"# ('--heuristic=vsids --restarts=L,100').\n"
295 		"#\n"
296 		"# SEE: clasp --help\n"
297 		"#\n"
298 		"# NOTE: The options '--configuration' and '--tester' must not occur in a\n"
299 		"#       configuration file. Furthermore, global options are ignored in all\n"
300 		"#       but the first configuration.\n"
301 		"#\n"
302 		"# NOTE: Options given on the command-line are added to all configurations in a\n"
303 		"#       configuration file. If an option is given both on the command-line and\n"
304 		"#       in a configuration file, the one from the command-line is preferred.\n"
305 		"#\n"
306 		"# NOTE: If, after adding command-line options, a configuration\n"
307 		"#       contains mutually exclusive options an error is raised.\n"
308 		"#\n", CLASP_VERSION);
309 	for (ConfigIter it = ClaspCliConfig::getConfig(Clasp::Cli::config_many); it.valid(); it.next()) {
310 		printf("%s: %s\n", it.name(), it.args());
311 	}
312 }
printVersion()313 void ClaspAppBase::printVersion() {
314 	Potassco::Application::printVersion();
315 	printLibClaspVersion();
316 	printLicense();
317 }
printLicense() const318 void ClaspAppBase::printLicense() const {
319 	printf("License: The MIT License <https://opensource.org/licenses/MIT>\n");
320 }
printLibClaspVersion() const321 void ClaspAppBase::printLibClaspVersion() const {
322 	printf("libclasp version %s (libpotassco version %s)\n", CLASP_VERSION, LIB_POTASSCO_VERSION);
323 	printf("Configuration: WITH_THREADS=%d\n", CLASP_HAS_THREADS);
324 	printf("%s\n", CLASP_LEGAL);
325 	fflush(stdout);
326 }
327 
printHelp(const Potassco::ProgramOptions::OptionContext & root)328 void ClaspAppBase::printHelp(const Potassco::ProgramOptions::OptionContext& root) {
329 	Potassco::Application::printHelp(root);
330 	if (root.getActiveDescLevel() >= Potassco::ProgramOptions::desc_level_e1) {
331 		printf("[asp] %s\n", ClaspCliConfig::getDefaults(Problem_t::Asp));
332 		printf("[cnf] %s\n", ClaspCliConfig::getDefaults(Problem_t::Sat));
333 		printf("[opb] %s\n", ClaspCliConfig::getDefaults(Problem_t::Pb));
334 	}
335 	if (root.getActiveDescLevel() >= Potassco::ProgramOptions::desc_level_e2) {
336 		printf("\nDefault configurations:\n");
337 		printDefaultConfigs();
338 	}
339 	else {
340 		const char* ht3 = "\nType ";
341 		if (root.getActiveDescLevel() == Potassco::ProgramOptions::desc_level_default) {
342 			printf("\nType '%s --help=2' for more options and defaults\n", getName());
343 			ht3 = "and ";
344 		}
345 		printf("%s '%s --help=3' for all options and configurations.\n", ht3, getName());
346 	}
347 	fflush(stdout);
348 }
printConfig(ConfigKey k) const349 void ClaspAppBase::printConfig(ConfigKey k) const {
350 	uint32 minW = 2, maxW = 80;
351 	ConfigIter it = ClaspCliConfig::getConfig(k);
352 	printf("%s:\n%*c", it.name(), minW-1, ' ');
353 	const char* opts = it.args();
354 	for (std::size_t size = std::strlen(opts), n = maxW - minW; n < size;) {
355 		while (n && opts[n] != ' ') { --n; }
356 		if (!n) { break; }
357 		printf("%.*s\n%*c", static_cast<int>(n), opts, static_cast<int>(minW - 1), ' ');
358 		size -= n + 1;
359 		opts += n + 1;
360 		n = (maxW - minW);
361 	}
362 	printf("%s\n", opts);
363 }
printDefaultConfigs() const364 void ClaspAppBase::printDefaultConfigs() const {
365 	for (int i = Clasp::Cli::config_default+1; i != Clasp::Cli::config_default_max_value; ++i) {
366 		printConfig(static_cast<Clasp::Cli::ConfigKey>(i));
367 	}
368 }
writeNonHcfs(const PrgDepGraph & graph) const369 void ClaspAppBase::writeNonHcfs(const PrgDepGraph& graph) const {
370 	Potassco::StringBuilder buf;
371 	for (PrgDepGraph::NonHcfIter it = graph.nonHcfBegin(), end = graph.nonHcfEnd(); it != end; ++it) {
372 		buf.appendFormat(".%u", (*it)->id());
373 		WriteCnf cnf(claspAppOpts_.hccOut + buf.c_str());
374 		const SharedContext& ctx = (*it)->ctx();
375 		cnf.writeHeader(ctx.numVars(), ctx.numConstraints());
376 		cnf.write(ctx.numVars(), ctx.shortImplications());
377 		Solver::DBRef db = ctx.master()->constraints();
378 		for (uint32 i = 0; i != db.size(); ++i) {
379 			if (ClauseHead* x = db[i]->clause()) { cnf.write(x); }
380 		}
381 		for (uint32 i = 0; i != ctx.master()->trail().size(); ++i) {
382 			cnf.write(ctx.master()->trail()[i]);
383 		}
384 		cnf.close();
385 		buf.clear();
386 	}
387 }
getStream(bool reopen) const388 std::istream& ClaspAppBase::getStream(bool reopen) const {
389 	static std::ifstream file;
390 	static bool isOpen = false;
391 	if (!isOpen || reopen) {
392 		file.close();
393 		isOpen = true;
394 		if (!claspAppOpts_.input.empty() && !isStdIn(claspAppOpts_.input[0])) {
395 			file.open(claspAppOpts_.input[0].c_str());
396 			POTASSCO_EXPECT(file.is_open(), "Can not read from '%s'!", claspAppOpts_.input[0].c_str());
397 		}
398 	}
399 	return file.is_open() ? file : std::cin;
400 }
401 
402 // Creates output object suitable for given input format
createOutput(ProblemType f)403 Output* ClaspAppBase::createOutput(ProblemType f) {
404 	SingleOwnerPtr<Output> out;
405 	if (claspAppOpts_.outf == ClaspAppOptions::out_none) {
406 		return 0;
407 	}
408 	if (claspAppOpts_.outf != ClaspAppOptions::out_json || claspAppOpts_.onlyPre) {
409 		TextOutput::Format outFormat = TextOutput::format_asp;
410 		if      (f == Problem_t::Sat){ outFormat = TextOutput::format_sat09; }
411 		else if (f == Problem_t::Pb) { outFormat = TextOutput::format_pb09;  }
412 		else if (f == Problem_t::Asp && claspAppOpts_.outf == ClaspAppOptions::out_comp) {
413 			outFormat = TextOutput::format_aspcomp;
414 		}
415 		out.reset(new TextOutput(verbose(), outFormat, claspAppOpts_.outAtom.c_str(), claspAppOpts_.ifs));
416 		if (claspConfig_.parse.isEnabled(ParserOptions::parse_maxsat) && f == Problem_t::Sat) {
417 			static_cast<TextOutput*>(out.get())->result[TextOutput::res_sat] = "UNKNOWN";
418 		}
419 	}
420 	else {
421 		out.reset(new JsonOutput(verbose()));
422 	}
423 	if (claspAppOpts_.quiet[0] != static_cast<uint8>(UCHAR_MAX)) {
424 		out->setModelQuiet((Output::PrintLevel)std::min(uint8(Output::print_no), claspAppOpts_.quiet[0]));
425 	}
426 	if (claspAppOpts_.quiet[1] != static_cast<uint8>(UCHAR_MAX)) {
427 		out->setOptQuiet((Output::PrintLevel)std::min(uint8(Output::print_no), claspAppOpts_.quiet[1]));
428 	}
429 	if (claspAppOpts_.quiet[2] != static_cast<uint8>(UCHAR_MAX)) {
430 		out->setCallQuiet((Output::PrintLevel)std::min(uint8(Output::print_no), claspAppOpts_.quiet[2]));
431 	}
432 	if (claspAppOpts_.hideAux && clasp_.get()) {
433 		clasp_->ctx.output.setFilter('_');
434 	}
435 	return out.release();
436 }
storeCommandArgs(const Potassco::ProgramOptions::ParsedValues &)437 void ClaspAppBase::storeCommandArgs(const Potassco::ProgramOptions::ParsedValues&) {
438 	/* We don't need the values */
439 }
handleStartOptions(ClaspFacade & clasp)440 void ClaspAppBase::handleStartOptions(ClaspFacade& clasp) {
441 	if (!clasp.incremental()) {
442 		claspConfig_.releaseOptions();
443 	}
444 	if (claspAppOpts_.compute && clasp.program()->type() == Problem_t::Asp) {
445 		Potassco::Lit_t lit = Potassco::neg(claspAppOpts_.compute);
446 		static_cast<Asp::LogicProgram*>(clasp.program())->addRule(Potassco::Head_t::Disjunctive, Potassco::toSpan<Potassco::Atom_t>(), Potassco::toSpan(&lit, 1));
447 	}
448 	if (!claspAppOpts_.lemmaIn.empty()) {
449 		class LemmaIn : public Potassco::AspifInput {
450 		public:
451 			typedef Potassco::AbstractProgram PrgAdapter;
452 			LemmaIn(const std::string& fn, PrgAdapter* prg) : Potassco::AspifInput(*prg), prg_(prg) {
453 				if (!isStdIn(fn)) { file_.open(fn.c_str()); }
454 				POTASSCO_REQUIRE(accept(getStream()), "'lemma-in': invalid input file!");
455 			}
456 			~LemmaIn() { delete prg_; }
457 		private:
458 			std::istream& getStream() { return file_.is_open() ? file_ : std::cin; }
459 			PrgAdapter*   prg_;
460 			std::ifstream file_;
461 		};
462 		SingleOwnerPtr<Potassco::AbstractProgram> prgTemp;
463 		if (clasp.program()->type() == Problem_t::Asp) { prgTemp = new Asp::LogicProgramAdapter(*static_cast<Asp::LogicProgram*>(clasp.program())); }
464 		else { prgTemp = new BasicProgramAdapter(*clasp.program()); }
465 		lemmaIn_ = new LemmaIn(claspAppOpts_.lemmaIn, prgTemp.release());
466 	}
467 }
handlePostGroundOptions(ProgramBuilder & prg)468 bool ClaspAppBase::handlePostGroundOptions(ProgramBuilder& prg) {
469 	if (!claspAppOpts_.onlyPre) {
470 		if (lemmaIn_.get()) { lemmaIn_->parse(); }
471 		if (logger_.get())  { logger_->startStep(prg, clasp_->incremental()); }
472 		return true;
473 	}
474 	prg.endProgram();
475 	if (prg.type() == Problem_t::Asp) {
476 		Asp::LogicProgram& asp = static_cast<Asp::LogicProgram&>(prg);
477 		AspParser::Format outf = static_cast<AspParser::Format>(claspAppOpts_.onlyPre);
478 		if (outf == AspParser::format_smodels && !asp.supportsSmodels()) {
479 			std::ofstream null;
480 			try { AspParser::write(asp, null, outf); }
481 			catch (const std::logic_error& e) {
482 				error("Option '--pre': unsupported input format!");
483 				info(std::string(e.what()).append(" in 'smodels' format").c_str());
484 				info("Try '--pre=aspif' to print in 'aspif' format");
485 				setExitCode(E_ERROR);
486 				return false;
487 			}
488 		}
489 		AspParser::write(asp, std::cout, outf);
490 	}
491 	else {
492 		error("Option '--pre': unsupported input format!");
493 		setExitCode(E_ERROR);
494 	}
495 	return false;
496 }
handlePreSolveOptions(ClaspFacade & clasp)497 bool ClaspAppBase::handlePreSolveOptions(ClaspFacade& clasp) {
498 	if (!claspAppOpts_.hccOut.empty() && clasp.ctx.sccGraph.get()){ writeNonHcfs(*clasp.ctx.sccGraph); }
499 	return true;
500 }
run(ClaspFacade & clasp)501 void ClaspAppBase::run(ClaspFacade& clasp) {
502 	clasp.start(claspConfig_, getStream());
503 	handleStartOptions(clasp);
504 	while (clasp.read()) {
505 		if (handlePostGroundOptions(*clasp.program())) {
506 			clasp.prepare();
507 			if (handlePreSolveOptions(clasp)) { clasp.solve(); }
508 		}
509 	}
510 }
511 /////////////////////////////////////////////////////////////////////////////////////////
512 // ClaspApp
513 /////////////////////////////////////////////////////////////////////////////////////////
ClaspApp()514 ClaspApp::ClaspApp() {}
515 
getProblemType()516 ProblemType ClaspApp::getProblemType() {
517 	return ClaspFacade::detectProblemType(getStream());
518 }
519 
run(ClaspFacade & clasp)520 void ClaspApp::run(ClaspFacade& clasp) {
521 	ClaspAppBase::run(clasp);
522 }
523 
printHelp(const Potassco::ProgramOptions::OptionContext & root)524 void ClaspApp::printHelp(const Potassco::ProgramOptions::OptionContext& root) {
525 	ClaspAppBase::printHelp(root);
526 	printf("\nclasp is part of Potassco: %s\n", "http://potassco.org/clasp");
527 	printf("Get help/report bugs via : %s\n"  , "http://potassco.org/support\n");
528 	fflush(stdout);
529 }
530 /////////////////////////////////////////////////////////////////////////////////////////
531 // LemmaLogger
532 /////////////////////////////////////////////////////////////////////////////////////////
LemmaLogger(const std::string & to,const Options & o)533 LemmaLogger::LemmaLogger(const std::string& to, const Options& o)
534 	: str_(isStdOut(to) ? stdout : fopen(to.c_str(), "w"))
535 	, inputType_(Problem_t::Asp)
536 	, options_(o)
537 	, step_(0) {
538 	POTASSCO_EXPECT(str_, "Could not open lemma log file '%s'!", to.c_str());
539 }
~LemmaLogger()540 LemmaLogger::~LemmaLogger() { close(); }
startStep(ProgramBuilder & prg,bool inc)541 void LemmaLogger::startStep(ProgramBuilder& prg, bool inc) {
542 	logged_ = 0;
543 	++step_;
544 	if (!options_.logText) {
545 		if (step_ == 1) { fprintf(str_, "asp 1 0 0%s\n", inc ? " incremental" : ""); }
546 		else            { fprintf(str_, "0\n"); }
547 	}
548 	if ((inputType_ = static_cast<Problem_t::Type>(prg.type())) == Problem_t::Asp && prg.endProgram()) {
549 		// create solver variable to potassco literal mapping
550 		Asp::LogicProgram& asp = static_cast<Asp::LogicProgram&>(prg);
551 		for (Asp::Atom_t a = asp.startAtom(); a != asp.startAuxAtom(); ++a) {
552 			Literal sLit = asp.getLiteral(a);
553 			if (sLit.var() >= solver2asp_.size()) {
554 				solver2asp_.resize(sLit.var() + 1, 0);
555 			}
556 			Potassco::Lit_t& p = solver2asp_[sLit.var()];
557 			if (!p || (!sLit.sign() && p < 0)) {
558 				p = !sLit.sign() ? Potassco::lit(a) : Potassco::neg(a);
559 			}
560 		}
561 	}
562 	solver2NameIdx_.clear();
563 	if (options_.logText && prg.endProgram()) {
564 		const SharedContext& ctx = *prg.ctx();
565 		for (OutputTable::pred_iterator beg = ctx.output.pred_begin(), it = beg, end = ctx.output.pred_end(); it != end; ++it) {
566 			Var v = it->cond.var();
567 			if (ctx.varInfo(v).output()) {
568 				if (solver2NameIdx_.size() <= v) { solver2NameIdx_.resize(v + 1, UINT32_MAX); }
569 				solver2NameIdx_[v] = static_cast<uint32>(it - beg);
570 			}
571 		}
572 	}
573 }
add(const Solver & s,const LitVec & cc,const ConstraintInfo & info)574 void LemmaLogger::add(const Solver& s, const LitVec& cc, const ConstraintInfo& info) {
575 	LitVec temp;
576 	const LitVec* out = &cc;
577 	uint32 lbd = info.lbd();
578 	if (lbd > options_.lbdMax || logged_ >= options_.logMax) { return; }
579 	if (info.aux() || options_.domOut || std::find_if(cc.begin(), cc.end(), std::not1(std::bind1st(std::mem_fun(&Solver::inputVar), &s))) != cc.end()) {
580 		uint8 vf = options_.domOut ? VarInfo::Input|VarInfo::Output : VarInfo::Input;
581 		if (!s.resolveToFlagged(cc, vf, temp, lbd) || lbd > options_.lbdMax) { return; }
582 		out = &temp;
583 	}
584 	char buffer[1024];
585 	Potassco::StringBuilder str(buffer, sizeof(buffer), Potassco::StringBuilder::Dynamic);
586 	if (options_.logText) { formatText(*out, s.sharedContext()->output, lbd, str); }
587 	else                  { formatAspif(*out, lbd, str); }
588 	fwrite(str.c_str(), sizeof(char), str.size(), str_);
589 	++logged_;
590 }
formatAspif(const LitVec & cc,uint32,Potassco::StringBuilder & out) const591 void LemmaLogger::formatAspif(const LitVec& cc, uint32, Potassco::StringBuilder& out) const {
592 	out.appendFormat("1 0 0 0 %u", (uint32)cc.size());
593 	for (LitVec::const_iterator it = cc.begin(), end = cc.end(); it != end; ++it) {
594 		Literal sLit = ~*it; // clause -> constraint
595 		Potassco::Lit_t a = toInt(sLit);
596 		if (inputType_ == Problem_t::Asp) {
597 			a = sLit.var() < solver2asp_.size() ? solver2asp_[sLit.var()] : 0;
598 			if (!a) { return; }
599 			if (sLit.sign() != (a < 0)) { a = -a; }
600 		}
601 		out.appendFormat(" %d", a);
602 	}
603 	out.append("\n");
604 }
formatText(const LitVec & cc,const OutputTable & tab,uint32 lbd,Potassco::StringBuilder & out) const605 void LemmaLogger::formatText(const LitVec& cc, const OutputTable& tab, uint32 lbd, Potassco::StringBuilder& out) const {
606 	out.append(":-");
607 	const char* sep = " ";
608 	for (LitVec::const_iterator it = cc.begin(), end = cc.end(); it != end; ++it) {
609 		Literal sLit = ~*it; // clause -> constraint
610 		uint32 idx = sLit.var() < solver2NameIdx_.size() ? solver2NameIdx_[sLit.var()] : UINT32_MAX;
611 		if (idx != UINT32_MAX) {
612 			const OutputTable::PredType& p = *(tab.pred_begin() + idx);
613 			assert(sLit.var() == p.cond.var());
614 			out.appendFormat("%s%s%s", sep, sLit.sign() != p.cond.sign() ? "not " : "", p.name.c_str());
615 		}
616 		else {
617 			if (inputType_ == Problem_t::Asp) {
618 				Potassco::Lit_t a = sLit.var() < solver2asp_.size() ? solver2asp_[sLit.var()] : 0;
619 				if (!a) { return; }
620 				if (sLit.sign() != (a < 0)) { a = -a; }
621 				sLit = Literal(Potassco::atom(a), a < 0);
622 			}
623 			out.appendFormat("%s%s__atom(%u)", sep, sLit.sign() ? "not " : "", sLit.var());
624 		}
625 		sep = ", ";
626 	}
627 	out.appendFormat(".  %%lbd = %u\n", lbd);
628 }
close()629 void LemmaLogger::close() {
630 	if (!str_) { return; }
631 	if (!options_.logText) { fprintf(str_, "0\n"); }
632 	fflush(str_);
633 	if (str_ != stdout) { fclose(str_); }
634 	str_ = 0;
635 	solver2asp_.clear();
636 }
637 /////////////////////////////////////////////////////////////////////////////////////////
638 // WriteCnf
639 /////////////////////////////////////////////////////////////////////////////////////////
WriteCnf(const std::string & outFile)640 WriteCnf::WriteCnf(const std::string& outFile) : str_(fopen(outFile.c_str(), "w")) {
641 	POTASSCO_EXPECT(str_, "Could not open cnf file '%s'!", outFile.c_str());
642 }
~WriteCnf()643 WriteCnf::~WriteCnf() { close(); }
writeHeader(uint32 numVars,uint32 numCons)644 void WriteCnf::writeHeader(uint32 numVars, uint32 numCons) {
645 	fprintf(str_, "p cnf %u %u\n", numVars, numCons);
646 }
write(ClauseHead * h)647 void WriteCnf::write(ClauseHead* h) {
648 	lits_.clear();
649 	h->toLits(lits_);
650 	for (LitVec::const_iterator it = lits_.begin(), end = lits_.end(); it != end; ++it) {
651 		fprintf(str_, "%d ", toInt(*it));
652 	}
653 	fprintf(str_, "%d\n", 0);
654 }
write(Var maxVar,const ShortImplicationsGraph & g)655 void WriteCnf::write(Var maxVar, const ShortImplicationsGraph& g) {
656 	for (Var v = 1; v <= maxVar; ++v) {
657 		g.forEach(posLit(v), *this);
658 		g.forEach(negLit(v), *this);
659 	}
660 }
write(Literal u)661 void WriteCnf::write(Literal u) {
662 	fprintf(str_, "%d 0\n", toInt(u));
663 }
unary(Literal p,Literal x) const664 bool WriteCnf::unary(Literal p, Literal x) const {
665 	return p.rep() >= x.rep() || fprintf(str_, "%d %d 0\n", toInt(~p), toInt(x)) > 0;
666 }
binary(Literal p,Literal x,Literal y) const667 bool WriteCnf::binary(Literal p, Literal x, Literal y) const {
668 	return p.rep() >= x.rep() || p.rep() >= y.rep() || fprintf(str_, "%d %d %d 0\n", toInt(~p), toInt(x), toInt(y)) > 0;
669 }
close()670 void WriteCnf::close() {
671 	if (str_) {
672 		fflush(str_);
673 		fclose(str_);
674 		str_ = 0;
675 	}
676 }
677 
678 }} // end of namespace Clasp::Cli
679 
680