Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
Z3SmtSolver.cpp
Go to the documentation of this file.
2
3#include <memory>
4
9
10namespace storm {
11namespace solver {
12#ifdef STORM_HAVE_Z3
13Z3SmtSolver::Z3ModelReference::Z3ModelReference(storm::expressions::ExpressionManager const& manager, z3::model const& model,
14 storm::adapters::Z3ExpressionAdapter& expressionAdapter)
15 : ModelReference(manager), model(model), expressionAdapter(expressionAdapter) {
16 // Intentionally left empty.
17}
18#endif
19
21#ifdef STORM_HAVE_Z3
22 STORM_LOG_ASSERT(variable.getManager() == this->getManager(), "Requested variable is managed by a different manager.");
23 z3::expr z3Expr = this->expressionAdapter.translateExpression(variable);
24 z3::expr z3ExprValuation = model.eval(z3Expr, true);
25 return this->expressionAdapter.translateExpression(z3ExprValuation).isTrue();
26#else
27 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException, "Storm is compiled without Z3 support.");
28#endif
29}
30
32#ifdef STORM_HAVE_Z3
33 STORM_LOG_ASSERT(variable.getManager() == this->getManager(), "Requested variable is managed by a different manager.");
34 z3::expr z3Expr = this->expressionAdapter.translateExpression(variable);
35 z3::expr z3ExprValuation = model.eval(z3Expr, true);
36 return this->expressionAdapter.translateExpression(z3ExprValuation).evaluateAsInt();
37#else
38 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException, "Storm is compiled without Z3 support.");
39#endif
40}
41
43#ifdef STORM_HAVE_Z3
44 STORM_LOG_ASSERT(variable.getManager() == this->getManager(), "Requested variable is managed by a different manager.");
45 z3::expr z3Expr = this->expressionAdapter.translateExpression(variable);
46 z3::expr z3ExprValuation = model.eval(z3Expr, true);
47 return this->expressionAdapter.translateExpression(z3ExprValuation).evaluateAsDouble();
48#else
49 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException, "Storm is compiled without Z3 support.");
50#endif
51}
52
54#ifdef STORM_HAVE_Z3
55 std::stringstream sstr;
56 sstr << model;
57 return sstr.str();
58#else
59 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException, "Storm is compiled without Z3 support.");
60#endif
61}
62
64 : SmtSolver(manager)
65#ifdef STORM_HAVE_Z3
66 ,
67 context(nullptr),
68 solver(nullptr),
69 expressionAdapter(nullptr),
70 lastCheckAssumptions(false),
71 lastResult(CheckResult::Unknown)
72#endif
73{
74#ifdef STORM_HAVE_Z3
75 z3::config config;
76 config.set("model", true);
77 context = std::make_unique<z3::context>(config);
78 solver = std::make_unique<z3::solver>(*context);
79 expressionAdapter = std::make_unique<storm::adapters::Z3ExpressionAdapter>(this->getManager(), *context);
80#endif
81}
82
84 // Intentionally left empty.
85}
86
88#ifdef STORM_HAVE_Z3
89 this->solver->push();
90#else
91 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException, "Storm is compiled without Z3 support.");
92#endif
93}
94
96#ifdef STORM_HAVE_Z3
97 this->solver->pop();
98#else
99 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException, "Storm is compiled without Z3 support.");
100#endif
101}
102
103void Z3SmtSolver::pop(uint_fast64_t n) {
104#ifdef STORM_HAVE_Z3
105 this->solver->pop(static_cast<unsigned int>(n));
106#else
107 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException, "Storm is compiled without Z3 support.");
108#endif
109}
110
112#ifdef STORM_HAVE_Z3
113 this->solver->reset();
114#else
115 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException, "Storm is compiled without Z3 support.");
116#endif
117}
118
120#ifdef STORM_HAVE_Z3
121 this->solver->add(expressionAdapter->translateExpression(assertion));
122#else
123 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException, "Storm is compiled without Z3 support.");
124#endif
125}
126
127void Z3SmtSolver::addNotCurrentModel(bool performSolverReset) {
128#ifdef STORM_HAVE_Z3
129 STORM_LOG_THROW(this->lastResult == SmtSolver::CheckResult::Sat, storm::exceptions::InvalidStateException,
130 "Unable to create model for formula that was not determined to be satisfiable.");
131
132 auto currentModel = this->solver->get_model();
133 z3::expr notThisModel = currentModel.ctx().bool_val(true);
134 for (auto const& variable : this->getManager().getVariables()) {
135 z3::expr var = this->expressionAdapter->translateExpression(variable);
136 auto value = currentModel.eval(var);
137 if (notThisModel.is_const()) {
138 notThisModel = var == value;
139 } else {
140 notThisModel = notThisModel && (var == value);
141 }
142 }
143 // https://stackoverflow.com/questions/78261966/z3-non-incremental-search-for-multiple-models-works-incremental-search-does-no
144 if (performSolverReset) {
145 auto const allAssertions = this->solver->assertions();
146 solver->reset();
147 for (auto const& assertion : allAssertions) {
148 solver->add(assertion);
149 }
150 }
151 this->solver->add(!notThisModel);
152#else
153 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException, "Storm is compiled without Z3 support.");
154#endif
155}
156
158#ifdef STORM_HAVE_Z3
159 lastCheckAssumptions = false;
160 switch (this->solver->check()) {
161 case z3::sat:
162 this->lastResult = SmtSolver::CheckResult::Sat;
163 break;
164 case z3::unsat:
165 this->lastResult = SmtSolver::CheckResult::Unsat;
166 break;
167 default:
168 this->lastResult = SmtSolver::CheckResult::Unknown;
169 break;
170 }
171 return this->lastResult;
172#else
173 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException, "Storm is compiled without Z3 support.");
174#endif
175}
176
177SmtSolver::CheckResult Z3SmtSolver::checkWithAssumptions(std::set<storm::expressions::Expression> const& assumptions) {
178#ifdef STORM_HAVE_Z3
179 lastCheckAssumptions = true;
180 z3::expr_vector z3Assumptions(*this->context);
181
182 for (storm::expressions::Expression assumption : assumptions) {
183 z3Assumptions.push_back(this->expressionAdapter->translateExpression(assumption));
184 }
185
186 switch (this->solver->check(z3Assumptions)) {
187 case z3::sat:
188 this->lastResult = SmtSolver::CheckResult::Sat;
189 break;
190 case z3::unsat:
191 this->lastResult = SmtSolver::CheckResult::Unsat;
192 break;
193 default:
194 this->lastResult = SmtSolver::CheckResult::Unknown;
195 break;
196 }
197 return this->lastResult;
198#else
199 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException, "Storm is compiled without Z3 support.");
200#endif
201}
202
203SmtSolver::CheckResult Z3SmtSolver::checkWithAssumptions(std::initializer_list<storm::expressions::Expression> const& assumptions) {
204#ifdef STORM_HAVE_Z3
205 lastCheckAssumptions = true;
206 z3::expr_vector z3Assumptions(*this->context);
207
208 for (storm::expressions::Expression assumption : assumptions) {
209 z3Assumptions.push_back(this->expressionAdapter->translateExpression(assumption));
210 }
211
212 switch (this->solver->check(z3Assumptions)) {
213 case z3::sat:
214 this->lastResult = SmtSolver::CheckResult::Sat;
215 break;
216 case z3::unsat:
217 this->lastResult = SmtSolver::CheckResult::Unsat;
218 break;
219 default:
220 this->lastResult = SmtSolver::CheckResult::Unknown;
221 break;
222 }
223 return this->lastResult;
224#else
225 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException, "Storm is compiled without Z3 support.");
226#endif
227}
228
230#ifdef STORM_HAVE_Z3
231 STORM_LOG_THROW(this->lastResult == SmtSolver::CheckResult::Sat, storm::exceptions::InvalidStateException,
232 "Unable to create model for formula that was not determined to be satisfiable.");
233 return this->convertZ3ModelToValuation(this->solver->get_model());
234#else
235 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException, "Storm is compiled without Z3 support.");
236#endif
237}
238
239std::shared_ptr<SmtSolver::ModelReference> Z3SmtSolver::getModel() {
240#ifdef STORM_HAVE_Z3
241 STORM_LOG_THROW(this->lastResult == SmtSolver::CheckResult::Sat, storm::exceptions::InvalidStateException,
242 "Unable to create model for formula that was not determined to be satisfiable.");
243 return std::shared_ptr<SmtSolver::ModelReference>(new Z3ModelReference(this->getManager(), this->solver->get_model(), *this->expressionAdapter));
244#else
245 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException, "Storm is compiled without Z3 support.");
246#endif
247}
248
249#ifdef STORM_HAVE_Z3
250storm::expressions::SimpleValuation Z3SmtSolver::convertZ3ModelToValuation(z3::model const& model) {
251 storm::expressions::SimpleValuation stormModel(this->getManager().getSharedPointer());
252
253 for (unsigned i = 0; i < model.num_consts(); ++i) {
254 z3::func_decl variableI = model.get_const_decl(i);
255 storm::expressions::Variable stormVariable = this->expressionAdapter->getVariable(variableI);
256 storm::expressions::Expression variableInterpretation = this->expressionAdapter->translateExpression(model.get_const_interp(variableI));
257
258 if (variableInterpretation.getType().isBooleanType()) {
259 stormModel.setBooleanValue(this->getManager().getVariable(variableI.name().str()), variableInterpretation.isTrue());
260 } else if (variableInterpretation.getType().isIntegerType()) {
261 stormModel.setIntegerValue(this->getManager().getVariable(variableI.name().str()), variableInterpretation.evaluateAsInt());
262 } else if (variableInterpretation.getType().isRationalType()) {
263 stormModel.setRationalValue(this->getManager().getVariable(variableI.name().str()), variableInterpretation.evaluateAsDouble());
264 } else {
265 STORM_LOG_ASSERT(false, "Variable interpretation in model is not of type bool, int or rational.");
266 }
267 }
268
269 return stormModel;
270}
271#endif
272
273std::vector<storm::expressions::SimpleValuation> Z3SmtSolver::allSat(std::vector<storm::expressions::Variable> const& important) {
274#ifdef STORM_HAVE_Z3
275 std::vector<storm::expressions::SimpleValuation> valuations;
276 this->allSat(important, static_cast<std::function<bool(storm::expressions::SimpleValuation&)>>(
277 [&valuations](storm::expressions::SimpleValuation const& valuation) -> bool {
278 valuations.push_back(valuation);
279 return true;
280 }));
281 return valuations;
282#else
283 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException, "Storm is compiled without Z3 support.");
284#endif
285}
286
287uint_fast64_t Z3SmtSolver::allSat(std::vector<storm::expressions::Variable> const& important,
288 std::function<bool(storm::expressions::SimpleValuation&)> const& callback) {
289#ifdef STORM_HAVE_Z3
290 for (storm::expressions::Variable const& variable : important) {
291 STORM_LOG_THROW(variable.hasBooleanType(), storm::exceptions::InvalidArgumentException, "The important atoms for AllSat must be boolean variables.");
292 }
293
294 uint_fast64_t numberOfModels = 0;
295 bool proceed = true;
296
297 // Save the current assertion stack, to be able to backtrack after the procedure.
298 this->push();
299
300 // Enumerate models as long as the conjunction is satisfiable and the callback has not aborted the enumeration.
301 while (proceed && this->check() == CheckResult::Sat) {
302 ++numberOfModels;
303 z3::model model = this->solver->get_model();
304
305 z3::expr modelExpr = this->context->bool_val(true);
306 storm::expressions::SimpleValuation valuation(this->getManager().getSharedPointer());
307
308 for (storm::expressions::Variable const& importantAtom : important) {
309 z3::expr z3ImportantAtom = this->expressionAdapter->translateExpression(importantAtom.getExpression());
310 z3::expr z3ImportantAtomValuation = model.eval(z3ImportantAtom, true);
311 modelExpr = modelExpr && (z3ImportantAtom == z3ImportantAtomValuation);
312 valuation.setBooleanValue(importantAtom, this->expressionAdapter->translateExpression(z3ImportantAtomValuation).isTrue());
313 }
314
315 // Check if we are required to proceed, and if so rule out the current model.
316 proceed = callback(valuation);
317 if (proceed) {
318 this->solver->add(!modelExpr);
319 }
320 }
321
322 // Restore the old assertion stack and return.
323 this->pop();
324 return numberOfModels;
325#else
326 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException, "Storm is compiled without Z3 support.");
327#endif
328}
329
330uint_fast64_t Z3SmtSolver::allSat(std::vector<storm::expressions::Variable> const& important, std::function<bool(SmtSolver::ModelReference&)> const& callback) {
331#ifdef STORM_HAVE_Z3
332 for (storm::expressions::Variable const& variable : important) {
333 STORM_LOG_THROW(variable.hasBooleanType(), storm::exceptions::InvalidArgumentException, "The important atoms for AllSat must be boolean variables.");
334 }
335
336 uint_fast64_t numberOfModels = 0;
337 bool proceed = true;
338
339 // Save the current assertion stack, to be able to backtrack after the procedure.
340 this->push();
341
342 // Enumerate models as long as the conjunction is satisfiable and the callback has not aborted the enumeration.
343 while (proceed && this->check() == CheckResult::Sat) {
344 ++numberOfModels;
345 z3::model model = this->solver->get_model();
346
347 z3::expr modelExpr = this->context->bool_val(true);
348 storm::expressions::SimpleValuation valuation(this->getManager().getSharedPointer());
349
350 for (storm::expressions::Variable const& importantAtom : important) {
351 z3::expr z3ImportantAtom = this->expressionAdapter->translateExpression(importantAtom.getExpression());
352 z3::expr z3ImportantAtomValuation = model.eval(z3ImportantAtom, true);
353 modelExpr = modelExpr && (z3ImportantAtom == z3ImportantAtomValuation);
354 }
355 Z3ModelReference modelRef(this->getManager(), model, *expressionAdapter);
356
357 // Check if we are required to proceed, and if so rule out the current model.
358 proceed = callback(modelRef);
359 if (proceed) {
360 this->solver->add(!modelExpr);
361 }
362 }
363
364 this->pop();
365 return numberOfModels;
366#else
367 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException, "Storm is compiled without Z3 support.");
368#endif
369}
370
371std::vector<storm::expressions::Expression> Z3SmtSolver::getUnsatAssumptions() {
372#ifdef STORM_HAVE_Z3
373 STORM_LOG_THROW(lastResult == SmtSolver::CheckResult::Unsat, storm::exceptions::InvalidStateException,
374 "Unable to generate unsatisfiable core of assumptions, because the last check did not determine the formulas to be unsatisfiable.");
375 STORM_LOG_THROW(lastCheckAssumptions, storm::exceptions::InvalidStateException,
376 "Unable to generate unsatisfiable core of assumptions, because the last check did not involve assumptions.");
377
378 z3::expr_vector z3UnsatAssumptions = this->solver->unsat_core();
379 std::vector<storm::expressions::Expression> unsatAssumptions;
380
381 for (unsigned int i = 0; i < z3UnsatAssumptions.size(); ++i) {
382 unsatAssumptions.push_back(this->expressionAdapter->translateExpression(z3UnsatAssumptions[i]));
383 }
384
385 return unsatAssumptions;
386#else
387 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException, "Storm is compiled without Z3 support.");
388#endif
389}
390
391bool Z3SmtSolver::setTimeout(uint_fast64_t milliseconds) {
392#ifdef STORM_HAVE_Z3
393 z3::params paramObject(*context);
394 paramObject.set(":timeout", static_cast<unsigned>(milliseconds));
395 solver->set(paramObject);
396 return true;
397#else
398 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException, "Storm is compiled without Z3 support.");
399#endif
400}
401
403#ifdef STORM_HAVE_Z3
404 z3::params paramObject(*context);
405 paramObject.set(":timeout", 0u);
406 solver->set(paramObject);
407 return true;
408#else
409 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException, "Storm is compiled without Z3 support.");
410#endif
411}
412
413std::string Z3SmtSolver::getSmtLibString() const {
414#ifdef STORM_HAVE_Z3
415 return solver->to_smt2();
416#else
417 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException, "Storm is compiled without Z3 support.");
418#endif
419}
420
421} // namespace solver
422} // namespace storm
int_fast64_t evaluateAsInt(Valuation const *valuation=nullptr) const
Evaluates the expression under the valuation of variables given by the valuation and returns the resu...
double evaluateAsDouble(Valuation const *valuation=nullptr) const
Evaluates the expression under the valuation of variables given by the valuation and returns the resu...
Type const & getType() const
Retrieves the type of the expression.
bool isTrue() const
Checks if the expression is equal to the boolean literal true.
This class is responsible for managing a set of typed variables and all expressions using these varia...
A simple implementation of the valuation interface.
virtual void setBooleanValue(Variable const &booleanVariable, bool value) override
Sets the value of the given boolean variable to the provided value.
bool isBooleanType() const
Checks whether this type is a boolean type.
Definition Type.cpp:194
bool isIntegerType() const
Checks whether this type is an integral type.
Definition Type.cpp:198
bool isRationalType() const
Checks whether this type is a rational type.
Definition Type.cpp:234
ExpressionManager const & getManager() const
Retrieves the manager responsible for this variable.
Definition Variable.cpp:54
The base class for all model references.
Definition SmtSolver.h:30
storm::expressions::ExpressionManager const & getManager() const
Retrieves the expression manager associated with the solver.
Definition SmtSolver.cpp:79
SmtSolver(storm::expressions::ExpressionManager &manager)
Constructs a new Smt solver with the given options.
Definition SmtSolver.cpp:17
CheckResult
possible check results
Definition SmtSolver.h:24
virtual bool getBooleanValue(storm::expressions::Variable const &variable) const override
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 void add(storm::expressions::Expression const &assertion) override
Adds an assertion to the solver's stack.
virtual bool unsetTimeout() override
If supported by the solver, this unsets a previous timeout.
virtual std::shared_ptr< SmtSolver::ModelReference > getModel() override
If the last call to check() or checkWithAssumptions() returned Sat, this method retrieves a model tha...
virtual storm::expressions::SimpleValuation getModelAsValuation() override
If the last call to check() or checkWithAssumptions() returned Sat, this method retrieves a model tha...
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...
virtual std::string getSmtLibString() const override
If supported by the solver, this function returns the current assertions in the SMT-LIB format.
Z3SmtSolver(storm::expressions::ExpressionManager &manager)
virtual std::vector< storm::expressions::SimpleValuation > allSat(std::vector< storm::expressions::Variable > const &important) override
Performs AllSat over the (provided) important atoms.
virtual void reset() override
Removes all assertions from the solver's stack.
virtual void push() override
Pushes a backtracking point on the solver's stack.
virtual std::vector< storm::expressions::Expression > getUnsatAssumptions() override
If the last call to checkWithAssumptions() returned Unsat, this function can be used to retrieve a su...
virtual bool setTimeout(uint_fast64_t milliseconds) override
If supported by the solver, this will limit all subsequent satisfiability queries to the given number...
virtual void addNotCurrentModel(bool performSolverReset=true) override
If supported by the solver, this function tells the SMT solver to produce a model different from the ...
virtual void pop() override
Pops a backtracking point from the solver's stack.
virtual CheckResult check() override
Checks whether the conjunction of assertions that are currently on the solver's stack is satisfiable.
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:28
SettingsManager const & manager()
Retrieves the settings manager.