Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
AutomaticAction.cpp
Go to the documentation of this file.
2
3#include <boost/graph/strong_components.hpp>
9
10namespace storm {
11namespace jani {
13AutomaticAction::AutomaticAction() : locationLimit(30), newTransitionLimit(20000), maxDomainSize(100), flatten(true) {}
14
15AutomaticAction::AutomaticAction(uint64_t locationLimit, uint64_t newTransitionLimit, uint64_t maxDomainSize, bool flatten)
16 : locationLimit(locationLimit), newTransitionLimit(newTransitionLimit), maxDomainSize(maxDomainSize), flatten(flatten) {}
17
19 return "AutomaticAction";
20}
21
23 if (flatten) {
24 if (session.getModel().getAutomata().size() > 1) {
25 STORM_LOG_TRACE("Flattening model");
26 session.flatten_automata();
27 }
28 std::string const &autName = session.getModel().getAutomata()[0].getName();
29 processAutomaton(session, autName);
30 } else {
31 for (uint64_t i = 0; i < session.getModel().getNumberOfAutomata(); i++) {
32 std::string const &autName = session.getModel().getAutomata()[i].getName();
33 STORM_LOG_TRACE("Processing automaton " + autName);
34 processAutomaton(session, autName);
35 }
36 }
37
38 STORM_LOG_TRACE("Finished automatic state-space reduction.");
39 if (session.getModel().getNumberOfAutomata() == 1) {
40 STORM_LOG_TRACE("Final model size: " + std::to_string(session.getModel().getAutomaton(0).getNumberOfEdges()) + " edges, " +
41 std::to_string(session.getModel().getAutomaton(0).getNumberOfLocations()) + " locations");
42 }
43}
44
45void AutomaticAction::processAutomaton(JaniLocalEliminator::Session &session, const std::string &autName) {
46 bool isOnlyAutomaton = session.getModel().getNumberOfAutomata() == 1;
47 STORM_LOG_TRACE("Generating variable dependency graph");
48 UnfoldDependencyGraph dependencyGraph(session.getModel());
49 STORM_LOG_TRACE(dependencyGraph.toString());
50
51 auto nextUnfold = chooseNextUnfold(session, autName, dependencyGraph, true);
52 if (!nextUnfold) {
53 STORM_LOG_TRACE("No property variable can be unfolded.");
54 return;
55 }
56 unfoldGroupAndDependencies(session, autName, dependencyGraph, nextUnfold.get());
57 STORM_LOG_TRACE("Performing automatic elimination");
58
59 EliminateAutomaticallyAction eliminatePropertyAction(autName, EliminateAutomaticallyAction::EliminationOrder::NewTransitionCount, newTransitionLimit,
60 !isOnlyAutomaton);
61 eliminatePropertyAction.doAction(session);
62
63 RebuildWithoutUnreachableAction rebuildAfterPropertyAction;
64 rebuildAfterPropertyAction.doAction(session);
65
66 while (session.getModel().getAutomaton(0).getLocations().size() < locationLimit) {
67 nextUnfold = chooseNextUnfold(session, autName, dependencyGraph, false);
68 if (!nextUnfold) {
69 break;
70 }
71
72 unfoldGroupAndDependencies(session, autName, dependencyGraph, nextUnfold.get());
73
74 RebuildWithoutUnreachableAction rebuildAfterUnfoldingAction;
75 rebuildAfterUnfoldingAction.doAction(session);
76
77 EliminateAutomaticallyAction eliminateAction(autName, EliminateAutomaticallyAction::EliminationOrder::NewTransitionCount, newTransitionLimit,
78 !isOnlyAutomaton);
79 eliminateAction.doAction(session);
80
81 RebuildWithoutUnreachableAction rebuildAfterEliminationAction;
82 rebuildAfterEliminationAction.doAction(session);
83 }
84}
85
86void AutomaticAction::unfoldGroupAndDependencies(JaniLocalEliminator::Session &session, const std::string &autName, UnfoldDependencyGraph &dependencyGraph,
87 uint32_t groupIndex) {
88 auto orderedDependencies = dependencyGraph.getOrderedDependencies(groupIndex, true);
89 STORM_LOG_TRACE("Unfolding " + dependencyGraph.variableGroups[groupIndex].getVariablesAsString() + " and their dependencies");
90 for (auto dependency : orderedDependencies) {
91 auto variables = dependencyGraph.variableGroups[dependency].variables;
92 STORM_LOG_THROW(variables.size() == 1, storm::exceptions::NotImplementedException,
93 "Unfolding variables with circular dependencies is currently not implemented.");
94 for (const auto &variable : variables) {
95 if (variable.isGlobal) {
96 // We currently always have to specify an automaton name, regardless of whether the
97 // variable is global or not. This isn't really a problem, as there is just one automaton
98 // due to the flattening done previously (and the name of that is stored in autName)
99 STORM_LOG_TRACE("\tUnfolding global variable " + variable.janiVariableName);
100 UnfoldAction unfoldAction(autName, variable.janiVariableName, variable.expressionVariableName);
101 unfoldAction.doAction(session);
102 } else {
103 STORM_LOG_TRACE("\tUnfolding variable " + variable.janiVariableName + " (automaton: " + variable.automatonName + ")");
104 UnfoldAction unfoldAction(variable.automatonName, variable.janiVariableName, variable.expressionVariableName);
105 unfoldAction.doAction(session);
106 }
107 }
108 dependencyGraph.markUnfolded(dependency);
109 }
110}
111
112std::map<std::string, double> AutomaticAction::getAssignmentCountByVariable(JaniLocalEliminator::Session &session, std::string const &automatonName) {
113 std::map<std::string, double> res;
114 auto automaton = session.getModel().getAutomaton(automatonName);
115 for (auto edge : automaton.getEdges()) {
116 size_t numDest = edge.getNumberOfDestinations();
117
118 // The factor is used to ensure all edges contribute equally. Otherwise, a single edge with hundreds of destinations may skew the scores
119 // significantly and lead to a variable being unfolded that isn't used throughout the model (thus creating few eliminable locations)
120 double factor = 1.0 / numDest;
121 for (const auto &dest : edge.getDestinations()) {
122 for (const auto &asg : dest.getOrderedAssignments()) {
123 auto name = asg.getExpressionVariable().getName();
124 if (res.count(name) == 0) {
125 res[name] = 0;
126 }
127 res[name] += factor;
128 }
129 }
130 }
131 return res;
132}
133
134boost::optional<uint32_t> AutomaticAction::chooseNextUnfold(JaniLocalEliminator::Session &session, std::string const &automatonName,
135 UnfoldDependencyGraph &dependencyGraph, bool onlyPropertyVariables) {
136 std::map<std::string, double> variableOccurrenceCounts = getAssignmentCountByVariable(session, automatonName);
137
138 auto propertyVariables = session.getProperty().getUsedVariablesAndConstants();
139 STORM_LOG_TRACE("Choosing next unfold");
140 if (onlyPropertyVariables) {
141 STORM_LOG_TRACE("\tOnly groups containing a variable from the property will be considered");
142 }
143 STORM_LOG_TRACE(dependencyGraph.toString());
144
145 std::set<uint32_t> groupsWithoutDependencies = dependencyGraph.getGroupsWithNoDependencies();
146
147 STORM_LOG_TRACE("\tAnalysing groups without dependencies:");
148 uint32_t bestValue = 0;
149 uint32_t bestGroup = 0;
150 for (auto groupIndex : groupsWithoutDependencies) {
151 bool containsPropertyVariable = false;
152 UnfoldDependencyGraph::VariableGroup &group = dependencyGraph.variableGroups[groupIndex];
153 double totalOccurrences = 0;
154 for (const auto &var : group.variables) {
155 if (variableOccurrenceCounts.count(var.expressionVariableName) > 0) {
156 totalOccurrences += variableOccurrenceCounts[var.expressionVariableName];
157 }
158 for (const auto &propertyVar : propertyVariables) {
159 if (propertyVar.getName() == var.expressionVariableName) {
160 containsPropertyVariable = true;
161 }
162 }
163 }
164 if (onlyPropertyVariables && !containsPropertyVariable) {
165 continue;
166 }
167 STORM_LOG_TRACE("\t\t{" + group.getVariablesAsString() + "}: " + std::to_string(totalOccurrences) + " occurrences");
168 if (dependencyGraph.variableGroups[groupIndex].domainSize < maxDomainSize) {
169 if (totalOccurrences > bestValue) {
170 bestValue = totalOccurrences;
171 bestGroup = groupIndex;
172 }
173 } else {
174 STORM_LOG_TRACE("\t\t\tSkipped (domain size too large)");
175 }
176 }
177
178 if (bestValue == 0) {
179 STORM_LOG_TRACE("No unfoldable variable occurs in any edges.");
180 return boost::none;
181 }
182
183 return bestGroup;
184}
185} // namespace elimination_actions
186} // namespace jani
187} // namespace storm
uint64_t getNumberOfEdges() const
Retrieves the number of edges.
std::vector< Location > const & getLocations() const
Retrieves the locations of the automaton.
Definition Automaton.cpp:98
uint64_t getNumberOfLocations() const
Retrieves the number of locations.
std::vector< Automaton > & getAutomata()
Retrieves the automata of the model.
Definition Model.cpp:868
std::size_t getNumberOfAutomata() const
Retrieves the number of automata in this model.
Definition Model.cpp:910
Automaton & getAutomaton(std::string const &name)
Retrieves the automaton with the given name.
Definition Model.cpp:884
void doAction(JaniLocalEliminator::Session &session) override
#define STORM_LOG_TRACE(message)
Definition logging.h:15
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:28