Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
UnfoldDependencyGraph.cpp
Go to the documentation of this file.
2#include <boost/graph/adjacency_list.hpp>
3#include <boost/graph/strong_components.hpp>
4#include <utility>
7
8namespace storm {
9namespace jani {
10namespace elimination_actions {
12
14 variables.push_back(variable);
15 this->domainSize *= variable.domainSize;
16 if (!variable.isConstBoundedInteger) {
18 }
19}
20
22 std::string res = "";
23 for (auto var : variables) {
24 if (res != "") {
25 res += ", ";
26 }
27 res += var.janiVariableName;
28 }
29 return res;
30}
31
33 buildGroups(model);
34}
35
36void UnfoldDependencyGraph::markUnfolded(uint32_t groupIndex) {
37 variableGroups[groupIndex].unfolded = true;
38
39 // Now that one group has been unfolded, update which groups can be unfolded
40 for (uint64_t i = 0; i < variableGroups.size(); i++) {
41 if (variableGroups[i].allDependenciesUnfolded) {
42 continue;
43 }
44 if (variableGroups[i].dependencies.count(i) != 0) {
45 bool allUnfolded = true;
46 for (uint32_t dep : variableGroups[i].dependencies) {
47 if (!variableGroups[dep].unfolded) {
48 allUnfolded = false;
49 }
50 }
51 variableGroups[i].allDependenciesUnfolded = allUnfolded;
52 }
53 }
54}
55
56uint32_t UnfoldDependencyGraph::findGroupIndex(std::string expressionVariableName) {
57 for (uint32_t i = 0; i < variableGroups.size(); i++) {
58 for (auto variable : variableGroups[i].variables) {
59 if (variable.expressionVariableName == expressionVariableName) {
60 return i;
61 }
62 }
63 }
64
65 STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException,
66 "The UnfoldDependencyGraph does not contain the variable " + expressionVariableName + ".");
67}
68
69std::vector<uint32_t> UnfoldDependencyGraph::getOrderedDependencies(uint32_t groupIndex, bool includeSelf) {
70 // This method creates a topological sort of all the dependencies of groupIndex.
71
72 std::vector<uint32_t> res;
73 std::set<uint32_t> closedSet; // Contains the same vertices as res, but provides easier and faster lookup
74
75 while (closedSet.count(groupIndex) == 0) {
76 uint32_t current = groupIndex;
77 bool isLeaf = false;
78 while (!isLeaf) {
79 isLeaf = true;
80 // Find dependency that is not yet listed in res:
81 for (auto dep : variableGroups[current].dependencies) {
82 if (variableGroups[dep].unfolded) {
83 continue;
84 }
85 if (closedSet.count(dep) == 0 && current != dep) {
86 isLeaf = false;
87 current = dep;
88 break;
89 }
90 }
91 }
92 closedSet.insert(current);
93 if (includeSelf || current != groupIndex) {
94 res.push_back(current);
95 }
96 }
97
98 return res;
99}
100
101uint32_t UnfoldDependencyGraph::getTotalBlowup(std::vector<uint32_t> groups) {
102 uint32_t res = 1;
103 for (uint32_t group : groups) {
104 res *= variableGroups[group].domainSize;
105 }
106 return res;
107}
108
110 std::set<uint32_t> res;
111 for (uint64_t i = 0; i < variableGroups.size(); i++) {
112 if (!variableGroups[i].unfolded && variableGroups[i].allVariablesUnfoldable && variableGroups[i].allDependenciesUnfolded) {
113 res.insert(i);
114 }
115 }
116
117 return res;
118}
119
120void UnfoldDependencyGraph::buildGroups(Model &model) {
121 std::vector<std::pair<std::string, VariableSet *>> variableSets;
122 // Use "" for the global set, as automata must not have an empty name according to the Jani spec
123 variableSets.emplace_back("", &model.getGlobalVariables());
124 for (auto &automaton : model.getAutomata()) {
125 variableSets.emplace_back(automaton.getName(), &automaton.getVariables());
126 }
127
128 std::vector<VariableInfo> variables;
129
130 for (const auto &variableSet : variableSets) {
131 for (auto &var : *variableSet.second) {
132 bool isConstBounded = false;
133 if (var.getType().isBoundedType() && var.getType().asBoundedType().isIntegerType()) {
134 auto biVar = var.getType().asBoundedType();
135 if (biVar.hasLowerBound() && biVar.hasUpperBound() && !biVar.getLowerBound().containsVariables() &&
136 !biVar.getUpperBound().containsVariables()) {
137 int lowerBound = biVar.getLowerBound().evaluateAsInt();
138 int upperBound = biVar.getUpperBound().evaluateAsInt();
139
140 variables.emplace_back(var.getExpressionVariable().getName(), var.getName(), variableSet.first == "", variableSet.first, true,
141 upperBound - lowerBound + 1);
142
143 isConstBounded = true;
144 }
145 } else if (var.getType().isBasicType() && var.getType().asBasicType().isBooleanType()) {
146 variables.emplace_back(var.getExpressionVariable().getName(), var.getName(), variableSet.first == "", variableSet.first, true, 2);
147
148 isConstBounded = true;
149 }
150 if (!isConstBounded) {
151 // Still add the variable (so that dependencies are computed correctly). The
152 // "isConstBoundedInteger" parameter ensures that this variable is not unfolded later
153 variables.emplace_back(var.getExpressionVariable().getName(), var.getName(), variableSet.first == "", variableSet.first, false, 0);
154 }
155 }
156 }
157
158 boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS> graph(variables.size());
159
160 for (auto &automaton : model.getAutomata()) {
161 for (auto &janiEdge : automaton.getEdges()) {
162 for (auto &dest : janiEdge.getDestinations()) {
163 for (auto &asg : dest.getOrderedAssignments().getAllAssignments()) {
164 std::string leftName = asg.getExpressionVariable().getName();
165 uint64_t lIndex = std::distance(variables.begin(), std::find_if(variables.begin(), variables.end(), [leftName](VariableInfo &v) {
166 return v.expressionVariableName == leftName;
167 }));
168
169 for (auto &rightVar : asg.getAssignedExpression().getVariables()) {
170 const std::string &rightName = rightVar.getName();
171 uint64_t rIndex = std::distance(variables.begin(), std::find_if(variables.begin(), variables.end(), [rightName](VariableInfo &v) {
172 return v.expressionVariableName == rightName;
173 }));
174 if (rIndex == lIndex) {
175 continue;
176 }
177 if (rIndex !=
178 variables.size()) { // TODO If the condition is false, we're probably dealing with a constant. This should be handled properly.
179 if (!edge(lIndex, rIndex, graph).second) {
180 add_edge(lIndex, rIndex, graph);
181 }
182 }
183 }
184 }
185 }
186 }
187 }
188
189 std::vector<int> sccs(variables.size());
190
191 int num = strong_components(graph, boost::make_iterator_property_map(sccs.begin(), get(boost::vertex_index, graph), sccs[0]));
192
193 for (int i = 0; i < num; i++) {
194 variableGroups.emplace_back();
195 }
196
197 std::vector<int>::iterator i;
198 for (i = sccs.begin(); i != sccs.end(); ++i) {
199 int index = i - sccs.begin();
200 int component = *i;
201 variableGroups[component].addVariable(variables[index]);
202 }
203
204 for (uint64_t i = 0; i < graph.m_vertices.size(); i++) {
205 for (const auto &outEdge : graph.m_vertices[i].m_out_edges) {
206 unsigned long target = findGroupIndex(variables[outEdge.m_target].expressionVariableName);
207 if (variableGroups[sccs[i]].dependencies.count(target) == 0) {
208 variableGroups[sccs[i]].dependencies.insert(target);
209 }
210 }
211 }
212
213 for (uint64_t i = 0; i < variableGroups.size(); i++) {
214 if (variableGroups[i].dependencies.empty()) {
215 variableGroups[i].allDependenciesUnfolded = true;
216 }
217 }
218}
219
228
230 int groupCounter = 0;
231 for (const auto &group : variableGroups) {
232 std::cout << "\nVariable Group " << groupCounter << '\n';
233 if (group.allVariablesUnfoldable) {
234 std::cout << "\tDomain size: " << group.domainSize << '\n';
235 std::cout << "\tCan be unfolded\n";
236 } else {
237 std::cout << "\tCan not be unfolded\n";
238 }
239 std::cout << "\tVariables:\n";
240 for (const auto &var : group.variables) {
241 std::cout << "\t\t" << var.expressionVariableName;
242 if (var.isConstBoundedInteger) {
243 std::cout << " (const-bounded integer with domain size " << var.domainSize << ")\n";
244 } else {
245 std::cout << " (not a const-bounded integer)\n";
246 }
247 }
248 if (group.dependencies.empty()) {
249 std::cout << "\tNo Dependencies\n";
250 } else {
251 std::cout << "\tDependencies:\n";
252 }
253 for (auto dep : group.dependencies) {
254 std::cout << "\t\t" << dep << '\n';
255 }
256
257 groupCounter++;
258 }
259}
260
262 auto dependencies = getOrderedDependencies(groupIndex, false);
263 return std::all_of(dependencies.begin(), dependencies.end(), [this](uint32_t dep) { return this->variableGroups[dep].allVariablesUnfoldable; });
264}
265
267 std::string res;
268
269 for (uint32_t i = 0; i < variableGroups.size(); i++) {
270 auto group = variableGroups[i];
271 std::vector<uint32_t> allDependencies = getOrderedDependencies(i, false);
272
273 res += "{" + group.getVariablesAsString() + "}:";
274 if (!group.dependencies.empty()) {
275 res += "\n\tDepends on ";
276 for (uint32_t dep : group.dependencies) {
277 res += "{" + variableGroups[dep].getVariablesAsString() + "}, ";
278 }
279 if (allDependencies.size() > group.dependencies.size()) {
280 res += "(";
281 for (uint32_t dep : allDependencies) {
282 if (group.dependencies.count(dep) == 0) {
283 res += "{" + variableGroups[dep].getVariablesAsString() + "}, ";
284 }
285 }
286 res = res.substr(0, res.length() - 2); // Remove trailing comma
287 res += ")";
288 } else {
289 res = res.substr(0, res.length() - 2); // Remove trailing comma
290 }
291 }
292 res += "\n\t";
293 if (group.unfolded) {
294 res += "Unfolded\n";
295 } else if (group.allVariablesUnfoldable && areDependenciesUnfoldable(i)) {
296 res += "Can be unfolded\n";
297 } else {
298 res += "Can't be unfolded:\n";
299 for (const auto &var : group.variables) {
300 if (!var.isConstBoundedInteger) {
301 res += "\t\tVariable " + var.expressionVariableName + " is not a const-bounded integer\n";
302 }
303 }
304 for (auto dep : allDependencies) {
305 if (!variableGroups[dep].allVariablesUnfoldable) {
306 res += "\t\tDependency {" + variableGroups[dep].getVariablesAsString() + "} can't be unfolded\n";
307 }
308 }
309 }
310 }
311
312 return res;
313}
314} // namespace elimination_actions
315} // namespace jani
316} // namespace storm
VariableSet & getGlobalVariables()
Retrieves the variables of this automaton.
Definition Model.cpp:717
std::vector< Automaton > & getAutomata()
Retrieves the automata of the model.
Definition Model.cpp:868
VariableInfo(std::string expressionVariableName, std::string janiVariableName, bool isGlobal, std::string automatonName, bool isConstBoundedInteger, int domainSize)
std::vector< uint32_t > getOrderedDependencies(uint32_t groupIndex, bool includeSelf=false)
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:28