Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
InternalSylvanSignatureRefiner.cpp
Go to the documentation of this file.
2
8
9#ifdef STORM_HAVE_SYLVAN
10#include "sylvan_cache.h"
11#endif
12
13namespace storm {
14namespace dd {
15namespace bisimulation {
16
17#ifdef STORM_HAVE_SYLVAN
18#pragma GCC diagnostic push
19#pragma GCC diagnostic ignored "-Wpedantic"
20#pragma clang diagnostic push
21#pragma clang diagnostic ignored "-Watomic-implicit-seq-cst"
22#pragma clang diagnostic ignored "-Wextra-semi-stmt"
23#pragma clang diagnostic ignored "-Wused-but-marked-unused"
24#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
25
26static const uint64_t NO_ELEMENT_MARKER = -1ull;
27
28InternalSylvanSignatureRefinerBase::InternalSylvanSignatureRefinerBase(storm::dd::DdManager<storm::dd::DdType::Sylvan> const& manager,
29 storm::expressions::Variable const& blockVariable,
30 std::set<storm::expressions::Variable> const& stateVariables,
31 storm::dd::Bdd<storm::dd::DdType::Sylvan> const& nondeterminismVariables,
32 storm::dd::Bdd<storm::dd::DdType::Sylvan> const& nonBlockVariables,
35 blockVariable(blockVariable),
36 stateVariables(stateVariables),
37 nondeterminismVariables(nondeterminismVariables),
38 nonBlockVariables(nonBlockVariables),
39 options(options),
40 numberOfBlockVariables(manager.getMetaVariable(blockVariable).getNumberOfDdVariables()),
41 blockCube(manager.getMetaVariable(blockVariable).getCube()),
42 nextFreeBlockIndex(0),
43 numberOfRefinements(0),
44 currentCapacity(1ull << 20),
45 resizeFlag(0) {
46 table.resize(3 * currentCapacity, NO_ELEMENT_MARKER);
47}
48
49template<typename ValueType>
50InternalSignatureRefiner<storm::dd::DdType::Sylvan, ValueType>::InternalSignatureRefiner(
52 std::set<storm::expressions::Variable> const& stateVariables, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& nondeterminismVariables,
53 storm::dd::Bdd<storm::dd::DdType::Sylvan> const& nonBlockVariables, InternalSignatureRefinerOptions const& options)
54 : storm::dd::bisimulation::InternalSylvanSignatureRefinerBase(manager, blockVariable, stateVariables, nondeterminismVariables, nonBlockVariables, options) {
55 // Intentionally left empty.
56}
57
58template<typename ValueType>
59Partition<storm::dd::DdType::Sylvan, ValueType> InternalSignatureRefiner<storm::dd::DdType::Sylvan, ValueType>::refine(
60 Partition<storm::dd::DdType::Sylvan, ValueType> const& oldPartition, Signature<storm::dd::DdType::Sylvan, ValueType> const& signature) {
61 std::pair<storm::dd::Bdd<storm::dd::DdType::Sylvan>, boost::optional<storm::dd::Bdd<storm::dd::DdType::Sylvan>>> newPartitionDds =
62 refine(oldPartition, signature.getSignatureAdd());
63 ++numberOfRefinements;
64
65 return oldPartition.replacePartition(newPartitionDds.first, nextFreeBlockIndex, nextFreeBlockIndex, newPartitionDds.second);
66}
67
68template<typename ValueType>
69void InternalSignatureRefiner<storm::dd::DdType::Sylvan, ValueType>::clearCaches() {
70 for (auto& e : this->table) {
71 e = NO_ELEMENT_MARKER;
72 }
73 for (auto& e : this->signatures) {
74 e = 0ull;
75 }
76}
77
78template<typename ValueType>
79std::pair<storm::dd::Bdd<storm::dd::DdType::Sylvan>, boost::optional<storm::dd::Bdd<storm::dd::DdType::Sylvan>>>
80InternalSignatureRefiner<storm::dd::DdType::Sylvan, ValueType>::refine(Partition<storm::dd::DdType::Sylvan, ValueType> const& oldPartition,
82 STORM_LOG_ASSERT(oldPartition.storedAsBdd(), "Expecting partition to be stored as BDD for Sylvan.");
83
84 nextFreeBlockIndex = options.reuseBlockNumbers ? oldPartition.getNextFreeBlockIndex() : 0;
85 signatures.resize(nextFreeBlockIndex);
86
87 // Perform the actual recursive refinement step.
88 std::pair<BDD, BDD> result(0, 0);
89 result.first =
90 RUN(sylvan_refine_partition, signatureAdd.getInternalAdd().getSylvanMtbdd().GetMTBDD(), oldPartition.asBdd().getInternalBdd().getSylvanBdd().GetBDD(),
91 nondeterminismVariables.getInternalBdd().getSylvanBdd().GetBDD(), nonBlockVariables.getInternalBdd().getSylvanBdd().GetBDD(), this);
92
93 // Construct resulting BDD from the obtained node and the meta information.
94 storm::dd::InternalBdd<storm::dd::DdType::Sylvan> internalNewPartitionBdd(&manager.getInternalDdManager(), sylvan::Bdd(result.first));
95 storm::dd::Bdd<storm::dd::DdType::Sylvan> newPartitionBdd(oldPartition.asBdd().getDdManager(), internalNewPartitionBdd,
96 oldPartition.asBdd().getContainedMetaVariables());
97
98 boost::optional<storm::dd::Bdd<storm::dd::DdType::Sylvan>> optionalChangedBdd;
99 if (options.createChangedStates && result.second != 0) {
100 storm::dd::InternalBdd<storm::dd::DdType::Sylvan> internalChangedBdd(&manager.getInternalDdManager(), sylvan::Bdd(result.second));
101 storm::dd::Bdd<storm::dd::DdType::Sylvan> changedBdd(oldPartition.asBdd().getDdManager(), internalChangedBdd, stateVariables);
102 optionalChangedBdd = changedBdd;
103 }
104
105 clearCaches();
106 return std::make_pair(newPartitionBdd, optionalChangedBdd);
107}
108
109/* Rotating 64-bit FNV-1a hash */
110static uint64_t sylvan_hash(uint64_t a, uint64_t b) {
111 const uint64_t prime = 1099511628211;
112 uint64_t hash = 14695981039346656037LLU;
113 hash = (hash ^ (a >> 32));
114 hash = (hash ^ a) * prime;
115 hash = (hash ^ b) * prime;
116 return hash ^ (hash >> 32);
117}
118
130VOID_TASK_3(sylvan_rehash, size_t, first, size_t, count, InternalSylvanSignatureRefinerBase*, refiner) {
131 if (count > 128) {
132 SPAWN(sylvan_rehash, first, count / 2, refiner);
133 CALL(sylvan_rehash, first + count / 2, count - count / 2, refiner);
134 SYNC(sylvan_rehash);
135 return;
136 }
137
138 while (count--) {
139 uint64_t* old_ptr = refiner->oldTable.data() + first * 3;
140 uint64_t a = old_ptr[0];
141 uint64_t b = old_ptr[1];
142 uint64_t c = old_ptr[2];
143
144 uint64_t hash = sylvan_hash(a, b);
145 uint64_t pos = hash % refiner->currentCapacity;
146
147 volatile uint64_t* ptr = nullptr;
148 for (;;) {
149 ptr = refiner->table.data() + pos * 3;
150 if (*ptr == 0) {
151 if (cas(ptr, 0, a)) {
152 ptr[1] = b;
153 ptr[2] = c;
154 break;
155 }
156 }
157 pos++;
158 if (pos >= refiner->currentCapacity) {
159 pos = 0;
160 }
161 }
162
163 first++;
164 }
165}
166
167VOID_TASK_1(sylvan_grow_it, InternalSylvanSignatureRefinerBase*, refiner) {
168 refiner->oldTable = std::move(refiner->table);
169
170 uint64_t oldCapacity = refiner->currentCapacity;
171 refiner->currentCapacity <<= 1;
172 refiner->table = std::vector<uint64_t>(3 * refiner->currentCapacity, NO_ELEMENT_MARKER);
173
174 CALL(sylvan_rehash, 0, oldCapacity, refiner);
175
176 refiner->oldTable.clear();
177}
178
179VOID_TASK_1(sylvan_grow, InternalSylvanSignatureRefinerBase*, refiner) {
180 if (cas(&refiner->resizeFlag, 0, 1)) {
181 NEWFRAME(sylvan_grow_it, refiner);
182 refiner->resizeFlag = 0;
183 } else {
184 /* wait for new frame to appear */
185 while (ATOMIC_READ(lace_newframe.t) == nullptr) {
186 }
187 lace_yield(__lace_worker, __lace_dq_head);
188 }
189}
190
191static uint64_t sylvan_search_or_insert(uint64_t sig, uint64_t previous_block, InternalSylvanSignatureRefinerBase* refiner) {
192 uint64_t hash = sylvan_hash(sig, previous_block);
193 uint64_t pos = hash % refiner->currentCapacity;
194
195 volatile uint64_t* ptr = nullptr;
196 uint64_t a, b, c;
197 int count = 0;
198 for (;;) {
199 ptr = refiner->table.data() + pos * 3;
200 a = *ptr;
201 if (a == sig) {
202 while ((b = ptr[1]) == NO_ELEMENT_MARKER) {
203 continue;
204 }
205 if (b == previous_block) {
206 while ((c = ptr[2]) == NO_ELEMENT_MARKER) {
207 continue;
208 }
209 return c;
210 }
211 } else if (a == NO_ELEMENT_MARKER) {
212 if (cas(ptr, NO_ELEMENT_MARKER, sig)) {
213 ptr[2] = __sync_fetch_and_add(&refiner->nextFreeBlockIndex, 1);
214 c = ptr[2];
215 ptr[1] = previous_block;
216 return c;
217 } else {
218 continue;
219 }
220 }
221 pos++;
222 if (pos >= refiner->currentCapacity) {
223 pos = 0;
224 }
225 if (++count >= 128) {
226 return NO_ELEMENT_MARKER;
227 }
228 }
229}
230
231TASK_1(uint64_t, sylvan_decode_block, BDD, block) {
232 uint64_t result = 0;
233 uint64_t mask = 1;
234 while (block != sylvan_true) {
235 BDD b_low = sylvan_low(block);
236 if (b_low == sylvan_false) {
237 result |= mask;
238 block = sylvan_high(block);
239 } else {
240 block = b_low;
241 }
242 mask <<= 1;
243 }
244 return result;
245}
246
247TASK_3(BDD, sylvan_encode_block, BDD, vars, uint64_t, numberOfVariables, uint64_t, blockIndex) {
248 std::vector<uint8_t> e(numberOfVariables);
249 for (uint64_t i = 0; i < numberOfVariables; ++i) {
250 e[i] = blockIndex & 1 ? 1 : 0;
251 blockIndex >>= 1;
252 }
253 return sylvan_cube(vars, e.data());
254}
255
256TASK_3(BDD, sylvan_assign_block, BDD, sig, BDD, previous_block, InternalSylvanSignatureRefinerBase*, refiner) {
257 STORM_LOG_ASSERT(previous_block != mtbdd_false, "Incorrect call: previous_block is mtbdd_false.");
258
259 // maybe do garbage collection
260 sylvan_gc_test();
261
262 if (sig == sylvan_false) {
263 // slightly different handling because sylvan_false == 0
264 sig = (uint64_t)-1;
265 }
266
267 if (refiner->options.reuseBlockNumbers) {
268 // try to claim previous block number
269 STORM_LOG_ASSERT(previous_block != sylvan_false, "Previous_block is sylvan_false.");
270 const uint64_t p_b = CALL(sylvan_decode_block, previous_block);
271 STORM_LOG_ASSERT(p_b < refiner->signatures.size(), "Block index out of range.");
272
273 for (;;) {
274 BDD cur = *(volatile BDD*)&refiner->signatures[p_b];
275 if (cur == sig) {
276 return previous_block;
277 }
278 if (cur != 0) {
279 break;
280 }
281 if (cas(&refiner->signatures[p_b], 0, sig)) {
282 return previous_block;
283 }
284 }
285 }
286
287 // no previous block number, search or insert
288 uint64_t c;
289 while ((c = sylvan_search_or_insert(sig, previous_block, refiner)) == NO_ELEMENT_MARKER) {
290 CALL(sylvan_grow, refiner);
291 }
292
293 return CALL(sylvan_encode_block, refiner->blockCube.getInternalBdd().getSylvanBdd().GetBDD(), refiner->numberOfBlockVariables, c);
294}
295
296TASK_5(BDD, sylvan_refine_partition, BDD, dd, BDD, previous_partition, BDD, nondetvars, BDD, vars, InternalSylvanSignatureRefinerBase*, refiner) {
297 /* expecting dd as in s,a,B */
298 /* expecting vars to be conjunction of variables in s */
299 /* expecting previous_partition as in t,B */
300
301 if (previous_partition == sylvan_false) {
302 /* it had no block in the previous iteration, therefore also not now */
303 return sylvan_false;
304 }
305
306 if (sylvan_set_isempty(vars)) {
307 BDD result;
308 if (cache_get(dd | (256LL << 42), vars, previous_partition | (refiner->numberOfRefinements << 40), &result)) {
309 return result;
310 }
311 result = CALL(sylvan_assign_block, dd, previous_partition, refiner);
312 cache_put(dd | (256LL << 42), vars, previous_partition | (refiner->numberOfRefinements << 40), result);
313 return result;
314 }
315
316 sylvan_gc_test();
317
318 /* vars != sylvan_false */
319 /* dd cannot be sylvan_true - if vars != sylvan_true, then dd is in a,B */
320
321 BDDVAR dd_var = sylvan_isconst(dd) ? 0xffffffff : sylvan_var(dd);
322 BDDVAR pp_var = sylvan_var(previous_partition);
323 BDDVAR vars_var = sylvan_var(vars);
324 BDDVAR nondetvars_var = sylvan_isconst(nondetvars) ? 0xffffffff : sylvan_var(nondetvars);
325 bool nondet = nondetvars_var == vars_var;
326 uint64_t offset = (nondet || !refiner->options.shiftStateVariables) ? 0 : 1;
327
328 while (vars_var < dd_var && vars_var + offset < pp_var) {
329 vars = sylvan_set_next(vars);
330 if (nondet) {
331 nondetvars = sylvan_set_next(nondetvars);
332 }
333 if (sylvan_set_isempty(vars)) {
334 return CALL(sylvan_refine_partition, dd, previous_partition, nondetvars, vars, refiner);
335 }
336 vars_var = sylvan_var(vars);
337 if (nondet) {
338 nondetvars_var = sylvan_isconst(nondetvars) ? 0xffffffff : sylvan_var(nondetvars);
339 nondet = nondetvars_var == vars_var;
340 offset = (nondet || !refiner->options.shiftStateVariables) ? 0 : 1;
341 }
342 }
343
344 /* Consult cache */
345 BDD result;
346 if (cache_get(dd | (256LL << 42), vars, previous_partition | (refiner->numberOfRefinements << 40), &result)) {
347 return result;
348 }
349
350 /* Compute cofactors */
351 BDD dd_low, dd_high;
352 if (vars_var == dd_var) {
353 dd_low = sylvan_low(dd);
354 dd_high = sylvan_high(dd);
355 } else {
356 dd_low = dd_high = dd;
357 }
358
359 BDD pp_low, pp_high;
360 if (vars_var + offset == pp_var) {
361 pp_low = sylvan_low(previous_partition);
362 pp_high = sylvan_high(previous_partition);
363 } else {
364 pp_low = pp_high = previous_partition;
365 }
366
367 /* Recursive steps */
368 BDD next_vars = sylvan_set_next(vars);
369 BDD next_nondetvars = nondet ? sylvan_set_next(nondetvars) : nondetvars;
370 bdd_refs_spawn(SPAWN(sylvan_refine_partition, dd_low, pp_low, next_nondetvars, next_vars, refiner));
371 BDD high = bdd_refs_push(CALL(sylvan_refine_partition, dd_high, pp_high, next_nondetvars, next_vars, refiner));
372 BDD low = bdd_refs_sync(SYNC(sylvan_refine_partition));
373 bdd_refs_pop(1);
374
375 /* rename from s to t */
376 result = sylvan_makenode(vars_var + offset, low, high);
377
378 /* Write to cache */
379 cache_put(dd | (256LL << 42), vars, previous_partition | (refiner->numberOfRefinements << 40), result);
380 return result;
381}
382
383#pragma clang diagnostic pop
384#pragma GCC diagnostic pop
385
386#else
388 storm::expressions::Variable const& blockVariable,
389 std::set<storm::expressions::Variable> const& stateVariables,
390 storm::dd::Bdd<storm::dd::DdType::Sylvan> const& nondeterminismVariables,
391 storm::dd::Bdd<storm::dd::DdType::Sylvan> const& nonBlockVariables,
392 InternalSignatureRefinerOptions const& options) {
393 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException,
394 "This version of Storm was compiled without support for Sylvan. Yet, a method was called that requires this support. Please choose a "
395 "version of Storm with Sylvan support.");
396}
397
398template<typename ValueType>
401 std::set<storm::expressions::Variable> const& stateVariables, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& nondeterminismVariables,
402 storm::dd::Bdd<storm::dd::DdType::Sylvan> const& nonBlockVariables, InternalSignatureRefinerOptions const& options)
403 : storm::dd::bisimulation::InternalSylvanSignatureRefinerBase(manager, blockVariable, stateVariables, nondeterminismVariables, nonBlockVariables, options) {
404 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException,
405 "This version of Storm was compiled without support for Sylvan. Yet, a method was called that requires this support. Please choose a "
406 "version of Storm with Sylvan support.");
407}
408
409template<typename ValueType>
412 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException,
413 "This version of Storm was compiled without support for Sylvan. Yet, a method was called that requires this support. Please choose a "
414 "version of Storm with Sylvan support.");
415}
416#endif
417
421
422} // namespace bisimulation
423} // namespace dd
424} // namespace storm
InternalAdd< LibraryType, ValueType > const & getInternalAdd() const
Retrieves the internal ADD.
Definition Add.cpp:1190
InternalBdd< LibraryType > const & getInternalBdd() const
Retrieves the internal BDD.
Definition Bdd.cpp:570
InternalSignatureRefiner(storm::dd::DdManager< storm::dd::DdType::Sylvan > const &manager, storm::expressions::Variable const &blockVariable, std::set< storm::expressions::Variable > const &stateVariables, storm::dd::Bdd< storm::dd::DdType::Sylvan > const &nondeterminismVariables, storm::dd::Bdd< storm::dd::DdType::Sylvan > const &nonBlockVariables, InternalSignatureRefinerOptions const &options)
InternalSylvanSignatureRefinerBase(storm::dd::DdManager< storm::dd::DdType::Sylvan > const &manager, storm::expressions::Variable const &blockVariable, std::set< storm::expressions::Variable > const &stateVariables, storm::dd::Bdd< storm::dd::DdType::Sylvan > const &nondeterminismVariables, storm::dd::Bdd< storm::dd::DdType::Sylvan > const &nonBlockVariables, InternalSignatureRefinerOptions const &options)
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:28
std::pair< storm::RationalNumber, storm::RationalNumber > count(std::vector< storm::storage::BitVector > const &origSets, std::vector< storm::storage::BitVector > const &intersects, std::vector< storm::storage::BitVector > const &intersectsInfo, storm::RationalNumber val, bool plus, uint64_t remdepth)
SettingsManager const & manager()
Retrieves the settings manager.