Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
Cvc5ExpressionAdapter.cpp
Go to the documentation of this file.
2
3#include <cstdint>
4#include <functional>
5#include <string>
6#include <string_view>
7#include <vector>
8
16
17namespace storm::adapters {
18
19#ifdef STORM_HAVE_CVC5
20
21namespace {
22
34storm::expressions::Expression translateNumericalConstant(storm::expressions::ExpressionManager& manager, std::string const& stringRepresentation,
35 bool isInteger) {
36 // Tokenize the string representation, e.g., "(- (/ 3 4))" results in the tokens "(-", "(/", "3", "4", ")", ")".
37 // The tokens are views into stringRepresentation, which outlives this function body.
38 std::vector<std::string_view> tokens;
39 std::string_view const input(stringRepresentation);
40 size_t tokenStart = 0;
41 bool inToken = false;
42 auto const flushToken = [&](size_t end) {
43 if (inToken) {
44 tokens.push_back(input.substr(tokenStart, end - tokenStart));
45 inToken = false;
46 }
47 };
48 for (size_t i = 0; i < input.size(); ++i) {
49 char const c = input[i];
50 if (std::isspace(static_cast<unsigned char>(c))) {
51 flushToken(i);
52 } else if (c == '(') {
53 flushToken(i);
54 // The operators for negative numbers and fractions are two-character symbols, i.e., "(-" and "(/".
55 if (i + 1 < input.size() && (input[i + 1] == '-' || input[i + 1] == '/')) {
56 tokens.push_back(input.substr(i, 2));
57 ++i;
58 } else {
59 tokens.push_back(input.substr(i, 1));
60 }
61 } else if (c == ')') {
62 flushToken(i);
63 tokens.push_back(input.substr(i, 1));
64 } else if (!inToken) {
65 inToken = true;
66 tokenStart = i;
67 }
68 }
69 flushToken(input.size());
70 STORM_LOG_THROW(!tokens.empty(), storm::exceptions::ExpressionEvaluationException,
71 "Failed to convert cvc5 expression. Encountered empty numerical constant.");
72
73 // cvc5 prints integer constants as a (possibly negated) plain number, e.g., "42" or "(- 42)".
74 if (isInteger) {
75 size_t position = 0;
76 bool isNegative = tokens[position] == "(-";
77 if (isNegative) {
78 ++position;
79 }
80 STORM_LOG_THROW(position < tokens.size(), storm::exceptions::ExpressionEvaluationException,
81 "Failed to convert cvc5 expression. Encountered malformed integer constant " << stringRepresentation << ".");
82 std::string const number(tokens[position]);
83 ++position;
84 if (isNegative) {
85 STORM_LOG_THROW(position < tokens.size() && tokens[position] == ")", storm::exceptions::ExpressionEvaluationException,
86 "Failed to convert cvc5 expression. Encountered malformed integer constant " << stringRepresentation << ".");
87 ++position;
88 }
89 STORM_LOG_THROW(position == tokens.size(), storm::exceptions::ExpressionEvaluationException,
90 "Failed to convert cvc5 expression. Encountered malformed integer constant " << stringRepresentation << ".");
91
92 // cvc5 integers are unbounded, so the value may not fit. We parse the signed string (rather than negating the parsed value)
93 // so that the lowest 64-bit integer is handled correctly.
94 int64_t value = 0;
95 try {
96 value = std::stoll(isNegative ? "-" + number : number);
97 } catch (std::logic_error const&) {
98 STORM_LOG_THROW(false, storm::exceptions::ExpressionEvaluationException,
99 "Failed to convert cvc5 expression. Integer constant " << stringRepresentation << " does not fit into a 64-bit integer.");
100 }
101 return manager.integer(value);
102 }
103
104 // cvc5 prints rational constants in the SMT-LIB notation, e.g., "2.0", "(- 1.5)", "(/ 7 3)", or "(/ (- 3) 4)".
105 size_t position = 0;
106 std::function<storm::RationalNumber(bool)> parse;
107 parse = [&tokens, &position, &parse, &stringRepresentation](bool isNegative) -> storm::RationalNumber {
108 STORM_LOG_THROW(position < tokens.size(), storm::exceptions::ExpressionEvaluationException,
109 "Failed to convert cvc5 expression. Encountered malformed rational constant " << stringRepresentation << ".");
110
111 // A negative number is printed as "(- x)".
112 if (tokens[position] == "(-") {
113 STORM_LOG_THROW(!isNegative, storm::exceptions::ExpressionEvaluationException,
114 "Failed to convert cvc5 expression. Encountered doubly negated rational constant " << stringRepresentation << ".");
115 ++position;
116 storm::RationalNumber result = parse(true);
117 STORM_LOG_THROW(position < tokens.size() && tokens[position] == ")", storm::exceptions::ExpressionEvaluationException,
118 "Failed to convert cvc5 expression. Encountered malformed rational constant " << stringRepresentation << ".");
119 ++position;
120 return result;
121 }
122
123 // A non-integral rational is printed as "(/ x y)", where a potential minus sign is part of the numerator.
124 if (tokens[position] == "(/") {
125 ++position;
126 storm::RationalNumber numerator = parse(isNegative);
127 storm::RationalNumber denominator = parse(false);
128 STORM_LOG_THROW(position < tokens.size() && tokens[position] == ")", storm::exceptions::ExpressionEvaluationException,
129 "Failed to convert cvc5 expression. Encountered malformed fraction " << stringRepresentation << ".");
130 ++position;
131 return numerator / denominator;
132 }
133
134 // Everything else is a plain (decimal) number.
135 std::string const number(tokens[position]);
136 ++position;
137 storm::RationalNumber const result = storm::utility::convertNumber<storm::RationalNumber>(std::string(number));
138 return isNegative ? -result : result;
139 };
140
141 storm::RationalNumber const value = parse(false);
142 STORM_LOG_THROW(position == tokens.size(), storm::exceptions::ExpressionEvaluationException,
143 "Failed to convert cvc5 expression. Encountered malformed rational constant " << stringRepresentation << ".");
144 return manager.rational(value);
145}
146
147} // namespace
148
149Cvc5ExpressionAdapter::Cvc5ExpressionAdapter(storm::expressions::ExpressionManager& manager, cvc5::Solver& solver)
150 : manager(manager), termManager(solver.getTermManager()), variableToExpressionMapping() {
151 // Intentionally left empty.
152}
153
154cvc5::Term Cvc5ExpressionAdapter::translateExpression(storm::expressions::Expression const& expression) {
155 STORM_LOG_ASSERT(expression.getManager() == this->manager, "Invalid expression for solver.");
156
157 cvc5::Term result = boost::any_cast<cvc5::Term>(expression.getBaseExpression().accept(*this, boost::none));
158 expressionCache.clear();
159 return result;
160}
161
162cvc5::Term Cvc5ExpressionAdapter::translateExpression(storm::expressions::Variable const& variable) {
163 STORM_LOG_ASSERT(variable.getManager() == this->manager, "Invalid expression for solver.");
164
165 auto const& variableExpressionPair = variableToExpressionMapping.find(variable);
166 if (variableExpressionPair == variableToExpressionMapping.end()) {
167 return createVariable(variable);
168 }
169
170 return variableExpressionPair->second;
171}
172
173storm::expressions::Expression Cvc5ExpressionAdapter::translateExpression(cvc5::Term const& term) {
174 // First, deal with the boolean constants, as they do not have a symbol that we could look up.
175 if (term.getSort().isBoolean()) {
176 if (term == this->termManager.mkBoolean(true)) {
177 return this->manager.boolean(true);
178 } else if (term == this->termManager.mkBoolean(false)) {
179 return this->manager.boolean(false);
180 }
181 }
182
183 // Second, deal with constants, i.e., variables and numerical constants.
184 if (term.getKind() == cvc5::Kind::CONSTANT) {
185 STORM_LOG_THROW(term.hasSymbol(), storm::exceptions::ExpressionEvaluationException,
186 "Failed to convert cvc5 expression. Encountered a constant without a symbol.");
187 return this->getVariable(term).getExpression();
188 } else if (term.getKind() == cvc5::Kind::CONST_INTEGER || term.getKind() == cvc5::Kind::CONST_RATIONAL) {
189 return translateNumericalConstant(this->manager, term.toString(), term.getKind() == cvc5::Kind::CONST_INTEGER);
190 }
191
192 switch (term.getKind()) {
193 case cvc5::Kind::CONST_BOOLEAN:
194 return term == this->termManager.mkBoolean(true) ? this->manager.boolean(true) : this->manager.boolean(false);
195 case cvc5::Kind::EQUAL: {
196 // Storm's operator== only accepts numerical operands, while cvc5's EQUAL is also how it represents
197 // boolean equivalence. Use iff for boolean operands so that translating back an unsatisfiable core
198 // that contains an equivalence does not throw.
199 storm::expressions::Expression const first = this->translateExpression(term[0]);
200 storm::expressions::Expression const second = this->translateExpression(term[1]);
201 if (first.hasBooleanType() && second.hasBooleanType()) {
202 return storm::expressions::iff(first, second);
203 }
204 return first == second;
205 }
206 case cvc5::Kind::DISTINCT: {
207 STORM_LOG_THROW(term.getNumChildren() != 0, storm::exceptions::ExpressionEvaluationException,
208 "Failed to convert cvc5 expression. DISTINCT (mutual !=) operator with 0-arity is assumed to be an error.");
209 if (term.getNumChildren() == 1) {
210 return this->manager.boolean(true);
211 } else {
212 storm::expressions::Expression retVal = this->translateExpression(term[0]) != this->translateExpression(term[1]);
213 for (size_t arg2 = 2; arg2 < term.getNumChildren(); ++arg2) {
214 retVal = retVal && (this->translateExpression(term[0]) != this->translateExpression(term[arg2]));
215 }
216 for (size_t arg1 = 1; arg1 < term.getNumChildren(); ++arg1) {
217 for (size_t arg2 = arg1 + 1; arg2 < term.getNumChildren(); ++arg2) {
218 retVal = retVal && (this->translateExpression(term[arg1]) != this->translateExpression(term[arg2]));
219 }
220 }
221 return retVal;
222 }
223 }
224 case cvc5::Kind::ITE:
225 return storm::expressions::ite(this->translateExpression(term[0]), this->translateExpression(term[1]), this->translateExpression(term[2]));
226 case cvc5::Kind::AND: {
227 STORM_LOG_THROW(term.getNumChildren() != 0, storm::exceptions::ExpressionEvaluationException,
228 "Failed to convert cvc5 expression. 0-ary AND is assumed to be an error.");
229 if (term.getNumChildren() == 1) {
230 return this->translateExpression(term[0]);
231 } else {
232 storm::expressions::Expression retVal = this->translateExpression(term[0]);
233 for (size_t i = 1; i < term.getNumChildren(); ++i) {
234 retVal = retVal && this->translateExpression(term[i]);
235 }
236 return retVal;
237 }
238 }
239 case cvc5::Kind::OR: {
240 STORM_LOG_THROW(term.getNumChildren() != 0, storm::exceptions::ExpressionEvaluationException,
241 "Failed to convert cvc5 expression. 0-ary OR is assumed to be an error.");
242 if (term.getNumChildren() == 1) {
243 return this->translateExpression(term[0]);
244 } else {
245 storm::expressions::Expression retVal = this->translateExpression(term[0]);
246 for (size_t i = 1; i < term.getNumChildren(); ++i) {
247 retVal = retVal || this->translateExpression(term[i]);
248 }
249 return retVal;
250 }
251 }
252 case cvc5::Kind::XOR:
253 return storm::expressions::xclusiveor(this->translateExpression(term[0]), this->translateExpression(term[1]));
254 case cvc5::Kind::NOT:
255 return !this->translateExpression(term[0]);
256 case cvc5::Kind::IMPLIES:
257 return storm::expressions::implies(this->translateExpression(term[0]), this->translateExpression(term[1]));
258 case cvc5::Kind::LEQ:
259 return this->translateExpression(term[0]) <= this->translateExpression(term[1]);
260 case cvc5::Kind::GEQ:
261 return this->translateExpression(term[0]) >= this->translateExpression(term[1]);
262 case cvc5::Kind::LT:
263 return this->translateExpression(term[0]) < this->translateExpression(term[1]);
264 case cvc5::Kind::GT:
265 return this->translateExpression(term[0]) > this->translateExpression(term[1]);
266 case cvc5::Kind::ADD: {
267 // Collect the operands and build a balanced expression to avoid deep recursion on large sums.
268 std::vector<storm::expressions::Expression> operands;
269 operands.reserve(term.getNumChildren());
270 for (size_t i = 0; i < term.getNumChildren(); ++i) {
271 operands.push_back(this->translateExpression(term[i]));
272 }
273 // A 0-ary sum is equal to 0.
274 return operands.empty() ? this->manager.integer(0) : storm::expressions::sum(operands);
275 }
276 case cvc5::Kind::SUB:
277 return this->translateExpression(term[0]) - this->translateExpression(term[1]);
278 case cvc5::Kind::NEG:
279 return -this->translateExpression(term[0]);
280 case cvc5::Kind::MULT: {
281 // Collect the operands and build a balanced expression to avoid deep recursion on large products.
282 std::vector<storm::expressions::Expression> operands;
283 operands.reserve(term.getNumChildren());
284 for (size_t i = 0; i < term.getNumChildren(); ++i) {
285 operands.push_back(this->translateExpression(term[i]));
286 }
287 // A 0-ary product is equal to 1.
288 return operands.empty() ? this->manager.integer(1) : storm::expressions::product(operands);
289 }
290 case cvc5::Kind::INTS_DIVISION:
291 // Storm has no integer division, and translating this to Storm's division would silently change the semantics.
292 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Failed to convert cvc5 expression. Integer division is not supported.");
293 case cvc5::Kind::DIVISION:
294 return this->translateExpression(term[0]) / this->translateExpression(term[1]);
295 case cvc5::Kind::POW:
296 return storm::expressions::pow(this->translateExpression(term[0]), this->translateExpression(term[1]));
297 case cvc5::Kind::TO_REAL:
298 return this->translateExpression(term[0]);
299 case cvc5::Kind::TO_INTEGER:
300 // cvc5's to_int operation applies the floor function.
301 return storm::expressions::floor(this->translateExpression(term[0]));
302 case cvc5::Kind::ABS:
303 return storm::expressions::abs(this->translateExpression(term[0]));
304 default:
305 STORM_LOG_THROW(false, storm::exceptions::ExpressionEvaluationException,
306 "Failed to convert cvc5 expression. Encountered unhandled kind " << term.getKind() << ".");
307 break;
308 }
309 STORM_LOG_THROW(false, storm::exceptions::ExpressionEvaluationException, "Failed to convert cvc5 expression.");
310}
311
312storm::expressions::Variable const& Cvc5ExpressionAdapter::getVariable(cvc5::Term const& constant) {
313 auto const& constantVariablePair = expressionToVariableMapping.find(constant);
314 STORM_LOG_THROW(constantVariablePair != expressionToVariableMapping.end(), storm::exceptions::ExpressionEvaluationException,
315 "Unable to find the variable corresponding to the given constant.");
316 return constantVariablePair->second;
317}
318
319boost::any Cvc5ExpressionAdapter::visit(storm::expressions::BinaryBooleanFunctionExpression const& expression, boost::any const& data) {
320 auto cacheIt = expressionCache.find(&expression);
321 if (cacheIt != expressionCache.end()) {
322 return cacheIt->second;
323 }
324
325 cvc5::Term leftResult = boost::any_cast<cvc5::Term>(expression.getFirstOperand()->accept(*this, data));
326 cvc5::Term rightResult = boost::any_cast<cvc5::Term>(expression.getSecondOperand()->accept(*this, data));
327
328 cvc5::Kind kind;
329 switch (expression.getOperatorType()) {
331 kind = cvc5::Kind::AND;
332 break;
334 kind = cvc5::Kind::OR;
335 break;
337 kind = cvc5::Kind::XOR;
338 break;
340 kind = cvc5::Kind::IMPLIES;
341 break;
343 kind = cvc5::Kind::EQUAL; // EQUAL on booleans is an iff in cvc5.
344 break;
345 default:
346 STORM_LOG_THROW(false, storm::exceptions::ExpressionEvaluationException,
347 "Cannot evaluate expression: unknown boolean binary operator '" << static_cast<int>(expression.getOperatorType())
348 << "' in expression " << expression << ".");
349 }
350
351 cvc5::Term result = this->termManager.mkTerm(kind, {leftResult, rightResult});
352
353 expressionCache.emplace(&expression, result);
354 return result;
355}
356
357cvc5::Term Cvc5ExpressionAdapter::convertToRealIfExpected(cvc5::Term const& term, bool const& isRealExpected) const {
358 if (isRealExpected && term.getSort().isInteger()) {
359 return this->termManager.mkTerm(cvc5::Kind::TO_REAL, {term});
360 }
361 return term;
362}
363
364void Cvc5ExpressionAdapter::unifyNumericalTypes(cvc5::Term& leftTerm, cvc5::Term& rightTerm) const {
365 if (leftTerm.getSort().isInteger() && rightTerm.getSort().isReal()) {
366 leftTerm = this->termManager.mkTerm(cvc5::Kind::TO_REAL, {leftTerm});
367 } else if (rightTerm.getSort().isInteger() && leftTerm.getSort().isReal()) {
368 rightTerm = this->termManager.mkTerm(cvc5::Kind::TO_REAL, {rightTerm});
369 }
370}
371
372boost::any Cvc5ExpressionAdapter::visit(storm::expressions::BinaryNumericalFunctionExpression const& expression, boost::any const& data) {
373 auto cacheIt = expressionCache.find(&expression);
374 if (cacheIt != expressionCache.end()) {
375 return cacheIt->second;
376 }
377
378 cvc5::Term leftResult = boost::any_cast<cvc5::Term>(expression.getFirstOperand()->accept(*this, data));
379 cvc5::Term rightResult = boost::any_cast<cvc5::Term>(expression.getSecondOperand()->accept(*this, data));
380
381 // cvc5 requires the operands of an arithmetic operation to be of the same type. Where Storm admits a
382 // mixed expression because the result is a real number, we convert the integer operand accordingly.
383 bool const isRealExpected = expression.getType().isRationalType();
384 leftResult = this->convertToRealIfExpected(leftResult, isRealExpected);
385 rightResult = this->convertToRealIfExpected(rightResult, isRealExpected);
386
387 // Storm assigns the type of the operands to the quotient, so the division of two integers is an integer in
388 // Storm. cvc5, in contrast, always divides over the rational numbers. We therefore convert the operands of a
389 // division to real numbers explicitly. Otherwise, the resulting real term would be combined with the
390 // surrounding integer terms of a parent expression, which cvc5 rejects in comparisons and ITEs.
392 leftResult = this->convertToRealIfExpected(leftResult, true);
393 rightResult = this->convertToRealIfExpected(rightResult, true);
394 }
395
396 // Since the sort of a translated operand may deviate from the type that Storm assigned to it (consider the
397 // division above), we always unify the sorts of the operands before constructing the term.
398 this->unifyNumericalTypes(leftResult, rightResult);
399
400 cvc5::Term result;
401 switch (expression.getOperatorType()) {
403 result = this->termManager.mkTerm(cvc5::Kind::ADD, {leftResult, rightResult});
404 break;
406 result = this->termManager.mkTerm(cvc5::Kind::SUB, {leftResult, rightResult});
407 break;
409 result = this->termManager.mkTerm(cvc5::Kind::MULT, {leftResult, rightResult});
410 break;
412 result = this->termManager.mkTerm(cvc5::Kind::DIVISION, {leftResult, rightResult});
413 break;
415 result = this->termManager.mkTerm(cvc5::Kind::ITE, {this->termManager.mkTerm(cvc5::Kind::LEQ, {leftResult, rightResult}), leftResult, rightResult});
416 break;
418 result = this->termManager.mkTerm(cvc5::Kind::ITE, {this->termManager.mkTerm(cvc5::Kind::GEQ, {leftResult, rightResult}), leftResult, rightResult});
419 break;
421 result = this->termManager.mkTerm(cvc5::Kind::POW, {leftResult, rightResult});
422 break;
423 default:
424 STORM_LOG_THROW(false, storm::exceptions::ExpressionEvaluationException,
425 "Cannot evaluate expression: unknown numerical binary operator '" << static_cast<int>(expression.getOperatorType())
426 << "' in expression " << expression << ".");
427 }
428
429 expressionCache.emplace(&expression, result);
430 return result;
431}
432
433boost::any Cvc5ExpressionAdapter::visit(storm::expressions::BinaryRelationExpression const& expression, boost::any const& data) {
434 auto cacheIt = expressionCache.find(&expression);
435 if (cacheIt != expressionCache.end()) {
436 return cacheIt->second;
437 }
438
439 cvc5::Term leftResult = boost::any_cast<cvc5::Term>(expression.getFirstOperand()->accept(*this, data));
440 cvc5::Term rightResult = boost::any_cast<cvc5::Term>(expression.getSecondOperand()->accept(*this, data));
441
442 // cvc5 requires the operands of a comparison to be of the same type, so a real number may be compared with an
443 // integer by converting the latter to a real number.
444 this->unifyNumericalTypes(leftResult, rightResult);
445
446 cvc5::Kind kind;
447 switch (expression.getRelationType()) {
449 kind = cvc5::Kind::EQUAL;
450 break;
452 kind = cvc5::Kind::DISTINCT;
453 break;
455 kind = cvc5::Kind::LT;
456 break;
458 kind = cvc5::Kind::LEQ;
459 break;
461 kind = cvc5::Kind::GT;
462 break;
464 kind = cvc5::Kind::GEQ;
465 break;
466 default:
467 STORM_LOG_THROW(false, storm::exceptions::ExpressionEvaluationException,
468 "Cannot evaluate expression: unknown boolean binary operator '" << static_cast<int>(expression.getRelationType())
469 << "' in expression " << expression << ".");
470 }
471
472 cvc5::Term result = this->termManager.mkTerm(kind, {leftResult, rightResult});
473
474 expressionCache.emplace(&expression, result);
475 return result;
476}
477
478boost::any Cvc5ExpressionAdapter::visit(storm::expressions::BooleanLiteralExpression const& expression, boost::any const&) {
479 auto cacheIt = expressionCache.find(&expression);
480 if (cacheIt != expressionCache.end()) {
481 return cacheIt->second;
482 }
483
484 cvc5::Term result = this->termManager.mkBoolean(expression.getValue());
485
486 expressionCache.emplace(&expression, result);
487 return result;
488}
489
490boost::any Cvc5ExpressionAdapter::visit(storm::expressions::RationalLiteralExpression const& expression, boost::any const&) {
491 auto cacheIt = expressionCache.find(&expression);
492 if (cacheIt != expressionCache.end()) {
493 return cacheIt->second;
494 }
495
496 cvc5::Term result = this->createRational(expression.getValue());
497
498 expressionCache.emplace(&expression, result);
499 return result;
500}
501
502boost::any Cvc5ExpressionAdapter::visit(storm::expressions::IntegerLiteralExpression const& expression, boost::any const&) {
503 auto cacheIt = expressionCache.find(&expression);
504 if (cacheIt != expressionCache.end()) {
505 return cacheIt->second;
506 }
507
508 cvc5::Term result = this->termManager.mkInteger(static_cast<int64_t>(expression.getValue()));
509
510 expressionCache.emplace(&expression, result);
511 return result;
512}
513
514boost::any Cvc5ExpressionAdapter::visit(storm::expressions::UnaryBooleanFunctionExpression const& expression, boost::any const& data) {
515 auto cacheIt = expressionCache.find(&expression);
516 if (cacheIt != expressionCache.end()) {
517 return cacheIt->second;
518 }
519
520 cvc5::Term result = boost::any_cast<cvc5::Term>(expression.getOperand()->accept(*this, data));
521
522 switch (expression.getOperatorType()) {
524 result = this->termManager.mkTerm(cvc5::Kind::NOT, {result});
525 break;
526 default:
527 STORM_LOG_THROW(false, storm::exceptions::ExpressionEvaluationException,
528 "Cannot evaluate expression: unknown boolean unary operator '" << static_cast<int>(expression.getOperatorType())
529 << "' in expression " << expression << ".");
530 }
531
532 expressionCache.emplace(&expression, result);
533 return result;
534}
535
536boost::any Cvc5ExpressionAdapter::visit(storm::expressions::UnaryNumericalFunctionExpression const& expression, boost::any const& data) {
537 auto cacheIt = expressionCache.find(&expression);
538 if (cacheIt != expressionCache.end()) {
539 return cacheIt->second;
540 }
541
542 cvc5::Term result = boost::any_cast<cvc5::Term>(expression.getOperand()->accept(*this, data));
543
544 switch (expression.getOperatorType()) {
546 result = this->termManager.mkTerm(cvc5::Kind::NEG, {result});
547 break;
549 // cvc5's to_int operation converts a real number to an integer using the floor function.
550 result = this->termManager.mkTerm(cvc5::Kind::TO_INTEGER, {result});
551 break;
552 }
554 // The ceiling of a real number is the negation of the floor of its negation.
555 result = this->termManager.mkTerm(cvc5::Kind::NEG,
556 {this->termManager.mkTerm(cvc5::Kind::TO_INTEGER, {this->termManager.mkTerm(cvc5::Kind::NEG, {result})})});
557 break;
558 }
559 default:
560 STORM_LOG_THROW(false, storm::exceptions::ExpressionEvaluationException,
561 "Cannot evaluate expression: unknown numerical unary operator '" << static_cast<int>(expression.getOperatorType())
562 << "' in expression " << expression << ".");
563 }
564
565 expressionCache.emplace(&expression, result);
566 return result;
567}
568
569boost::any Cvc5ExpressionAdapter::visit(storm::expressions::IfThenElseExpression const& expression, boost::any const& data) {
570 auto cacheIt = expressionCache.find(&expression);
571 if (cacheIt != expressionCache.end()) {
572 return cacheIt->second;
573 }
574
575 cvc5::Term conditionResult = boost::any_cast<cvc5::Term>(expression.getCondition()->accept(*this, data));
576 cvc5::Term thenResult = boost::any_cast<cvc5::Term>(expression.getThenExpression()->accept(*this, data));
577 cvc5::Term elseResult = boost::any_cast<cvc5::Term>(expression.getElseExpression()->accept(*this, data));
578
579 // cvc5 requires both branches of an ITE to be of the same type. Storm, in contrast, admits an expression that
580 // selects between an integer and a rational number, so we convert the integer branch if necessary.
581 this->unifyNumericalTypes(thenResult, elseResult);
582
583 cvc5::Term result = this->termManager.mkTerm(cvc5::Kind::ITE, {conditionResult, thenResult, elseResult});
584
585 expressionCache.emplace(&expression, result);
586 return result;
587}
588
589boost::any Cvc5ExpressionAdapter::visit(storm::expressions::VariableExpression const& expression, boost::any const&) {
590 return this->translateExpression(expression.getVariable());
591}
592
593cvc5::Term Cvc5ExpressionAdapter::createRational(storm::RationalNumber const& value) {
594 // Both GMP and CLN print a rational number as "numerator/denominator" (e.g., "-123/122"), which is accepted by mkReal.
595 std::stringstream fractionStream;
596 fractionStream << value;
597 return this->termManager.mkReal(fractionStream.str());
598}
599
600cvc5::Term Cvc5ExpressionAdapter::createVariable(storm::expressions::Variable const& variable) {
601 cvc5::Sort sort;
602 if (variable.getType().isBooleanType()) {
603 sort = this->termManager.getBooleanSort();
604 } else if (variable.getType().isRationalType()) {
605 sort = this->termManager.getRealSort();
606 } else {
607 // A bitvector variable is an integer type in Storm, and its width carries no semantics: expressions have no bitvector
608 // operators. It is therefore modelled as an unbounded integer, whose width is not enforced, as in the Z3 and MathSAT adapters.
609 STORM_LOG_THROW(variable.getType().isIntegerType(), storm::exceptions::InvalidTypeException,
610 "Encountered variable '" << variable.getName() << "' with unknown type while trying to create solver variables.");
611 sort = this->termManager.getIntegerSort();
612 }
613 cvc5::Term cvc5Variable = this->termManager.mkConst(sort, variable.getName());
614 variableToExpressionMapping.insert(std::make_pair(variable, cvc5Variable));
615 expressionToVariableMapping.insert(std::make_pair(cvc5Variable, variable));
616 return cvc5Variable;
617}
618
619#endif
620} // namespace storm::adapters
virtual boost::any accept(ExpressionVisitor &visitor, boost::any const &data) const =0
Accepts the given visitor by calling its visit method.
Type const & getType() const
Retrieves the type of the expression.
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.
bool hasBooleanType() const
Retrieves whether the expression has a boolean return type.
BaseExpression const & getBaseExpression() const
Retrieves the base expression underlying this expression object.
ExpressionManager const & getManager() const
Retrieves the manager responsible for this expression.
Expression integer(int_fast64_t value) const
Creates an expression that characterizes the given integer literal.
Expression boolean(bool value) const
Creates an expression that characterizes the given boolean literal.
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
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
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: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 abs(Expression const &first)
Expression sum(std::vector< storm::expressions::Expression > const &expressions)
Expression product(std::vector< storm::expressions::Expression > const &expressions)
Expression pow(Expression const &base, Expression const &exponent, bool allowIntegerType)
The type of the resulting expression is.
Expression floor(Expression const &first)
Expression xclusiveor(Expression const &first, Expression const &second)
Expression implies(Expression const &first, Expression const &second)
SettingsManager const & manager()
Retrieves the settings manager.
NumberTraits< RationalType >::IntegerType denominator(RationalType const &number)
NumberTraits< RationalType >::IntegerType numerator(RationalType const &number)
TargetType convertNumber(SourceType const &number)