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