Storm
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 {
12namespace elimination_actions {
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 if (variables.size() != 1) {
93 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Unfolding variables with circular dependencies is currently implemented");
94 }
95 for (const auto &variable : variables) {
96 if (variable.isGlobal) {
97 // We currently always have to specify an automaton name, regardless of whether the
98 // variable is global or not. This isn't really a problem, as there is just one automaton
99 // due to the flattening done previously (and the name of that is stored in autName)
100 STORM_LOG_TRACE("\tUnfolding global variable " + variable.janiVariableName);
101 UnfoldAction unfoldAction(autName, variable.janiVariableName, variable.expressionVariableName);
102 unfoldAction.doAction(session);
103 } else {
104 STORM_LOG_TRACE("\tUnfolding variable " + variable.janiVariableName + " (automaton: " + variable.automatonName + ")");
105 UnfoldAction unfoldAction(variable.automatonName, variable.janiVariableName, variable.expressionVariableName);
106 unfoldAction.doAction(session);
107 }
108 }
109 dependencyGraph.markUnfolded(dependency);
110 }
111}
112
113std::map<std::string, double> AutomaticAction::getAssignmentCountByVariable(JaniLocalEliminator::Session &session, std::string const &automatonName) {
114 std::map<std::string, double> res;
115 auto automaton = session.getModel().getAutomaton(automatonName);
116 for (auto edge : automaton.getEdges()) {
117 size_t numDest = edge.getNumberOfDestinations();
118
119 // The factor is used to ensure all edges contribute equally. Otherwise, a single edge with hundreds of destinations may skew the scores
120 // significantly and lead to a variable being unfolded that isn't used throughout the model (thus creating few eliminable locations)
121 double factor = 1.0 / numDest;
122 for (const auto &dest : edge.getDestinations()) {
123 for (const auto &asg : dest.getOrderedAssignments()) {
124 auto name = asg.getExpressionVariable().getName();
125 if (res.count(name) == 0) {
126 res[name] = 0;
127 }
128 res[name] += factor;
129 }
130 }
131 }
132 return res;
133}
134
135boost::optional<uint32_t> AutomaticAction::chooseNextUnfold(JaniLocalEliminator::Session &session, std::string const &automatonName,
136 UnfoldDependencyGraph &dependencyGraph, bool onlyPropertyVariables) {
137 std::map<std::string, double> variableOccurrenceCounts = getAssignmentCountByVariable(session, automatonName);
138
139 auto propertyVariables = session.getProperty().getUsedVariablesAndConstants();
140 STORM_LOG_TRACE("Choosing next unfold");
141 if (onlyPropertyVariables) {
142 STORM_LOG_TRACE("\tOnly groups containing a variable from the property will be considered");
143 }
144 STORM_LOG_TRACE(dependencyGraph.toString());
145
146 std::set<uint32_t> groupsWithoutDependencies = dependencyGraph.getGroupsWithNoDependencies();
147
148 STORM_LOG_TRACE("\tAnalysing groups without dependencies:");
149 uint32_t bestValue = 0;
150 uint32_t bestGroup = 0;
151 for (auto groupIndex : groupsWithoutDependencies) {
152 bool containsPropertyVariable = false;
153 UnfoldDependencyGraph::VariableGroup &group = dependencyGraph.variableGroups[groupIndex];
154 double totalOccurrences = 0;
155 for (const auto &var : group.variables) {
156 if (variableOccurrenceCounts.count(var.expressionVariableName) > 0) {
157 totalOccurrences += variableOccurrenceCounts[var.expressionVariableName];
158 }
159 for (const auto &propertyVar : propertyVariables) {
160 if (propertyVar.getName() == var.expressionVariableName) {
161 containsPropertyVariable = true;
162 }
163 }
164 }
165 if (onlyPropertyVariables && !containsPropertyVariable) {
166 continue;
167 }
168 STORM_LOG_TRACE("\t\t{" + group.getVariablesAsString() + "}: " + std::to_string(totalOccurrences) + " occurrences");
169 if (dependencyGraph.variableGroups[groupIndex].domainSize < maxDomainSize) {
170 if (totalOccurrences > bestValue) {
171 bestValue = totalOccurrences;
172 bestGroup = groupIndex;
173 }
174 } else {
175 STORM_LOG_TRACE("\t\t\tSkipped (domain size too large)");
176 }
177 }
178
179 if (bestValue == 0) {
180 STORM_LOG_TRACE("No unfoldable variable occurs in any edges.");
181 return boost::none;
182 }
183
184 return bestGroup;
185}
186} // namespace elimination_actions
187} // namespace jani
188} // 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:872
std::size_t getNumberOfAutomata() const
Retrieves the number of automata in this model.
Definition Model.cpp:914
Automaton & getAutomaton(std::string const &name)
Retrieves the automaton with the given name.
Definition Model.cpp:888
void doAction(JaniLocalEliminator::Session &session) override
#define STORM_LOG_TRACE(message)
Definition logging.h:17
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
LabParser.cpp.
Definition cli.cpp:18