Storm
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
ArrayEliminator.cpp
Go to the documentation of this file.
1#include "ArrayEliminator.h"
2
3#include <unordered_map>
4
12
16
21
22namespace storm {
23namespace jani {
24
25namespace detail {
27 public:
29
31 typedef std::unordered_map<storm::expressions::Variable, Replacement> ReplMap;
32
33 ArrayReplacementsCollectorExpressionVisitor(Model& model) : model(model), converged(false), declaringAutomaton(nullptr) {};
35
40 bool get(storm::expressions::Expression const& expression, storm::jani::Variable const& arrayVariable, Replacement& currentReplacement,
41 ReplMap const& allCollectedReplacements, Automaton* declaringAutomaton = nullptr, std::vector<std::size_t>* arrayAccessIndices = nullptr) {
42 currentVar = &arrayVariable;
43 this->declaringAutomaton = declaringAutomaton;
44 STORM_LOG_ASSERT(currentVar->getType().isArrayType(), "expected array variable");
45 STORM_LOG_ASSERT((declaringAutomaton == nullptr && model.hasGlobalVariable(currentVar->getName()) ||
46 declaringAutomaton != nullptr && declaringAutomaton->hasVariable(currentVar->getName())),
47 "Cannot find variable declaration.");
48 collectedRepl = &allCollectedReplacements;
49
50 std::vector<std::size_t> indices;
51 std::vector<std::size_t>* indicesPtr;
52 if (arrayAccessIndices) {
53 indicesPtr = arrayAccessIndices;
54 } else {
55 indicesPtr = &indices;
56 }
57
58 converged = true;
59 expression.accept(*this, std::make_pair(&currentReplacement, indicesPtr));
60 return converged;
61 }
62
63 virtual boost::any visit(storm::expressions::IfThenElseExpression const& expression, boost::any const& data) override {
64 if (expression.getCondition()->containsVariables()) {
65 expression.getThenExpression()->accept(*this, data);
66 expression.getElseExpression()->accept(*this, data);
67 } else {
68 if (expression.getCondition()->evaluateAsBool()) {
69 expression.getThenExpression()->accept(*this, data);
70 } else {
71 expression.getElseExpression()->accept(*this, data);
72 }
73 }
74 return boost::any();
75 }
76
77 virtual boost::any visit(storm::expressions::BinaryBooleanFunctionExpression const& expression, boost::any const& data) override {
78 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected type of expression.");
79 return boost::any();
80 }
81
82 virtual boost::any visit(storm::expressions::BinaryNumericalFunctionExpression const& expression, boost::any const& data) override {
83 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected type of expression.");
84 return boost::any();
85 }
86
87 virtual boost::any visit(storm::expressions::BinaryRelationExpression const& expression, boost::any const& data) override {
88 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected type of expression.");
89 return boost::any();
90 }
91
92 void varToVarAssignmentHelper(Replacement& cur, Replacement const& other, std::vector<std::size_t>& indices) {
93 if (other.isVariable()) {
94 if (!cur.isVariable()) {
95 STORM_LOG_ASSERT(cur.size() == 0, "Unexpected replacement type. Assignments incompatible?");
96 cur = Replacement(addReplacementVariable(indices));
97 }
98 } else {
99 STORM_LOG_ASSERT(!cur.isVariable(), "Unexpected replacement type. Assignments incompatible?");
100 cur.grow(other.size());
101 indices.push_back(0);
102 for (std::size_t i = 0; i < other.size(); ++i) {
103 indices.back() = i;
104 varToVarAssignmentHelper(cur.at(i), other.at(i), indices);
105 }
106 indices.pop_back();
107 }
108 }
109
110 virtual boost::any visit(storm::expressions::VariableExpression const& expression, boost::any const& data) override {
111 if (expression.getType().isArrayType()) {
112 auto varIt = collectedRepl->find(expression.getVariable());
113 if (varIt != collectedRepl->end()) {
114 // We have to make sure that the sizes for the current replacements fit at least the number of other replacements
115 auto current = boost::any_cast<std::pair<Replacement*, std::vector<std::size_t>*>>(data);
116 varToVarAssignmentHelper(*current.first, varIt->second, *current.second);
117 }
118 }
119 return boost::any();
120 }
121
122 virtual boost::any visit(storm::expressions::UnaryBooleanFunctionExpression const& expression, boost::any const& data) override {
123 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected type of expression.");
124 return boost::any();
125 }
126
127 virtual boost::any visit(storm::expressions::UnaryNumericalFunctionExpression const& expression, boost::any const& data) override {
128 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected type of expression.");
129 return boost::any();
130 }
131
132 virtual boost::any visit(storm::expressions::BooleanLiteralExpression const&, boost::any const&) override {
133 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected type of expression.");
134 return boost::any();
135 }
136
137 virtual boost::any visit(storm::expressions::IntegerLiteralExpression const&, boost::any const&) override {
138 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected type of expression.");
139 return boost::any();
140 }
141
142 virtual boost::any visit(storm::expressions::RationalLiteralExpression const&, boost::any const&) override {
143 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected type of expression.");
144 return boost::any();
145 }
146
147 void arrayExpressionHelper(storm::expressions::ArrayExpression const& expression, Replacement& curRepl, std::vector<size_t>& curIndices) {
148 STORM_LOG_ASSERT(!expression.size()->containsVariables(), "Did not expect variables in size expression.");
149 auto expSize = static_cast<uint64_t>(expression.size()->evaluateAsInt());
150 STORM_LOG_ASSERT(!curRepl.isVariable(), "Unexpected replacement type. Illegal array assignment?");
151 curRepl.grow(expSize);
152 curIndices.push_back(0);
153 for (std::size_t i = 0; i < expSize; ++i) {
154 curIndices.back() = i;
155 auto subexpr = expression.at(i);
156 if (subexpr->getType().isArrayType()) {
157 // This is a nested array.
158 subexpr->accept(*this, std::make_pair(&curRepl.at(i), &curIndices));
159 } else {
160 if (!curRepl.at(i).isVariable()) {
161 STORM_LOG_ASSERT(curRepl.at(i).size() == 0, "Unexpected replacement type. Illegal array assignment?");
162 curRepl.at(i) = Replacement(addReplacementVariable(curIndices));
163 }
164 }
165 }
166 curIndices.pop_back();
167 }
168
169 virtual boost::any visit(storm::expressions::ValueArrayExpression const& expression, boost::any const& data) override {
170 STORM_LOG_ASSERT(expression.size()->isIntegerLiteralExpression(),
171 "unexpected kind of size expression of ValueArrayExpression (" << expression.size()->toExpression() << ").");
172 auto current = boost::any_cast<std::pair<Replacement*, std::vector<std::size_t>*>>(data);
173 arrayExpressionHelper(expression, *current.first, *current.second);
174 return boost::any();
175 }
176
177 virtual boost::any visit(storm::expressions::ConstructorArrayExpression const& expression, boost::any const& data) override {
178 if (!expression.size()->containsVariables()) {
179 auto current = boost::any_cast<std::pair<Replacement*, std::vector<std::size_t>*>>(data);
180 arrayExpressionHelper(expression, *current.first, *current.second);
181 } else {
182 auto vars = expression.size()->toExpression().getVariables();
183 std::string variables = "";
184 for (auto const& v : vars) {
185 if (variables != "") {
186 variables += ", ";
187 }
188 variables += v.getName();
189 }
190 if (vars.size() == 1) {
191 variables = "variable " + variables;
192 } else {
193 variables = "variables " + variables;
194 }
196 false, storm::exceptions::NotSupportedException,
197 "Unable to determine array size: Size of ConstructorArrayExpression '" << expression << "' still contains the " << variables << ".");
198 }
199 return boost::any();
200 }
201
202 void arrayVariableAccessHelper(std::vector<std::shared_ptr<storm::expressions::BaseExpression const>>& indexStack, Replacement& cur,
203 Replacement const& other, std::vector<std::size_t>& curIndices) {
204 if (indexStack.empty()) {
205 varToVarAssignmentHelper(cur, other, curIndices);
206 } else {
207 uint64_t i, size;
208 if (indexStack.back()->containsVariables()) {
209 // We have to run over all possible indices
210 i = 0;
211 size = other.size();
212 } else {
213 // only consider the accessed index
214 i = indexStack.back()->evaluateAsInt();
215 size = std::min<uint64_t>(i + 1, other.size()); // Avoid invalid access, e.g. if the sizes of the other variable aren't ready yet
216 }
217 indexStack.pop_back();
218 for (; i < size; ++i) {
219 arrayVariableAccessHelper(indexStack, cur, other.at(i), curIndices);
220 }
221 }
222 }
223
224 void arrayAccessHelper(std::shared_ptr<storm::expressions::BaseExpression const> const& base,
225 std::vector<std::shared_ptr<storm::expressions::BaseExpression const>>& indexStack, boost::any const& data) {
226 // We interpret the input as expression base[i_n][i_{n-1}]...[i_1] for (potentially empty) indexStack = [i_1, ..., i_n].
227 // This deals with *nested* array accesses
228 auto baseAsArrayAccessExp = std::dynamic_pointer_cast<storm::expressions::ArrayAccessExpression const>(base);
229 auto baseAsArrayExp = std::dynamic_pointer_cast<storm::expressions::ArrayExpression const>(base);
230 if (indexStack.empty()) {
231 base->accept(*this, data);
232 } else if (baseAsArrayAccessExp) {
233 indexStack.push_back(baseAsArrayAccessExp->getSecondOperand());
234 arrayAccessHelper(baseAsArrayAccessExp->getFirstOperand(), indexStack, data);
235 } else if (baseAsArrayExp) {
236 // the base is an array expression.
237 uint64_t i, size;
238 if (indexStack.back()->containsVariables()) {
239 // We have to run over all possible indices
241 !baseAsArrayExp->size()->containsVariables(), storm::exceptions::NotSupportedException,
242 "Array elimination failed because array access expression considers array expression '" << *baseAsArrayExp << "' of unknown size.");
243 i = 0;
244 size = baseAsArrayExp->size()->evaluateAsInt();
245 } else {
246 // only consider the accessed index
247 i = indexStack.back()->evaluateAsInt();
248 size = i + 1;
249 STORM_LOG_THROW(baseAsArrayExp->size()->containsVariables() || i < static_cast<uint64_t>(baseAsArrayExp->size()->evaluateAsInt()),
250 storm::exceptions::InvalidOperationException, "Array access " << base << "[" << i << "] out of bounds.");
251 }
252 indexStack.pop_back();
253 for (; i < size; ++i) {
254 auto indexStackCpy = indexStack;
255 arrayAccessHelper(baseAsArrayExp->at(i), indexStackCpy, data);
256 }
257 } else if (base->isVariableExpression()) {
258 // the lhs is an array variable.
259 STORM_LOG_ASSERT(base->asVariableExpression().getVariable().getType().isArrayType(), "Unexpected variable type in array access.");
260 auto varIt = collectedRepl->find(base->asVariableExpression().getVariable());
261 if (varIt != collectedRepl->end()) {
262 STORM_LOG_ASSERT(!varIt->second.isVariable(), "Unexpected replacement type. Invalid array assignment?");
263 auto current = boost::any_cast<std::pair<Replacement*, std::vector<std::size_t>*>>(data);
264 arrayVariableAccessHelper(indexStack, *current.first, varIt->second, *current.second);
265 }
266 } else {
267 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected kind of array access expression");
268 }
269 }
270
271 virtual boost::any visit(storm::expressions::ArrayAccessExpression const& expression, boost::any const& data) override {
272 std::vector<std::shared_ptr<storm::expressions::BaseExpression const>> indexStack = {expression.getSecondOperand()};
273 arrayAccessHelper(expression.getFirstOperand(), indexStack, data);
274 return boost::any();
275 }
276
277 virtual boost::any visit(storm::expressions::FunctionCallExpression const&, boost::any const&) override {
279 false, storm::exceptions::UnexpectedException,
280 "Found Function call expression within an array expression. This is not expected since functions are expected to be eliminated at this point.");
281 }
282
283 virtual boost::any visit(storm::expressions::TranscendentalNumberLiteralExpression const&, boost::any const&) override {
284 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected type of expression.");
285 return boost::any();
286 }
287
288 private:
289 storm::jani::Variable const& addReplacementVariable(std::vector<std::size_t> const& indices) {
290 auto& manager = model.getExpressionManager();
291 std::string name = currentVar->getExpressionVariable().getName();
292 for (auto const& i : indices) {
293 name += "_at_" + std::to_string(i);
294 }
295
297 if (currentVar->hasInitExpression()) {
298 initValue = currentVar->getInitExpression();
299 for (auto const& i : indices) {
300 auto aa = std::make_shared<storm::expressions::ArrayAccessExpression>(
301 manager, initValue.getType().getElementType(), initValue.getBaseExpressionPointer(), manager.integer(i).getBaseExpressionPointer());
302 initValue = storm::expressions::Expression(aa);
303 }
304 }
305
306 auto const& baseType = currentVar->getType().asArrayType().getBaseTypeRecursive();
307 storm::expressions::Variable exprVariable;
308 if (baseType.isBasicType()) {
309 switch (baseType.asBasicType().get()) {
311 exprVariable = manager.declareBooleanVariable(name);
312 break;
314 exprVariable = manager.declareIntegerVariable(name);
315 break;
317 exprVariable = manager.declareRationalVariable(name);
318 break;
319 }
320 } else if (baseType.isBoundedType()) {
321 switch (baseType.asBoundedType().getBaseType()) {
323 exprVariable = manager.declareIntegerVariable(name);
324 break;
326 exprVariable = manager.declareRationalVariable(name);
327 break;
328 }
329 } else {
330 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Unhandled base type for array of type " << currentVar->getType());
331 }
332
333 auto janiVar = storm::jani::Variable::makeVariable(name, baseType, exprVariable, initValue, currentVar->isTransient());
334
335 converged = false;
336 if (declaringAutomaton) {
337 return declaringAutomaton->addVariable(*janiVar);
338 } else {
339 return model.addVariable(*janiVar);
340 }
341 }
342
343 Model& model;
344 bool converged;
345 jani::Variable const* currentVar;
346 Automaton* declaringAutomaton; // nullptr if currentVar is global
347 ReplMap const* collectedRepl;
348};
349
355 public:
356 ArrayEliminatorDataCollector(Model& model) : model(model), currentAutomaton(nullptr), exprVisitor(model), converged(false) {}
357 virtual ~ArrayEliminatorDataCollector() = default;
358
360 // To correctly determine the array sizes, we repeat the steps until convergence. This is to cover assignments of one array variable to another (A := B)
361 ArrayEliminatorData result;
362 do {
363 converged = true;
364 JaniTraverser::traverse(model, &result);
365 } while (!converged);
366
367 // drop all occurring array variables
368 auto elVars = model.getGlobalVariables().dropAllArrayVariables();
369 result.eliminatedArrayVariables.insert(result.eliminatedArrayVariables.end(), elVars.begin(), elVars.end());
370 for (auto& automaton : model.getAutomata()) {
371 elVars = automaton.getVariables().dropAllArrayVariables();
372 result.eliminatedArrayVariables.insert(result.eliminatedArrayVariables.end(), elVars.begin(), elVars.end());
373 }
374
375 return result;
376 }
377
378 virtual void traverse(Automaton& automaton, boost::any const& data) override {
379 currentAutomaton = &automaton;
380 JaniTraverser::traverse(automaton, data);
381 currentAutomaton = nullptr;
382 }
383
385 if (currentAutomaton && currentAutomaton->getVariables().hasVariable(var)) {
386 return currentAutomaton;
387 }
388 STORM_LOG_ASSERT(model.getGlobalVariables().hasVariable(var), "Variable " << var.getName() << " not found.");
389 return nullptr;
390 }
391
392 virtual void traverse(VariableSet& variableSet, boost::any const& data) override {
393 // *Only* traverse array variables.
394 // We do this to make the adding of new replacements (which adds new non-array variables to the model) a bit more safe.
395 // (Adding new elements to a container while iterating over it might be a bit dangerous, depending on the container)
396 for (auto& v : variableSet.getArrayVariables()) {
397 traverse(v, data);
398 }
399 }
400
401 void gatherArrayAccessIndices(std::vector<std::vector<std::size_t>>& gatheredIndices, std::vector<std::size_t>& current,
402 typename ArrayEliminatorData::Replacement const& repl, std::vector<storm::expressions::Expression> const& indices) {
403 STORM_LOG_ASSERT(!repl.isVariable(), "Unexpected replacement type. Assignments incompatible?");
404 if (current.size() < indices.size()) {
405 auto const& indexExp = indices[current.size()];
406 if (indexExp.containsVariables()) {
407 current.push_back(std::numeric_limits<std::size_t>::max());
408 // We have to consider all possible indices
409 for (std::size_t i = 0; i < repl.size(); ++i) {
410 current.back() = i;
411 gatherArrayAccessIndices(gatheredIndices, current, repl.at(i), indices);
412 }
413 current.pop_back();
414 } else {
415 auto i = static_cast<uint64_t>(indexExp.evaluateAsInt());
416 if (i < repl.size()) {
417 current.push_back(i);
418 gatherArrayAccessIndices(gatheredIndices, current, repl.at(i), indices);
419 current.pop_back();
420 } // The else case might either be an invalid array access or we haven't processed the corresponding assignment yet (and thus will be covered
421 // in a subsequent run)
422 }
423 } else {
424 gatheredIndices.push_back(current);
425 }
426 }
427
428 virtual void traverse(Assignment& assignment, boost::any const& data) override {
429 if (assignment.getLValue().isArray()) {
430 auto const& var = assignment.getLValue().getVariable();
431 if (!assignment.getLValue().isArrayAccess()) {
432 // Variable assignment
433 auto& result = *boost::any_cast<ArrayEliminatorData*>(data);
434 converged &= exprVisitor.get(assignment.getAssignedExpression(), var, result.replacements[var.getExpressionVariable()], result.replacements,
435 declaringAutomaton(var));
436 } else if (!assignment.getLValue().isFullArrayAccess()) {
437 // Incomplete array assignment
438 auto& result = *boost::any_cast<ArrayEliminatorData*>(data);
439 auto& arrayVarReplacements = result.replacements[var.getExpressionVariable()]; // creates if it does not yet exist
440
441 // If the indices are not constant expressions, we run through every possible combination of indices
442 std::vector<std::vector<std::size_t>> gatheredIndices;
443 std::vector<std::size_t> tmp;
444 gatherArrayAccessIndices(gatheredIndices, tmp, result.replacements[var.getExpressionVariable()], assignment.getLValue().getArrayIndexVector());
445 for (auto& indices : gatheredIndices) {
446 converged &= exprVisitor.get(assignment.getAssignedExpression(), var, arrayVarReplacements.at(indices), result.replacements,
447 declaringAutomaton(var), &indices);
448 }
449 }
450 }
451 }
452
453 virtual void traverse(Variable& variable, boost::any const& data) override {
454 STORM_LOG_ASSERT(variable.getType().isArrayType(), "Expected to only traverse over array variables.");
455 auto& result = *boost::any_cast<ArrayEliminatorData*>(data);
456 if (variable.hasInitExpression()) {
457 converged &= exprVisitor.get(variable.getInitExpression(), variable, result.replacements[variable.getExpressionVariable()], result.replacements,
458 declaringAutomaton(variable));
459 }
460 }
461
462 private:
463 Model& model;
464 Automaton* currentAutomaton;
466 bool converged;
467};
468
471 public:
473
474 typedef std::shared_ptr<storm::expressions::BaseExpression const> BaseExprPtr;
476 public:
477 ResultType(ResultType const& other) = default;
478 ResultType(BaseExprPtr expression) : expression(expression) {}
481 STORM_LOG_ASSERT(!isArrayOutOfBounds(), "Tried to get the result expression, but the expression is out-of-bounds");
482 return expression;
483 };
485 return expression.get() == nullptr;
486 };
487
488 private:
489 BaseExprPtr expression;
490 };
491
492 typedef std::vector<storm::expressions::Expression> ArrayAccessIndices;
493
494 ArrayExpressionEliminationVisitor(ArrayEliminatorData const& data) : replacements(data.replacements) {}
495
496 ArrayAccessIndices const& getArrayAccessIndices(boost::any const& data) {
497 STORM_LOG_ASSERT(!data.empty(), "tried to convert data but it is empty.");
498 return *boost::any_cast<ArrayAccessIndices*>(data);
499 }
500
502 auto res = eliminateChkSizes(expression);
503 STORM_LOG_THROW(!res.isArrayOutOfBounds(), storm::exceptions::OutOfRangeException,
504 "Cannot eliminate expression " << expression << " as it is out-of-bounds.");
505 return res.expr();
506 }
507
509 // We use the data parameter of the visitors to keep track of the array access indices.
510 // If data is empty, there currently are no array accesses. Otherwise, it is a vector where the last entry corresponds to the inner most access, i.e., 1
511 // in a[1][2]
512 ArrayAccessIndices arrayAccessIndices;
513 auto res = boost::any_cast<ResultType>(expression.accept(*this, &arrayAccessIndices));
514 if (!res.isArrayOutOfBounds()) {
515 STORM_LOG_ASSERT(!containsArrayExpression(res.expr()->toExpression()), "Expression still contains array expressions. Before: \n"
516 << expression << "\nAfter:\n"
517 << res.expr()->toExpression());
518 res.expr() = res.expr()->simplify();
519 }
520
521 return res;
522 }
523
524 virtual boost::any visit(storm::expressions::IfThenElseExpression const& expression, boost::any const& data) override {
525 // for the condition expression, array access indices do not matter. We can still have array accesses inside, though
526 ArrayAccessIndices conditionArrayAccessIndices;
527 ResultType conditionResult = boost::any_cast<ResultType>(expression.getCondition()->accept(*this, &conditionArrayAccessIndices));
528 if (conditionResult.isArrayOutOfBounds()) {
529 return conditionResult;
530 }
531
532 // We need to handle expressions of the kind '42<size ? A[42] : 0', where size is a variable and A[42] might be out of bounds.
533 ResultType thenResult = boost::any_cast<ResultType>(expression.getThenExpression()->accept(*this, data));
534 ResultType elseResult = boost::any_cast<ResultType>(expression.getElseExpression()->accept(*this, data));
535 if (thenResult.isArrayOutOfBounds()) {
536 if (elseResult.isArrayOutOfBounds()) {
537 return thenResult;
538 } else {
539 // Assume the else expression
540 return elseResult;
541 }
542 } else if (elseResult.isArrayOutOfBounds()) {
543 // Assume the then expression
544 return thenResult;
545 } else {
546 // If the arguments did not change, we simply push the expression itself.
547 if (conditionResult.expr().get() == expression.getCondition().get() && thenResult.expr().get() == expression.getThenExpression().get() &&
548 elseResult.expr().get() == expression.getElseExpression().get()) {
549 return ResultType(expression.getSharedPointer());
550 } else {
551 return ResultType(std::const_pointer_cast<storm::expressions::BaseExpression const>(
552 std::shared_ptr<storm::expressions::BaseExpression>(new storm::expressions::IfThenElseExpression(
553 expression.getManager(), thenResult.expr()->getType(), conditionResult.expr(), thenResult.expr(), elseResult.expr()))));
554 }
555 }
556 }
557
558 virtual boost::any visit(storm::expressions::BinaryBooleanFunctionExpression const& expression, boost::any const& data) override {
560 "BinaryBooleanFunctionExpressions should not be direct subexpressions of array access expressions. However, the expression "
561 << expression << " is.");
562 ResultType firstResult = boost::any_cast<ResultType>(expression.getFirstOperand()->accept(*this, data));
563 ResultType secondResult = boost::any_cast<ResultType>(expression.getSecondOperand()->accept(*this, data));
564
565 if (firstResult.isArrayOutOfBounds()) {
566 return firstResult;
567 } else if (secondResult.isArrayOutOfBounds()) {
568 return secondResult;
569 }
570
571 // If the arguments did not change, we simply push the expression itself.
572 if (firstResult.expr().get() == expression.getFirstOperand().get() && secondResult.expr().get() == expression.getSecondOperand().get()) {
573 return ResultType(expression.getSharedPointer());
574 } else {
575 return ResultType(std::const_pointer_cast<storm::expressions::BaseExpression const>(
576 std::shared_ptr<storm::expressions::BaseExpression>(new storm::expressions::BinaryBooleanFunctionExpression(
577 expression.getManager(), expression.getType(), firstResult.expr(), secondResult.expr(), expression.getOperatorType()))));
578 }
579 }
580
581 virtual boost::any visit(storm::expressions::BinaryNumericalFunctionExpression const& expression, boost::any const& data) override {
583 "BinaryNumericalFunctionExpression should not be direct subexpressions of array access expressions. However, the expression "
584 << expression << " is.");
585 ResultType firstResult = boost::any_cast<ResultType>(expression.getFirstOperand()->accept(*this, data));
586 ResultType secondResult = boost::any_cast<ResultType>(expression.getSecondOperand()->accept(*this, data));
587
588 if (firstResult.isArrayOutOfBounds()) {
589 return firstResult;
590 } else if (secondResult.isArrayOutOfBounds()) {
591 return secondResult;
592 }
593
594 // If the arguments did not change, we simply push the expression itself.
595 if (firstResult.expr().get() == expression.getFirstOperand().get() && secondResult.expr().get() == expression.getSecondOperand().get()) {
596 return ResultType(expression.getSharedPointer());
597 } else {
598 return ResultType(std::const_pointer_cast<storm::expressions::BaseExpression const>(
599 std::shared_ptr<storm::expressions::BaseExpression>(new storm::expressions::BinaryNumericalFunctionExpression(
600 expression.getManager(), expression.getType(), firstResult.expr(), secondResult.expr(), expression.getOperatorType()))));
601 }
602 }
603
604 virtual boost::any visit(storm::expressions::BinaryRelationExpression const& expression, boost::any const& data) override {
606 getArrayAccessIndices(data).empty(),
607 "BinaryRelationExpression should not be direct subexpressions of array access expressions. However, the expression " << expression << " is.");
608 ResultType firstResult = boost::any_cast<ResultType>(expression.getFirstOperand()->accept(*this, data));
609 ResultType secondResult = boost::any_cast<ResultType>(expression.getSecondOperand()->accept(*this, data));
610
611 if (firstResult.isArrayOutOfBounds()) {
612 return firstResult;
613 } else if (secondResult.isArrayOutOfBounds()) {
614 return secondResult;
615 }
616
617 // If the arguments did not change, we simply push the expression itself.
618 if (firstResult.expr().get() == expression.getFirstOperand().get() && secondResult.expr().get() == expression.getSecondOperand().get()) {
619 return ResultType(expression.getSharedPointer());
620 } else {
621 return ResultType(std::const_pointer_cast<storm::expressions::BaseExpression const>(
622 std::shared_ptr<storm::expressions::BaseExpression>(new storm::expressions::BinaryRelationExpression(
623 expression.getManager(), expression.getType(), firstResult.expr(), secondResult.expr(), expression.getRelationType()))));
624 }
625 }
626
628 uint64_t const& pos) {
629 if (pos == 0) {
630 STORM_LOG_ASSERT(replacement.isVariable(), "Replacement is not a variable. Is this a full array access?");
631 return replacement.getVariable().getExpressionVariable().getExpression();
632 } else {
633 STORM_LOG_ASSERT(!replacement.isVariable(), "Are there too many nested array accesses?");
634 auto indexExpr = indices[pos - 1];
636 if (indexExpr.containsVariables()) {
637 for (uint64_t index = 0; index < replacement.size(); ++index) {
638 auto child = varElimHelper(replacement.at(index), indices, pos - 1);
639 if (child.isInitialized()) { // i.e. there is no out-of-bounds situation for the child
640 if (result.isInitialized()) {
641 auto currIndexExpr = (indexExpr == indexExpr.getManager().integer(index));
642 result = storm::expressions::ite(currIndexExpr, child, result);
643 } else {
644 result = child;
645 }
646 }
647 }
648 // At this point result remains uninitialized iff all childs are uninitialized (i.e. out-of-bounds).
649 // The underlying assumption here is that indexExpr will never evaluate to an index where the access is out-of-bounds.
650 return result;
651 } else {
652 auto index = static_cast<uint64_t>(indexExpr.evaluateAsInt());
653 if (index < replacement.size()) {
654 return varElimHelper(replacement.at(index), indices, pos - 1);
655 } else {
656 // We have an out-of-bounds situation
658 }
659 }
660 }
661 }
662
663 virtual boost::any visit(storm::expressions::VariableExpression const& expression, boost::any const& data) override {
664 if (expression.getType().isArrayType()) {
665 auto const& indices = getArrayAccessIndices(data);
666 STORM_LOG_THROW(!indices.empty(), storm::exceptions::NotSupportedException,
667 "Unable to translate array variable to basic variable, since it does not seem to be within an array access expression.");
668 STORM_LOG_ASSERT(replacements.count(expression.getVariable()) > 0, "Unable to find array variable " << expression << " in array replacements.");
669 auto const& arrayVarReplacements = replacements.at(expression.getVariable());
670 auto result = varElimHelper(arrayVarReplacements, indices, indices.size());
671 // Try to get the replacement variable
672 if (result.isInitialized()) {
673 return ResultType(result.getBaseExpressionPointer());
674 } else {
675 return ResultType();
676 }
677 } else {
679 "VariableExpression of non-array variable should not be a subexpressions of array access expressions. However, the expression "
680 << expression << " is.");
681 return ResultType(expression.getSharedPointer());
682 }
683 }
684
685 virtual boost::any visit(storm::expressions::UnaryBooleanFunctionExpression const& expression, boost::any const& data) override {
687 getArrayAccessIndices(data).empty(),
688 "UnaryBooleanFunctionExpression should not be direct subexpressions of array access expressions. However, the expression " << expression << " is.");
689 ResultType operandResult = boost::any_cast<ResultType>(expression.getOperand()->accept(*this, data));
690
691 if (operandResult.isArrayOutOfBounds()) {
692 return operandResult;
693 }
694
695 // If the argument did not change, we simply push the expression itself.
696 if (operandResult.expr().get() == expression.getOperand().get()) {
697 return ResultType(expression.getSharedPointer());
698 } else {
699 return ResultType(std::const_pointer_cast<storm::expressions::BaseExpression const>(
700 std::shared_ptr<storm::expressions::BaseExpression>(new storm::expressions::UnaryBooleanFunctionExpression(
701 expression.getManager(), expression.getType(), operandResult.expr(), expression.getOperatorType()))));
702 }
703 }
704
705 virtual boost::any visit(storm::expressions::UnaryNumericalFunctionExpression const& expression, boost::any const& data) override {
707 getArrayAccessIndices(data).empty(),
708 "UnaryBooleanFunctionExpression should not be direct subexpressions of array access expressions. However, the expression " << expression << " is.");
709
710 ResultType operandResult = boost::any_cast<ResultType>(expression.getOperand()->accept(*this, data));
711
712 if (operandResult.isArrayOutOfBounds()) {
713 return operandResult;
714 }
715
716 // If the argument did not change, we simply push the expression itself.
717 if (operandResult.expr().get() == expression.getOperand().get()) {
718 return ResultType(expression.getSharedPointer());
719 } else {
720 return ResultType(std::const_pointer_cast<storm::expressions::BaseExpression const>(
721 std::shared_ptr<storm::expressions::BaseExpression>(new storm::expressions::UnaryNumericalFunctionExpression(
722 expression.getManager(), expression.getType(), operandResult.expr(), expression.getOperatorType()))));
723 }
724 }
725
726 virtual boost::any visit(storm::expressions::BooleanLiteralExpression const& expression, boost::any const&) override {
727 return ResultType(expression.getSharedPointer());
728 }
729
730 virtual boost::any visit(storm::expressions::IntegerLiteralExpression const& expression, boost::any const&) override {
731 return ResultType(expression.getSharedPointer());
732 }
733
734 virtual boost::any visit(storm::expressions::RationalLiteralExpression const& expression, boost::any const&) override {
735 return ResultType(expression.getSharedPointer());
736 }
737
738 boost::any arrayExprHelper(storm::expressions::ArrayExpression const& expression, boost::any const& data) {
739 auto& indices = getArrayAccessIndices(data);
740 STORM_LOG_THROW(!indices.empty(), storm::exceptions::NotSupportedException,
741 "Unable to translate ValueArrayExpression to element expression since it does not seem to be within an array access expression.");
742 auto indexExpr = indices.back();
743 auto childIndices = indices;
744 childIndices.pop_back();
745 if (indexExpr.containsVariables()) {
746 STORM_LOG_THROW(!expression.size()->containsVariables(), storm::exceptions::NotSupportedException,
747 "Unable to eliminate array expression of unknown size.");
748 auto exprSize = static_cast<uint64_t>(expression.size()->evaluateAsInt());
750 for (uint64_t index = 0; index < exprSize; ++index) {
751 auto child = boost::any_cast<ResultType>(expression.at(index)->accept(*this, &childIndices));
752 if (!child.isArrayOutOfBounds()) {
753 if (result.isInitialized()) {
754 auto currIndexExpr = (indexExpr == indexExpr.getManager().integer(index));
755 result = storm::expressions::ite(currIndexExpr, child.expr()->toExpression(), result);
756 } else {
757 result = child.expr()->toExpression();
758 }
759 }
760 }
761 if (result.isInitialized()) {
762 return ResultType(result.getBaseExpressionPointer());
763 } else {
764 return ResultType();
765 }
766 } else {
767 auto index = indexExpr.evaluateAsInt();
768 if (expression.size()->containsVariables()) {
769 STORM_LOG_WARN("ignoring size of array expression " << expression << " as it is not constant.");
770 } else {
771 if (index >= expression.size()->evaluateAsInt()) {
772 return ResultType();
773 }
774 }
775 return expression.at(index)->accept(*this, &childIndices);
776 }
777 }
778
779 virtual boost::any visit(storm::expressions::ValueArrayExpression const& expression, boost::any const& data) override {
780 STORM_LOG_ASSERT(expression.size()->isIntegerLiteralExpression(),
781 "unexpected kind of size expression of ValueArrayExpression (" << expression.size()->toExpression() << ").");
782 return arrayExprHelper(expression, data);
783 }
784
785 virtual boost::any visit(storm::expressions::ConstructorArrayExpression const& expression, boost::any const& data) override {
786 return arrayExprHelper(expression, data);
787 }
788
789 virtual boost::any visit(storm::expressions::ArrayAccessExpression const& expression, boost::any const& data) override {
790 // Eliminate arrays in the index expression e.g. for a[b[i]]
791 ArrayAccessIndices aaIndicesInIndexExpr;
792 auto indexExprResult = boost::any_cast<ResultType>(expression.getSecondOperand()->accept(*this, &aaIndicesInIndexExpr));
793 if (indexExprResult.isArrayOutOfBounds()) {
794 return indexExprResult;
795 }
796 auto& indices = getArrayAccessIndices(data);
797 auto childIndices = indices;
798 childIndices.push_back(indexExprResult.expr()->toExpression());
799 return expression.getFirstOperand()->accept(*this, &childIndices);
800 }
801
802 virtual boost::any visit(storm::expressions::FunctionCallExpression const&, boost::any const&) override {
803 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException,
804 "Found Function call expression while eliminating array expressions. This is not expected since functions are expected to be "
805 "eliminated at this point.");
806 return false;
807 }
808
809 virtual boost::any visit(storm::expressions::TranscendentalNumberLiteralExpression const& expression, boost::any const&) override {
810 return ResultType(expression.getSharedPointer());
811 }
812
813 private:
814 std::unordered_map<storm::expressions::Variable, typename ArrayEliminatorData::Replacement> const& replacements;
815};
816
818 public:
819 ArrayVariableReplacer(ArrayEliminatorData const& data, bool keepNonTrivialArrayAccess = false)
820 : elimData(data), arrayExprEliminator(data), keepNonTrivialArrayAccess(keepNonTrivialArrayAccess) {}
821
822 virtual ~ArrayVariableReplacer() = default;
823 void replace(Model& model) {
824 traverse(model, boost::any());
825 }
826
827 virtual void traverse(Model& model, boost::any const& data) override {
828 // Traverse relevant model components
829 for (auto& c : model.getConstants()) {
830 traverse(c, data);
831 }
832 STORM_LOG_ASSERT(model.getGlobalFunctionDefinitions().empty(), "Expected functions to be eliminated at this point");
834 for (auto& aut : model.getAutomata()) {
835 traverse(aut, data);
836 }
837
838 // Replace expressions
839 if (model.hasInitialStatesRestriction()) {
840 model.setInitialStatesRestriction(arrayExprEliminator.eliminate(model.getInitialStatesRestriction()));
841 }
842 for (auto& nonTrivRew : model.getNonTrivialRewardExpressions()) {
843 nonTrivRew.second = arrayExprEliminator.eliminate(nonTrivRew.second);
844 }
845 }
846
847 virtual void traverse(Automaton& automaton, boost::any const& data) override {
848 JaniTraverser::traverse(automaton.getVariables(), data);
849 STORM_LOG_ASSERT(automaton.getFunctionDefinitions().empty(), "Expected functions to be eliminated at this point");
850 for (auto& loc : automaton.getLocations()) {
851 traverse(loc, data);
852 }
853 JaniTraverser::traverse(automaton.getEdgeContainer(), data);
854 if (automaton.hasInitialStatesRestriction()) {
855 automaton.setInitialStatesRestriction(arrayExprEliminator.eliminate(automaton.getInitialStatesRestriction()));
856 }
857 }
858
859 void traverse(Constant& constant, boost::any const& data) override {
860 // There are no array constants in the JANI standard.
861 // Still, we might have some constant array expressions with (constant) array access here.
862 if (constant.isDefined()) {
863 constant.define(arrayExprEliminator.eliminate(constant.getExpression()));
864 }
865 if (constant.hasConstraint()) {
866 constant.setConstraintExpression(arrayExprEliminator.eliminate(constant.getConstraintExpression()));
867 }
868 }
869
870 virtual void traverse(Location& location, boost::any const& data) override {
871 traverse(location.getAssignments(), data);
872 if (location.hasTimeProgressInvariant()) {
873 location.setTimeProgressInvariant(arrayExprEliminator.eliminate(location.getTimeProgressInvariant()));
874 }
875 }
876
877 void traverse(Variable& variable, boost::any const& data) override {
878 // It's important to traverse the type first as we might be using expressions within the type in getOutOfBoundsValue
879 traverse(variable.getType(), data);
880
881 if (variable.hasInitExpression()) {
882 auto eliminationRes = arrayExprEliminator.eliminateChkSizes(variable.getInitExpression());
883 if (eliminationRes.isArrayOutOfBounds()) {
884 // We have an out-of-bounds situation. This is expected when we have an incomplete array access
885 // For example, when we set a:=b where b is smaller than the maximal size of a
886 variable.setInitExpression(getOutOfBoundsValue(variable));
887 } else {
888 variable.setInitExpression(eliminationRes.expr());
889 }
890 }
891 }
892
893 void traverse(JaniType& type, boost::any const& data) override {
894 STORM_LOG_ASSERT(!type.isArrayType(), "did not expect any array variable declarations at this point.");
895 if (type.isBoundedType()) {
896 auto& boundedType = type.asBoundedType();
897 if (boundedType.hasLowerBound()) {
898 boundedType.setLowerBound(arrayExprEliminator.eliminate(boundedType.getLowerBound()));
899 }
900 if (boundedType.hasUpperBound()) {
901 boundedType.setUpperBound(arrayExprEliminator.eliminate(boundedType.getUpperBound()));
902 }
903 }
904 }
905
906 void traverse(TemplateEdge& templateEdge, boost::any const& data) override {
907 templateEdge.setGuard(arrayExprEliminator.eliminate(templateEdge.getGuard()));
908 for (auto& dest : templateEdge.getDestinations()) {
909 JaniTraverser::traverse(dest, data);
910 }
911 traverse(templateEdge.getAssignments(), data);
912 }
913
914 void traverse(Edge& edge, boost::any const& data) override {
915 if (edge.hasRate()) {
916 edge.setRate(arrayExprEliminator.eliminate(edge.getRate()));
917 }
918 for (auto& dest : edge.getDestinations()) {
919 traverse(dest, data);
920 }
921 }
922
923 void traverse(EdgeDestination& edgeDestination, boost::any const&) override {
924 edgeDestination.setProbability(arrayExprEliminator.eliminate(edgeDestination.getProbability()));
925 }
926
927 virtual void traverse(OrderedAssignments& orderedAssignments, boost::any const& data) override {
928 // Replace array occurrences in LValues and assigned expressions.
929 std::vector<Assignment> newAssignments;
930 if (!orderedAssignments.empty()) {
931 int64_t level = orderedAssignments.getLowestLevel();
932 // Collect for each array variable all kinds of assignments to that variable on the current level
933 std::unordered_map<storm::expressions::Variable, std::vector<Assignment const*>> collectedArrayAssignments;
934
935 auto processCollectedAssignments = [&]() {
936 for (auto& arrayAssignments : collectedArrayAssignments) {
937 handleArrayAssignments(arrayAssignments.second, level, elimData.replacements.at(arrayAssignments.first), newAssignments);
938 }
939 collectedArrayAssignments.clear();
940 };
941
942 for (Assignment const& assignment : orderedAssignments) {
943 if (assignment.getLevel() != level) {
944 STORM_LOG_ASSERT(assignment.getLevel() > level, "Ordered Assignment does not have the expected order.");
945 processCollectedAssignments();
946 level = assignment.getLevel();
947 }
948
949 if (assignment.getLValue().isArray()) {
950 auto insertionRes = collectedArrayAssignments.emplace(assignment.getVariable().getExpressionVariable(), std::vector<Assignment const*>());
951 insertionRes.first->second.push_back(&assignment);
952 } else {
953 // In this case we have a non-array variable, we don't need to eliminate anything in the lhs
954 newAssignments.emplace_back(assignment.getLValue(), arrayExprEliminator.eliminate(assignment.getAssignedExpression()),
955 assignment.getLevel());
956 }
957 }
958 processCollectedAssignments();
959
960 // Now insert new assignments
961 orderedAssignments.clear();
962 for (auto const& assignment : newAssignments) {
963 orderedAssignments.add(assignment);
964 STORM_LOG_ASSERT(!storm::jani::containsArrayExpression(assignment.getAssignedExpression()),
965 "Assignment still contains array expression: " << assignment);
966 }
967 }
968 }
969
970 private:
971 template<class InsertionCallback>
972 void insertArrayAssignmentReplacements(std::vector<storm::expressions::Expression> const& aaIndices, uint64_t const& currDepth,
973 typename ArrayEliminatorData::Replacement const& currReplacement, storm::expressions::Expression const& currRhs,
974 storm::expressions::Expression const& currCondition, InsertionCallback const& insert) {
975 if (currDepth < aaIndices.size()) {
976 STORM_LOG_ASSERT(!currReplacement.isVariable(), "Did not expect a variable replacement at this depth.");
977 auto currIndexExpr = arrayExprEliminator.eliminate(aaIndices[currDepth]);
978 if (currIndexExpr.containsVariables()) {
979 for (uint64_t index = 0; index < currReplacement.size(); ++index) {
980 auto indexExpr = currIndexExpr.getManager().integer(index);
981 auto childCondition = (currIndexExpr == indexExpr);
982 if (currCondition.isInitialized()) {
983 childCondition = currCondition && childCondition;
984 }
985 // if the current index expression is a single variable, we can substitute it by the concrete value in the rhs
986 if (indexExpr.isVariable()) {
987 auto sub = std::map<storm::expressions::Variable, storm::expressions::Expression>(
988 {{indexExpr.getBaseExpression().asVariableExpression().getVariable(), indexExpr}});
989 auto childRhs = currRhs.substitute(sub);
990 insertArrayAssignmentReplacements(aaIndices, currDepth + 1, currReplacement.at(index), childRhs, childCondition, insert);
991 } else {
992 insertArrayAssignmentReplacements(aaIndices, currDepth + 1, currReplacement.at(index), currRhs, childCondition, insert);
993 }
994 }
995 } else {
996 insertArrayAssignmentReplacements(aaIndices, currDepth + 1, currReplacement.at(currIndexExpr.evaluateAsInt()), currRhs, currCondition, insert);
997 }
998 } else if (currReplacement.isVariable()) {
999 auto eliminationRes = arrayExprEliminator.eliminateChkSizes(currRhs);
1000 if (eliminationRes.isArrayOutOfBounds()) {
1001 // We have an out-of-bounds situation. This is expected if we have an incomplete array access
1002 // For example, when we set a:=b where b is smaller than a
1003 STORM_LOG_THROW(currDepth > aaIndices.size(), storm::exceptions::InvalidOperationException,
1004 "Detected out of bounds array access in assignment to " << currReplacement.getVariable().getName() << ".");
1005 insert(currReplacement.getVariable(), {}, currCondition);
1006 } else {
1007 insert(currReplacement.getVariable(), eliminationRes.expr(), currCondition);
1008 }
1009 } else {
1010 for (uint64_t index = 0; index < currReplacement.size(); ++index) {
1011 auto childRhs = std::make_shared<storm::expressions::ArrayAccessExpression>(currRhs.getManager(), currRhs.getType().getElementType(),
1012 currRhs.getBaseExpressionPointer(),
1013 currRhs.getManager().integer(index).getBaseExpressionPointer())
1014 ->toExpression();
1015 insertArrayAssignmentReplacements(aaIndices, currDepth + 1, currReplacement.at(index), childRhs, currCondition, insert);
1016 }
1017 }
1018 }
1019
1027 void handleArrayAssignments(std::vector<Assignment const*> const& arrayAssignments, int64_t level,
1028 typename ArrayEliminatorData::Replacement const& replacements, std::vector<Assignment>& newAssignments) {
1029 // We keep array access assignments if we are requested to do so, if all array accesses are full, and if at least one index is not constant
1030 if (keepNonTrivialArrayAccess &&
1031 std::all_of(arrayAssignments.begin(), arrayAssignments.end(), [](Assignment const* a) { return a->getLValue().isFullArrayAccess(); }) &&
1032 std::any_of(arrayAssignments.begin(), arrayAssignments.end(), [](Assignment const* a) { return a->getLValue().arrayIndexContainsVariable(); })) {
1033 for (auto aa : arrayAssignments) {
1034 // Eliminate array expressions in array access indices and the assigned expression
1035 auto const& lValue = aa->getLValue();
1036 STORM_LOG_ASSERT(lValue.isFullArrayAccess(), "unexpected type of lValue");
1037 std::vector<storm::expressions::Expression> newAaIndices;
1038 newAaIndices.reserve(lValue.getArrayIndexVector().size());
1039 for (auto const& i : lValue.getArrayIndexVector()) {
1040 newAaIndices.push_back(arrayExprEliminator.eliminate(i));
1041 }
1042 newAssignments.emplace_back(storm::jani::LValue(lValue.getVariable(), newAaIndices), arrayExprEliminator.eliminate(aa->getAssignedExpression()),
1043 level);
1044 }
1045 } else {
1046 convertToNonArrayAssignments(arrayAssignments, level, replacements, newAssignments);
1047 }
1048 }
1049
1057 void convertToNonArrayAssignments(std::vector<Assignment const*> const& arrayAssignments, int64_t level,
1058 typename ArrayEliminatorData::Replacement const& replacements, std::vector<Assignment>& newAssignments) {
1059 std::map<storm::expressions::Variable, Assignment> collectedAssignments;
1060 auto insert = [&](storm::jani::Variable const& var, storm::expressions::Expression const& newRhs, storm::expressions::Expression const& condition) {
1061 STORM_LOG_ASSERT(!condition.isInitialized() || !storm::jani::containsArrayExpression(condition),
1062 "condition " << condition << " still contains array expressions.");
1063 auto rhs = newRhs.isInitialized() ? newRhs : getOutOfBoundsValue(var);
1064 auto findIt = collectedAssignments.find(var.getExpressionVariable());
1065 if (findIt == collectedAssignments.end()) {
1066 if (condition.isInitialized()) {
1067 rhs = storm::expressions::ite(condition, rhs, var.getExpressionVariable().getExpression());
1068 }
1069 collectedAssignments.emplace(var.getExpressionVariable(), Assignment(LValue(var), rhs, level));
1070 } else {
1071 auto otherRhs = findIt->second.getAssignedExpression();
1072 // Deal with cases like 'a[0]:=x; a:=b' or 'a:=b; a[0]:=x'
1073 STORM_LOG_THROW(condition.isInitialized(), storm::exceptions::InvalidOperationException,
1074 "Found conflicting array assignments to " << var.getName());
1075 STORM_LOG_THROW(otherRhs.getBaseExpression().isIfThenElseExpression(), storm::exceptions::InvalidOperationException,
1076 "Found conflicting array assignments to " << var.getName());
1077 rhs = storm::expressions::ite(condition, rhs, otherRhs);
1078 findIt->second.setAssignedExpression(rhs);
1079 }
1080 };
1081
1082 for (auto aa : arrayAssignments) {
1083 auto const& lValue = aa->getLValue();
1084 if (lValue.isArrayAccess()) {
1085 insertArrayAssignmentReplacements(lValue.getArrayIndexVector(), 0, replacements, aa->getAssignedExpression(), storm::expressions::Expression(),
1086 insert);
1087 } else {
1088 insertArrayAssignmentReplacements({}, 0, replacements, aa->getAssignedExpression(), storm::expressions::Expression(), insert);
1089 }
1090 }
1091
1092 for (auto& newAs : collectedAssignments) {
1093 newAssignments.push_back(std::move(newAs.second));
1094 }
1095 }
1096
1097 // Returns some dedicated value used in out-of-bound situations.
1098 storm::expressions::Expression getOutOfBoundsValue(Variable const& var) const {
1099 auto const& expressionManager = var.getExpressionVariable().getManager();
1100 return getOutOfBoundsValue(var.getType(), expressionManager);
1101 }
1102
1103 storm::expressions::Expression getOutOfBoundsValue(JaniType const& type, storm::expressions::ExpressionManager const& expressionManager) const {
1104 if (type.isBasicType()) {
1105 switch (type.asBasicType().get()) {
1107 return expressionManager.boolean(false);
1109 return expressionManager.integer(0);
1111 return expressionManager.rational(0.0);
1112 }
1113 } else if (type.isBoundedType()) {
1114 auto const& bndType = type.asBoundedType();
1115 if (bndType.hasLowerBound()) {
1116 return bndType.getLowerBound();
1117 } else if (bndType.hasUpperBound()) {
1118 return bndType.getUpperBound();
1119 } else {
1120 switch (bndType.getBaseType()) {
1122 return expressionManager.integer(0);
1124 return expressionManager.rational(0.0);
1125 }
1126 }
1127 }
1128 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "unhandled variable type");
1130 }
1131
1132 ArrayEliminatorData const& elimData;
1133 ArrayExpressionEliminationVisitor arrayExprEliminator;
1134 bool const keepNonTrivialArrayAccess;
1135};
1136} // namespace detail
1137
1139 // Intentionally left empty
1140}
1141
1143 // Intentionally left empty
1144}
1145
1147 return data.which() == 0;
1148}
1149
1151 STORM_LOG_ASSERT(isVariable(), "unexpected replacement type");
1152 return *boost::get<storm::jani::Variable const*>(data);
1153}
1154
1155std::vector<typename ArrayEliminatorData::Replacement> const& ArrayEliminatorData::Replacement::getReplacements() const {
1156 STORM_LOG_ASSERT(!isVariable(), "unexpected replacement type");
1157 return boost::get<std::vector<Replacement>>(data);
1158}
1159
1160typename ArrayEliminatorData::Replacement const& ArrayEliminatorData::Replacement::at(std::size_t const& index) const {
1161 STORM_LOG_ASSERT(!isVariable(), "unexpected replacement type");
1162 return getReplacements().at(index);
1163}
1164
1166 STORM_LOG_ASSERT(!isVariable(), "unexpected replacement type");
1167 return boost::get<std::vector<Replacement>>(data).at(index);
1168}
1169
1170typename ArrayEliminatorData::Replacement const& ArrayEliminatorData::Replacement::at(std::vector<std::size_t> const& indices) const {
1171 auto const* cur = this;
1172 for (auto const& i : indices) {
1173 cur = &cur->at(i);
1174 }
1175 return *cur;
1176}
1177
1178typename ArrayEliminatorData::Replacement& ArrayEliminatorData::Replacement::at(std::vector<std::size_t> const& indices) {
1179 auto* cur = this;
1180 for (auto const& i : indices) {
1181 cur = &cur->at(i);
1182 }
1183 return *cur;
1184}
1185
1187 return getReplacements().size();
1188}
1189
1190void ArrayEliminatorData::Replacement::grow(std::size_t const& minimumSize) {
1191 if (getReplacements().size() < minimumSize) {
1192 boost::get<std::vector<Replacement>>(data).resize(minimumSize);
1193 }
1194}
1195
1198 return eliminator.eliminate(arrayExpression);
1199}
1200
1202 property = property.substitute([this](storm::expressions::Expression const& exp) { return transformExpression(exp); });
1203}
1204
1205ArrayEliminatorData ArrayEliminator::eliminate(Model& model, bool keepNonTrivialArrayAccess) {
1206 // Only perform actions if there actually are arrays.
1207 if (model.getModelFeatures().hasArrays()) {
1208 auto elimData = detail::ArrayEliminatorDataCollector(model).get();
1209 detail::ArrayVariableReplacer(elimData, keepNonTrivialArrayAccess).replace(model);
1210 if (!keepNonTrivialArrayAccess) {
1212 }
1213 model.finalize();
1214 STORM_LOG_ASSERT(keepNonTrivialArrayAccess || !containsArrayExpression(model), "the model still contains array expressions.");
1215 return elimData;
1216 }
1217 STORM_LOG_ASSERT(!containsArrayExpression(model), "the model contains array expressions although the array feature is not enabled.");
1218 return {};
1219}
1220} // namespace jani
1221} // namespace storm
The base class of all array expressions.
virtual std::shared_ptr< BaseExpression const > size() const =0
virtual std::shared_ptr< BaseExpression const > at(uint64_t i) const =0
ExpressionManager const & getManager() const
Retrieves the manager responsible for this expression.
Type const & getType() const
Retrieves the type of the expression.
std::shared_ptr< BaseExpression const > getSharedPointer() const
Retrieves a shared pointer to this 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.
Represents an array of the given size, where the i'th entry is determined by the elementExpression,...
virtual std::shared_ptr< BaseExpression const > size() const override
boost::any accept(ExpressionVisitor &visitor, boost::any const &data) const
Accepts the given visitor.
std::shared_ptr< BaseExpression const > const & getBaseExpressionPointer() const
Retrieves a pointer to the base expression underlying this expression object.
Type const & getType() const
Retrieves the type of the expression.
ExpressionManager const & getManager() const
Retrieves the manager responsible for this expression.
Expression substitute(std::map< Variable, Expression > const &variableToExpressionMap) const
Substitutes all occurrences of the variables according to the given map.
bool isInitialized() const
Checks whether the object encapsulates a base-expression.
This class is responsible for managing a set of typed variables and all expressions using these varia...
Expression integer(int_fast64_t value) const
Creates an expression that characterizes the given integer literal.
Expression rational(double value) const
Creates an expression that characterizes the given rational literal.
Expression boolean(bool value) const
Creates an expression that characterizes the given boolean literal.
virtual boost::any visit(IfThenElseExpression const &expression, boost::any const &data)=0
Represents an array with a given list of elements.
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.
Type getElementType() const
Retrieves the element type of the type, provided that it is an Array type.
Definition Type.cpp:210
bool isArrayType() const
Checks whether this type is an array type.
Definition Type.cpp:194
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.
Represents an array with a given list of elements.
virtual std::shared_ptr< BaseExpression const > size() const override
Variable const & getVariable() const
Retrieves the variable associated with this expression.
storm::expressions::Expression getExpression() const
Retrieves an expression that represents the variable.
Definition Variable.cpp:34
std::string const & getName() const
Retrieves the name of the variable.
Definition Variable.cpp:46
Replacement(storm::jani::Variable const &variable)
Replacement const & at(std::size_t const &index) const
std::vector< Replacement > const & getReplacements() const
storm::jani::Variable const & getVariable() const
std::size_t size() const
equivalent to .at(i_1).at(i_2). ... .at(i_n) if indices = {i_1,i_2, ... i_n}
void grow(std::size_t const &minimumSize)
assumes this is not a variable
ArrayEliminatorData eliminate(Model &model, bool keepNonTrivialArrayAccess=false)
Eliminates all array references in the given model by replacing them with basic variables.
JaniType const & getBaseTypeRecursive() const
Definition ArrayType.cpp:26
storm::expressions::Expression const & getAssignedExpression() const
Retrieves the expression whose value is assigned to the target variable.
storm::jani::LValue const & getLValue() const
Retrieves the lValue that is written in this assignment.
VariableSet & getVariables()
Retrieves the variables of this automaton.
Definition Automaton.cpp:59
storm::expressions::Expression const & getInitialStatesRestriction() const
Gets the expression restricting the legal initial values of the automaton's variables.
bool hasVariable(std::string const &name) const
Definition Automaton.cpp:55
void setInitialStatesRestriction(storm::expressions::Expression const &initialStatesRestriction)
Sets the expression restricting the legal initial values of the automaton's variables.
Variable const & addVariable(Variable const &variable)
Adds the given variable to this automaton.
Definition Automaton.cpp:51
std::vector< Location > const & getLocations() const
Retrieves the locations of the automaton.
Definition Automaton.cpp:98
bool hasInitialStatesRestriction() const
Retrieves whether this automaton has an initial states restriction.
std::unordered_map< std::string, FunctionDefinition > const & getFunctionDefinitions() const
Retrieves all function definitions of this automaton.
Definition Automaton.cpp:86
EdgeContainer const & getEdgeContainer() const
Retrieves the container of all edges of this automaton.
void setLowerBound(storm::expressions::Expression const &expression)
void define(storm::expressions::Expression const &expression)
Defines the constant with the given expression.
Definition Constant.cpp:38
bool hasConstraint() const
Retrieves whether there is a constraint for the possible values of this constant.
Definition Constant.cpp:72
storm::expressions::Expression const & getConstraintExpression() const
Retrieves the expression that constraints the possible values of this constant (if any).
Definition Constant.cpp:76
void setConstraintExpression(storm::expressions::Expression const &expression)
Sets a constraint expression.
Definition Constant.cpp:81
bool isDefined() const
Retrieves whether the constant is defined in the sense that it has a defining expression.
Definition Constant.cpp:34
storm::expressions::Expression const & getExpression() const
Retrieves the expression that defines this constant (if any).
Definition Constant.cpp:67
storm::expressions::Expression const & getProbability() const
Retrieves the probability of choosing this destination.
void setProbability(storm::expressions::Expression const &probability)
Sets a new probability for this edge destination.
void setRate(storm::expressions::Expression const &rate)
Sets a new rate for this edge.
Definition Edge.cpp:61
std::vector< EdgeDestination > const & getDestinations() const
Retrieves the destinations of this edge.
Definition Edge.cpp:77
bool hasRate() const
Retrieves whether this edge has an associated rate.
Definition Edge.cpp:49
storm::expressions::Expression const & getRate() const
Retrieves the rate of this edge.
Definition Edge.cpp:53
virtual void traverse(Model &model, boost::any const &data)
ArrayType const & asArrayType() const
Definition JaniType.cpp:47
virtual bool isArrayType() const
Definition JaniType.cpp:19
virtual bool isBoundedType() const
Definition JaniType.cpp:15
BoundedType const & asBoundedType() const
Definition JaniType.cpp:39
bool isFullArrayAccess() const
Returns true if the lValue refers to an array access.
Definition LValue.cpp:36
bool isArray() const
Returns true if the lValue refers to a variable (potentially of type array).
Definition LValue.cpp:24
std::vector< storm::expressions::Expression > & getArrayIndexVector()
Returns the referred variable. In case of an array access, this is the referred array variable.
Definition LValue.cpp:40
bool isArrayAccess() const
Returns true if the lValue refers either to an array variable or to an array access.
Definition LValue.cpp:32
storm::jani::Variable const & getVariable() const
To check if the array is fully accessed, so result will be of the most inner child Type....
Definition LValue.cpp:28
Jani Location:
Definition Location.h:15
OrderedAssignments const & getAssignments() const
Retrieves the assignments of this location.
Definition Location.cpp:23
void setTimeProgressInvariant(storm::expressions::Expression const &expression)
Sets the time progress invariant of this location.
Definition Location.cpp:44
storm::expressions::Expression const & getTimeProgressInvariant() const
Retrieves the time progress invariant.
Definition Location.cpp:40
bool hasTimeProgressInvariant() const
Retrieves whether a time progress invariant is attached to this location.
Definition Location.cpp:36
void remove(ModelFeature const &modelFeature)
void setInitialStatesRestriction(storm::expressions::Expression const &initialStatesRestriction)
Sets the expression restricting the legal initial values of the global variables.
Definition Model.cpp:1280
VariableSet & getGlobalVariables()
Retrieves the variables of this automaton.
Definition Model.cpp:721
storm::expressions::ExpressionManager & getExpressionManager() const
Retrieves the manager responsible for the expressions in the JANI model.
Definition Model.cpp:789
std::unordered_map< std::string, storm::expressions::Expression > const & getNonTrivialRewardExpressions() const
Retrieves all available non-trivial reward model names and expressions of the model.
Definition Model.cpp:855
bool hasInitialStatesRestriction() const
Retrieves whether there is an expression restricting the legal initial values of the global variables...
Definition Model.cpp:1284
storm::expressions::Expression const & getInitialStatesRestriction() const
Gets the expression restricting the legal initial values of the global variables.
Definition Model.cpp:1288
std::vector< Automaton > & getAutomata()
Retrieves the automata of the model.
Definition Model.cpp:872
std::vector< Constant > const & getConstants() const
Retrieves the constants of the model.
Definition Model.cpp:689
Variable const & addVariable(Variable const &variable)
Adds the given variable to this model.
Definition Model.cpp:717
bool hasGlobalVariable(std::string const &name) const
Retrieves whether this model has a global variable with the given name.
Definition Model.cpp:757
ModelFeatures const & getModelFeatures() const
Retrieves the enabled model features.
Definition Model.cpp:129
void finalize()
After adding all components to the model, this method has to be called.
Definition Model.cpp:1399
std::unordered_map< std::string, FunctionDefinition > const & getGlobalFunctionDefinitions() const
Retrieves all global function definitions.
Definition Model.cpp:781
bool empty(bool onlyTransient=false) const
Retrieves whether this set of assignments is empty.
int64_t getLowestLevel(bool onlyTransient=false) const
Retrieves the lowest level among all assignments.
void clear()
Removes all assignments from this set.
bool add(Assignment const &assignment, bool addToExisting=false)
Adds the given assignment to the set of assignments.
storm::expressions::Expression const & getGuard() const
std::vector< TemplateEdgeDestination > const & getDestinations() const
void setGuard(storm::expressions::Expression const &newGuard)
OrderedAssignments const & getAssignments() const
bool hasInitExpression() const
Retrieves whether an initial expression is set.
Definition Variable.cpp:46
storm::expressions::Variable const & getExpressionVariable() const
Retrieves the associated expression variable.
Definition Variable.cpp:26
JaniType & getType()
Definition Variable.cpp:67
bool isTransient() const
Definition Variable.cpp:42
static std::shared_ptr< Variable > makeVariable(std::string const &name, JaniType const &type, storm::expressions::Variable const &variable, boost::optional< storm::expressions::Expression > const &initValue, bool transient)
Convenience functions to call the appropriate constructor and return a shared pointer to the variable...
Definition Variable.cpp:95
storm::expressions::Expression const & getInitExpression() const
Retrieves the initial expression Should only be called if an initial expression is set for this varia...
Definition Variable.cpp:50
void setInitExpression(storm::expressions::Expression const &initialExpression)
Sets the initial expression for this variable.
Definition Variable.cpp:55
std::string const & getName() const
Retrieves the name of the variable.
Definition Variable.cpp:34
bool hasVariable(std::string const &name) const
Retrieves whether this variable set contains a variable with the given name.
std::vector< std::shared_ptr< Variable > > dropAllArrayVariables()
Removes all array variables in this set.
detail::Variables< Variable > getArrayVariables()
Retrieves the Array variables in this set.
Gets the data necessary for array elimination.
Automaton * declaringAutomaton(Variable const &var) const
void gatherArrayAccessIndices(std::vector< std::vector< std::size_t > > &gatheredIndices, std::vector< std::size_t > &current, typename ArrayEliminatorData::Replacement const &repl, std::vector< storm::expressions::Expression > const &indices)
virtual void traverse(VariableSet &variableSet, boost::any const &data) override
virtual void traverse(Automaton &automaton, boost::any const &data) override
virtual void traverse(Assignment &assignment, boost::any const &data) override
virtual void traverse(Variable &variable, boost::any const &data) override
Eliminates the array accesses in the given expression, for example ([[1],[2,3]])[i][j] --> i=0 ?...
virtual boost::any visit(storm::expressions::FunctionCallExpression const &, boost::any const &) override
virtual boost::any visit(storm::expressions::BinaryNumericalFunctionExpression const &expression, boost::any const &data) override
virtual boost::any visit(storm::expressions::UnaryNumericalFunctionExpression const &expression, boost::any const &data) override
std::shared_ptr< storm::expressions::BaseExpression const > BaseExprPtr
virtual boost::any visit(storm::expressions::ArrayAccessExpression const &expression, boost::any const &data) override
virtual boost::any visit(storm::expressions::TranscendentalNumberLiteralExpression const &expression, boost::any const &) override
virtual boost::any visit(storm::expressions::ValueArrayExpression const &expression, boost::any const &data) override
std::vector< storm::expressions::Expression > ArrayAccessIndices
ResultType eliminateChkSizes(storm::expressions::Expression const &expression)
storm::expressions::Expression eliminate(storm::expressions::Expression const &expression)
boost::any arrayExprHelper(storm::expressions::ArrayExpression const &expression, boost::any const &data)
virtual boost::any visit(storm::expressions::BinaryRelationExpression const &expression, boost::any const &data) override
virtual boost::any visit(storm::expressions::BinaryBooleanFunctionExpression const &expression, boost::any const &data) override
virtual boost::any visit(storm::expressions::UnaryBooleanFunctionExpression const &expression, boost::any const &data) override
ArrayAccessIndices const & getArrayAccessIndices(boost::any const &data)
ArrayExpressionEliminationVisitor(ArrayEliminatorData const &data)
storm::expressions::Expression varElimHelper(typename ArrayEliminatorData::Replacement const &replacement, ArrayAccessIndices const &indices, uint64_t const &pos)
virtual boost::any visit(storm::expressions::VariableExpression const &expression, boost::any const &data) override
virtual boost::any visit(storm::expressions::IntegerLiteralExpression const &expression, boost::any const &) override
virtual boost::any visit(storm::expressions::IfThenElseExpression const &expression, boost::any const &data) override
virtual boost::any visit(storm::expressions::BooleanLiteralExpression const &expression, boost::any const &) override
virtual boost::any visit(storm::expressions::ConstructorArrayExpression const &expression, boost::any const &data) override
virtual boost::any visit(storm::expressions::RationalLiteralExpression const &expression, boost::any const &) override
virtual boost::any visit(storm::expressions::ConstructorArrayExpression const &expression, boost::any const &data) override
virtual boost::any visit(storm::expressions::VariableExpression const &expression, boost::any const &data) override
void varToVarAssignmentHelper(Replacement &cur, Replacement const &other, std::vector< std::size_t > &indices)
virtual boost::any visit(storm::expressions::BinaryBooleanFunctionExpression const &expression, boost::any const &data) override
virtual boost::any visit(storm::expressions::TranscendentalNumberLiteralExpression const &, boost::any const &) override
void arrayAccessHelper(std::shared_ptr< storm::expressions::BaseExpression const > const &base, std::vector< std::shared_ptr< storm::expressions::BaseExpression const > > &indexStack, boost::any const &data)
virtual boost::any visit(storm::expressions::UnaryBooleanFunctionExpression const &expression, boost::any const &data) override
virtual boost::any visit(storm::expressions::IntegerLiteralExpression const &, boost::any const &) override
std::unordered_map< storm::expressions::Variable, Replacement > ReplMap
virtual boost::any visit(storm::expressions::FunctionCallExpression const &, boost::any const &) override
virtual boost::any visit(storm::expressions::BinaryRelationExpression const &expression, boost::any const &data) override
bool get(storm::expressions::Expression const &expression, storm::jani::Variable const &arrayVariable, Replacement &currentReplacement, ReplMap const &allCollectedReplacements, Automaton *declaringAutomaton=nullptr, std::vector< std::size_t > *arrayAccessIndices=nullptr)
Adds necessary replacements for the given array variable, assuming that the given expression is assig...
virtual boost::any visit(storm::expressions::IfThenElseExpression const &expression, boost::any const &data) override
virtual boost::any visit(storm::expressions::RationalLiteralExpression const &, boost::any const &) override
virtual boost::any visit(storm::expressions::ArrayAccessExpression const &expression, boost::any const &data) override
void arrayVariableAccessHelper(std::vector< std::shared_ptr< storm::expressions::BaseExpression const > > &indexStack, Replacement &cur, Replacement const &other, std::vector< std::size_t > &curIndices)
virtual boost::any visit(storm::expressions::ValueArrayExpression const &expression, boost::any const &data) override
virtual boost::any visit(storm::expressions::BinaryNumericalFunctionExpression const &expression, boost::any const &data) override
virtual boost::any visit(storm::expressions::BooleanLiteralExpression const &, boost::any const &) override
void arrayExpressionHelper(storm::expressions::ArrayExpression const &expression, Replacement &curRepl, std::vector< size_t > &curIndices)
virtual boost::any visit(storm::expressions::UnaryNumericalFunctionExpression const &expression, boost::any const &data) override
virtual void traverse(Model &model, boost::any const &data) override
virtual void traverse(OrderedAssignments &orderedAssignments, boost::any const &data) override
void traverse(Variable &variable, boost::any const &data) override
void traverse(TemplateEdge &templateEdge, boost::any const &data) override
void traverse(EdgeDestination &edgeDestination, boost::any const &) override
void traverse(Edge &edge, boost::any const &data) override
void traverse(Constant &constant, boost::any const &data) override
ArrayVariableReplacer(ArrayEliminatorData const &data, bool keepNonTrivialArrayAccess=false)
void traverse(JaniType &type, boost::any const &data) override
virtual void traverse(Location &location, boost::any const &data) override
virtual void traverse(Automaton &automaton, boost::any const &data) override
#define STORM_LOG_WARN(message)
Definition logging.h:30
#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)
bool containsArrayExpression(Model const &model)
SettingsManager const & manager()
Retrieves the settings manager.
LabParser.cpp.
Definition cli.cpp:18
storm::expressions::Expression transformExpression(storm::expressions::Expression const &arrayExpression) const
std::unordered_map< storm::expressions::Variable, Replacement > replacements
void transformProperty(storm::jani::Property &property) const
std::vector< std::shared_ptr< Variable > > eliminatedArrayVariables