spot  2.16
lpar13.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 <spot/twa/acc.hh>
23 #include <spot/mc/unionfind.hh>
24 #include <spot/mc/intersect.hh>
25 #include <spot/mc/mc.hh>
26 #include <spot/misc/timer.hh>
27 #include <spot/twacube/twacube.hh>
28 #include <spot/twacube/fwd.hh>
29 
30 namespace spot
31 {
38  template<typename State, typename SuccIterator,
39  typename StateHash, typename StateEqual>
40  class SPOT_API lpar13
41  {
42  struct product_state
43  {
44  State st_kripke;
45  unsigned st_prop;
46  };
47 
48  struct product_state_equal
49  {
50  bool
51  operator()(const product_state lhs,
52  const product_state rhs) const
53  {
54  StateEqual equal;
55  return (lhs.st_prop == rhs.st_prop) &&
56  equal(lhs.st_kripke, rhs.st_kripke);
57  }
58  };
59 
60  struct product_state_hash
61  {
62  size_t
63  operator()(const product_state that) const noexcept
64  {
65  // FIXME: wang32_hash(that.st_prop) could have been
66  // pre-calculated!
67  StateHash hasher;
68  return wang32_hash(that.st_prop) ^ hasher(that.st_kripke);
69  }
70  };
71 
72  public:
73 
75  using shared_map = int; // Useless here.
77  using shared_struct = int; // Useless here.
78 
81  {
82  return nullptr; // Useless
83  }
84 
88  shared_map& map, /* useless here */
89  shared_struct*, /* useless here */
90  unsigned tid,
91  std::atomic<bool>& stop)
92  : sys_(sys), twa_(twa), tid_(tid), stop_(stop),
93  acc_(twa->acc()), sccs_(0U)
94  {
95  static_assert(spot::is_a_kripkecube_ptr<decltype(&sys),
96  State, SuccIterator>::value,
97  "error: does not match the kripkecube requirements");
98  }
99 
101  virtual ~lpar13()
102  {
103  map.clear();
104  while (!todo.empty())
105  {
106  sys_.recycle(todo.back().it_kripke, tid_);
107  todo.pop_back();
108  }
109  }
110 
112  bool run()
113  {
114  setup();
115  product_state initial = {sys_.initial(tid_), twa_->get_initial()};
116  if (SPOT_LIKELY(push_state(initial, dfs_number+1, {})))
117  {
118  todo.push_back({initial, sys_.succ(initial.st_kripke, tid_),
119  twa_->succ(initial.st_prop)});
120 
121  // Not going further! It's a product with a single state.
122  if (todo.back().it_prop->done())
123  return false;
124 
125  forward_iterators(sys_, twa_, todo.back().it_kripke,
126  todo.back().it_prop, true, 0);
127  map[initial] = ++dfs_number;
128  }
129  while (!todo.empty() && !stop_.load(std::memory_order_relaxed))
130  {
131  // Check the kripke is enough since it's the outer loop. More
132  // details in forward_iterators.
133  if (todo.back().it_kripke->done())
134  {
135  bool is_init = todo.size() == 1;
136  auto newtop = is_init? todo.back().st: todo[todo.size() -2].st;
137  if (SPOT_LIKELY(pop_state(todo.back().st,
138  map[todo.back().st],
139  is_init,
140  newtop,
141  map[newtop])))
142  {
143  sys_.recycle(todo.back().it_kripke, tid_);
144  // FIXME: a local storage for twacube iterator?
145  todo.pop_back();
146  if (SPOT_UNLIKELY(found_))
147  {
148  finalize();
149  return true;
150  }
151  }
152  }
153  else
154  {
155  ++trans_;
156  product_state dst =
157  {
158  todo.back().it_kripke->state(),
159  twa_->trans_storage(todo.back().it_prop, tid_).dst
160  };
161  auto acc = twa_->trans_data(todo.back().it_prop, tid_).acc_;
162  forward_iterators(sys_, twa_, todo.back().it_kripke,
163  todo.back().it_prop, false, 0);
164  auto it = map.find(dst);
165  if (it == map.end())
166  {
167  if (SPOT_LIKELY(push_state(dst, dfs_number+1, acc)))
168  {
169  map[dst] = ++dfs_number;
170  todo.push_back({dst, sys_.succ(dst.st_kripke, tid_),
171  twa_->succ(dst.st_prop)});
172  forward_iterators(sys_, twa_, todo.back().it_kripke,
173  todo.back().it_prop, true, 0);
174  }
175  }
176  else if (SPOT_UNLIKELY(update(todo.back().st,
177  dfs_number,
178  dst, map[dst], acc)))
179  {
180  finalize();
181  return true;
182  }
183  }
184  }
185  finalize();
186  return false;
187  }
188 
190  void setup()
191  {
192  tm_.start("DFS thread " + std::to_string(tid_));
193  }
194 
196  bool push_state(product_state, unsigned dfsnum, acc_cond::mark_t cond)
197  {
198  uf_.makeset(dfsnum);
199  roots_.push_back({dfsnum, cond, {}});
200  return true;
201  }
202 
210  bool pop_state(product_state, unsigned top_dfsnum, bool,
211  product_state, unsigned)
212  {
213  if (top_dfsnum == roots_.back().dfsnum)
214  {
215  roots_.pop_back();
216  ++sccs_;
217  uf_.markdead(top_dfsnum);
218  }
219  dfs_ = todo.size() > dfs_ ? todo.size() : dfs_;
220  return true;
221  }
222 
227  bool update(product_state, unsigned,
228  product_state, unsigned dst_dfsnum,
229  acc_cond::mark_t cond)
230  {
231  if (uf_.isdead(dst_dfsnum))
232  return false;
233 
234  while (!uf_.sameset(dst_dfsnum, roots_.back().dfsnum))
235  {
236  auto& el = roots_.back();
237  roots_.pop_back();
238  uf_.unite(dst_dfsnum, el.dfsnum);
239  cond |= el.acc | el.ingoing;
240  }
241  roots_.back().acc |= cond;
242  found_ = acc_.accepting(roots_.back().acc);
243  if (SPOT_UNLIKELY(found_))
244  stop_ = true;
245  return found_;
246  }
247 
249  void finalize()
250  {
251  bool tst_val = false;
252  bool new_val = true;
253  bool exchanged = stop_.compare_exchange_strong(tst_val, new_val);
254  if (exchanged)
255  finisher_ = true;
256  tm_.stop("DFS thread " + std::to_string(tid_));
257  }
258 
260  bool finisher()
261  {
262  return finisher_;
263  }
264 
266  unsigned int states()
267  {
268  return dfs_number;
269  }
270 
272  unsigned int transitions()
273  {
274  return trans_;
275  }
276 
278  unsigned walltime()
279  {
280  return tm_.timer("DFS thread " + std::to_string(tid_)).walltime();
281  }
282 
284  std::string name()
285  {
286  return "renault_lpar13";
287  }
288 
290  int sccs()
291  {
292  return sccs_;
293  }
294 
297  {
298  return !found_ ? mc_rvalue::EMPTY : mc_rvalue::NOT_EMPTY;
299  }
300 
302  std::string trace()
303  {
304  SPOT_ASSERT(found_);
305  std::string res = "Prefix:\n";
306 
307  // Compute the prefix of the accepting run
308  for (auto& s : todo)
309  res += " " + std::to_string(s.st.st_prop) +
310  + "*" + sys_.to_string(s.st.st_kripke) + "\n";
311 
312  // Compute the accepting cycle
313  res += "Cycle:\n";
314 
315  struct ctrx_element
316  {
317  const product_state* prod_st;
318  ctrx_element* parent_st;
319  SuccIterator* it_kripke;
320  std::shared_ptr<trans_index> it_prop;
321  };
322  std::queue<ctrx_element*> bfs;
323 
324  acc_cond::mark_t acc = {};
325 
326  bfs.push(new ctrx_element({&todo.back().st, nullptr,
327  sys_.succ(todo.back().st.st_kripke, tid_),
328  twa_->succ(todo.back().st.st_prop)}));
329  while (true)
330  {
331  here:
332  auto* front = bfs.front();
333  bfs.pop();
334  // PUSH all successors of the state.
335  while (!front->it_kripke->done())
336  {
337  while (!front->it_prop->done())
338  {
339  if (twa_->get_cubeset().intersect
340  (twa_->trans_data(front->it_prop, tid_).cube_,
341  front->it_kripke->condition()))
342  {
343  const product_state dst = {
344  front->it_kripke->state(),
345  twa_->trans_storage(front->it_prop).dst
346  };
347 
348  // Skip Unknown states or not same SCC
349  auto it = map.find(dst);
350  if (it == map.end() ||
351  !uf_.sameset(it->second,
352  map[todo.back().st]))
353  {
354  front->it_prop->next();
355  continue;
356  }
357 
358  // This is a valid transition. If this transition
359  // is the one we are looking for, update the counter-
360  // -example and flush the bfs queue.
361  auto mark = twa_->trans_data(front->it_prop,
362  tid_).acc_;
363  if (!(acc & mark))
364  {
365  ctrx_element* current = front;
366  while (current != nullptr)
367  {
368  // FIXME: also display acc?
369  res = res + " " +
370  std::to_string(current->prod_st->st_prop) +
371  + "*" +
372  sys_. to_string(current->prod_st->st_kripke) +
373  "\n";
374  current = current->parent_st;
375  }
376 
377  // empty the queue
378  while (!bfs.empty())
379  {
380  auto* e = bfs.front();
381  sys_.recycle(e->it_kripke, tid_);
382  bfs.pop();
383  delete e;
384  }
385  sys_.recycle(front->it_kripke, tid_);
386  delete front;
387 
388  // update acceptance
389  acc |= mark;
390  if (twa_->acc().accepting(acc))
391  {
392  return res;
393  }
394 
395  const product_state* q = &(it->first);
396  ctrx_element* root = new ctrx_element({
397  q , nullptr,
398  sys_.succ(q->st_kripke, tid_),
399  twa_->succ(q->st_prop)
400  });
401  bfs.push(root);
402  goto here;
403  }
404 
405  // Otherwise increment iterator and push successor.
406  const product_state* q = &(it->first);
407  ctrx_element* root = new ctrx_element({
408  q , nullptr,
409  sys_.succ(q->st_kripke, tid_),
410  twa_->succ(q->st_prop)
411  });
412  bfs.push(root);
413  }
414  front->it_prop->next();
415  }
416  front->it_prop->reset();
417  front->it_kripke->next();
418  }
419  sys_.recycle(front->it_kripke, tid_);
420  delete front;
421  }
422 
423  // never reach here;
424  return res;
425  }
426 
427  private:
428 
429  struct todo_element
430  {
431  product_state st;
432  SuccIterator* it_kripke;
433  std::shared_ptr<trans_index> it_prop;
434  };
435 
436  struct root_element {
437  unsigned dfsnum;
438  acc_cond::mark_t ingoing;
439  acc_cond::mark_t acc;
440  };
441 
442  typedef std::unordered_map<const product_state, int,
443  product_state_hash,
444  product_state_equal> visited_map;
445 
446  kripkecube<State, SuccIterator>& sys_;
447  twacube_ptr twa_;
448  std::vector<todo_element> todo;
449  visited_map map;
450  unsigned int dfs_number = 0;
451  unsigned int trans_ = 0;
452  unsigned tid_;
453  std::atomic<bool>& stop_;
454  bool found_ = false;
455  std::vector<root_element> roots_;
456  int_unionfind uf_;
457  acc_cond acc_;
458  unsigned sccs_;
459  unsigned dfs_;
460  spot::timer_map tm_;
461  bool finisher_ = false;
462  };
463 }
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
This class implements the sequential emptiness check as presented in "Three SCC-based Emptiness Check...
Definition: lpar13.hh:41
bool run()
Run the algorithm.
Definition: lpar13.hh:112
bool update(product_state, unsigned, product_state, unsigned dst_dfsnum, acc_cond::mark_t cond)
This method is called for every closing, back, or forward edge.
Definition: lpar13.hh:227
unsigned int transitions()
Return number of transitions traversed.
Definition: lpar13.hh:272
mc_rvalue result()
Return emptiness check result.
Definition: lpar13.hh:296
bool push_state(product_state, unsigned dfsnum, acc_cond::mark_t cond)
Push product state to stack.
Definition: lpar13.hh:196
unsigned int states()
Return number of states visited.
Definition: lpar13.hh:266
int shared_map
Type alias for shared map (useless for sequential algorithm)
Definition: lpar13.hh:75
int sccs()
Return number of SCCs found.
Definition: lpar13.hh:290
bool finisher()
Check if this thread finished the search.
Definition: lpar13.hh:260
void setup()
Setup thread resources.
Definition: lpar13.hh:190
bool pop_state(product_state, unsigned top_dfsnum, bool, product_state, unsigned)
This method is called to notify the emptiness checks that a state will be popped. If the method retur...
Definition: lpar13.hh:210
void finalize()
Finalize thread resources.
Definition: lpar13.hh:249
unsigned walltime()
Return wall time in milliseconds.
Definition: lpar13.hh:278
int shared_struct
Type alias for shared structure (useless here)
Definition: lpar13.hh:77
std::string name()
Return algorithm name.
Definition: lpar13.hh:284
std::string trace()
Return trace.
Definition: lpar13.hh:302
static shared_struct * make_shared_structure(shared_map m, unsigned i)
Create shared structure for thread tid.
Definition: lpar13.hh:80
virtual ~lpar13()
Destructor.
Definition: lpar13.hh:101
lpar13(kripkecube< State, SuccIterator > &sys, twacube_ptr twa, shared_map &map, shared_struct *, unsigned tid, std::atomic< bool > &stop)
Constructor for LPAR13 emptiness check.
Definition: lpar13.hh:86
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
@ U
until
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.
An acceptance mark.
Definition: acc.hh:76

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