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