Storm
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
SmtlibSmtSolver.cpp
Go to the documentation of this file.
1#ifndef WINDOWS
2#include <errno.h>
3#include <signal.h>
4#include <sys/ioctl.h>
5#include <sys/types.h>
6#include <sys/wait.h>
7#include <unistd.h>
8#endif
9
11
19#include "storm/io/file.h"
23
24#include <boost/algorithm/string.hpp>
25
26namespace storm {
27namespace solver {
28
32
34 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "functionality not (yet) implemented");
35}
36
38 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "functionality not (yet) implemented");
39}
40
42 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "functionality not (yet) implemented");
43}
44
46 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "functionality not (yet) implemented");
47}
48
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");
53#endif
54#ifndef WINDOWS
55 processIdOfSolver = 0;
56#endif
57 this->expressionAdapter =
58 std::unique_ptr<storm::adapters::Smt2ExpressionAdapter>(new storm::adapters::Smt2ExpressionAdapter(this->getManager(), this->useReadableVarNames));
59 init();
60}
61
63 writeCommand("( exit )",
64 false); // do not wait for success because it does not matter at this point and may cause problems if the solver is not running properly
65#ifndef WINDOWS
66 if (processIdOfSolver != 0) {
67 // Since the process has been opened successfully, it means that we have to close our fds
68 close(fromSolver);
69 close(toSolver);
70 kill(processIdOfSolver, SIGTERM);
71 waitpid(processIdOfSolver, nullptr, 0); // make sure the process has exited
72 }
73#endif
74}
75
77 expressionAdapter->increaseScope();
78 writeCommand("( push 1 ) ", true);
79}
80
82 expressionAdapter->decreaseScope();
83 writeCommand("( pop 1 ) ", true);
84}
85
86void SmtlibSmtSolver::pop(uint_fast64_t n) {
87 expressionAdapter->decreaseScope(n);
88 writeCommand("( pop " + std::to_string(n) + " ) ", true);
89}
90
92 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "functionality not (yet) implemented");
93}
94
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");
98}
99
100#ifdef STORM_HAVE_CARL
101void SmtlibSmtSolver::add(storm::RationalFunction const& leftHandSide, storm::CompareRelation const& relation, storm::RationalFunction const& rightHandSide) {
102 STORM_LOG_THROW(useCarlExpressions, storm::exceptions::IllegalFunctionCallException, "This solver was initialized without allowing carl expressions");
103 // if some of the occurring variables are not declared yet, we will have to.
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);
110 }
111 writeCommand("( assert " + expressionAdapter->translateExpression(leftHandSide, relation, rightHandSide) + " )", true);
112}
113
114void SmtlibSmtSolver::add(const storm::RationalFunctionVariable& variable, bool value) {
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);
122 }
123 std::string varName = carl::VariablePool::getInstance().getName(variable, this->useReadableVarNames);
124 if (value) {
125 writeCommand("( assert " + varName + " )", true);
126 } else {
127 writeCommand("( assert (not " + varName + ") )", true);
128 }
129}
130
131#endif
132
134 writeCommand("( check-sat )", false);
135#ifdef WINDOWS
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\"");
138#else
139
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()); // remove spaces
146 if (solverOutput[0] == "sat")
148 if (solverOutput[0] == "unsat")
150 if (solverOutput[0] == "unknown")
152 // if we reach this point, something unexpected happened. Lets return unknown and print some debug output
153 STORM_LOG_DEBUG("unexpected solver output: " << solverOutput[0] << ". Returning result 'unknown'");
155 } else {
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\"");
158 }
159#endif
160}
161
162SmtSolver::CheckResult SmtlibSmtSolver::checkWithAssumptions(std::set<storm::expressions::Expression> const&) {
163 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "functionality not (yet) implemented");
164}
165
166#ifndef WINDOWS
167
168SmtSolver::CheckResult SmtlibSmtSolver::checkWithAssumptions(std::initializer_list<storm::expressions::Expression> const&) {
169 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "functionality not (yet) implemented");
170}
171#endif
172
173void SmtlibSmtSolver::init() {
175#ifdef WINDOWS
176 STORM_LOG_WARN("opening a thread for the smt solver is not implemented on Windows. Hence, no actual solving will be done")
177#else
178 signal(SIGPIPE, SIG_IGN);
179 this->needsRestart = false;
180
181 // for executing the solver we will need the path to the executable file as well as the arguments as a char* array
182 // where the first argument is the path to the executable file and the last is NULL
183 const std::string cmdString = storm::settings::getModule<storm::settings::modules::Smt2SmtSolverSettings>().getSolverCommand();
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());
190 }
191 solverArgs[solverCommandVec.size()] = NULL;
192
193 // get the pipes started
194 int pipeIn[2];
195 int pipeOut[2];
196 const int READ = 0;
197 const int WRITE = 1;
198 STORM_LOG_THROW(pipe(pipeIn) == 0 && pipe(pipeOut) == 0, storm::exceptions::UnexpectedException, "Could not open pipe to new process");
199
200 // now start the child process, i.e., the solver
201 pid_t pid = fork();
202 STORM_LOG_THROW(pid >= 0, storm::exceptions::UnexpectedException, "Could not start new process for the smt solver");
203 if (pid == 0) {
204 // Child process
205 // duplicate the fd so that standard input and output will be send to our pipes
206 dup2(pipeIn[READ], STDIN_FILENO);
207 dup2(pipeOut[WRITE], STDOUT_FILENO);
208 dup2(pipeOut[WRITE], STDERR_FILENO);
209 // we can now close everything since our child process will use std in and out to address the pipes
210 close(pipeIn[READ]);
211 close(pipeIn[WRITE]);
212 close(pipeOut[READ]);
213 close(pipeOut[WRITE]);
214
215 execv(solverCommandVec[0].c_str(), solverArgs); //"-smt2 -in"
216 // if we reach this point, execl was not successful
217 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Could not execute the solver correctly");
218 }
219 // Parent Process
220 toSolver = pipeIn[WRITE];
221 fromSolver = pipeOut[READ];
222 close(pipeOut[WRITE]);
223 close(pipeIn[READ]);
224 processIdOfSolver = pid;
225
226#endif
227 } else {
228 STORM_LOG_WARN("No SMT-LIBv2 Solver Command specified, which means that no actual SMT solving can be done");
229 }
230
232 STORM_LOG_DEBUG("The SMT-LIBv2 commands are exportet to the given file");
234 isCommandFileOpen = true;
235 // TODO also close file
236 }
237
238 // some initial commands
239 writeCommand("( set-option :print-success true )", true);
240 writeCommand("( set-logic QF_NRA )", true);
241 // writeCommand("( get-info :name )");
242}
243
245 return this->needsRestart;
246}
247
248void SmtlibSmtSolver::writeCommand(std::string smt2Command, bool expectSuccess) {
249 if (isCommandFileOpen) {
250 commandFile << smt2Command << '\n';
251 }
252
253#ifndef WINDOWS
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.");
257 }
258 if (expectSuccess) {
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");
266 }
267 }
268#endif
269}
270
271std::vector<std::string> SmtlibSmtSolver::readSolverOutput(bool waitForOutput) {
272#ifndef WINDOWS
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>();
276 }
277 int bytesReadable;
278 if (waitForOutput) {
279 bytesReadable = 1; // just assume that there are bytes readable
280 } else if (ioctl(fromSolver, FIONREAD, &bytesReadable) < 0) { // actually obtain the readable bytes
281 STORM_LOG_ERROR("Could not check if the solver has output");
282 return std::vector<std::string>();
283 }
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) { // obtain the new amount of readable bytes
292 STORM_LOG_ERROR("Could not check if the solver has output");
293 return std::vector<std::string>();
294 }
295 if (bytesReadable == 0 && solverOutput.back() != '\n') {
297 "Solver Output '"
298 << solverOutput
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");
300 bytesReadable = 1; // we expect more output!
301 }
302 if (bytesReadable > 0) {
303 pid_t w = waitpid(processIdOfSolver, nullptr, WNOHANG);
304 if (w != 0) {
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;
310 bytesReadable = 0;
311 }
312 }
313 }
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"));
319 // note: this is a little bit unsafe as \n can be contained within a solver response
320 }
321 return solverOutputAsVector;
322#endif
323}
324
325void SmtlibSmtSolver::checkForErrorMessage(const std::string message) {
326 size_t errorOccurrance = message.find("error");
327 // do not throw an exception for timeout or memout errors
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);
345 }
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);
349 return;
350 }
351 }
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);
354 }
355}
356} // namespace solver
357} // namespace storm
This class is responsible for managing a set of typed variables and all expressions using these varia...
The base class for all model references.
Definition SmtSolver.h:31
An interface that captures the functionality of an SMT solver.
Definition SmtSolver.h:22
storm::expressions::ExpressionManager const & getManager() const
Retrieves the expression manager associated with the solver.
Definition SmtSolver.cpp:79
CheckResult
possible check results
Definition SmtSolver.h:25
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 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...
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)
Definition logging.h:29
#define STORM_LOG_WARN(message)
Definition logging.h:30
#define STORM_LOG_DEBUG(message)
Definition logging.h:23
#define STORM_LOG_ERROR(message)
Definition logging.h:31
#define STORM_LOG_WARN_COND(cond, message)
Definition macros.h:38
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
void openFile(std::string const &filepath, std::ofstream &filestream, bool append=false, bool silent=false)
Open the given file for writing.
Definition file.h:18
SettingsType const & getModule()
Get module.
LabParser.cpp.
Definition cli.cpp:18
carl::Relation CompareRelation
carl::Variable RationalFunctionVariable