Storm
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
InternalCuddSignatureRefiner.cpp
Go to the documentation of this file.
2
4
7
8namespace storm {
9namespace dd {
10namespace bisimulation {
11
12template<typename ValueType>
14 storm::expressions::Variable const& blockVariable,
15 std::set<storm::expressions::Variable> const& stateVariables,
16 storm::dd::Bdd<storm::dd::DdType::CUDD> const& nondeterminismVariables,
17 storm::dd::Bdd<storm::dd::DdType::CUDD> const& nonBlockVariables,
19 : manager(manager),
20 ddman(manager.getInternalDdManager().getCuddManager().getManager()),
21 blockVariable(blockVariable),
22 stateVariables(stateVariables),
23 nondeterminismVariables(nondeterminismVariables),
24 nonBlockVariables(nonBlockVariables),
25 options(options),
26 nextFreeBlockIndex(0),
27 numberOfRefinements(0),
28 signatureCache(),
29 reuseBlocksCache() {
30 // Initialize precomputed data.
31 auto const& ddMetaVariable = manager.getMetaVariable(blockVariable);
32 blockDdVariableIndices = ddMetaVariable.getIndices();
33
34 // Create initialized block encoding where all variables are "don't care".
35 blockEncoding = std::vector<int>(static_cast<uint64_t>(manager.getInternalDdManager().getCuddManager().ReadSize()), static_cast<int>(2));
36}
37
38template<typename ValueType>
41 std::pair<storm::dd::Add<storm::dd::DdType::CUDD, ValueType>, boost::optional<storm::dd::Add<storm::dd::DdType::CUDD, ValueType>>> newPartitionDds =
42 refine(oldPartition, signature.getSignatureAdd());
43 ;
44 ++numberOfRefinements;
45 return oldPartition.replacePartition(newPartitionDds.first, nextFreeBlockIndex, nextFreeBlockIndex, newPartitionDds.second);
46}
47
48template<typename ValueType>
50 signatureCache.clear();
51 reuseBlocksCache.clear();
52}
53
54template<typename ValueType>
55std::pair<storm::dd::Add<storm::dd::DdType::CUDD, ValueType>, boost::optional<storm::dd::Add<storm::dd::DdType::CUDD, ValueType>>>
58 STORM_LOG_ASSERT(oldPartition.storedAsAdd(), "Expecting partition to be stored as ADD for CUDD.");
59
60 nextFreeBlockIndex = options.reuseBlockNumbers ? oldPartition.getNextFreeBlockIndex() : 0;
61
62 // Perform the actual recursive refinement step.
63 std::pair<DdNodePtr, DdNodePtr> result =
64 refine(oldPartition.asAdd().getInternalAdd().getCuddDdNode(), signatureAdd.getInternalAdd().getCuddDdNode(),
65 nondeterminismVariables.getInternalBdd().getCuddDdNode(), nonBlockVariables.getInternalBdd().getCuddDdNode());
66
67 // Construct resulting ADD from the obtained node and the meta information.
69 &manager.getInternalDdManager(), cudd::ADD(manager.getInternalDdManager().getCuddManager(), result.first));
70 storm::dd::Add<storm::dd::DdType::CUDD, ValueType> newPartitionAdd(oldPartition.asAdd().getDdManager(), internalNewPartitionAdd,
71 oldPartition.asAdd().getContainedMetaVariables());
72
73 boost::optional<storm::dd::Add<storm::dd::DdType::CUDD, ValueType>> optionalChangedAdd;
74 if (result.second) {
76 &manager.getInternalDdManager(), cudd::ADD(manager.getInternalDdManager().getCuddManager(), result.second));
77 storm::dd::Add<storm::dd::DdType::CUDD, ValueType> changedAdd(oldPartition.asAdd().getDdManager(), internalChangedAdd, stateVariables);
78 optionalChangedAdd = changedAdd;
79 }
80
81 clearCaches();
82 return std::make_pair(newPartitionAdd, optionalChangedAdd);
83}
84
85template<typename ValueType>
86DdNodePtr InternalSignatureRefiner<storm::dd::DdType::CUDD, ValueType>::encodeBlock(uint64_t blockIndex) {
87 for (auto const& blockDdVariableIndex : blockDdVariableIndices) {
88 blockEncoding[blockDdVariableIndex] = blockIndex & 1 ? 1 : 0;
89 blockIndex >>= 1;
90 }
91 DdNodePtr bddEncoding = Cudd_CubeArrayToBdd(ddman, blockEncoding.data());
92 Cudd_Ref(bddEncoding);
93 DdNodePtr result = Cudd_BddToAdd(ddman, bddEncoding);
94 Cudd_Ref(result);
95 Cudd_RecursiveDeref(ddman, bddEncoding);
96 Cudd_Deref(result);
97 return result;
98}
99
100template<typename ValueType>
101std::pair<DdNodePtr, DdNodePtr> InternalSignatureRefiner<storm::dd::DdType::CUDD, ValueType>::reuseOrRelabel(DdNode* partitionNode,
102 DdNode* nondeterminismVariablesNode,
103 DdNode* nonBlockVariablesNode) {
104 // If we arrived at the constant zero node, then this was an illegal state encoding.
105 if (partitionNode == Cudd_ReadZero(ddman)) {
106 if (options.createChangedStates) {
107 return std::make_pair(partitionNode, Cudd_ReadZero(ddman));
108 } else {
109 return std::make_pair(partitionNode, nullptr);
110 }
111 }
112
113 // Check the cache whether we have seen the same node before.
114 auto nodePair = std::make_pair(nullptr, partitionNode);
115
116 auto it = signatureCache.find(nodePair);
117 if (it != signatureCache.end()) {
118 // If so, we return the corresponding result.
119 return it->second;
120 }
121
122 // If there are no more non-block variables, we hit the signature.
123 if (Cudd_IsConstant(nonBlockVariablesNode)) {
124 if (options.reuseBlockNumbers) {
125 // If this is the first time (in this traversal) that we encounter this signature, we check
126 // whether we can assign the old block number to it.
127 auto& reuseEntry = reuseBlocksCache[partitionNode];
128 if (!reuseEntry.isReused()) {
129 reuseEntry.setReused();
130 std::pair<DdNodePtr, DdNodePtr> result = std::make_pair(partitionNode, options.createChangedStates ? Cudd_ReadZero(ddman) : nullptr);
131 signatureCache[nodePair] = result;
132 return result;
133 }
134 }
135
136 std::pair<DdNodePtr, DdNodePtr> result = std::make_pair(encodeBlock(nextFreeBlockIndex++), options.createChangedStates ? Cudd_ReadOne(ddman) : nullptr);
137 signatureCache[nodePair] = result;
138 return result;
139 } else {
140 // If there are more variables that belong to the non-block part of the encoding, we need to recursively descend.
141
142 bool skipped = true;
143 DdNode* partitionThen;
144 DdNode* partitionElse;
145 short offset;
146 bool isNondeterminismVariable;
147 while (skipped && !Cudd_IsConstant(nonBlockVariablesNode)) {
148 // Remember an offset that indicates whether the top variable is a nondeterminism variable or not.
149 offset = options.shiftStateVariables ? 1 : 0;
150 isNondeterminismVariable = false;
151 if (!Cudd_IsConstant(nondeterminismVariablesNode) && Cudd_NodeReadIndex(nondeterminismVariablesNode) == Cudd_NodeReadIndex(nonBlockVariablesNode)) {
152 offset = 0;
153 isNondeterminismVariable = true;
154 }
155
156 if (Cudd_NodeReadIndex(partitionNode) - offset == Cudd_NodeReadIndex(nonBlockVariablesNode)) {
157 partitionThen = Cudd_T(partitionNode);
158 partitionElse = Cudd_E(partitionNode);
159 skipped = false;
160 } else {
161 partitionThen = partitionElse = partitionNode;
162 }
163
164 // If we skipped the next variable, we fast-forward.
165 if (skipped) {
166 // If the current variable is a nondeterminism variable, we need to advance both variable sets otherwise just the non-block variables.
167 nonBlockVariablesNode = Cudd_T(nonBlockVariablesNode);
168 if (isNondeterminismVariable) {
169 nondeterminismVariablesNode = Cudd_T(nondeterminismVariablesNode);
170 }
171 }
172 }
173
174 // If there are no more non-block variables remaining, make a recursive call to enter the base case.
175 if (Cudd_IsConstant(nonBlockVariablesNode)) {
176 return reuseOrRelabel(partitionNode, nondeterminismVariablesNode, nonBlockVariablesNode);
177 }
178
179 std::pair<DdNodePtr, DdNodePtr> combinedThenResult = reuseOrRelabel(
180 partitionThen, isNondeterminismVariable ? Cudd_T(nondeterminismVariablesNode) : nondeterminismVariablesNode, Cudd_T(nonBlockVariablesNode));
181 DdNodePtr thenResult = combinedThenResult.first;
182 DdNodePtr changedThenResult = combinedThenResult.second;
183 Cudd_Ref(thenResult);
184 if (options.createChangedStates) {
185 Cudd_Ref(changedThenResult);
186 } else {
187 STORM_LOG_ASSERT(!changedThenResult, "Expected not changed state DD.");
188 }
189 std::pair<DdNodePtr, DdNodePtr> combinedElseResult = reuseOrRelabel(
190 partitionElse, isNondeterminismVariable ? Cudd_T(nondeterminismVariablesNode) : nondeterminismVariablesNode, Cudd_T(nonBlockVariablesNode));
191 DdNodePtr elseResult = combinedElseResult.first;
192 DdNodePtr changedElseResult = combinedElseResult.second;
193 Cudd_Ref(elseResult);
194 if (options.createChangedStates) {
195 Cudd_Ref(changedElseResult);
196 } else {
197 STORM_LOG_ASSERT(!changedThenResult, "Expected not changed state DD.");
198 }
199
200 DdNodePtr result;
201 if (thenResult == elseResult) {
202 Cudd_Deref(thenResult);
203 Cudd_Deref(elseResult);
204 result = thenResult;
205 } else {
206 // Get the node to connect the subresults.
207 bool complemented = Cudd_IsComplement(thenResult);
208 result = cuddUniqueInter(ddman, Cudd_NodeReadIndex(nonBlockVariablesNode) + offset, Cudd_Regular(thenResult),
209 complemented ? Cudd_Not(elseResult) : elseResult);
210 if (complemented) {
211 result = Cudd_Not(result);
212 }
213 Cudd_Deref(thenResult);
214 Cudd_Deref(elseResult);
215 }
216
217 DdNodePtr changedResult = nullptr;
218 if (options.createChangedStates) {
219 if (changedThenResult == changedElseResult) {
220 Cudd_Deref(changedThenResult);
221 Cudd_Deref(changedElseResult);
222 changedResult = changedThenResult;
223 } else {
224 // Get the node to connect the subresults.
225 bool complemented = Cudd_IsComplement(changedThenResult);
226 changedResult = cuddUniqueInter(ddman, Cudd_NodeReadIndex(nonBlockVariablesNode) + offset, Cudd_Regular(changedThenResult),
227 complemented ? Cudd_Not(changedElseResult) : changedElseResult);
228 if (complemented) {
229 changedResult = Cudd_Not(changedResult);
230 }
231 Cudd_Deref(changedThenResult);
232 Cudd_Deref(changedElseResult);
233 }
234 }
235
236 // Store the result in the cache.
237 auto pairResult = std::make_pair(result, changedResult);
238 signatureCache[nodePair] = pairResult;
239 return pairResult;
240 }
241}
242
243template<typename ValueType>
244std::pair<DdNodePtr, DdNodePtr> InternalSignatureRefiner<storm::dd::DdType::CUDD, ValueType>::refine(DdNode* partitionNode, DdNode* signatureNode,
245 DdNode* nondeterminismVariablesNode,
246 DdNode* nonBlockVariablesNode) {
247 // If we arrived at the constant zero node, then this was an illegal state encoding.
248 if (partitionNode == Cudd_ReadZero(ddman)) {
249 if (options.createChangedStates) {
250 return std::make_pair(partitionNode, Cudd_ReadZero(ddman));
251 } else {
252 return std::make_pair(partitionNode, nullptr);
253 }
254 } else if (signatureNode == Cudd_ReadZero(ddman)) {
255 std::pair<DdNodePtr, DdNodePtr> result = reuseOrRelabel(partitionNode, nondeterminismVariablesNode, nonBlockVariablesNode);
256 signatureCache[std::make_pair(signatureNode, partitionNode)] = result;
257 return result;
258 }
259
260 // Check the cache whether we have seen the same node before.
261 auto nodePair = std::make_pair(signatureNode, partitionNode);
262
263 auto it = signatureCache.find(nodePair);
264 if (it != signatureCache.end()) {
265 // If so, we return the corresponding result.
266 return it->second;
267 }
268
269 // If there are no more non-block variables, we hit the signature.
270 if (Cudd_IsConstant(nonBlockVariablesNode)) {
271 if (options.reuseBlockNumbers) {
272 // If this is the first time (in this traversal) that we encounter this signature, we check
273 // whether we can assign the old block number to it.
274 auto& reuseEntry = reuseBlocksCache[partitionNode];
275 if (!reuseEntry.isReused()) {
276 reuseEntry.setReused();
277 std::pair<DdNodePtr, DdNodePtr> result = std::make_pair(partitionNode, options.createChangedStates ? Cudd_ReadZero(ddman) : nullptr);
278 signatureCache[nodePair] = result;
279 return result;
280 }
281 }
282
283 std::pair<DdNodePtr, DdNodePtr> result = std::make_pair(encodeBlock(nextFreeBlockIndex++), options.createChangedStates ? Cudd_ReadOne(ddman) : nullptr);
284 signatureCache[nodePair] = result;
285 return result;
286 } else {
287 // If there are more variables that belong to the non-block part of the encoding, we need to recursively descend.
288
289 bool skippedBoth = true;
290 DdNode* partitionThen;
291 DdNode* partitionElse;
292 DdNode* signatureThen;
293 DdNode* signatureElse;
294 short offset;
295 bool isNondeterminismVariable;
296 while (skippedBoth && !Cudd_IsConstant(nonBlockVariablesNode)) {
297 // Remember an offset that indicates whether the top variable is a nondeterminism variable or not.
298 offset = options.shiftStateVariables ? 1 : 0;
299 isNondeterminismVariable = false;
300 if (!Cudd_IsConstant(nondeterminismVariablesNode) && Cudd_NodeReadIndex(nondeterminismVariablesNode) == Cudd_NodeReadIndex(nonBlockVariablesNode)) {
301 offset = 0;
302 isNondeterminismVariable = true;
303 }
304
305 if (Cudd_NodeReadIndex(partitionNode) - offset == Cudd_NodeReadIndex(nonBlockVariablesNode)) {
306 partitionThen = Cudd_T(partitionNode);
307 partitionElse = Cudd_E(partitionNode);
308 skippedBoth = false;
309 } else {
310 partitionThen = partitionElse = partitionNode;
311 }
312
313 if (Cudd_NodeReadIndex(signatureNode) == Cudd_NodeReadIndex(nonBlockVariablesNode)) {
314 signatureThen = Cudd_T(signatureNode);
315 signatureElse = Cudd_E(signatureNode);
316 skippedBoth = false;
317 } else {
318 signatureThen = signatureElse = signatureNode;
319 }
320
321 // If both (signature and partition) skipped the next variable, we fast-forward.
322 if (skippedBoth) {
323 // If the current variable is a nondeterminism variable, we need to advance both variable sets otherwise just the non-block variables.
324 nonBlockVariablesNode = Cudd_T(nonBlockVariablesNode);
325 if (isNondeterminismVariable) {
326 nondeterminismVariablesNode = Cudd_T(nondeterminismVariablesNode);
327 }
328 }
329 }
330
331 // If there are no more non-block variables remaining, make a recursive call to enter the base case.
332 if (Cudd_IsConstant(nonBlockVariablesNode)) {
333 return refine(partitionNode, signatureNode, nondeterminismVariablesNode, nonBlockVariablesNode);
334 }
335
336 std::pair<DdNodePtr, DdNodePtr> combinedThenResult =
337 refine(partitionThen, signatureThen, isNondeterminismVariable ? Cudd_T(nondeterminismVariablesNode) : nondeterminismVariablesNode,
338 Cudd_T(nonBlockVariablesNode));
339 DdNodePtr thenResult = combinedThenResult.first;
340 DdNodePtr changedThenResult = combinedThenResult.second;
341 Cudd_Ref(thenResult);
342 if (options.createChangedStates) {
343 Cudd_Ref(changedThenResult);
344 } else {
345 STORM_LOG_ASSERT(!changedThenResult, "Expected not changed state DD.");
346 }
347 std::pair<DdNodePtr, DdNodePtr> combinedElseResult =
348 refine(partitionElse, signatureElse, isNondeterminismVariable ? Cudd_T(nondeterminismVariablesNode) : nondeterminismVariablesNode,
349 Cudd_T(nonBlockVariablesNode));
350 DdNodePtr elseResult = combinedElseResult.first;
351 DdNodePtr changedElseResult = combinedElseResult.second;
352 Cudd_Ref(elseResult);
353 if (options.createChangedStates) {
354 Cudd_Ref(changedElseResult);
355 } else {
356 STORM_LOG_ASSERT(!changedThenResult, "Expected not changed state DD.");
357 }
358
359 DdNodePtr result;
360 if (thenResult == elseResult) {
361 Cudd_Deref(thenResult);
362 Cudd_Deref(elseResult);
363 result = thenResult;
364 } else {
365 // Get the node to connect the subresults.
366 bool complemented = Cudd_IsComplement(thenResult);
367 result = cuddUniqueInter(ddman, Cudd_NodeReadIndex(nonBlockVariablesNode) + offset, Cudd_Regular(thenResult),
368 complemented ? Cudd_Not(elseResult) : elseResult);
369 if (complemented) {
370 result = Cudd_Not(result);
371 }
372 Cudd_Deref(thenResult);
373 Cudd_Deref(elseResult);
374 }
375
376 DdNodePtr changedResult = nullptr;
377 if (options.createChangedStates) {
378 if (changedThenResult == changedElseResult) {
379 Cudd_Deref(changedThenResult);
380 Cudd_Deref(changedElseResult);
381 changedResult = changedThenResult;
382 } else {
383 // Get the node to connect the subresults.
384 bool complemented = Cudd_IsComplement(changedThenResult);
385 changedResult = cuddUniqueInter(ddman, Cudd_NodeReadIndex(nonBlockVariablesNode) + offset, Cudd_Regular(changedThenResult),
386 complemented ? Cudd_Not(changedElseResult) : changedElseResult);
387 if (complemented) {
388 changedResult = Cudd_Not(changedResult);
389 }
390 Cudd_Deref(changedThenResult);
391 Cudd_Deref(changedElseResult);
392 }
393 }
394
395 // Store the result in the cache.
396 auto pairResult = std::make_pair(result, changedResult);
397 signatureCache[nodePair] = pairResult;
398 return pairResult;
399 }
400}
401
402template class InternalSignatureRefiner<storm::dd::DdType::CUDD, double>;
403
404} // namespace bisimulation
405} // namespace dd
406} // namespace storm
InternalAdd< LibraryType, ValueType > const & getInternalAdd() const
Retrieves the internal ADD.
Definition Add.cpp:1197
std::set< storm::expressions::Variable > const & getContainedMetaVariables() const
Retrieves the set of all meta variables contained in the DD.
Definition Dd.cpp:29
DdManager< LibraryType > & getDdManager() const
Retrieves the manager that is responsible for this DD.
Definition Dd.cpp:39
Partition< storm::dd::DdType::CUDD, ValueType > refine(Partition< storm::dd::DdType::CUDD, ValueType > const &oldPartition, Signature< storm::dd::DdType::CUDD, ValueType > const &signature)
storm::dd::Add< DdType, ValueType > const & asAdd() const
Partition< DdType, ValueType > replacePartition(storm::dd::Add< DdType, ValueType > const &newPartitionAdd, uint64_t numberOfBlocks, uint64_t nextFreeBlockIndex, boost::optional< storm::dd::Add< DdType, ValueType > > const &changedStates=boost::none) const
Definition Partition.cpp:67
storm::dd::Add< DdType, ValueType > const & getSignatureAdd() const
Definition Signature.cpp:13
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
LabParser.cpp.
Definition cli.cpp:18