spot 2.16
Loading...
Searching...
No Matches
bloemen_ec.hh
1// -*- coding: utf-8 -*-
2// Copyright (C) by the Spot authors, see the AUTHORS file for details.
3//
4// This file is part of Spot, a model checking library.
5//
6// Spot is free software; you can redistribute it and/or modify it
7// under the terms of the GNU General Public License as published by
8// the Free Software Foundation; either version 3 of the License, or
9// (at your option) any later version.
10//
11// Spot is distributed in the hope that it will be useful, but WITHOUT
12// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14// License for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19#pragma once
20
21#include <atomic>
22#include <chrono>
23#include <spot/bricks/brick-hashset>
24#include <stdlib.h>
25#include <thread>
26#include <vector>
27#include <utility>
28#include <spot/misc/common.hh>
29#include <spot/kripke/kripke.hh>
30#include <spot/misc/fixpool.hh>
31#include <spot/misc/timer.hh>
32#include <spot/twacube/twacube.hh>
33#include <spot/twacube/fwd.hh>
34#include <spot/mc/intersect.hh>
35#include <spot/mc/mc.hh>
36
37namespace spot
38{
41 template<typename State,
42 typename StateHash,
43 typename StateEqual>
45 {
46
47 public:
49 enum class uf_status { LIVE, LOCK, DEAD };
51 enum class list_status { BUSY, LOCK, DONE };
53 enum class claim_status { CLAIM_FOUND, CLAIM_NEW, CLAIM_DEAD };
54
57 {
59 State st_kripke;
61 unsigned st_prop;
65 std::mutex acc_mutex_;
67 std::atomic<uf_element*> parent;
69 std::atomic<unsigned> worker_;
71 std::atomic<uf_element*> next_;
73 std::atomic<uf_status> uf_status_;
75 std::atomic<list_status> list_status_;
76 };
77
80 {
84
86 uf_element_hasher() = default;
87
89 brick::hash::hash128_t
90 hash(const uf_element* lhs) const
91 {
92 StateHash hash;
93 // Not modulo 31 according to brick::hashset specifications.
94 unsigned u = hash(lhs->st_kripke) % (1<<30);
95 u = wang32_hash(lhs->st_prop) ^ u;
96 u = u % (1<<30);
97 return {u, u};
98 }
99
101 bool equal(const uf_element* lhs,
102 const uf_element* rhs) const
103 {
104 StateEqual equal;
105 return (lhs->st_prop == rhs->st_prop)
106 && equal(lhs->st_kripke, rhs->st_kripke);
107 }
108 };
109
111 using shared_map = brick::hashset::FastConcurrent <uf_element*,
113
116 map_(uf.map_), tid_(uf.tid_), size_(std::thread::hardware_concurrency()),
117 nb_th_(std::thread::hardware_concurrency()), inserted_(0),
118 p_(sizeof(uf_element))
119 { }
120
122 iterable_uf_ec(shared_map& map, unsigned tid):
123 map_(map), tid_(tid), size_(std::thread::hardware_concurrency()),
124 nb_th_(std::thread::hardware_concurrency()), inserted_(0),
125 p_(sizeof(uf_element))
126 { }
127
130
132 std::pair<claim_status, uf_element*>
133 make_claim(State kripke, unsigned prop)
134 {
135 unsigned w_id = (1U << tid_);
136
137 // Setup and try to insert the new state in the shared map.
138 uf_element* v = (uf_element*) p_.allocate();
139 new (v) (uf_element); // required, otherwise the mutex is uninitialized
140 v->st_kripke = kripke;
141 v->st_prop = prop;
142 v->acc = {};
143 v->parent = v;
144 v->next_ = v;
145 v->worker_ = 0;
146 v->uf_status_ = uf_status::LIVE;
147 v->list_status_ = list_status::BUSY;
148
149 auto it = map_.insert({v});
150 bool b = it.isnew();
151
152 // Insertion failed, delete element
153 // FIXME Should we add a local cache to avoid useless allocations?
154 if (!b)
155 p_.deallocate(v);
156 else
157 ++inserted_;
158
159 uf_element* a_root = find(*it);
160 if (a_root->uf_status_.load() == uf_status::DEAD)
161 return {claim_status::CLAIM_DEAD, *it};
162
163 if ((a_root->worker_.load() & w_id) != 0)
164 return {claim_status::CLAIM_FOUND, *it};
165
166 atomic_fetch_or(&(a_root->worker_), w_id);
167 while (a_root->parent.load() != a_root)
168 {
169 a_root = find(a_root);
170 atomic_fetch_or(&(a_root->worker_), w_id);
171 }
172
173 return {claim_status::CLAIM_NEW, *it};
174 }
175
178 {
179 uf_element* parent = a->parent.load();
180 uf_element* x = a;
181 uf_element* y;
182
183 while (x != parent)
184 {
185 y = parent;
186 parent = y->parent.load();
187 if (parent == y)
188 return y;
189 x->parent.store(parent);
190 x = parent;
191 parent = x->parent.load();
192 }
193 return x;
194 }
195
198 {
199 while (true)
200 {
201 uf_element* a_root = find(a);
202 uf_element* b_root = find(b);
203 if (a_root == b_root)
204 return true;
205
206 if (a_root->parent.load() == a_root)
207 return false;
208 }
209 }
210
213 {
214 uf_status expected = uf_status::LIVE;
215 if (a->uf_status_.load() == expected)
216 {
217 if (std::atomic_compare_exchange_strong
218 (&(a->uf_status_), &expected, uf_status::LOCK))
219 {
220 if (a->parent.load() == a)
221 return true;
222 unlock_root(a);
223 }
224 }
225 return false;
226 }
227
229 inline void unlock_root(uf_element* a)
230 {
231 a->uf_status_.store(uf_status::LIVE);
232 }
233
236 {
237 uf_element* a_list = a;
238 while (true)
239 {
240 bool dontcare = false;
241 a_list = pick_from_list(a_list, &dontcare);
242 if (a_list == nullptr)
243 {
244 return nullptr;
245 }
246
247 auto expected = list_status::BUSY;
248 bool b = std::atomic_compare_exchange_strong
249 (&(a_list->list_status_), &expected, list_status::LOCK);
250
251 if (b)
252 return a_list;
253
254 a_list = a_list->next_.load();
255 }
256 }
257
260 {
261 a->list_status_.store(list_status::BUSY);
262 }
263
267 {
268 uf_element* a_root;
269 uf_element* b_root;
270 uf_element* q;
271 uf_element* r;
272
273 do
274 {
275 a_root = find(a);
276 b_root = find(b);
277
278 if (a_root == b_root)
279 {
280 // Update acceptance condition
281 {
282 std::lock_guard<std::mutex> rlock(a_root->acc_mutex_);
283 acc |= a_root->acc;
284 a_root->acc = acc;
285 }
286
287 while (a_root->parent.load() != a_root)
288 {
289 a_root = find(a_root);
290 std::lock_guard<std::mutex> rlock(a_root->acc_mutex_);
291 acc |= a_root->acc;
292 a_root->acc = acc;
293 }
294 return acc;
295 }
296
297 r = std::max(a_root, b_root);
298 q = std::min(a_root, b_root);
299 }
300 while (!lock_root(q));
301
302 uf_element* a_list = lock_list(a);
303 if (a_list == nullptr)
304 {
305 unlock_root(q);
306 return acc;
307 }
308
309 uf_element* b_list = lock_list(b);
310 if (b_list == nullptr)
311 {
312 unlock_list(a_list);
313 unlock_root(q);
314 return acc;
315 }
316
317 SPOT_ASSERT(a_list->list_status_.load() == list_status::LOCK);
318 SPOT_ASSERT(b_list->list_status_.load() == list_status::LOCK);
319
320 // Swapping
321 uf_element* a_next = a_list->next_.load();
322 uf_element* b_next = b_list->next_.load();
323 SPOT_ASSERT(a_next != nullptr);
324 SPOT_ASSERT(b_next != nullptr);
325
326 a_list->next_.store(b_next);
327 b_list->next_.store(a_next);
328 q->parent.store(r);
329
330 // Update workers
331 unsigned q_worker = q->worker_.load();
332 unsigned r_worker = r->worker_.load();
333 if ((q_worker|r_worker) != r_worker)
334 {
335 atomic_fetch_or(&(r->worker_), q_worker);
336 while (r->parent.load() != r)
337 {
338 r = find(r);
339 atomic_fetch_or(&(r->worker_), q_worker);
340 }
341 }
342
343 // Update acceptance condition
344 {
345 std::lock_guard<std::mutex> rlock(r->acc_mutex_);
346 std::lock_guard<std::mutex> qlock(q->acc_mutex_);
347 acc |= r->acc | q->acc;
348 r->acc = q->acc = acc;
349 }
350
351 while (r->parent.load() != r)
352 {
353 r = find(r);
354 std::lock_guard<std::mutex> rlock(r->acc_mutex_);
355 std::lock_guard<std::mutex> qlock(q->acc_mutex_);
356 acc |= r->acc | q->acc;
357 r->acc = acc;
358 }
359
360 unlock_list(a_list);
361 unlock_list(b_list);
362 unlock_root(q);
363 return acc;
364 }
365
368 {
369 uf_element* a = u;
370 while (true)
371 {
372 list_status a_status;
373 while (true)
374 {
375 a_status = a->list_status_.load();
376
377 if (a_status == list_status::BUSY)
378 return a;
379
380 if (a_status == list_status::DONE)
381 break;
382 }
383
384 uf_element* b = a->next_.load();
385
386 // ------------------------------ NO LAZY : start
387 // if (b == u)
388 // {
389 // uf_element* a_root = find(a);
390 // uf_status status = a_root->uf_status_.load();
391 // while (status != uf_status::DEAD)
392 // {
393 // if (status == uf_status::LIVE)
394 // *sccfound = std::atomic_compare_exchange_strong
395 // (&(a_root->uf_status_), &status, uf_status::DEAD);
396 // status = a_root->uf_status_.load();
397 // }
398 // return nullptr;
399 // }
400 // a = b;
401 // ------------------------------ NO LAZY : end
402
403 if (a == b)
404 {
405 uf_element* a_root = find(u);
406 uf_status status = a_root->uf_status_.load();
407 while (status != uf_status::DEAD)
408 {
409 if (status == uf_status::LIVE)
410 *sccfound = std::atomic_compare_exchange_strong
411 (&(a_root->uf_status_), &status, uf_status::DEAD);
412 status = a_root->uf_status_.load();
413 }
414 return nullptr;
415 }
416
417 list_status b_status;
418 while (true)
419 {
420 b_status = b->list_status_.load();
421
422 if (b_status == list_status::BUSY)
423 return b;
424
425 if (b_status == list_status::DONE)
426 break;
427 }
428
429 SPOT_ASSERT(b_status == list_status::DONE);
430 SPOT_ASSERT(a_status == list_status::DONE);
431
432 uf_element* c = b->next_.load();
433 a->next_.store(c);
434 a = c;
435 }
436 }
437
440 {
441 while (true)
442 {
443 list_status a_status = a->list_status_.load();
444
445 if (a_status == list_status::DONE)
446 break;
447
448 if (a_status == list_status::BUSY)
449 std::atomic_compare_exchange_strong
450 (&(a->list_status_), &a_status, list_status::DONE);
451 }
452 }
453
455 unsigned inserted()
456 {
457 return inserted_;
458 }
459
460 private:
461 iterable_uf_ec() = default;
462
463 shared_map map_;
464 unsigned tid_;
465 unsigned size_;
466 unsigned nb_th_;
467 unsigned inserted_;
469 };
470
477 template<typename State, typename SuccIterator,
478 typename StateHash, typename StateEqual>
480 {
481 private:
482 swarmed_bloemen_ec() = delete;
483 public:
484
488 using uf_element = typename uf::uf_element;
489
493 using shared_map = typename uf::shared_map;
494
497 {
498 return new uf(m, i);
499 }
500
504 shared_map& map, /* useless here */
506 unsigned tid,
507 std::atomic<bool>& stop):
508 sys_(sys), twa_(twa), uf_(*uf), tid_(tid),
509 nb_th_(std::thread::hardware_concurrency()),
510 stop_(stop)
511 {
512 static_assert(spot::is_a_kripkecube_ptr<decltype(&sys),
513 State, SuccIterator>::value,
514 "error: does not match the kripkecube requirements");
515 }
516
519
521 void run()
522 {
523 setup();
524 State init_kripke = sys_.initial(tid_);
525 unsigned init_twa = twa_->get_initial();
526 auto pair = uf_.make_claim(init_kripke, init_twa);
527 todo_.push_back(pair.second);
528 Rp_.push_back(pair.second);
529 ++states_;
530
531 while (!todo_.empty())
532 {
533 bloemen_recursive_start:
534 while (!stop_.load(std::memory_order_relaxed))
535 {
536 bool sccfound = false;
537 uf_element* v_prime = uf_.pick_from_list(todo_.back(), &sccfound);
538 if (v_prime == nullptr)
539 {
540 // The SCC has been explored!
541 sccs_ += sccfound;
542 break;
543 }
544
545 auto it_kripke = sys_.succ(v_prime->st_kripke, tid_);
546 auto it_prop = twa_->succ(v_prime->st_prop);
547 forward_iterators(sys_, twa_, it_kripke, it_prop, true, tid_);
548 while (!it_kripke->done())
549 {
550 auto w = uf_.make_claim(it_kripke->state(),
551 twa_->trans_storage(it_prop, tid_)
552 .dst);
553 auto trans_acc = twa_->trans_storage(it_prop, tid_).acc_;
554 ++transitions_;
555 if (w.first == uf::claim_status::CLAIM_NEW)
556 {
557 todo_.push_back(w.second);
558 Rp_.push_back(w.second);
559 ++states_;
560 sys_.recycle(it_kripke, tid_);
561 goto bloemen_recursive_start;
562 }
563 else if (w.first == uf::claim_status::CLAIM_FOUND)
564 {
565 acc_cond::mark_t scc_acc = trans_acc;
566
567 // This operation is mandatory to update acceptance marks.
568 // Otherwise, when w.second and todo.back() are
569 // already in the same set, the acceptance condition will
570 // not be added.
571 scc_acc |= uf_.unite(w.second, w.second, scc_acc);
572
573 while (!uf_.sameset(todo_.back(), w.second))
574 {
575 uf_element* r = Rp_.back();
576 Rp_.pop_back();
577 uf_.unite(r, Rp_.back(), scc_acc);
578 }
579
580
581 {
582 auto root = uf_.find(w.second);
583 std::lock_guard<std::mutex> lock(root->acc_mutex_);
584 scc_acc = root->acc;
585 }
586
587 // cycle found in SCC and it contains acceptance condition
588 if (twa_->acc().accepting(scc_acc))
589 {
590 sys_.recycle(it_kripke, tid_);
591 stop_ = true;
592 is_empty_ = false;
593 tm_.stop("DFS thread " + std::to_string(tid_));
594 return;
595 }
596 }
597 forward_iterators(sys_, twa_, it_kripke, it_prop,
598 false, tid_);
599 }
600 uf_.remove_from_list(v_prime);
601 sys_.recycle(it_kripke, tid_);
602 }
603
604 if (todo_.back() == Rp_.back())
605 Rp_.pop_back();
606 todo_.pop_back();
607 }
608 finalize();
609 }
610
612 void setup()
613 {
614 tm_.start("DFS thread " + std::to_string(tid_));
615 }
616
618 void finalize()
619 {
620 bool tst_val = false;
621 bool new_val = true;
622 bool exchanged = stop_.compare_exchange_strong(tst_val, new_val);
623 if (exchanged)
624 finisher_ = true;
625 tm_.stop("DFS thread " + std::to_string(tid_));
626 }
627
629 bool finisher()
630 {
631 return finisher_;
632 }
633
635 unsigned states()
636 {
637 return states_;
638 }
639
641 unsigned transitions()
642 {
643 return transitions_;
644 }
645
647 unsigned walltime()
648 {
649 return tm_.timer("DFS thread " + std::to_string(tid_)).walltime();
650 }
651
653 std::string name()
654 {
655 return "bloemen_ec";
656 }
657
659 int sccs()
660 {
661 return sccs_;
662 }
663
666 {
667 return is_empty_ ? mc_rvalue::EMPTY : mc_rvalue::NOT_EMPTY;
668 }
669
671 std::string trace()
672 {
673 return "Not implemented";
674 }
675
676 private:
678 twacube_ptr twa_;
679 std::vector<uf_element*> todo_;
680 std::vector<uf_element*> Rp_;
682 unsigned tid_;
683 unsigned nb_th_;
684 unsigned inserted_ = 0;
685 unsigned states_ = 0;
686 unsigned transitions_ = 0;
687 unsigned sccs_ = 0;
688 bool is_empty_ = true;
689 spot::timer_map tm_;
690 std::atomic<bool>& stop_;
691 bool finisher_ = false;
692 };
693}
A fixed-size memory pool implementation.
Definition fixpool.hh:46
void * allocate()
Allocate size bytes of memory.
Definition fixpool.hh:88
This class allows to ensure (at compile time) if a given parameter is of type kripkecube....
Definition kripke.hh:71
Iterable Union-Find for parallel emptiness-check algorithms.
Definition bloemen_ec.hh:45
uf_element * find(uf_element *a)
Find root of element using path compression.
Definition bloemen_ec.hh:177
claim_status
Status values for claim operations.
Definition bloemen_ec.hh:53
~iterable_uf_ec()
Destructor.
Definition bloemen_ec.hh:129
void unlock_root(uf_element *a)
Unlock root element.
Definition bloemen_ec.hh:229
list_status
Status values for list operations.
Definition bloemen_ec.hh:51
acc_cond::mark_t unite(uf_element *a, uf_element *b, acc_cond::mark_t acc)
Unite two sets with acceptance condition; return new acc.
Definition bloemen_ec.hh:266
brick::hashset::FastConcurrent< uf_element *, uf_element_hasher > shared_map
Concurrent hashset for shared state storage.
Definition bloemen_ec.hh:112
unsigned inserted()
Return number of successfully inserted states.
Definition bloemen_ec.hh:455
void remove_from_list(uf_element *a)
Mark element as removed from list.
Definition bloemen_ec.hh:439
iterable_uf_ec(shared_map &map, unsigned tid)
Constructor from shared map and thread ID.
Definition bloemen_ec.hh:122
uf_element * lock_list(uf_element *a)
Lock next element in list.
Definition bloemen_ec.hh:235
iterable_uf_ec(const iterable_uf_ec< State, StateHash, StateEqual > &uf)
Copy constructor.
Definition bloemen_ec.hh:115
std::pair< claim_status, uf_element * > make_claim(State kripke, unsigned prop)
Try to claim a state; returns status and element pointer.
Definition bloemen_ec.hh:133
void unlock_list(uf_element *a)
Unlock list element.
Definition bloemen_ec.hh:259
uf_status
Status values for union-find elements.
Definition bloemen_ec.hh:49
bool sameset(uf_element *a, uf_element *b)
Check if elements are in same set.
Definition bloemen_ec.hh:197
bool lock_root(uf_element *a)
Lock root element if live; return true if successful.
Definition bloemen_ec.hh:212
uf_element * pick_from_list(uf_element *u, bool *sccfound)
Pick element from list; mark SCC as dead if complete.
Definition bloemen_ec.hh:367
Interface for a Kripke structure.
Definition kripke.hh:178
This class is a template representation of a Kripke structure. It is composed of two template paramet...
Definition kripke.hh:40
Bloemen parallel SCC decomposition algorithm for emptiness check.
Definition bloemen_ec.hh:480
std::string trace()
Return trace (not implemented)
Definition bloemen_ec.hh:671
mc_rvalue result()
Return emptiness check result.
Definition bloemen_ec.hh:665
unsigned transitions()
Return number of transitions traversed.
Definition bloemen_ec.hh:641
unsigned states()
Return number of states visited.
Definition bloemen_ec.hh:635
void run()
Run the algorithm.
Definition bloemen_ec.hh:521
typename uf::shared_map shared_map
Type alias for shared map.
Definition bloemen_ec.hh:493
typename uf::uf_element uf_element
Type alias for union-find element.
Definition bloemen_ec.hh:488
int sccs()
Return number of SCCs found.
Definition bloemen_ec.hh:659
~swarmed_bloemen_ec()=default
Destructor.
std::string name()
Return algorithm name.
Definition bloemen_ec.hh:653
void setup()
Setup thread resources.
Definition bloemen_ec.hh:612
void finalize()
Finalize thread resources.
Definition bloemen_ec.hh:618
iterable_uf_ec< State, StateHash, StateEqual > uf
Type alias for iterable union-find.
Definition bloemen_ec.hh:486
static shared_struct * make_shared_structure(shared_map m, unsigned i)
Create shared structure for thread tid.
Definition bloemen_ec.hh:496
unsigned walltime()
Return wall time in milliseconds.
Definition bloemen_ec.hh:647
bool finisher()
Check if this thread finished the search.
Definition bloemen_ec.hh:629
swarmed_bloemen_ec(kripkecube< State, SuccIterator > &sys, twacube_ptr twa, shared_map &map, iterable_uf_ec< State, StateHash, StateEqual > *uf, unsigned tid, std::atomic< bool > &stop)
Constructor for parallel Bloemen algorithm.
Definition bloemen_ec.hh:502
A map of timer, where each timer has a name.
Definition timer.hh:231
void stop(const std::string &name)
Stop timer name.
Definition timer.hh:251
void start(const std::string &name)
Start a timer with name name.
Definition timer.hh:240
const spot::timer & timer(const std::string &name) const
Return the timer name.
Definition timer.hh:276
std::chrono::milliseconds::rep walltime() const
Return cumulative wall time.
Definition timer.hh:208
A Transition-based ω-Automaton.
Definition twa.hh:648
size_t wang32_hash(size_t key)
Thomas Wang's 32 bit hash function.
Definition hashfunc.hh:37
void deallocate(void *ptr)
Recycle size bytes of memory.
Definition fixpool.hh:133
std::shared_ptr< twacube > twacube_ptr
Definition fwd.hh:28
Definition automata.hh:26
mc_rvalue
Return value of a parallel model-checking algorithm.
Definition mc.hh:50
@ NOT_EMPTY
The product is not empty.
@ EMPTY
The product is empty.
An acceptance mark.
Definition acc.hh:76
Hasher for union-find elements.
Definition bloemen_ec.hh:80
bool equal(const uf_element *lhs, const uf_element *rhs) const
Check equality of elements.
Definition bloemen_ec.hh:101
uf_element_hasher()=default
Default constructor.
uf_element_hasher(const uf_element *)
Constructor from element pointer.
Definition bloemen_ec.hh:82
brick::hash::hash128_t hash(const uf_element *lhs) const
Compute hash of element.
Definition bloemen_ec.hh:90
Represents a Union-Find element.
Definition bloemen_ec.hh:57
State st_kripke
the kripke state handled by the element
Definition bloemen_ec.hh:59
std::mutex acc_mutex_
mutex for acceptance condition
Definition bloemen_ec.hh:65
std::atomic< uf_element * > parent
reference to the pointer
Definition bloemen_ec.hh:67
std::atomic< unsigned > worker_
The set of worker for a given state.
Definition bloemen_ec.hh:69
std::atomic< list_status > list_status_
current status for the list
Definition bloemen_ec.hh:75
unsigned st_prop
the prop state handled by the element
Definition bloemen_ec.hh:61
acc_cond::mark_t acc
acceptance conditions of the union
Definition bloemen_ec.hh:63
std::atomic< uf_status > uf_status_
current status for the element
Definition bloemen_ec.hh:73
std::atomic< uf_element * > next_
next element for work stealing
Definition bloemen_ec.hh:71

Please direct any question, comment, or bug report to the Spot mailing list at spot@lrde.epita.fr.
Generated on Fri Feb 27 2015 10:00:07 for spot by doxygen 1.9.8