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
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 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 CheckResult checkWithAssumptions(storm::expressions::ExpressionSet const &assumptions) override
Checks whether the conjunction of assertions that are currently on the solver's stack together with t...
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:39
std::set< Expression, ExpressionComparator > ExpressionSet
Definition Expression.h:515
SettingsManager const & manager()
Retrieves the settings manager.