Storm 1.11.1.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
SmtlibSmtSolver.cpp
Go to the documentation of this file.
2
3#include <signal.h>
4#include <sys/ioctl.h>
5#include <sys/wait.h>
6#include <unistd.h>
7#include <boost/algorithm/string.hpp>
8
14#include "storm/io/file.h"
18
19namespace storm {
20namespace solver {
21
25
27 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "functionality not (yet) implemented");
28}
29
31 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "functionality not (yet) implemented");
32}
33
35 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "functionality not (yet) implemented");
36}
37
39 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "functionality not (yet) implemented");
40}
41
43 : SmtSolver(manager), isCommandFileOpen(false), expressionAdapter(nullptr), useCarlExpressions(useCarlExpressions) {
44 processIdOfSolver = 0;
45 this->expressionAdapter =
46 std::unique_ptr<storm::adapters::Smt2ExpressionAdapter>(new storm::adapters::Smt2ExpressionAdapter(this->getManager(), this->useReadableVarNames));
47 init();
48}
49
51 writeCommand("( exit )",
52 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
53
54 if (processIdOfSolver != 0) {
55 // Since the process has been opened successfully, it means that we have to close our fds
56 close(fromSolver);
57 close(toSolver);
58 kill(processIdOfSolver, SIGTERM);
59 waitpid(processIdOfSolver, nullptr, 0); // make sure the process has exited
60 }
61}
62
64 expressionAdapter->increaseScope();
65 writeCommand("( push 1 ) ", true);
66}
67
69 expressionAdapter->decreaseScope();
70 writeCommand("( pop 1 ) ", true);
71}
72
73void SmtlibSmtSolver::pop(uint_fast64_t n) {
74 expressionAdapter->decreaseScope(n);
75 writeCommand("( pop " + std::to_string(n) + " ) ", true);
76}
77
79 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "functionality not (yet) implemented");
80}
81
83 STORM_LOG_THROW(!useCarlExpressions, storm::exceptions::IllegalFunctionCallException, "This solver was initialized without allowing carl expressions");
84 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "functionality not (yet) implemented");
85}
86
87void SmtlibSmtSolver::add(storm::RationalFunction const& leftHandSide, storm::CompareRelation const& relation, storm::RationalFunction const& rightHandSide) {
88 STORM_LOG_THROW(useCarlExpressions, storm::exceptions::IllegalFunctionCallException, "This solver was initialized without allowing carl expressions");
89 // if some of the occurring variables are not declared yet, we will have to.
90 std::set<storm::RationalFunctionVariable> variables;
91 leftHandSide.gatherVariables(variables);
92 rightHandSide.gatherVariables(variables);
93 std::vector<std::string> const varDeclarations = expressionAdapter->checkForUndeclaredVariables(variables);
94 for (auto declaration : varDeclarations) {
95 writeCommand(declaration, true);
96 }
97 writeCommand("( assert " + expressionAdapter->translateExpression(leftHandSide, relation, rightHandSide) + " )", true);
98}
99
100void SmtlibSmtSolver::add(const storm::RationalFunctionVariable& variable, bool value) {
101 STORM_LOG_THROW((variable.type() == carl::VariableType::VT_BOOL), storm::exceptions::IllegalArgumentException,
102 "Tried to add a constraint that consists of a non-boolean variable.");
103 std::set<storm::RationalFunctionVariable> variableSet;
104 variableSet.insert(variable);
105 std::vector<std::string> const varDeclarations = expressionAdapter->checkForUndeclaredVariables(variableSet);
106 for (auto declaration : varDeclarations) {
107 writeCommand(declaration, true);
108 }
109 std::string varName = carl::VariablePool::getInstance().getName(variable, this->useReadableVarNames);
110 if (value) {
111 writeCommand("( assert " + varName + " )", true);
112 } else {
113 writeCommand("( assert (not " + varName + ") )", true);
114 }
115}
116
118 writeCommand("( check-sat )", false);
119
120 if (processIdOfSolver != 0) {
121 auto solverOutput = readSolverOutput();
123 solverOutput.size() == 1, storm::exceptions::UnexpectedException,
124 "expected a single line of output after smt2 command ( check-sat ). Got " + std::to_string(solverOutput.size()) + " lines of output instead.");
125 solverOutput[0].erase(std::remove_if(solverOutput[0].begin(), solverOutput[0].end(), ::isspace), solverOutput[0].end()); // remove spaces
126 if (solverOutput[0] == "sat")
128 if (solverOutput[0] == "unsat")
130 if (solverOutput[0] == "unknown")
132 // if we reach this point, something unexpected happened. Lets return unknown and print some debug output
133 STORM_LOG_DEBUG("unexpected solver output: " << solverOutput[0] << ". Returning result 'unknown'");
135 } else {
136 STORM_LOG_WARN("No SMT-LIBv2 Solver Command specified, which means that no actual SMT solving is done... Assume that the result is \"unknown\"");
138 }
139}
140
141SmtSolver::CheckResult SmtlibSmtSolver::checkWithAssumptions(std::set<storm::expressions::Expression> const&) {
142 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "functionality not (yet) implemented");
143}
144
145SmtSolver::CheckResult SmtlibSmtSolver::checkWithAssumptions(std::initializer_list<storm::expressions::Expression> const&) {
146 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "functionality not (yet) implemented");
147}
148
149void SmtlibSmtSolver::init() {
150 if (storm::settings::getModule<storm::settings::modules::Smt2SmtSolverSettings>().isSolverCommandSet()) {
151 signal(SIGPIPE, SIG_IGN);
152 this->needsRestart = false;
153
154 // for executing the solver we will need the path to the executable file as well as the arguments as a char* array
155 // where the first argument is the path to the executable file and the last is NULL
156 const std::string cmdString = storm::settings::getModule<storm::settings::modules::Smt2SmtSolverSettings>().getSolverCommand();
157 std::vector<std::string> solverCommandVec;
158 boost::split(solverCommandVec, cmdString, boost::is_any_of("\t "));
159 char** solverArgs = new char*[solverCommandVec.size() + 1];
160 std::string solverPath = solverCommandVec[0].substr(0, cmdString.rfind('/') + 1);
161 solverArgs[0] = const_cast<char*>(solverPath.c_str());
162 for (uint_fast64_t argumentIndex = 1; argumentIndex < solverCommandVec.size(); ++argumentIndex) {
163 solverArgs[argumentIndex] = const_cast<char*>(solverCommandVec[argumentIndex].c_str());
164 }
165 solverArgs[solverCommandVec.size()] = NULL;
166
167 // get the pipes started
168 int pipeIn[2];
169 int pipeOut[2];
170 const int READ = 0;
171 const int WRITE = 1;
172 STORM_LOG_THROW(pipe(pipeIn) == 0 && pipe(pipeOut) == 0, storm::exceptions::UnexpectedException, "Could not open pipe to new process");
173
174 // now start the child process, i.e., the solver
175 pid_t pid = fork();
176 STORM_LOG_THROW(pid >= 0, storm::exceptions::UnexpectedException, "Could not start new process for the smt solver");
177 if (pid == 0) {
178 // Child process
179 // duplicate the fd so that standard input and output will be send to our pipes
180 dup2(pipeIn[READ], STDIN_FILENO);
181 dup2(pipeOut[WRITE], STDOUT_FILENO);
182 dup2(pipeOut[WRITE], STDERR_FILENO);
183 // we can now close everything since our child process will use std in and out to address the pipes
184 close(pipeIn[READ]);
185 close(pipeIn[WRITE]);
186 close(pipeOut[READ]);
187 close(pipeOut[WRITE]);
188
189 execv(solverCommandVec[0].c_str(), solverArgs); //"-smt2 -in"
190 // if we reach this point, execl was not successful
191 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Could not execute the solver correctly");
192 }
193 // Parent Process
194 toSolver = pipeIn[WRITE];
195 fromSolver = pipeOut[READ];
196 close(pipeOut[WRITE]);
197 close(pipeIn[READ]);
198 processIdOfSolver = pid;
199
200 } else {
201 STORM_LOG_WARN("No SMT-LIBv2 Solver Command specified, which means that no actual SMT solving can be done");
202 }
203
204 if (storm::settings::getModule<storm::settings::modules::Smt2SmtSolverSettings>().isExportSmtLibScriptSet()) {
205 STORM_LOG_DEBUG("The SMT-LIBv2 commands are exportet to the given file");
206 storm::io::openFile(storm::settings::getModule<storm::settings::modules::Smt2SmtSolverSettings>().getExportSmtLibScriptPath(), commandFile);
207 isCommandFileOpen = true;
208 // TODO also close file
209 }
210
211 // some initial commands
212 writeCommand("( set-option :print-success true )", true);
213 writeCommand("( set-logic QF_NRA )", true);
214 // writeCommand("( get-info :name )");
215}
216
218 return this->needsRestart;
219}
220
221void SmtlibSmtSolver::writeCommand(std::string smt2Command, bool expectSuccess) {
222 if (isCommandFileOpen) {
223 commandFile << smt2Command << '\n';
224 }
225
226 if (processIdOfSolver != 0) {
227 if (write(toSolver, (smt2Command + "\n").c_str(), smt2Command.length() + 1) < 0) {
228 STORM_LOG_DEBUG("Was not able to write " << smt2Command << "to the solver.");
229 }
230 if (expectSuccess) {
231 auto output = readSolverOutput();
232 STORM_LOG_THROW(output.size() == 1, storm::exceptions::UnexpectedException,
233 "expected a single success response after smt2 command " + smt2Command + ". Got " + std::to_string(output.size()) +
234 " lines of output instead.");
235 output[0].erase(std::remove_if(output[0].begin(), output[0].end(), ::isspace), output[0].end());
236 STORM_LOG_THROW(output[0] == "success", storm::exceptions::UnexpectedException,
237 "expected <<success>> response after smt2 command " + smt2Command + ". Got <<" + output[0] + ">> instead");
238 }
239 }
240}
241
242std::vector<std::string> SmtlibSmtSolver::readSolverOutput(bool waitForOutput) {
243 if (processIdOfSolver == 0) {
244 STORM_LOG_DEBUG("failed to read solver output as the solver is not running");
245 return std::vector<std::string>();
246 }
247 int bytesReadable;
248 if (waitForOutput) {
249 bytesReadable = 1; // just assume that there are bytes readable
250 } else if (ioctl(fromSolver, FIONREAD, &bytesReadable) < 0) { // actually obtain the readable bytes
251 STORM_LOG_ERROR("Could not check if the solver has output");
252 return std::vector<std::string>();
253 }
254 std::string solverOutput = "";
255 const ssize_t MAX_CHUNK_SIZE = 256;
256 char chunk[MAX_CHUNK_SIZE];
257 while (bytesReadable > 0) {
258 ssize_t chunkSize = read(fromSolver, chunk, MAX_CHUNK_SIZE);
259 STORM_LOG_THROW(chunkSize >= 0, storm::exceptions::UnexpectedException, "failed to read solver output");
260 solverOutput += std::string(chunk, chunkSize);
261 if (ioctl(fromSolver, FIONREAD, &bytesReadable) < 0) { // obtain the new amount of readable bytes
262 STORM_LOG_ERROR("Could not check if the solver has output");
263 return std::vector<std::string>();
264 }
265 if (bytesReadable == 0 && solverOutput.back() != '\n') {
267 "Solver Output '"
268 << solverOutput
269 << "' 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");
270 bytesReadable = 1; // we expect more output!
271 }
272 if (bytesReadable > 0) {
273 pid_t w = waitpid(processIdOfSolver, nullptr, WNOHANG);
274 if (w != 0) {
275 STORM_LOG_WARN_COND(w > 0, "Error when checking whether the solver is still running. Will assume that solver has terminated.");
276 STORM_LOG_WARN("The solver exited unexpectedly when reading output: " << solverOutput);
277 solverOutput += "terminated";
278 this->needsRestart = true;
279 this->processIdOfSolver = 0;
280 bytesReadable = 0;
281 }
282 }
283 }
284 checkForErrorMessage(solverOutput);
285 std::vector<std::string> solverOutputAsVector;
286 if (solverOutput.length() > 0) {
287 solverOutput = solverOutput.substr(0, solverOutput.length() - 1);
288 boost::split(solverOutputAsVector, solverOutput, boost::is_any_of("\n"));
289 // note: this is a little bit unsafe as \n can be contained within a solver response
290 }
291 return solverOutputAsVector;
292}
293
294void SmtlibSmtSolver::checkForErrorMessage(const std::string message) {
295 size_t errorOccurrance = message.find("error");
296 // do not throw an exception for timeout or memout errors
297 if (message.find("timeout") != std::string::npos) {
298 STORM_LOG_INFO("SMT solver answered: '" << message << "' and I am interpreting this as timeout ");
299 this->needsRestart = true;
300 this->processIdOfSolver = 0;
301 } else if (message.find("memory") != std::string::npos) {
302 STORM_LOG_INFO("SMT solver answered: '" << message << "' and I am interpreting this as out of memory ");
303 this->needsRestart = true;
304 this->processIdOfSolver = 0;
305 } else if (errorOccurrance != std::string::npos) {
306 this->needsRestart = true;
307 std::string errorMsg = "An error was detected while checking the solver output. ";
308 STORM_LOG_DEBUG("Detected an error message in the solver response:\n" + message);
309 size_t firstQuoteSign = message.find('\"', errorOccurrance);
310 if (firstQuoteSign != std::string::npos && message.find("\\\"", firstQuoteSign - 1) != firstQuoteSign - 1) {
311 size_t secondQuoteSign = message.find('\"', firstQuoteSign + 1);
312 while (secondQuoteSign != std::string::npos && message.find("\\\"", secondQuoteSign - 1) == secondQuoteSign - 1) {
313 secondQuoteSign = message.find('\"', secondQuoteSign + 1);
314 }
315 if (secondQuoteSign != std::string::npos) {
316 errorMsg += "The error message was: <<" + message.substr(errorOccurrance, secondQuoteSign - errorOccurrance + 1) + ">>.";
317 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, errorMsg);
318 return;
319 }
320 }
321 errorMsg += "The error message could not be parsed correctly. Snippet:\n" + message.substr(errorOccurrance, 200);
322 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, errorMsg);
323 }
324}
325} // namespace solver
326} // 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:24
#define STORM_LOG_WARN(message)
Definition logging.h:25
#define STORM_LOG_DEBUG(message)
Definition logging.h:18
#define STORM_LOG_ERROR(message)
Definition logging.h:26
#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
carl::Relation CompareRelation
carl::Variable RationalFunctionVariable