24#include <boost/algorithm/string.hpp>
34 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"functionality not (yet) implemented");
38 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"functionality not (yet) implemented");
42 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"functionality not (yet) implemented");
46 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"functionality not (yet) implemented");
50 :
SmtSolver(manager), isCommandFileOpen(false), expressionAdapter(nullptr), useCarlExpressions(useCarlExpressions) {
51#ifndef STORM_HAVE_CARL
52 STORM_LOG_THROW(!useCarlExpressions, storm::exceptions::IllegalArgumentException,
"Tried to use carl expressions but storm is not linked with CARL");
55 processIdOfSolver = 0;
57 this->expressionAdapter =
63 writeCommand(
"( exit )",
66 if (processIdOfSolver != 0) {
70 kill(processIdOfSolver, SIGTERM);
71 waitpid(processIdOfSolver,
nullptr, 0);
77 expressionAdapter->increaseScope();
78 writeCommand(
"( push 1 ) ",
true);
82 expressionAdapter->decreaseScope();
83 writeCommand(
"( pop 1 ) ",
true);
87 expressionAdapter->decreaseScope(n);
88 writeCommand(
"( pop " + std::to_string(n) +
" ) ",
true);
92 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"functionality not (yet) implemented");
96 STORM_LOG_THROW(!useCarlExpressions, storm::exceptions::IllegalFunctionCallException,
"This solver was initialized without allowing carl expressions");
97 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"functionality not (yet) implemented");
100#ifdef STORM_HAVE_CARL
102 STORM_LOG_THROW(useCarlExpressions, storm::exceptions::IllegalFunctionCallException,
"This solver was initialized without allowing carl expressions");
104 std::set<storm::RationalFunctionVariable> variables;
105 leftHandSide.gatherVariables(variables);
106 rightHandSide.gatherVariables(variables);
107 std::vector<std::string>
const varDeclarations = expressionAdapter->checkForUndeclaredVariables(variables);
108 for (
auto declaration : varDeclarations) {
109 writeCommand(declaration,
true);
111 writeCommand(
"( assert " + expressionAdapter->translateExpression(leftHandSide, relation, rightHandSide) +
" )",
true);
115 STORM_LOG_THROW((variable.type() == carl::VariableType::VT_BOOL), storm::exceptions::IllegalArgumentException,
116 "Tried to add a constraint that consists of a non-boolean variable.");
117 std::set<storm::RationalFunctionVariable> variableSet;
118 variableSet.insert(variable);
119 std::vector<std::string>
const varDeclarations = expressionAdapter->checkForUndeclaredVariables(variableSet);
120 for (
auto declaration : varDeclarations) {
121 writeCommand(declaration,
true);
123 std::string varName = carl::VariablePool::getInstance().getName(variable, this->useReadableVarNames);
125 writeCommand(
"( assert " + varName +
" )",
true);
127 writeCommand(
"( assert (not " + varName +
") )",
true);
134 writeCommand(
"( check-sat )",
false);
136 STORM_LOG_WARN(
"SMT-LIBv2 Solver can not be started on Windows as this is not yet implemented. Assume that the check-result is \"unknown\"");
140 if (processIdOfSolver != 0) {
141 auto solverOutput = readSolverOutput();
143 solverOutput.size() == 1, storm::exceptions::UnexpectedException,
144 "expected a single line of output after smt2 command ( check-sat ). Got " + std::to_string(solverOutput.size()) +
" lines of output instead.");
145 solverOutput[0].erase(std::remove_if(solverOutput[0].begin(), solverOutput[0].end(), ::isspace), solverOutput[0].end());
146 if (solverOutput[0] ==
"sat")
148 if (solverOutput[0] ==
"unsat")
150 if (solverOutput[0] ==
"unknown")
153 STORM_LOG_DEBUG(
"unexpected solver output: " << solverOutput[0] <<
". Returning result 'unknown'");
156 STORM_LOG_WARN(
"No SMT-LIBv2 Solver Command specified, which means that no actual SMT solving is done... Assume that the result is \"unknown\"");
163 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"functionality not (yet) implemented");
169 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"functionality not (yet) implemented");
173void SmtlibSmtSolver::init() {
176 STORM_LOG_WARN(
"opening a thread for the smt solver is not implemented on Windows. Hence, no actual solving will be done")
178 signal(SIGPIPE, SIG_IGN);
179 this->needsRestart =
false;
184 std::vector<std::string> solverCommandVec;
185 boost::split(solverCommandVec, cmdString, boost::is_any_of(
"\t "));
186 char** solverArgs =
new char*[solverCommandVec.size() + 1];
187 solverArgs[0] =
const_cast<char*
>(solverCommandVec[0].substr(0, cmdString.rfind(
'/') + 1).c_str());
188 for (uint_fast64_t argumentIndex = 1; argumentIndex < solverCommandVec.size(); ++argumentIndex) {
189 solverArgs[argumentIndex] =
const_cast<char*
>(solverCommandVec[argumentIndex].c_str());
191 solverArgs[solverCommandVec.size()] = NULL;
198 STORM_LOG_THROW(pipe(pipeIn) == 0 && pipe(pipeOut) == 0, storm::exceptions::UnexpectedException,
"Could not open pipe to new process");
202 STORM_LOG_THROW(pid >= 0, storm::exceptions::UnexpectedException,
"Could not start new process for the smt solver");
206 dup2(pipeIn[READ], STDIN_FILENO);
207 dup2(pipeOut[WRITE], STDOUT_FILENO);
208 dup2(pipeOut[WRITE], STDERR_FILENO);
211 close(pipeIn[WRITE]);
212 close(pipeOut[READ]);
213 close(pipeOut[WRITE]);
215 execv(solverCommandVec[0].c_str(), solverArgs);
217 STORM_LOG_THROW(
false, storm::exceptions::UnexpectedException,
"Could not execute the solver correctly");
220 toSolver = pipeIn[WRITE];
221 fromSolver = pipeOut[READ];
222 close(pipeOut[WRITE]);
224 processIdOfSolver = pid;
228 STORM_LOG_WARN(
"No SMT-LIBv2 Solver Command specified, which means that no actual SMT solving can be done");
232 STORM_LOG_DEBUG(
"The SMT-LIBv2 commands are exportet to the given file");
234 isCommandFileOpen =
true;
239 writeCommand(
"( set-option :print-success true )",
true);
240 writeCommand(
"( set-logic QF_NRA )",
true);
245 return this->needsRestart;
248void SmtlibSmtSolver::writeCommand(std::string smt2Command,
bool expectSuccess) {
249 if (isCommandFileOpen) {
250 commandFile << smt2Command <<
'\n';
254 if (processIdOfSolver != 0) {
255 if (write(toSolver, (smt2Command +
"\n").c_str(), smt2Command.length() + 1) < 0) {
256 STORM_LOG_DEBUG(
"Was not able to write " << smt2Command <<
"to the solver.");
259 auto output = readSolverOutput();
260 STORM_LOG_THROW(output.size() == 1, storm::exceptions::UnexpectedException,
261 "expected a single success response after smt2 command " + smt2Command +
". Got " + std::to_string(output.size()) +
262 " lines of output instead.");
263 output[0].erase(std::remove_if(output[0].begin(), output[0].end(), ::isspace), output[0].end());
264 STORM_LOG_THROW(output[0] ==
"success", storm::exceptions::UnexpectedException,
265 "expected <<success>> response after smt2 command " + smt2Command +
". Got <<" + output[0] +
">> instead");
271std::vector<std::string> SmtlibSmtSolver::readSolverOutput(
bool waitForOutput) {
273 if (processIdOfSolver == 0) {
274 STORM_LOG_DEBUG(
"failed to read solver output as the solver is not running");
275 return std::vector<std::string>();
280 }
else if (ioctl(fromSolver, FIONREAD, &bytesReadable) < 0) {
282 return std::vector<std::string>();
284 std::string solverOutput =
"";
285 const ssize_t MAX_CHUNK_SIZE = 256;
286 char chunk[MAX_CHUNK_SIZE];
287 while (bytesReadable > 0) {
288 ssize_t chunkSize = read(fromSolver, chunk, MAX_CHUNK_SIZE);
289 STORM_LOG_THROW(chunkSize >= 0, storm::exceptions::UnexpectedException,
"failed to read solver output");
290 solverOutput += std::string(chunk, chunkSize);
291 if (ioctl(fromSolver, FIONREAD, &bytesReadable) < 0) {
293 return std::vector<std::string>();
295 if (bytesReadable == 0 && solverOutput.back() !=
'\n') {
299 <<
"' did not end with newline symbol (\\n). Since we assume that this should be the case, we will wait for more output from the solver");
302 if (bytesReadable > 0) {
303 pid_t w = waitpid(processIdOfSolver,
nullptr, WNOHANG);
305 STORM_LOG_WARN_COND(w > 0,
"Error when checking whether the solver is still running. Will assume that solver has terminated.");
306 STORM_LOG_WARN(
"The solver exited unexpectedly when reading output: " << solverOutput);
307 solverOutput +=
"terminated";
308 this->needsRestart =
true;
309 this->processIdOfSolver = 0;
314 checkForErrorMessage(solverOutput);
315 std::vector<std::string> solverOutputAsVector;
316 if (solverOutput.length() > 0) {
317 solverOutput = solverOutput.substr(0, solverOutput.length() - 1);
318 boost::split(solverOutputAsVector, solverOutput, boost::is_any_of(
"\n"));
321 return solverOutputAsVector;
325void SmtlibSmtSolver::checkForErrorMessage(
const std::string message) {
326 size_t errorOccurrance = message.find(
"error");
328 if (message.find(
"timeout") != std::string::npos) {
329 STORM_LOG_INFO(
"SMT solver answered: '" << message <<
"' and I am interpreting this as timeout ");
330 this->needsRestart =
true;
331 this->processIdOfSolver = 0;
332 }
else if (message.find(
"memory") != std::string::npos) {
333 STORM_LOG_INFO(
"SMT solver answered: '" << message <<
"' and I am interpreting this as out of memory ");
334 this->needsRestart =
true;
335 this->processIdOfSolver = 0;
336 }
else if (errorOccurrance != std::string::npos) {
337 this->needsRestart =
true;
338 std::string errorMsg =
"An error was detected while checking the solver output. ";
339 STORM_LOG_DEBUG(
"Detected an error message in the solver response:\n" + message);
340 size_t firstQuoteSign = message.find(
'\"', errorOccurrance);
341 if (firstQuoteSign != std::string::npos && message.find(
"\\\"", firstQuoteSign - 1) != firstQuoteSign - 1) {
342 size_t secondQuoteSign = message.find(
'\"', firstQuoteSign + 1);
343 while (secondQuoteSign != std::string::npos && message.find(
"\\\"", secondQuoteSign - 1) == secondQuoteSign - 1) {
344 secondQuoteSign = message.find(
'\"', secondQuoteSign + 1);
346 if (secondQuoteSign != std::string::npos) {
347 errorMsg +=
"The error message was: <<" + message.substr(errorOccurrance, secondQuoteSign - errorOccurrance + 1) +
">>.";
348 STORM_LOG_THROW(
false, storm::exceptions::UnexpectedException, errorMsg);
352 errorMsg +=
"The error message could not be parsed correctly. Snippet:\n" + message.substr(errorOccurrance, 200);
353 STORM_LOG_THROW(
false, storm::exceptions::UnexpectedException, errorMsg);
This class is responsible for managing a set of typed variables and all expressions using these varia...
The base class for all model references.
An interface that captures the functionality of an SMT solver.
storm::expressions::ExpressionManager const & getManager() const
Retrieves the expression manager associated with the solver.
CheckResult
possible check results
virtual bool getBooleanValue(storm::expressions::Variable const &variable) const override
SmtlibModelReference(storm::expressions::ExpressionManager const &manager)
virtual std::string toString() const override
virtual int_fast64_t getIntegerValue(storm::expressions::Variable const &variable) const override
virtual double getRationalValue(storm::expressions::Variable const &variable) const override
virtual ~SmtlibSmtSolver()
virtual CheckResult check() override
Checks whether the conjunction of assertions that are currently on the solver's stack is satisfiable.
virtual void push() override
Pushes a backtracking point on the solver's stack.
virtual void reset() override
Removes all assertions from the solver's stack.
virtual void add(storm::expressions::Expression const &assertion) override
Adds an assertion to the solver's stack.
virtual CheckResult checkWithAssumptions(std::set< storm::expressions::Expression > const &assumptions) override
Checks whether the conjunction of assertions that are currently on the solver's stack together with t...
bool isNeedsRestart() const
SmtlibSmtSolver(storm::expressions::ExpressionManager &manager, bool useCarlExpressions=false)
Creates a new solver with the given manager.
virtual void pop() override
Pops a backtracking point from the solver's stack.
#define STORM_LOG_INFO(message)
#define STORM_LOG_WARN(message)
#define STORM_LOG_DEBUG(message)
#define STORM_LOG_ERROR(message)
#define STORM_LOG_WARN_COND(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
void openFile(std::string const &filepath, std::ofstream &filestream, bool append=false, bool silent=false)
Open the given file for writing.
SettingsType const & getModule()
Get module.
carl::Relation CompareRelation
carl::Variable RationalFunctionVariable