Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
JaniLocalEliminator.cpp
Go to the documentation of this file.
9
11
12namespace storm {
13namespace jani {
14
15JaniLocalEliminator::JaniLocalEliminator(Model const &original, storm::jani::Property &property, bool addMissingGuards)
16 : original(original), addMissingGuards(addMissingGuards) {
17 setProperty(property);
19}
20
21JaniLocalEliminator::JaniLocalEliminator(const Model &original, std::vector<storm::jani::Property> &properties, bool addMissingGuards)
22 : original(original), addMissingGuards(addMissingGuards) {
23 if (properties.size() > 1) {
24 STORM_LOG_WARN("Only the first property will be used for local elimination.");
25 }
26 STORM_LOG_THROW(!properties.empty(), storm::exceptions::InvalidArgumentException, "Local elimination requires at least one property.");
27
28 setProperty(properties[0]);
29
31}
32
33Model JaniLocalEliminator::eliminateAutomatically(const Model &model, std::vector<jani::Property> properties, uint64_t locationHeuristic,
34 uint64_t edgesHeuristic) {
35 auto eliminator = storm::jani::JaniLocalEliminator(model, properties);
36 eliminator.scheduler.addAction(std::make_unique<elimination_actions::AutomaticAction>(locationHeuristic, edgesHeuristic));
37 eliminator.eliminate();
38 return eliminator.getResult();
39}
40
42 newModel = original;
43
44 Session session = Session(newModel, property, flatten);
45
46 if (addMissingGuards) {
47 session.addMissingGuards(session.getModel().getAutomaton(0).getName());
48 }
49
50 for (auto &automaton : session.getModel().getAutomata()) {
51 bool hasTransientAssignments = false;
52 for (auto loc : automaton.getLocations()) {
53 if (loc.getAssignments().hasTransientAssignment()) {
54 hasTransientAssignments = true;
55 }
56 }
57 if (hasTransientAssignments) {
58 STORM_LOG_TRACE("Pushing transient location assignments to edge destinations");
59 automaton.pushTransientRealLocationAssignmentsToEdges();
60 automaton.pushEdgeAssignmentsToDestinations();
61 }
62 }
63
64 while (!session.getFinished()) {
65 std::unique_ptr<Action> action = scheduler.getNextAction();
66 action->doAction(session);
67 }
68
69 newModel = session.getModel();
70 newModel.finalize();
71}
72
74 return newModel;
75}
76
77bool JaniLocalEliminator::Session::isEliminable(const std::string &automatonName, std::string const &locationName) {
78 return !isPossiblyInitial(automatonName, locationName) && !hasLoops(automatonName, locationName) && !isPartOfProp(automatonName, locationName);
79}
80bool JaniLocalEliminator::Session::hasLoops(const std::string &automatonName, std::string const &locationName) {
81 Automaton &automaton = model.getAutomaton(automatonName);
82 uint64_t locationIndex = automaton.getLocationIndex(locationName);
83 for (Edge edge : automaton.getEdgesFromLocation(locationIndex)) {
84 for (const EdgeDestination &dest : edge.getDestinations()) {
85 if (dest.getLocationIndex() == locationIndex) {
86 return true;
87 }
88 }
89 }
90 return false;
91}
92bool JaniLocalEliminator::Session::hasNamedActions(const std::string &automatonName, std::string const &locationName) {
93 Automaton &automaton = model.getAutomaton(automatonName);
94 uint64_t locationIndex = automaton.getLocationIndex(locationName);
95 for (const Edge &edge : automaton.getEdgesFromLocation(locationIndex)) {
96 if (!edge.hasSilentAction()) {
97 return true;
98 }
99 }
100 return false;
101}
102
103bool JaniLocalEliminator::Session::isPossiblyInitial(const std::string &automatonName, std::string const &locationName) {
104 Automaton &automaton = model.getAutomaton(automatonName);
105 auto location = automaton.getLocation(automaton.getLocationIndex(locationName));
106 for (const auto &asg : location.getAssignments()) {
107 if (!asg.isTransient()) {
108 continue;
109 }
110 if (asg.getAssignedExpression().containsVariables() ||
111 (asg.getVariable().hasInitExpression() && asg.getVariable().getInitExpression().containsVariables())) {
112 continue;
113 }
114 if (asg.getVariable().getType().isBoundedType() && asg.getVariable().getType().asBoundedType().isIntegerType()) {
115 if (asg.getVariable().hasInitExpression()) {
116 int initValue = asg.getVariable().getInitExpression().evaluateAsInt();
117 int currentValue = asg.getAssignedExpression().evaluateAsInt();
118 if (initValue != currentValue) {
119 return false;
120 }
121 } else {
122 STORM_LOG_WARN("Variable " + asg.getVariable().getName() + " has no init expression. The result may not be correct.");
123 }
124 } else if (asg.getVariable().getType().isBasicType() && asg.getVariable().getType().asBasicType().isBooleanType()) {
125 if (asg.getVariable().hasInitExpression()) {
126 bool initValue = asg.getVariable().getInitExpression().evaluateAsBool();
127 bool currentValue = asg.getAssignedExpression().evaluateAsBool();
128 if (initValue != currentValue) {
129 return false;
130 }
131 } else {
132 STORM_LOG_WARN("Variable " + asg.getVariable().getName() + " has no init expression. The result may not be correct.");
133 }
134 }
135 }
136
137 return true;
138}
139
140bool JaniLocalEliminator::Session::isPartOfProp(const std::string &automatonName, std::string const &locationName) {
141 uint64_t locationIndex = model.getAutomaton(automatonName).getLocationIndex(locationName);
142 return isPartOfProp(automatonName, locationIndex);
143}
144
145bool JaniLocalEliminator::Session::isPartOfProp(const std::string &automatonName, uint64_t locationIndex) {
146 AutomatonInfo &autInfo = automataInfo[automatonName];
147 return autInfo.potentiallyPartOfProp.count(locationIndex) == 1;
148}
149
150bool JaniLocalEliminator::Session::computeIsPartOfProp(const std::string &automatonName, const std::string &locationName) {
151 Automaton &automaton = model.getAutomaton(automatonName);
152 uint64_t locationIndex = automaton.getLocationIndex(locationName);
153 return computeIsPartOfProp(automatonName, locationIndex);
154}
155
156bool JaniLocalEliminator::Session::computeIsPartOfProp(const std::string &automatonName, uint64_t locationIndex) {
157 Automaton &automaton = model.getAutomaton(automatonName);
158 auto location = automaton.getLocation(locationIndex);
159 std::map<expressions::Variable, expressions::Expression> substitutionMap;
160 for (auto &asg : location.getAssignments()) {
161 if (!asg.isTransient()) {
162 continue;
163 }
164 substitutionMap.insert(std::pair<expressions::Variable, expressions::Expression>(asg.getExpressionVariable(), asg.getAssignedExpression()));
165 }
166 return computeIsPartOfProp(substitutionMap);
167}
168
169bool JaniLocalEliminator::Session::computeIsPartOfProp(const std::map<expressions::Variable, expressions::Expression> &substitutionMap) {
170 storm::solver::Z3SmtSolver solver(model.getExpressionManager());
171 auto propertyFormula = property.getRawFormula()->substitute(substitutionMap);
172 auto expression = model.getExpressionManager().boolean(false);
173 if (propertyFormula->isProbabilityOperatorFormula() || propertyFormula->isRewardOperatorFormula()) {
174 auto subformula = &propertyFormula->asUnaryStateFormula().getSubformula();
175 if (subformula->isEventuallyFormula()) {
176 expression = subformula->asEventuallyFormula().getSubformula().toExpression(model.getExpressionManager());
177 } else if (subformula->isUntilFormula()) {
178 const auto &untilFormula = subformula->asUntilFormula();
179 if (untilFormula.getLeftSubformula().isTrueFormula() && untilFormula.getRightSubformula().isAtomicExpressionFormula()) {
180 expression = untilFormula.getRightSubformula().toExpression(model.getExpressionManager());
181 } else {
182 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Until formulas are only supported if the left subformula is \"true\".");
183 }
184 } else {
185 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "This type of formula is not supported.");
186 }
187 }
188 auto simplified = expression.simplify();
189 if (simplified.isLiteral()) {
190 return simplified.evaluateAsBool();
191 }
192 solver.add(simplified);
193
194 auto result = solver.check();
196}
197
198void JaniLocalEliminator::Session::setPartOfProp(const std::string &automatonName, const std::string &locationName, bool isPartOfProp) {
199 uint64_t locationIndex = model.getAutomaton(automatonName).getLocationIndex(locationName);
200 return setPartOfProp(automatonName, locationIndex, isPartOfProp);
201}
202
203void JaniLocalEliminator::Session::setPartOfProp(const std::string &automatonName, uint64_t locationIndex, bool isPartOfProp) {
204 AutomatonInfo &autInfo = automataInfo[automatonName];
205 if (autInfo.potentiallyPartOfProp.count(locationIndex) == 1) {
206 if (!isPartOfProp) {
207 autInfo.potentiallyPartOfProp.erase(locationIndex);
208 }
209 } else {
210 if (isPartOfProp) {
211 autInfo.potentiallyPartOfProp.insert(locationIndex);
212 }
213 }
214}
215
216void JaniLocalEliminator::Session::clearIsPartOfProp(const std::string &automatonName) {
217 AutomatonInfo &autInfo = automataInfo[automatonName];
218 autInfo.potentiallyPartOfProp.clear();
219}
220
221void JaniLocalEliminator::setProperty(storm::jani::Property &newProperty) {
222 auto raw = newProperty.getRawFormula();
223 bool supported = false;
224
225 if (raw->isProbabilityOperatorFormula()) {
226 auto subformula = &raw->asProbabilityOperatorFormula().getSubformula();
227 if (subformula->isEventuallyFormula()) {
228 supported = true;
229 } else if (subformula->isUntilFormula()) {
230 const auto &untilFormula = subformula->asUntilFormula();
231 if (untilFormula.getLeftSubformula().isTrueFormula()) {
232 supported = true;
233 }
234 }
235 }
236 if (raw->isRewardOperatorFormula()) {
237 auto subformula = &raw->asRewardOperatorFormula().getSubformula();
238 if (subformula->isEventuallyFormula()) {
239 supported = true;
240 } else if (subformula->isUntilFormula()) {
241 const auto &untilFormula = subformula->asUntilFormula();
242 if (untilFormula.getLeftSubformula().isTrueFormula()) {
243 supported = true;
244 }
245 }
246 }
247
248 STORM_LOG_THROW(supported, storm::exceptions::NotSupportedException, "This type of property is not supported for location elimination.");
249
250 this->property = newProperty;
251}
252
254
255std::unique_ptr<JaniLocalEliminator::Action> JaniLocalEliminator::EliminationScheduler::getNextAction() {
256 if (actionQueue.empty()) {
257 return std::make_unique<elimination_actions::FinishAction>();
258 }
259 std::unique_ptr<JaniLocalEliminator::Action> val = std::move(actionQueue.front());
260 actionQueue.pop();
261 return val;
262}
263
264void JaniLocalEliminator::EliminationScheduler::addAction(std::unique_ptr<JaniLocalEliminator::Action> action) {
265 actionQueue.push(std::move(action));
266}
267
268JaniLocalEliminator::Session::Session(Model model, Property property, bool flatten) : model(model), property(property), finished(false) {
269 if (flatten && model.getNumberOfAutomata() > 1) {
270 flatten_automata();
271 }
272
274
275 if (property.getRawFormula()->isRewardOperatorFormula()) {
276 isRewardFormula = true;
277 rewardModels = property.getRawFormula()->getReferencedRewardModels();
278 } else if (property.getRawFormula()->isProbabilityOperatorFormula()) {
279 isRewardFormula = false;
280 } else {
281 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This type of property is currently not supported.");
282 }
283
284 for (auto &var : property.getUsedVariablesAndConstants()) {
285 expressionVarsInProperty.insert(var.getIndex());
286 }
287}
288
292
294 this->model = model;
295}
296
300
302 return finished;
303}
304
306 this->finished = finished;
307}
308
313
317
319 std::set<std::string> &rewardVariables) {
320 STORM_LOG_THROW(!first.usesAssignmentLevels() && !then.usesAssignmentLevels(), storm::exceptions::NotImplementedException,
321 "Assignment levels are currently not supported.");
322
323 OrderedAssignments newOa;
324
325 // This method takes two OrderedAssignments and returns an OrderedAssignments that is equivalent to executing the two in sequence.
326 // This is done by removing those assignments from the first set that also occur in the second set (because that first assignment would be
327 // overwritten by the second one). We then modify the second assignment to that variable so that we still get the same end result.
328
329 // Collect variables that occur in the second set of assignments. This will be used to decide which first assignments to keep and which to discard.
330 std::set<expressions::Variable> thenVariables;
331 for (const auto &assignment : then.getOrderedAssignments()) {
332 thenVariables.emplace(assignment.getExpressionVariable());
333 }
334
335 // Add the remaining assignments from the first OrderedAssignments to the new assignment.
336 for (const auto &assignment : first.getOrderedAssignments()) {
337 if (thenVariables.find(assignment.getExpressionVariable()) != thenVariables.end()) {
338 continue;
339 }
340 newOa.add(assignment);
341 }
342
343 // Finally add all assignments from the second OrderedAssignments to the new OrderedAssignments. While doing this, we need to replace all variables
344 // that were updated in the first assignment with the expression assigned to them.
345 std::map<expressions::Variable, expressions::Expression> substitutionMap = first.getAsVariableToExpressionMap();
346 for (const auto &assignment : then.getOrderedAssignments()) {
347 bool isReward = rewardVariables.count(assignment.getExpressionVariable().getName());
348 auto firstAssignment = substitutionMap.find(assignment.getExpressionVariable());
349 if (isReward && firstAssignment != substitutionMap.end()) {
350 auto newAssignment = firstAssignment->second + assignment.getAssignedExpression().substitute(substitutionMap);
351
352 newOa.add(Assignment(assignment.getVariable(), newAssignment));
353 } else {
354 newOa.add(Assignment(assignment.getVariable(), assignment.getAssignedExpression().substitute(substitutionMap).simplify()));
355 }
356 }
357 return newOa;
358}
359
360bool JaniLocalEliminator::Session::isVariablePartOfProperty(const std::string &expressionVariableName) {
361 auto expressionVariable = model.getExpressionManager().getVariable(expressionVariableName);
362 uint_fast64_t expressionVariableIndex = expressionVariable.getIndex();
363 return expressionVarsInProperty.count(expressionVariableIndex) != 0;
364}
365
367 model = model.flattenComposition();
368 automataInfo.clear();
370}
371
372void JaniLocalEliminator::Session::addMissingGuards(const std::string &automatonName) {
373 auto &automaton = model.getAutomaton(automatonName);
374
375 std::string sinkName = "sink_location";
376 while (automaton.hasLocation(sinkName)) {
377 sinkName += "_";
378 }
379 Location sink(sinkName, OrderedAssignments());
380 automaton.addLocation(sink);
381 uint64_t sinkIndex = automaton.getNumberOfLocations() - 1;
382
383 automataInfo[automatonName].hasSink = true;
384 automataInfo[automatonName].sinkIndex = sinkIndex;
385
386 for (uint64_t i = 0; i < automaton.getNumberOfLocations(); i++) {
387 if (i == sinkIndex) {
388 continue;
389 }
390 auto outgoingEdges = automaton.getEdgesFromLocation(i);
391 expressions::Expression allGuards;
392 allGuards = model.getExpressionManager().boolean(false);
393 for (const auto &edge : outgoingEdges) {
394 allGuards = edge.getGuard() || allGuards;
395 }
396 expressions::Expression newGuard = !allGuards;
397
398 // Before we add the edge, check whether it is satisfiable:
399 auto variables = newGuard.getVariables();
400 storm::solver::Z3SmtSolver solver(model.getExpressionManager());
401 solver.add(newGuard);
402 for (const auto &var : model.getGlobalVariables()) {
403 if (var.getType().isBoundedType() && var.getType().asBoundedType().isIntegerType() && variables.count(var.getExpressionVariable()) > 0) {
404 auto &biVariable = var.getType().asBoundedType();
405 solver.add(var.getExpressionVariable().getExpression() >= biVariable.getLowerBound());
406 solver.add(var.getExpressionVariable().getExpression() <= biVariable.getUpperBound());
407 }
408 }
409 for (const auto &var : automaton.getVariables()) {
410 if (var.getType().isBoundedType() && var.getType().asBoundedType().isIntegerType() && variables.count(var.getExpressionVariable()) > 0) {
411 auto &biVariable = var.getType().asBoundedType();
412 solver.add(var.getExpressionVariable().getExpression() >= biVariable.getLowerBound());
413 solver.add(var.getExpressionVariable().getExpression() <= biVariable.getUpperBound());
414 }
415 }
416 auto result = solver.check();
417
419 STORM_LOG_TRACE("\tAdding missing guard from location " + automaton.getLocation(i).getName());
421 STORM_LOG_TRACE("\t\tThe guard was satisfiable with assignment\n"
422 << ([&] {
423 auto satisfyingAssignment = solver.getModel();
424 std::string message;
425 for (auto &var : variables) {
426 if (var.hasIntegerType()) {
427 message += "\t\t\t" + var.getName() + ": " + std::to_string(satisfyingAssignment->getIntegerValue(var));
428 } else if (var.hasBooleanType()) {
429 message += "\t\t\t" + var.getName() + ": " + std::to_string(satisfyingAssignment->getBooleanValue(var));
430 } else if (var.hasRationalType()) {
431 message += "\t\t\t" + var.getName() + ": " + std::to_string(satisfyingAssignment->getRationalValue(var));
432 }
433 }
434 return message;
435 })());
436 } else {
437 STORM_LOG_TRACE("\t\tThe solver could not determine whether the guard was satisfiable");
438 }
439 std::vector<std::pair<uint64_t, storm::expressions::Expression>> destinationLocationsAndProbabilities;
440 std::shared_ptr<storm::jani::TemplateEdge> templateEdge = std::make_shared<storm::jani::TemplateEdge>(newGuard);
442 templateEdge->addDestination(ted);
443 destinationLocationsAndProbabilities.emplace_back(sinkIndex, model.getExpressionManager().rational(1.0));
444
445 automaton.addEdge(storm::jani::Edge(i, 0, boost::none, templateEdge, destinationLocationsAndProbabilities));
446 } else {
447 STORM_LOG_TRACE("\tLocation " + automaton.getLocation(i).getName() + " has no missing guard");
448 }
449 }
450}
451
453 for (auto &aut : model.getAutomata()) {
454 automataInfo[aut.getName()] = AutomatonInfo();
455 for (auto &loc : aut.getLocations()) {
456 bool isPartOfProp = computeIsPartOfProp(aut.getName(), loc.getName());
457 setPartOfProp(aut.getName(), loc.getName(), isPartOfProp);
458 }
459 }
460}
461
462JaniLocalEliminator::AutomatonInfo &JaniLocalEliminator::Session::getAutomatonInfo(const std::string &name) {
463 return automataInfo[name];
464}
465
466JaniLocalEliminator::AutomatonInfo::AutomatonInfo() : hasSink(false), sinkIndex(0) {}
467} // namespace jani
468} // namespace storm
Expression simplify() const
Simplifies the expression according to some basic rules.
std::set< storm::expressions::Variable > getVariables() const
Retrieves the set of all variables that appear in the expression.
Expression substitute(std::map< Variable, Expression > const &variableToExpressionMap) const
Substitutes all occurrences of the variables according to the given map.
Location const & getLocation(uint64_t index) const
Retrieves the location with the given index.
Edges getEdgesFromLocation(std::string const &name)
Retrieves the edges of the location with the given name.
std::string const & getName() const
Retrieves the name of the automaton.
Definition Automaton.cpp:47
uint64_t getLocationIndex(std::string const &name) const
std::map< storm::expressions::Variable, storm::expressions::Expression > getAsVariableToExpressionMap() const
Retrieves the mapping from variables to their assigned expressions that corresponds to the assignment...
bool usesAssignmentLevels() const
Retrieves whether the edge uses an assignment level other than zero.
storm::expressions::Expression const & getProbability() const
Retrieves the probability of choosing this destination.
OrderedAssignments const & getOrderedAssignments() const
Retrieves the assignments to make when choosing this destination.
storm::expressions::Expression const & getGuard() const
Retrieves the guard of this edge.
Definition Edge.cpp:65
bool computeIsPartOfProp(const std::string &automatonName, const std::string &locationName)
Session(Model model, Property property, bool flatten=true)
expressions::Expression getNewGuard(const Edge &edge, const EdgeDestination &dest, const Edge &outgoing)
bool hasLoops(const std::string &automatonName, std::string const &locationName)
void setPartOfProp(const std::string &automatonName, const std::string &locationName, bool isPartOfProp)
expressions::Expression getProbability(const EdgeDestination &first, const EdgeDestination &then)
bool hasNamedActions(const std::string &automatonName, std::string const &locationName)
bool isPartOfProp(const std::string &automatonName, std::string const &locationName)
bool isPossiblyInitial(const std::string &automatonName, std::string const &locationName)
bool isVariablePartOfProperty(const std::string &expressionVariableName)
void addMissingGuards(const std::string &automatonName)
AutomatonInfo & getAutomatonInfo(const std::string &name)
void clearIsPartOfProp(const std::string &automatonName)
bool isEliminable(const std::string &automatonName, std::string const &locationName)
OrderedAssignments executeInSequence(const EdgeDestination &first, const EdgeDestination &then, std::set< std::string > &rewardVariables)
static Model eliminateAutomatically(const Model &model, std::vector< jani::Property > properties, uint64_t locationHeuristic, uint64_t edgesHeuristic)
JaniLocalEliminator(Model const &original, storm::jani::Property &property, bool addMissingGuards=false)
Jani Location:
Definition Location.h:15
std::vector< Automaton > & getAutomata()
Retrieves the automata of the model.
Definition Model.cpp:868
Automaton & getAutomaton(std::string const &name)
Retrieves the automaton with the given name.
Definition Model.cpp:884
void finalize()
After adding all components to the model, this method has to be called.
Definition Model.cpp:1410
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.
std::shared_ptr< storm::logic::Formula const > getRawFormula() const
Definition Property.cpp:92
#define STORM_LOG_WARN(message)
Definition logging.h:28
#define STORM_LOG_TRACE(message)
Definition logging.h:15
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:28