spot 2.16
Loading...
Searching...
No Matches
cndfs.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 <thread>
23#include <vector>
24
25#include <spot/bricks/brick-hashset>
26#include <spot/kripke/kripke.hh>
27#include <spot/misc/common.hh>
28#include <spot/misc/fixpool.hh>
29#include <spot/misc/timer.hh>
30#include <spot/twacube/twacube.hh>
31#include <spot/mc/mc.hh>
32
33namespace spot
34{
37 template<typename State, typename SuccIterator,
38 typename StateHash, typename StateEqual>
39 class SPOT_API swarmed_cndfs
40 {
41 struct local_colors
42 {
43 bool cyan;
44 bool is_in_Rp;
45 };
46
48 struct cndfs_colors
49 {
50 std::atomic<bool> blue;
51 std::atomic<bool> red;
52 local_colors l[1];
53 };
54
55 struct product_state
56 {
57 State st_kripke;
58 unsigned st_prop;
59 cndfs_colors* colors;
60 };
61
63 struct state_hasher
64 {
65 state_hasher(const product_state&)
66 { }
67
68 state_hasher() = default;
69
70 brick::hash::hash128_t
71 hash(const product_state& lhs) const
72 {
73 StateHash hash;
74 // Not modulo 31 according to brick::hashset specifications.
75 unsigned u = hash(lhs.st_kripke) % (1<<30);
76 u = wang32_hash(lhs.st_prop) ^ u;
77 u = u % (1<<30);
78 return {u, u};
79 }
80
81 bool equal(const product_state& lhs,
82 const product_state& rhs) const
83 {
84 StateEqual equal;
85 return (lhs.st_prop == rhs.st_prop)
86 && equal(lhs.st_kripke, rhs.st_kripke);
87 }
88 };
89
90 struct todo_element
91 {
92 product_state st;
93 SuccIterator* it_kripke;
94 std::shared_ptr<trans_index> it_prop;
95 bool from_accepting;
96 };
97
98 public:
99
101 using shared_map = brick::hashset::FastConcurrent <product_state,
102 state_hasher>;
105
108 {
109 return nullptr; // Useless here.
110 }
111
114 shared_map& map, shared_struct* /* useless here*/,
115 unsigned tid, std::atomic<bool>& stop):
116 sys_(sys), twa_(twa), tid_(tid), map_(map),
117 nb_th_(std::thread::hardware_concurrency()),
118 p_colors_(sizeof(cndfs_colors) +
119 sizeof(local_colors)*(std::thread::hardware_concurrency() - 1)),
120 stop_(stop)
121 {
122 static_assert(spot::is_a_kripkecube_ptr<decltype(&sys),
123 State, SuccIterator>::value,
124 "error: does not match the kripkecube requirements");
125 SPOT_ASSERT(nb_th_ > tid);
126 }
127
128 virtual ~swarmed_cndfs()
129 {
130 while (!todo_blue_.empty())
131 {
132 sys_.recycle(todo_blue_.back().it_kripke, tid_);
133 todo_blue_.pop_back();
134 }
135 while (!todo_red_.empty())
136 {
137 sys_.recycle(todo_red_.back().it_kripke, tid_);
138 todo_red_.pop_back();
139 }
140 }
141
143 void run()
144 {
145 setup();
146 blue_dfs();
147 finalize();
148 }
149
151 void setup()
152 {
153 tm_.start("DFS thread " + std::to_string(tid_));
154 }
155
157 std::pair<bool, product_state>
158 push_blue(product_state s, bool from_accepting)
159 {
160 cndfs_colors* c = (cndfs_colors*) p_colors_.allocate();
161 c->red = false;
162 c->blue = false;
163 for (unsigned i = 0; i < nb_th_; ++i)
164 {
165 c->l[i].cyan = false;
166 c->l[i].is_in_Rp = false;
167 }
168
169 s.colors = c;
170
171 // Try to insert the new state in the shared map.
172 auto it = map_.insert(s);
173 bool b = it.isnew();
174
175 // Insertion failed, delete element
176 // FIXME Should we add a local cache to avoid useless allocations?
177 if (!b)
178 {
179 p_colors_.deallocate(c);
180 bool blue = ((*it)).colors->blue.load();
181 bool cyan = ((*it)).colors->l[tid_].cyan;
182 if (blue || cyan)
183 return {false, *it};
184 }
185
186 // Mark state as visited.
187 ((*it)).colors->l[tid_].cyan = true;
188 ++states_;
189 todo_blue_.push_back({*it,
190 sys_.succ(((*it)).st_kripke, tid_),
191 twa_->succ(((*it)).st_prop),
192 from_accepting});
193 return {true, *it};
194 }
195
197 std::pair<bool, product_state>
198 push_red(product_state s, bool ignore_cyan)
199 {
200 // Try to insert the new state in the shared map.
201 auto it = map_.insert(s);
202 SPOT_ASSERT(!it.isnew()); // should never be new in a red DFS
203 bool red = ((*it)).colors->red.load();
204 bool cyan = ((*it)).colors->l[tid_].cyan;
205 bool in_Rp = ((*it)).colors->l[tid_].is_in_Rp;
206 if (red || (cyan && !ignore_cyan) || in_Rp)
207 return {false, *it}; // couldn't insert
208
209 // Mark state as visited.
210 ((*it)).colors->l[tid_].is_in_Rp = true;
211 Rp_.push_back(*it);
212 ++states_;
213 todo_red_.push_back({*it,
214 sys_.succ(((*it)).st_kripke, tid_),
215 twa_->succ(((*it)).st_prop),
216 false});
217 return {true, *it};
218 }
219
221 bool pop_blue()
222 {
223 // Track maximum dfs size
224 dfs_ = todo_blue_.size() > dfs_ ? todo_blue_.size() : dfs_;
225
226 todo_blue_.back().st.colors->l[tid_].cyan = false;
227 sys_.recycle(todo_blue_.back().it_kripke, tid_);
228 todo_blue_.pop_back();
229 return true;
230 }
231
233 bool pop_red()
234 {
235 // Track maximum dfs size
236 dfs_ = todo_blue_.size() + todo_red_.size() > dfs_ ?
237 todo_blue_.size() + todo_red_.size() : dfs_;
238
239
240 sys_.recycle(todo_red_.back().it_kripke, tid_);
241 todo_red_.pop_back();
242 return true;
243 }
244
246 void finalize()
247 {
248 bool tst_val = false;
249 bool new_val = true;
250 bool exchanged = stop_.compare_exchange_strong(tst_val, new_val);
251 if (exchanged)
252 finisher_ = true;
253 tm_.stop("DFS thread " + std::to_string(tid_));
254 }
255
257 bool finisher()
258 {
259 return finisher_;
260 }
261
263 unsigned states()
264 {
265 return states_;
266 }
267
269 unsigned transitions()
270 {
271 return transitions_;
272 }
273
275 unsigned walltime()
276 {
277 return tm_.timer("DFS thread " + std::to_string(tid_)).walltime();
278 }
279
281 std::string name()
282 {
283 return "cndfs";
284 }
285
287 int sccs()
288 {
289 return -1;
290 }
291
294 {
295 return is_empty_ ? mc_rvalue::EMPTY : mc_rvalue::NOT_EMPTY;
296 }
297
299 std::string trace()
300 {
301 SPOT_ASSERT(!is_empty_);
302 StateEqual equal;
303 auto state_equal = [equal](product_state a, product_state b)
304 {
305 return a.st_prop == b.st_prop
306 && equal(a.st_kripke, b.st_kripke);
307 };
308
309 std::string res = "Prefix:\n";
310
311 auto it = todo_blue_.begin();
312 while (it != todo_blue_.end())
313 {
314 if (state_equal(((*it)).st, cycle_start_))
315 break;
316 res += " " + std::to_string(((*it)).st.st_prop)
317 + "*" + sys_.to_string(((*it)).st.st_kripke) + "\n";
318 ++it;
319 }
320
321 res += "Cycle:\n";
322 while (it != todo_blue_.end())
323 {
324 res += " " + std::to_string(((*it)).st.st_prop)
325 + "*" + sys_.to_string(((*it)).st.st_kripke) + "\n";
326 ++it;
327 }
328
329 if (!todo_red_.empty())
330 {
331 it = todo_red_.begin() + 1; // skip first element, also in blue
332 while (it != todo_red_.end())
333 {
334 res += " " + std::to_string(((*it)).st.st_prop)
335 + "*" + sys_.to_string(((*it)).st.st_kripke) + "\n";
336 ++it;
337 }
338 }
339 res += " " + std::to_string(cycle_start_.st_prop)
340 + "*" + sys_.to_string(cycle_start_.st_kripke) + "\n";
341
342 return res;
343 }
344
345 private:
346 void blue_dfs()
347 {
348 product_state initial = {sys_.initial(tid_),
349 twa_->get_initial(),
350 nullptr};
351 if (!push_blue(initial, false).first)
352 return;
353
354 // Property automaton has only one state
355 if (todo_blue_.back().it_prop->done())
356 return;
357
358 forward_iterators(sys_, twa_, todo_blue_.back().it_kripke,
359 todo_blue_.back().it_prop, true, tid_);
360
361 while (!todo_blue_.empty() && !stop_.load(std::memory_order_relaxed))
362 {
363 auto current = todo_blue_.back();
364
365 if (!current.it_kripke->done())
366 {
367 ++transitions_;
368 product_state s = {
369 current.it_kripke->state(),
370 twa_->trans_storage(current.it_prop, tid_).dst,
371 nullptr
372 };
373
374 bool acc = (bool) twa_->trans_storage(current.it_prop, tid_).acc_;
375 forward_iterators(sys_, twa_, todo_blue_.back().it_kripke,
376 todo_blue_.back().it_prop, false, tid_);
377
378 auto tmp = push_blue(s, acc);
379 if (tmp.first)
380 forward_iterators(sys_, twa_, todo_blue_.back().it_kripke,
381 todo_blue_.back().it_prop, true, tid_);
382 else if (acc)
383 {
384 // The state is cyan and we can reach it through an
385 // accepting transition; an accepting cycle has been
386 // found without launching a red dfs
387 if (tmp.second.colors->l[tid_].cyan)
388 {
389 cycle_start_ = s;
390 is_empty_ = false;
391 stop_.store(true);
392 return;
393 }
394
395 SPOT_ASSERT(tmp.second.colors->blue);
396
397 red_dfs(s);
398 if (!is_empty_)
399 return;
400 post_red_dfs();
401 }
402 }
403 else
404 {
405 current.st.colors->blue.store(true);
406
407 // backtracked an accepting transition; launch red DFS
408 if (current.from_accepting)
409 {
410 red_dfs(todo_blue_.back().st);
411 if (!is_empty_)
412 return;
413 post_red_dfs();
414 }
415
416 pop_blue();
417 }
418 }
419 }
420
421 void post_red_dfs()
422 {
423 for (product_state& s: Rp_acc_)
424 {
425 while (s.colors->red.load() && !stop_.load())
426 {
427 // await
428 }
429 }
430 for (product_state& s: Rp_)
431 {
432 s.colors->red.store(true);
433 s.colors->l[tid_].is_in_Rp = false; // empty Rp
434 }
435
436 Rp_.clear();
437 Rp_acc_.clear();
438 }
439
440 void red_dfs(product_state initial)
441 {
442 auto init_push = push_red(initial, true);
443 SPOT_ASSERT(init_push.second.colors->blue);
444
445 if (!init_push.first)
446 return;
447
448 forward_iterators(sys_, twa_, todo_red_.back().it_kripke,
449 todo_red_.back().it_prop, true, tid_);
450
451 while (!todo_red_.empty() && !stop_.load(std::memory_order_relaxed))
452 {
453 auto current = todo_red_.back();
454
455 if (!current.it_kripke->done())
456 {
457 ++transitions_;
458 product_state s = {
459 current.it_kripke->state(),
460 twa_->trans_storage(current.it_prop, tid_).dst,
461 nullptr
462 };
463 bool acc = (bool) twa_->trans_storage(current.it_prop, tid_).acc_;
464 forward_iterators(sys_, twa_, todo_red_.back().it_kripke,
465 todo_red_.back().it_prop, false, tid_);
466
467 auto res = push_red(s, false);
468 if (res.first) // could push properly
469 {
470 forward_iterators(sys_, twa_, todo_red_.back().it_kripke,
471 todo_red_.back().it_prop, true, tid_);
472
473 SPOT_ASSERT(res.second.colors->blue);
474
475 // The transition is accepting, we want to keep
476 // track of this state
477 if (acc)
478 {
479 // Do not insert twice a state
480 bool found = false;
481 for (auto& st: Rp_acc_)
482 {
483 if (st.colors == res.second.colors)
484 {
485 found = true;
486 break;
487 }
488 }
489 if (!found)
490 Rp_acc_.push_back(Rp_.back());
491 }
492 }
493 else
494 {
495 if (res.second.colors->l[tid_].cyan)
496 {
497 // color pointers are unique to each element,
498 // comparing them is equivalent (but faster) to comparing
499 // st_kripke and st_prop individually.
500 if (init_push.second.colors == res.second.colors && !acc)
501 continue;
502 is_empty_ = false;
503 cycle_start_ = s;
504 stop_.store(true);
505 return;
506 }
507 else if (acc && res.second.colors->l[tid_].is_in_Rp)
508 {
509 auto it = map_.insert(s);
510 Rp_acc_.push_back(*it);
511 }
512 }
513 }
514 else
515 {
516 pop_red();
517 }
518 }
519 }
520
521 kripkecube<State, SuccIterator>& sys_;
522 twacube_ptr twa_;
523 std::vector<todo_element> todo_blue_;
524 std::vector<todo_element> todo_red_;
525 unsigned transitions_ = 0;
526 unsigned tid_;
527 shared_map map_;
528 spot::timer_map tm_;
529 unsigned states_ = 0;
530 unsigned dfs_ = 0;
531 unsigned nb_th_ = 0;
532 fixed_size_pool<pool_type::Unsafe> p_colors_;
533 bool is_empty_ = true;
534 std::atomic<bool>& stop_;
535 std::vector<product_state> Rp_;
536 std::vector<product_state> Rp_acc_;
537 product_state cycle_start_;
538 bool finisher_ = false;
539 };
540}
This class allows to ensure (at compile time) if a given parameter is of type kripkecube....
Definition kripke.hh:71
This class is a template representation of a Kripke structure. It is composed of two template paramet...
Definition kripke.hh:40
Swarmed variant of the CNDFS parallel emptiness-check algorithm.
Definition cndfs.hh:40
std::pair< bool, product_state > push_blue(product_state s, bool from_accepting)
Push state to blue stack.
Definition cndfs.hh:158
unsigned walltime()
Return wall time in milliseconds.
Definition cndfs.hh:275
shared_map shared_struct
Type alias for shared structure.
Definition cndfs.hh:104
std::string name()
Return algorithm name.
Definition cndfs.hh:281
void finalize()
Finalize thread resources.
Definition cndfs.hh:246
int sccs()
Return number of SCCs found (returns -1)
Definition cndfs.hh:287
swarmed_cndfs(kripkecube< State, SuccIterator > &sys, twacube_ptr twa, shared_map &map, shared_struct *, unsigned tid, std::atomic< bool > &stop)
Constructor for parallel CNDFS algorithm.
Definition cndfs.hh:113
bool pop_blue()
Pop state from blue stack.
Definition cndfs.hh:221
brick::hashset::FastConcurrent< product_state, state_hasher > shared_map
Concurrent hashset for shared state storage.
Definition cndfs.hh:102
bool finisher()
Check if this thread finished the search.
Definition cndfs.hh:257
mc_rvalue result()
Return emptiness check result.
Definition cndfs.hh:293
bool pop_red()
Pop state from red stack.
Definition cndfs.hh:233
static shared_struct * make_shared_structure(shared_map m, unsigned i)
Create shared structure for thread tid.
Definition cndfs.hh:107
void setup()
Setup thread resources.
Definition cndfs.hh:151
unsigned states()
Return number of states visited.
Definition cndfs.hh:263
std::string trace()
Return trace.
Definition cndfs.hh:299
unsigned transitions()
Return number of transitions traversed.
Definition cndfs.hh:269
std::pair< bool, product_state > push_red(product_state s, bool ignore_cyan)
Push state to red stack.
Definition cndfs.hh:198
void run()
Run the algorithm.
Definition cndfs.hh:143
A map of timer, where each timer has a name.
Definition timer.hh:231
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
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

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