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