Storm
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
Z3ExpressionAdapter.cpp
Go to the documentation of this file.
2
3#include <cstdint>
4
12
13namespace storm {
14namespace adapters {
15
16#ifdef STORM_HAVE_Z3
17
18#ifdef STORM_Z3_API_USES_STANDARD_INTEGERS
19typedef int64_t Z3_SIGNED_INTEGER;
20typedef uint64_t Z3_UNSIGNED_INTEGER;
21#else
22typedef long long Z3_SIGNED_INTEGER;
23typedef unsigned long long Z3_UNSIGNED_INTEGER;
24#endif
25
26Z3ExpressionAdapter::Z3ExpressionAdapter(storm::expressions::ExpressionManager& manager, z3::context& context)
27 : manager(manager), context(context), additionalAssertions(), variableToExpressionMapping() {
28 // Intentionally left empty.
29}
30
31z3::expr Z3ExpressionAdapter::translateExpression(storm::expressions::Expression const& expression) {
32 STORM_LOG_ASSERT(expression.getManager() == this->manager, "Invalid expression for solver.");
33
34 z3::expr result = boost::any_cast<z3::expr>(expression.getBaseExpression().accept(*this, boost::none));
35 expressionCache.clear();
36
37 for (z3::expr const& assertion : additionalAssertions) {
38 result = result && assertion;
39 }
40 additionalAssertions.clear();
41 return result.simplify();
42}
43
44z3::expr Z3ExpressionAdapter::translateExpression(storm::expressions::Variable const& variable) {
45 STORM_LOG_ASSERT(variable.getManager() == this->manager, "Invalid expression for solver.");
46
47 auto const& variableExpressionPair = variableToExpressionMapping.find(variable);
48 if (variableExpressionPair == variableToExpressionMapping.end()) {
49 return createVariable(variable);
50 }
51
52 return variableExpressionPair->second;
53}
54
55storm::expressions::Variable const& Z3ExpressionAdapter::getVariable(z3::func_decl z3Declaration) {
56 auto const& declarationVariablePair = declarationToVariableMapping.find(z3Declaration);
57 STORM_LOG_ASSERT(declarationVariablePair != declarationToVariableMapping.end(), "Unable to find declaration.");
58 return declarationVariablePair->second;
59}
60
61storm::expressions::Expression Z3ExpressionAdapter::translateExpression(z3::expr const& expr) {
62 if (expr.is_app()) {
63 switch (expr.decl().decl_kind()) {
64 case Z3_OP_TRUE:
65 return manager.boolean(true);
66 case Z3_OP_FALSE:
67 return manager.boolean(false);
68 case Z3_OP_EQ:
69 return this->translateExpression(expr.arg(0)) == this->translateExpression(expr.arg(1));
70 case Z3_OP_DISTINCT: {
71 unsigned args = expr.num_args();
72 STORM_LOG_THROW(args != 0, storm::exceptions::ExpressionEvaluationException,
73 "Failed to convert Z3 expression. DISTINCT (mutual != ) operator with 0-arity is assumed to be an error.");
74 if (args == 1) {
75 return manager.boolean(true);
76 } else {
77 storm::expressions::Expression retVal = this->translateExpression(expr.arg(0)) != this->translateExpression(expr.arg(1));
78 for (unsigned arg2 = 2; arg2 < args; ++arg2) {
79 retVal = retVal && (this->translateExpression(expr.arg(0)) != this->translateExpression(expr.arg(arg2)));
80 }
81 for (unsigned arg1 = 1; arg1 < args; ++arg1) {
82 for (unsigned arg2 = arg1 + 1; arg2 < args; ++arg2) {
83 retVal = retVal && (this->translateExpression(expr.arg(arg1)) != this->translateExpression(expr.arg(arg2)));
84 }
85 }
86 return retVal;
87 }
88 }
89 case Z3_OP_ITE:
90 return storm::expressions::ite(this->translateExpression(expr.arg(0)), this->translateExpression(expr.arg(1)),
91 this->translateExpression(expr.arg(2)));
92 case Z3_OP_AND: {
93 unsigned args = expr.num_args();
94 STORM_LOG_THROW(args != 0, storm::exceptions::ExpressionEvaluationException,
95 "Failed to convert Z3 expression. 0-ary AND is assumed to be an error.");
96 if (args == 1) {
97 return this->translateExpression(expr.arg(0));
98 } else {
99 storm::expressions::Expression retVal = this->translateExpression(expr.arg(0));
100 for (unsigned i = 1; i < args; i++) {
101 retVal = retVal && this->translateExpression(expr.arg(i));
102 }
103 return retVal;
104 }
105 }
106 case Z3_OP_OR: {
107 unsigned args = expr.num_args();
108 STORM_LOG_THROW(args != 0, storm::exceptions::ExpressionEvaluationException,
109 "Failed to convert Z3 expression. 0-ary OR is assumed to be an error.");
110 if (args == 1) {
111 return this->translateExpression(expr.arg(0));
112 } else {
113 storm::expressions::Expression retVal = this->translateExpression(expr.arg(0));
114 for (unsigned i = 1; i < args; i++) {
115 retVal = retVal || this->translateExpression(expr.arg(i));
116 }
117 return retVal;
118 }
119 }
120 case Z3_OP_IFF:
121 return storm::expressions::iff(this->translateExpression(expr.arg(0)), this->translateExpression(expr.arg(1)));
122 case Z3_OP_XOR:
123 return storm::expressions::xclusiveor(this->translateExpression(expr.arg(0)), this->translateExpression(expr.arg(1)));
124 case Z3_OP_NOT:
125 return !this->translateExpression(expr.arg(0));
126 case Z3_OP_IMPLIES:
127 return storm::expressions::implies(this->translateExpression(expr.arg(0)), this->translateExpression(expr.arg(1)));
128 case Z3_OP_LE:
129 return this->translateExpression(expr.arg(0)) <= this->translateExpression(expr.arg(1));
130 case Z3_OP_GE:
131 return this->translateExpression(expr.arg(0)) >= this->translateExpression(expr.arg(1));
132 case Z3_OP_LT:
133 return this->translateExpression(expr.arg(0)) < this->translateExpression(expr.arg(1));
134 case Z3_OP_GT:
135 return this->translateExpression(expr.arg(0)) > this->translateExpression(expr.arg(1));
136 case Z3_OP_ADD:
137 return this->translateExpression(expr.arg(0)) + this->translateExpression(expr.arg(1));
138 case Z3_OP_SUB:
139 return this->translateExpression(expr.arg(0)) - this->translateExpression(expr.arg(1));
140 case Z3_OP_UMINUS:
141 return -this->translateExpression(expr.arg(0));
142 case Z3_OP_MUL:
143 return this->translateExpression(expr.arg(0)) * this->translateExpression(expr.arg(1));
144 case Z3_OP_DIV:
145 return this->translateExpression(expr.arg(0)) / this->translateExpression(expr.arg(1));
146 case Z3_OP_IDIV:
147 return this->translateExpression(expr.arg(0)) / this->translateExpression(expr.arg(1));
148 case Z3_OP_ANUM:
149 // Arithmetic numeral.
150 if (expr.is_int() && expr.is_const()) {
151 Z3_SIGNED_INTEGER value;
152 if (Z3_get_numeral_int64(expr.ctx(), expr, &value)) {
153 return manager.integer(value);
154 } else {
155 STORM_LOG_THROW(false, storm::exceptions::ExpressionEvaluationException,
156 "Failed to convert Z3 expression. Expression is constant integer and value does not fit into 64-bit integer.");
157 }
158 } else {
159 STORM_LOG_ASSERT(expr.is_real() && expr.is_const(), "Cannot handle numerical expression");
160 Z3_SIGNED_INTEGER num;
161 Z3_SIGNED_INTEGER den;
162 if (Z3_get_numeral_rational_int64(expr.ctx(), expr, &num, &den)) {
163 return manager.rational(storm::utility::convertNumber<storm::RationalNumber>(static_cast<int_fast64_t>(num)) /
164 storm::utility::convertNumber<storm::RationalNumber>(static_cast<int_fast64_t>(den)));
165 } else {
166 return manager.rational(storm::utility::convertNumber<storm::RationalNumber>(std::string(Z3_get_numeral_string(expr.ctx(), expr))));
167 }
168 }
169 case Z3_OP_UNINTERPRETED:
170 // Currently, we only support uninterpreted constant functions.
171 STORM_LOG_THROW(expr.is_const(), storm::exceptions::ExpressionEvaluationException,
172 "Failed to convert Z3 expression. Encountered non-constant uninterpreted function.");
173 return manager.getVariable(expr.decl().name().str()).getExpression();
174 default:
175 STORM_LOG_THROW(false, storm::exceptions::ExpressionEvaluationException,
176 "Failed to convert Z3 expression. Encountered unhandled Z3_decl_kind " << expr.decl().decl_kind() << ".");
177 break;
178 }
179 } else {
180 STORM_LOG_THROW(false, storm::exceptions::ExpressionEvaluationException, "Failed to convert Z3 expression. Encountered unknown expression type.");
181 }
182}
183
184boost::any Z3ExpressionAdapter::visit(storm::expressions::BinaryBooleanFunctionExpression const& expression, boost::any const& data) {
185 auto cacheIt = expressionCache.find(&expression);
186 if (cacheIt != expressionCache.end()) {
187 return cacheIt->second;
188 }
189
190 z3::expr leftResult = boost::any_cast<z3::expr>(expression.getFirstOperand()->accept(*this, data));
191 z3::expr rightResult = boost::any_cast<z3::expr>(expression.getSecondOperand()->accept(*this, data));
192
193 z3::expr result(context);
194 switch (expression.getOperatorType()) {
196 result = leftResult && rightResult;
197 break;
199 result = leftResult || rightResult;
200 break;
202 result = z3::expr(context, Z3_mk_xor(context, leftResult, rightResult));
203 break;
205 result = z3::expr(context, Z3_mk_implies(context, leftResult, rightResult));
206 break;
208 result = z3::expr(context, Z3_mk_iff(context, leftResult, rightResult));
209 break;
210 default:
211 STORM_LOG_THROW(false, storm::exceptions::ExpressionEvaluationException,
212 "Cannot evaluate expression: unknown boolean binary operator '" << static_cast<int>(expression.getOperatorType())
213 << "' in expression " << expression << ".");
214 }
215
216 expressionCache.emplace(&expression, result);
217 return result;
218}
219
220boost::any Z3ExpressionAdapter::visit(storm::expressions::BinaryNumericalFunctionExpression const& expression, boost::any const& data) {
221 auto cacheIt = expressionCache.find(&expression);
222 if (cacheIt != expressionCache.end()) {
223 return cacheIt->second;
224 }
225
226 z3::expr leftResult = boost::any_cast<z3::expr>(expression.getFirstOperand()->accept(*this, data));
227 z3::expr rightResult = boost::any_cast<z3::expr>(expression.getSecondOperand()->accept(*this, data));
228
229 z3::expr result(context);
230 switch (expression.getOperatorType()) {
232 result = leftResult + rightResult;
233 break;
235 result = leftResult - rightResult;
236 break;
238 result = leftResult * rightResult;
239 break;
241 if (leftResult.is_int() && rightResult.is_int()) {
242 leftResult = z3::expr(context, Z3_mk_int2real(context, leftResult));
243 }
244 result = leftResult / rightResult;
245 break;
247 result = ite(leftResult <= rightResult, leftResult, rightResult);
248 break;
250 result = ite(leftResult >= rightResult, leftResult, rightResult);
251 break;
253 result = pw(leftResult, rightResult);
254 break;
255 default:
256 STORM_LOG_THROW(false, storm::exceptions::ExpressionEvaluationException,
257 "Cannot evaluate expression: unknown numerical binary operator '" << static_cast<int>(expression.getOperatorType())
258 << "' in expression " << expression << ".");
259 }
260
261 expressionCache.emplace(&expression, result);
262 return result;
263}
264
265boost::any Z3ExpressionAdapter::visit(storm::expressions::BinaryRelationExpression const& expression, boost::any const& data) {
266 auto cacheIt = expressionCache.find(&expression);
267 if (cacheIt != expressionCache.end()) {
268 return cacheIt->second;
269 }
270
271 z3::expr leftResult = boost::any_cast<z3::expr>(expression.getFirstOperand()->accept(*this, data));
272 z3::expr rightResult = boost::any_cast<z3::expr>(expression.getSecondOperand()->accept(*this, data));
273
274 z3::expr result(context);
275 switch (expression.getRelationType()) {
277 result = leftResult == rightResult;
278 break;
280 result = leftResult != rightResult;
281 break;
283 result = leftResult < rightResult;
284 break;
286 result = leftResult <= rightResult;
287 break;
289 result = leftResult > rightResult;
290 break;
292 result = leftResult >= rightResult;
293 break;
294 default:
295 STORM_LOG_THROW(false, storm::exceptions::ExpressionEvaluationException,
296 "Cannot evaluate expression: unknown boolean binary operator '" << static_cast<int>(expression.getRelationType())
297 << "' in expression " << expression << ".");
298 }
299
300 expressionCache.emplace(&expression, result);
301 return result;
302}
303
304boost::any Z3ExpressionAdapter::visit(storm::expressions::BooleanLiteralExpression const& expression, boost::any const&) {
305 auto cacheIt = expressionCache.find(&expression);
306 if (cacheIt != expressionCache.end()) {
307 return cacheIt->second;
308 }
309
310 z3::expr result = context.bool_val(expression.getValue());
311
312 expressionCache.emplace(&expression, result);
313 return result;
314}
315
316boost::any Z3ExpressionAdapter::visit(storm::expressions::RationalLiteralExpression const& expression, boost::any const&) {
317 auto cacheIt = expressionCache.find(&expression);
318 if (cacheIt != expressionCache.end()) {
319 return cacheIt->second;
320 }
321
322 std::stringstream fractionStream;
323 fractionStream << expression.getValue();
324 z3::expr result = context.real_val(fractionStream.str().c_str());
325
326 expressionCache.emplace(&expression, result);
327 return result;
328}
329
330boost::any Z3ExpressionAdapter::visit(storm::expressions::IntegerLiteralExpression const& expression, boost::any const&) {
331 auto cacheIt = expressionCache.find(&expression);
332 if (cacheIt != expressionCache.end()) {
333 return cacheIt->second;
334 }
335
336 z3::expr result = context.int_val(static_cast<Z3_SIGNED_INTEGER>(expression.getValue()));
337
338 expressionCache.emplace(&expression, result);
339 return result;
340}
341
342boost::any Z3ExpressionAdapter::visit(storm::expressions::UnaryBooleanFunctionExpression const& expression, boost::any const& data) {
343 auto cacheIt = expressionCache.find(&expression);
344 if (cacheIt != expressionCache.end()) {
345 return cacheIt->second;
346 }
347
348 z3::expr result = boost::any_cast<z3::expr>(expression.getOperand()->accept(*this, data));
349
350 switch (expression.getOperatorType()) {
352 result = !result;
353 break;
354 default:
355 STORM_LOG_THROW(false, storm::exceptions::ExpressionEvaluationException,
356 "Cannot evaluate expression: unknown boolean binary operator '" << static_cast<int>(expression.getOperatorType())
357 << "' in expression " << expression << ".");
358 }
359
360 expressionCache.emplace(&expression, result);
361 return result;
362}
363
364boost::any Z3ExpressionAdapter::visit(storm::expressions::UnaryNumericalFunctionExpression const& expression, boost::any const& data) {
365 auto cacheIt = expressionCache.find(&expression);
366 if (cacheIt != expressionCache.end()) {
367 return cacheIt->second;
368 }
369
370 z3::expr result = boost::any_cast<z3::expr>(expression.getOperand()->accept(*this, data));
371
372 switch (expression.getOperatorType()) {
374 result = 0 - result;
375 break;
377 storm::expressions::Variable freshAuxiliaryVariable = manager.declareFreshVariable(manager.getIntegerType(), true);
378 z3::expr floorVariable = context.int_const(freshAuxiliaryVariable.getName().c_str());
379 additionalAssertions.push_back(z3::expr(context, Z3_mk_int2real(context, floorVariable)) <= result &&
380 result < (z3::expr(context, Z3_mk_int2real(context, floorVariable)) + 1));
381 result = floorVariable;
382 break;
383 }
385 storm::expressions::Variable freshAuxiliaryVariable = manager.declareFreshVariable(manager.getIntegerType(), true);
386 z3::expr ceilVariable = context.int_const(freshAuxiliaryVariable.getName().c_str());
387 additionalAssertions.push_back(z3::expr(context, Z3_mk_int2real(context, ceilVariable)) - 1 < result &&
388 result <= z3::expr(context, Z3_mk_int2real(context, ceilVariable)));
389 result = ceilVariable;
390 break;
391 }
392 default:
393 STORM_LOG_THROW(false, storm::exceptions::ExpressionEvaluationException,
394 "Cannot evaluate expression: unknown numerical unary operator '" << static_cast<int>(expression.getOperatorType()) << "'.");
395 }
396
397 expressionCache.emplace(&expression, result);
398 return result;
399}
400
401boost::any Z3ExpressionAdapter::visit(storm::expressions::IfThenElseExpression const& expression, boost::any const& data) {
402 auto cacheIt = expressionCache.find(&expression);
403 if (cacheIt != expressionCache.end()) {
404 return cacheIt->second;
405 }
406
407 z3::expr conditionResult = boost::any_cast<z3::expr>(expression.getCondition()->accept(*this, data));
408 z3::expr thenResult = boost::any_cast<z3::expr>(expression.getThenExpression()->accept(*this, data));
409 z3::expr elseResult = boost::any_cast<z3::expr>(expression.getElseExpression()->accept(*this, data));
410 z3::expr result = z3::expr(context, Z3_mk_ite(context, conditionResult, thenResult, elseResult));
411
412 expressionCache.emplace(&expression, result);
413 return result;
414}
415
416boost::any Z3ExpressionAdapter::visit(storm::expressions::VariableExpression const& expression, boost::any const&) {
417 return this->translateExpression(expression.getVariable());
418}
419
420z3::expr Z3ExpressionAdapter::createVariable(storm::expressions::Variable const& variable) {
421 z3::expr z3Variable(context);
422 if (variable.getType().isBooleanType()) {
423 z3Variable = context.bool_const(variable.getName().c_str());
424 } else if (variable.getType().isIntegerType()) {
425 z3Variable = context.int_const(variable.getName().c_str());
426 } else if (variable.getType().isBitVectorType()) {
427 z3Variable = context.bv_const(variable.getName().c_str(), variable.getType().getWidth());
428 } else if (variable.getType().isRationalType()) {
429 z3Variable = context.real_const(variable.getName().c_str());
430 } else {
431 STORM_LOG_THROW(false, storm::exceptions::InvalidTypeException,
432 "Encountered variable '" << variable.getName() << "' with unknown type while trying to create solver variables.");
433 }
434 variableToExpressionMapping.insert(std::make_pair(variable, z3Variable));
435 declarationToVariableMapping.insert(std::make_pair(z3Variable.decl(), variable));
436 return z3Variable;
437}
438
439#endif
440} // namespace adapters
441} // namespace storm
virtual boost::any accept(ExpressionVisitor &visitor, boost::any const &data) const =0
Accepts the given visitor by calling its visit method.
OperatorType getOperatorType() const
Retrieves the operator associated with the expression.
std::shared_ptr< BaseExpression const > const & getSecondOperand() const
Retrieves the second operand of the expression.
std::shared_ptr< BaseExpression const > const & getFirstOperand() const
Retrieves the first operand of the expression.
OperatorType getOperatorType() const
Retrieves the operator associated with the expression.
RelationType getRelationType() const
Retrieves the relation associated with the expression.
bool getValue() const
Retrieves the value of the boolean literal.
BaseExpression const & getBaseExpression() const
Retrieves the base expression underlying this expression object.
ExpressionManager const & getManager() const
Retrieves the manager responsible for this expression.
This class is responsible for managing a set of typed variables and all expressions using these varia...
std::shared_ptr< BaseExpression const > getElseExpression() const
Retrieves the else expression of the if-then-else expression.
std::shared_ptr< BaseExpression const > getCondition() const
Retrieves the condition expression of the if-then-else expression.
std::shared_ptr< BaseExpression const > getThenExpression() const
Retrieves the then expression of the if-then-else expression.
int_fast64_t getValue() const
Retrieves the value of the integer literal.
storm::RationalNumber getValue() const
Retrieves the value of the double literal.
bool isBooleanType() const
Checks whether this type is a boolean type.
Definition Type.cpp:178
std::size_t getWidth() const
Retrieves the bit width of the type, provided that it is a bitvector type.
Definition Type.cpp:206
bool isIntegerType() const
Checks whether this type is an integral type.
Definition Type.cpp:182
bool isRationalType() const
Checks whether this type is a rational type.
Definition Type.cpp:214
bool isBitVectorType() const
Checks whether this type is a bitvector type.
Definition Type.cpp:186
OperatorType getOperatorType() const
Retrieves the operator associated with this expression.
virtual std::shared_ptr< BaseExpression const > getOperand(uint_fast64_t operandIndex) const override
Retrieves the given operand from the expression.
OperatorType getOperatorType() const
Retrieves the operator associated with this expression.
Variable const & getVariable() const
Retrieves the variable associated with this expression.
ExpressionManager const & getManager() const
Retrieves the manager responsible for this variable.
Definition Variable.cpp:54
Type const & getType() const
Retrieves the type of the variable.
Definition Variable.cpp:50
std::string const & getName() const
Retrieves the name of the variable.
Definition Variable.cpp:46
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
Expression ite(Expression const &condition, Expression const &thenExpression, Expression const &elseExpression)
Expression iff(Expression const &first, Expression const &second)
Expression xclusiveor(Expression const &first, Expression const &second)
Expression implies(Expression const &first, Expression const &second)
SettingsManager const & manager()
Retrieves the settings manager.
LabParser.cpp.
Definition cli.cpp:18