spot  2.16
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 
33 namespace 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
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
std::pair< bool, product_state > push_blue(product_state s, bool from_accepting)
Push state to blue stack.
Definition: cndfs.hh:158
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:25
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.

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.1