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