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