spot  2.15.1
graph.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 <spot/misc/common.hh>
22 #include <spot/misc/_config.h>
23 #include <spot/misc/permute.hh>
24 #include <vector>
25 #include <type_traits>
26 #include <tuple>
27 #include <cassert>
28 #include <iterator>
29 #include <algorithm>
30 #include <map>
31 #include <iostream>
32 #ifdef SPOT_ENABLE_PTHREAD
33 # include <thread>
34 #endif // SPOT_ENABLE_PTHREAD
35 
40 
41 namespace spot
42 {
43  template <typename State_Data, typename Edge_Data>
44  class SPOT_API digraph;
45 
46  namespace internal
47  {
48 #ifndef SWIG
49  template <typename Of, typename ...Args>
50  struct first_is_base_of
51  {
52  static const bool value = false;
53  };
54 
55  template <typename Of, typename Arg1, typename ...Args>
56  struct first_is_base_of<Of, Arg1, Args...>
57  {
58  static const bool value =
59  std::is_base_of<Of, typename std::decay<Arg1>::type>::value;
60  };
61 #endif
62 
63  // The boxed_label class stores Data as an attribute called
64  // "label" if boxed is true. It is an empty class if Data is
65  // void, and it simply inherits from Data if boxed is false.
66  //
67  // The data() method offers an homogeneous access to the Data
68  // instance.
69  template <typename Data, bool boxed = !std::is_class<Data>::value>
70  struct SPOT_API boxed_label
71  {
72  typedef Data data_t;
73  Data label;
74 
75 #ifndef SWIG
76  template <typename... Args,
77  typename = typename std::enable_if<
78  !first_is_base_of<boxed_label, Args...>::value>::type>
79  boxed_label(Args&&... args)
80  noexcept(std::is_nothrow_constructible<Data, Args...>::value)
81  : label{std::forward<Args>(args)...}
82  {
83  }
84 #endif
85 
86  // if Data is a POD type, G++ 4.8.2 wants default values for all
87  // label fields unless we define this default constructor here.
88  explicit boxed_label()
89  noexcept(std::is_nothrow_constructible<Data>::value)
90  {
91  }
92 
93  Data& data()
94  {
95  return label;
96  }
97 
98  const Data& data() const
99  {
100  return label;
101  }
102 
103  bool operator<(const boxed_label& other) const
104  {
105  return label < other.label;
106  }
107  };
108 
109  template <>
110  struct SPOT_API boxed_label<void, true>: public std::tuple<>
111  {
112  typedef std::tuple<> data_t;
113  std::tuple<>& data()
114  {
115  return *this;
116  }
117 
118  const std::tuple<>& data() const
119  {
120  return *this;
121  }
122 
123  };
124 
125  template <typename Data>
126  struct SPOT_API boxed_label<Data, false>: public Data
127  {
128  typedef Data data_t;
129 
130 #ifndef SWIG
131  template <typename... Args,
132  typename = typename std::enable_if<
133  !first_is_base_of<boxed_label, Args...>::value>::type>
134  boxed_label(Args&&... args)
135  noexcept(std::is_nothrow_constructible<Data, Args...>::value)
136  : Data{std::forward<Args>(args)...}
137  {
138  }
139 #endif
140 
141  // if Data is a POD type, G++ 4.8.2 wants default values for all
142  // label fields unless we define this default constructor here.
143  explicit boxed_label()
144  noexcept(std::is_nothrow_constructible<Data>::value)
145  {
146  }
147 
148  Data& data()
149  {
150  return *this;
151  }
152 
153  const Data& data() const
154  {
155  return *this;
156  }
157  };
158 
160  // State storage for digraphs
162 
163  // We have two implementations, one with attached State_Data, and
164  // one without.
165 
166  template <typename Edge, typename State_Data>
167  struct SPOT_API distate_storage final: public State_Data
168  {
169  Edge succ = 0; // First outgoing edge (used when iterating)
170  Edge succ_tail = 0; // Last outgoing edge (used for
171  // appending new edges)
172 #ifndef SWIG
173  template <typename... Args,
174  typename = typename std::enable_if<
175  !first_is_base_of<distate_storage, Args...>::value>::type>
176  distate_storage(Args&&... args)
177  noexcept(std::is_nothrow_constructible<State_Data, Args...>::value)
178  : State_Data{std::forward<Args>(args)...}
179  {
180  }
181 #endif
182  };
183 
185  // Edge storage
187 
188  // Again two implementation: one with label, and one without.
189 
190  template <typename StateIn,
191  typename StateOut, typename Edge, typename Edge_Data>
192  struct SPOT_API edge_storage final: public Edge_Data
193  {
194  typedef Edge edge;
195 
196  StateOut dst; // destination
197  Edge next_succ; // next outgoing edge with same
198  // source, or 0
199  StateIn src; // source
200 
201  explicit edge_storage()
202  noexcept(std::is_nothrow_constructible<Edge_Data>::value)
203  : Edge_Data{}
204  {
205  }
206 
207 #ifndef SWIG
208  template <typename... Args>
209  edge_storage(StateOut dst, Edge next_succ,
210  StateIn src, Args&&... args)
211  noexcept(std::is_nothrow_constructible<Edge_Data, Args...>::value
212  && std::is_nothrow_constructible<StateOut, StateOut>::value
213  && std::is_nothrow_constructible<Edge, Edge>::value)
214  : Edge_Data{std::forward<Args>(args)...},
215  dst(dst), next_succ(next_succ), src(src)
216  {
217  }
218 #endif
219 
220  bool operator<(const edge_storage& other) const
221  {
222  if (src < other.src)
223  return true;
224  if (src > other.src)
225  return false;
226  // This might be costly if the destination is a vector
227  if (dst < other.dst)
228  return true;
229  if (dst > other.dst)
230  return false;
231  return this->data() < other.data();
232  }
233 
234  bool operator==(const edge_storage& other) const
235  {
236  return src == other.src &&
237  dst == other.dst &&
238  this->data() == other.data();
239  }
240  };
241 
243  // Edge iterator
245 
246  // This holds a graph and a edge number that is the start of
247  // a list, and it iterates over all the edge_storage_t elements
248  // of that list.
249 
250  template <typename Graph>
251  class SPOT_API edge_iterator
252  {
253  public:
254  typedef typename std::conditional<std::is_const<Graph>::value,
255  const typename Graph::edge_storage_t,
256  typename Graph::edge_storage_t>::type
257  value_type;
258  typedef value_type& reference;
259  typedef value_type* pointer;
260  typedef std::ptrdiff_t difference_type;
261  typedef std::forward_iterator_tag iterator_category;
262 
263  typedef typename Graph::edge edge;
264 
265  edge_iterator() noexcept
266  : g_(nullptr), t_(0)
267  {
268  }
269 
270  edge_iterator(Graph* g, edge t) noexcept
271  : g_(g), t_(t)
272  {
273  }
274 
275  bool operator==(edge_iterator o) const
276  {
277  return t_ == o.t_;
278  }
279 
280  bool operator!=(edge_iterator o) const
281  {
282  return t_ != o.t_;
283  }
284 
285  reference operator*() const
286  {
287  return g_->edge_storage(t_);
288  }
289 
290  pointer operator->() const
291  {
292  return &g_->edge_storage(t_);
293  }
294 
295  edge_iterator operator++()
296  {
297  t_ = operator*().next_succ;
298  return *this;
299  }
300 
301  edge_iterator operator++(int)
302  {
303  edge_iterator ti = *this;
304  t_ = operator*().next_succ;
305  return ti;
306  }
307 
308  operator bool() const
309  {
310  return t_;
311  }
312 
313  edge trans() const
314  {
315  return t_;
316  }
317 
318  protected:
319  Graph* g_;
320  edge t_;
321  };
322 
323  template <typename Graph>
324  class SPOT_API killer_edge_iterator: public edge_iterator<Graph>
325  {
326  typedef edge_iterator<Graph> super;
327  public:
328  typedef typename Graph::state_storage_t state_storage_t;
329  typedef typename Graph::edge edge;
330 
331  killer_edge_iterator(Graph* g, edge t, state_storage_t& src) noexcept
332  : super(g, t), src_(src), prev_(0)
333  {
334  }
335 
336  killer_edge_iterator operator++()
337  {
338  prev_ = this->t_;
339  this->t_ = this->operator*().next_succ;
340  return *this;
341  }
342 
343  killer_edge_iterator operator++(int)
344  {
345  killer_edge_iterator ti = *this;
346  ++*this;
347  return ti;
348  }
349 
350  // Erase the current edge and advance the iterator.
351  void erase()
352  {
353  edge next = this->operator*().next_succ;
354 
355  // Update source state and previous edges
356  if (prev_)
357  {
358  this->g_->edge_storage(prev_).next_succ = next;
359  }
360  else
361  {
362  if (src_.succ == this->t_)
363  src_.succ = next;
364  }
365  if (src_.succ_tail == this->t_)
366  {
367  src_.succ_tail = prev_;
368  SPOT_ASSERT(next == 0);
369  }
370 
371  // Erased edges have themselves as next_succ.
372  this->operator*().next_succ = this->t_;
373 
374  // Advance iterator to next edge.
375  this->t_ = next;
376 
377  ++this->g_->killed_edge_;
378  }
379 
380  protected:
381  state_storage_t& src_;
382  edge prev_;
383  };
384 
385 
387  // State OUT
389 
390  // Fake container listing the outgoing edges of a state.
391 
392  template <typename Graph>
393  class SPOT_API state_out
394  {
395  public:
396  typedef typename Graph::edge edge;
397  state_out(Graph* g, edge t) noexcept
398  : g_(g), t_(t)
399  {
400  }
401 
402  edge_iterator<Graph> begin() const
403  {
404  return {g_, t_};
405  }
406 
407  edge_iterator<Graph> end() const
408  {
409  return {};
410  }
411 
412  void recycle(edge t)
413  {
414  t_ = t;
415  }
416 
417  protected:
418  Graph* g_;
419  edge t_;
420  };
421 
423  // all_trans
425 
426  template <typename Graph>
427  class SPOT_API all_edge_iterator
428  {
429  public:
430  typedef typename std::conditional<std::is_const<Graph>::value,
431  const typename Graph::edge_storage_t,
432  typename Graph::edge_storage_t>::type
433  value_type;
434  typedef value_type& reference;
435  typedef value_type* pointer;
436  typedef std::ptrdiff_t difference_type;
437  typedef std::forward_iterator_tag iterator_category;
438 
439  protected:
440  typedef typename std::conditional<std::is_const<Graph>::value,
441  const typename Graph::edge_vector_t,
442  typename Graph::edge_vector_t>::type
443  tv_t;
444 
445  unsigned t_;
446  tv_t& tv_;
447 
448  void skip_()
449  {
450  unsigned s = tv_.size();
451  do
452  ++t_;
453  while (t_ < s && tv_[t_].next_succ == t_);
454  }
455 
456  public:
457  all_edge_iterator(unsigned pos, tv_t& tv) noexcept
458  : t_(pos), tv_(tv)
459  {
460  skip_();
461  }
462 
463  all_edge_iterator(tv_t& tv) noexcept
464  : t_(tv.size()), tv_(tv)
465  {
466  }
467 
468  all_edge_iterator& operator++()
469  {
470  skip_();
471  return *this;
472  }
473 
474  all_edge_iterator operator++(int)
475  {
476  all_edge_iterator old = *this;
477  ++*this;
478  return old;
479  }
480 
481  bool operator==(all_edge_iterator o) const
482  {
483  return t_ == o.t_;
484  }
485 
486  bool operator!=(all_edge_iterator o) const
487  {
488  return t_ != o.t_;
489  }
490 
491  reference operator*() const
492  {
493  return tv_[t_];
494  }
495 
496  pointer operator->() const
497  {
498  return &tv_[t_];
499  }
500  };
501 
502 
503  template <typename Graph>
504  class SPOT_API all_trans
505  {
506  public:
507  typedef typename std::conditional<std::is_const<Graph>::value,
508  const typename Graph::edge_vector_t,
509  typename Graph::edge_vector_t>::type
510  tv_t;
511  typedef all_edge_iterator<Graph> iter_t;
512  private:
513  tv_t& tv_;
514  public:
515 
516  all_trans(tv_t& tv) noexcept
517  : tv_(tv)
518  {
519  }
520 
521  iter_t begin() const
522  {
523  return {0, tv_};
524  }
525 
526  iter_t end() const
527  {
528  return {tv_};
529  }
530  };
531 
532  class SPOT_API const_universal_dests
533  {
534  private:
535  const unsigned* begin_;
536  const unsigned* end_;
537  unsigned tmp_;
538  public:
539  const_universal_dests(const unsigned* begin, const unsigned* end) noexcept
540  : begin_(begin), end_(end)
541  {
542  }
543 
544  const_universal_dests(unsigned state) noexcept
545  : begin_(&tmp_), end_(&tmp_ + 1), tmp_(state)
546  {
547  }
548 
549  const unsigned* begin() const
550  {
551  return begin_;
552  }
553 
554  const unsigned* end() const
555  {
556  return end_;
557  }
558  };
559 
560  template<class G>
561  class univ_dest_mapper
562  {
563  std::map<std::vector<unsigned>, unsigned> uniq_;
564  G& g_;
565  public:
566 
567  univ_dest_mapper(G& graph)
568  : g_(graph)
569  {
570  }
571 
572  template<class I>
573  unsigned new_univ_dests(I begin, I end)
574  {
575  std::vector<unsigned> tmp(begin, end);
576  std::sort(tmp.begin(), tmp.end());
577  tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end());
578  auto p = uniq_.emplace(tmp, 0);
579  if (p.second)
580  p.first->second = g_.new_univ_dests(tmp.begin(), tmp.end());
581  return p.first->second;
582  }
583 
584  unsigned new_univ_dests(std::vector<unsigned>&& tmp)
585  {
586  std::sort(tmp.begin(), tmp.end());
587  tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end());
588  auto p = uniq_.emplace(tmp, 0);
589  if (p.second)
590  p.first->second = g_.new_univ_dests(tmp.begin(), tmp.end());
591  return p.first->second;
592  }
593  };
594 
595  } // namespace internal
596 
597 
603  template <typename State_Data, typename Edge_Data>
604  class digraph
605  {
606  friend class internal::edge_iterator<digraph>;
607  friend class internal::edge_iterator<const digraph>;
608  friend class internal::killer_edge_iterator<digraph>;
609 
610  public:
611  typedef internal::edge_iterator<digraph> iterator;
612  typedef internal::edge_iterator<const digraph> const_iterator;
613 
614  // Extra data to store on each state or edge.
615  typedef State_Data state_data_t;
616  typedef Edge_Data edge_data_t;
617 
618  // State and edges are identified by their indices in some
619  // vector.
620  typedef unsigned state;
621  typedef unsigned edge;
622 
623  typedef internal::distate_storage<edge,
624  internal::boxed_label<State_Data>>
625  state_storage_t;
626  typedef internal::edge_storage<state, state, edge,
627  internal::boxed_label<Edge_Data>>
628  edge_storage_t;
629  typedef std::vector<state_storage_t> state_vector;
630  typedef std::vector<edge_storage_t> edge_vector_t;
631 
632  // A sequence of universal destination groups of the form:
633  // (n state_1 state_2 ... state_n)*
634  typedef std::vector<unsigned> dests_vector_t;
635 
636  protected:
637  state_vector states_;
638  edge_vector_t edges_;
639  dests_vector_t dests_; // Only used by alternating automata.
640  // Number of erased edges.
641  unsigned killed_edge_;
642  public:
649  digraph(unsigned max_states = 10, unsigned max_trans = 0)
650  : killed_edge_(0)
651  {
652  states_.reserve(max_states);
653  if (max_trans == 0)
654  max_trans = max_states * 2;
655  edges_.reserve(max_trans + 1);
656  // Edge number 0 is not used, because we use this index
657  // to mark the absence of a edge.
658  edges_.resize(1);
659  // This causes edge 0 to be considered as dead.
660  edges_[0].next_succ = 0;
661  }
662 
664  unsigned num_states() const
665  {
666  return states_.size();
667  }
668 
672  unsigned num_edges() const
673  {
674  return edges_.size() - killed_edge_ - 1;
675  }
676 
678  bool is_existential() const
679  {
680  return dests_.empty();
681  }
682 
688  template <typename... Args>
689  state new_state(Args&&... args)
690  {
691  state s = states_.size();
692  states_.emplace_back(std::forward<Args>(args)...);
693  return s;
694  }
695 
702  template <typename... Args>
703  state new_states(unsigned n, Args&&... args)
704  {
705  state s = states_.size();
706  states_.reserve(s + n);
707  while (n--)
708  states_.emplace_back(std::forward<Args>(args)...);
709  return s;
710  }
711 
717  state_storage_t&
718  state_storage(state s)
719  {
720  return states_[s];
721  }
722 
723  const state_storage_t&
724  state_storage(state s) const
725  {
726  return states_[s];
727  }
729 
735  typename state_storage_t::data_t&
736  state_data(state s)
737  {
738  return states_[s].data();
739  }
740 
741  const typename state_storage_t::data_t&
742  state_data(state s) const
743  {
744  return states_[s].data();
745  }
747 
753  edge_storage_t&
754  edge_storage(edge s)
755  {
756  return edges_[s];
757  }
758 
759  const edge_storage_t&
760  edge_storage(edge s) const
761  {
762  return edges_[s];
763  }
765 
771  typename edge_storage_t::data_t&
772  edge_data(edge s)
773  {
774  return edges_[s].data();
775  }
776 
777  const typename edge_storage_t::data_t&
778  edge_data(edge s) const
779  {
780  return edges_[s].data();
781  }
783 
789  template <typename... Args>
790  edge
791  new_edge(state src, state dst, Args&&... args)
792  {
793  edge t = edges_.size();
794  edges_.emplace_back(dst, 0, src, std::forward<Args>(args)...);
795 
796  edge st = states_[src].succ_tail;
797  SPOT_ASSERT(st < t || !st);
798  if (!st)
799  states_[src].succ = t;
800  else
801  edges_[st].next_succ = t;
802  states_[src].succ_tail = t;
803  return t;
804  }
805 
813  template <typename I>
814  state
815  new_univ_dests(I dst_begin, I dst_end)
816  {
817  unsigned sz = std::distance(dst_begin, dst_end);
818  if (sz == 1)
819  return *dst_begin;
820  SPOT_ASSERT(sz > 1);
821  unsigned d = dests_.size();
822  if (!dests_.empty()
823  && &*dst_begin >= &dests_.front()
824  && &*dst_begin <= &dests_.back()
825  && (dests_.capacity() - dests_.size()) < (sz + 1))
826  {
827  // If dst_begin...dst_end points into dests_ and dests_ risk
828  // being reallocated, we have to save the destination
829  // states before we lose them.
830  std::vector<unsigned> tmp(dst_begin, dst_end);
831  dests_.emplace_back(sz);
832  dests_.insert(dests_.end(), tmp.begin(), tmp.end());
833  }
834  else
835  {
836  dests_.emplace_back(sz);
837  dests_.insert(dests_.end(), dst_begin, dst_end);
838  }
839  return ~d;
840  }
841 
848  template <typename I, typename... Args>
849  edge
850  new_univ_edge(state src, I dst_begin, I dst_end, Args&&... args)
851  {
852  return new_edge(src, new_univ_dests(dst_begin, dst_end),
853  std::forward<Args>(args)...);
854  }
855 
861  template <typename... Args>
862  edge
863  new_univ_edge(state src, const std::initializer_list<state>& dsts,
864  Args&&... args)
865  {
866  return new_univ_edge(src, dsts.begin(), dsts.end(),
867  std::forward<Args>(args)...);
868  }
869 
870  internal::const_universal_dests univ_dests(state src) const
871  {
872  if ((int)src < 0)
873  {
874  unsigned pos = ~src;
875  const unsigned* d = dests_.data();
876  d += pos;
877  unsigned num = *d;
878  return { d + 1, d + num + 1 };
879  }
880  else
881  {
882  return src;
883  }
884  }
885 
886  internal::const_universal_dests univ_dests(const edge_storage_t& e) const
887  {
888  return univ_dests(e.dst);
889  }
890 
892  state index_of_state(const state_storage_t& ss) const
893  {
894  SPOT_ASSERT(!states_.empty());
895  return &ss - &states_.front();
896  }
897 
899  edge index_of_edge(const edge_storage_t& tt) const
900  {
901  SPOT_ASSERT(!edges_.empty());
902  return &tt - &edges_.front();
903  }
904 
907  internal::state_out<digraph>
908  out(state src)
909  {
910  return {this, states_[src].succ};
911  }
912 
913  internal::state_out<digraph>
914  out(state_storage_t& src)
915  {
916  return out(index_of_state(src));
917  }
918 
919  internal::state_out<const digraph>
920  out(state src) const
921  {
922  return {this, states_[src].succ};
923  }
924 
925  internal::state_out<const digraph>
926  out(state_storage_t& src) const
927  {
928  return out(index_of_state(src));
929  }
931 
936  internal::killer_edge_iterator<digraph>
937  out_iteraser(state_storage_t& src)
938  {
939  return {this, src.succ, src};
940  }
941 
942  internal::killer_edge_iterator<digraph>
943  out_iteraser(state src)
944  {
945  return out_iteraser(state_storage(src));
946  }
948 
952  const state_vector& states() const
953  {
954  return states_;
955  }
956 
957  state_vector& states()
958  {
959  return states_;
960  }
962 
967  internal::all_trans<const digraph> edges() const
968  {
969  return edges_;
970  }
971 
972  internal::all_trans<digraph> edges()
973  {
974  return edges_;
975  }
977 
986  const edge_vector_t& edge_vector() const
987  {
988  return edges_;
989  }
990 
991  edge_vector_t& edge_vector()
992  {
993  return edges_;
994  }
996 
1003  bool is_valid_edge(edge t) const
1004  {
1005  // Erased edges have their next_succ pointing to
1006  // themselves.
1007  return (t < edges_.size() &&
1008  edges_[t].next_succ != t);
1009  }
1010 
1015  bool is_dead_edge(unsigned t) const
1016  {
1017  return edges_[t].next_succ == t;
1018  }
1019 
1020  bool is_dead_edge(const edge_storage_t& t) const
1021  {
1022  return t.next_succ == index_of_edge(t);
1023  }
1025 
1031  const dests_vector_t& dests_vector() const
1032  {
1033  return dests_;
1034  }
1035 
1036  dests_vector_t& dests_vector()
1037  {
1038  return dests_;
1039  }
1041 
1043  void dump_storage(std::ostream& o) const
1044  {
1045  unsigned tend = edges_.size();
1046  for (unsigned t = 1; t < tend; ++t)
1047  {
1048  o << 't' << t << ": (s"
1049  << edges_[t].src << ", ";
1050  int d = edges_[t].dst;
1051  if (d < 0)
1052  o << 'd' << ~d;
1053  else
1054  o << 's' << d;
1055  o << ") t" << edges_[t].next_succ << '\n';
1056  }
1057  unsigned send = states_.size();
1058  for (unsigned s = 0; s < send; ++s)
1059  {
1060  o << 's' << s << ": t"
1061  << states_[s].succ << " t"
1062  << states_[s].succ_tail << '\n';
1063  }
1064  unsigned dend = dests_.size();
1065  unsigned size = 0;
1066  for (unsigned s = 0; s < dend; ++s)
1067  {
1068  o << 'd' << s << ": ";
1069  if (size == 0)
1070  {
1071  o << '#';
1072  size = dests_[s];
1073  }
1074  else
1075  {
1076  o << 's';
1077  --size;
1078  }
1079  o << dests_[s] << '\n';
1080  }
1081  }
1082 
1083  enum dump_storage_items {
1084  DSI_GraphHeader = 1,
1085  DSI_GraphFooter = 2,
1086  DSI_StatesHeader = 4,
1087  DSI_StatesBody = 8,
1088  DSI_StatesFooter = 16,
1089  DSI_States = DSI_StatesHeader | DSI_StatesBody | DSI_StatesFooter,
1090  DSI_EdgesHeader = 32,
1091  DSI_EdgesBody = 64,
1092  DSI_EdgesFooter = 128,
1093  DSI_Edges = DSI_EdgesHeader | DSI_EdgesBody | DSI_EdgesFooter,
1094  DSI_DestsHeader = 256,
1095  DSI_DestsBody = 512,
1096  DSI_DestsFooter = 1024,
1097  DSI_Dests = DSI_DestsHeader | DSI_DestsBody | DSI_DestsFooter,
1098  DSI_All =
1099  DSI_GraphHeader | DSI_States | DSI_Edges | DSI_Dests | DSI_GraphFooter,
1100  };
1101 
1103  void dump_storage_as_dot(std::ostream& o, int dsi = DSI_All) const
1104  {
1105  if (dsi & DSI_GraphHeader)
1106  o << "digraph g { \nnode [shape=plaintext]\n";
1107  unsigned send = states_.size();
1108  if (dsi & DSI_StatesHeader)
1109  {
1110  o << ("states [label=<\n"
1111  "<table border='0' cellborder='1' cellspacing='0'>\n"
1112  "<tr><td sides='b' bgcolor='yellow' port='s'>states</td>\n");
1113  for (unsigned s = 0; s < send; ++s)
1114  o << "<td sides='b' bgcolor='yellow' port='s" << s << "'>"
1115  << s << "</td>\n";
1116  o << "</tr>\n";
1117  }
1118  if (dsi & DSI_StatesBody)
1119  {
1120  o << "<tr><td port='ss'>succ</td>\n";
1121  for (unsigned s = 0; s < send; ++s)
1122  {
1123  o << "<td port='ss" << s;
1124  if (states_[s].succ)
1125  o << "' bgcolor='cyan";
1126  o << "'>" << states_[s].succ << "</td>\n";
1127  }
1128  o << "</tr><tr><td port='st'>succ_tail</td>\n";
1129  for (unsigned s = 0; s < send; ++s)
1130  {
1131  o << "<td port='st" << s;
1132  if (states_[s].succ_tail)
1133  o << "' bgcolor='cyan";
1134  o << "'>" << states_[s].succ_tail << "</td>\n";
1135  }
1136  o << "</tr>\n";
1137  }
1138  if (dsi & DSI_StatesFooter)
1139  o << "</table>>]\n";
1140  unsigned eend = edges_.size();
1141  if (dsi & DSI_EdgesHeader)
1142  {
1143  o << ("edges [label=<\n"
1144  "<table border='0' cellborder='1' cellspacing='0'>\n"
1145  "<tr><td sides='b' bgcolor='cyan' port='e'>edges</td>\n");
1146  for (unsigned e = 1; e < eend; ++e)
1147  {
1148  o << "<td sides='b' bgcolor='"
1149  << (e != edges_[e].next_succ ? "cyan" : "gray")
1150  << "' port='e" << e << "'>" << e << "</td>\n";
1151  }
1152  o << "</tr>";
1153  }
1154  if (dsi & DSI_EdgesBody)
1155  {
1156  o << "<tr><td port='ed'>dst</td>\n";
1157  for (unsigned e = 1; e < eend; ++e)
1158  {
1159  o << "<td port='ed" << e;
1160  int d = edges_[e].dst;
1161  if (d < 0)
1162  o << "' bgcolor='pink'>~" << ~d;
1163  else
1164  o << "' bgcolor='yellow'>" << d;
1165  o << "</td>\n";
1166  }
1167  o << "</tr><tr><td port='en'>next_succ</td>\n";
1168  for (unsigned e = 1; e < eend; ++e)
1169  {
1170  o << "<td port='en" << e;
1171  if (edges_[e].next_succ)
1172  {
1173  if (edges_[e].next_succ != e)
1174  o << "' bgcolor='cyan";
1175  else
1176  o << "' bgcolor='gray";
1177  }
1178  o << "'>" << edges_[e].next_succ << "</td>\n";
1179  }
1180  o << "</tr><tr><td port='es'>src</td>\n";
1181  for (unsigned e = 1; e < eend; ++e)
1182  o << "<td port='es" << e << "' bgcolor='yellow'>"
1183  << edges_[e].src << "</td>\n";
1184  o << "</tr>\n";
1185  }
1186  if (dsi & DSI_EdgesFooter)
1187  o << "</table>>]\n";
1188  if (!dests_.empty())
1189  {
1190  unsigned dend = dests_.size();
1191  if (dsi & DSI_DestsHeader)
1192  {
1193  o << ("dests [label=<\n"
1194  "<table border='0' cellborder='1' cellspacing='0'>\n"
1195  "<tr><td sides='b' bgcolor='pink' port='d'>dests</td>\n");
1196  unsigned d = 0;
1197  while (d < dend)
1198  {
1199  o << "<td sides='b' bgcolor='pink' port='d"
1200  << d << "'>~" << d << "</td>\n";
1201  unsigned cnt = dests_[d];
1202  d += cnt + 1;
1203  while (cnt--)
1204  o << "<td sides='b'></td>\n";
1205  }
1206  o << "</tr>\n";
1207  }
1208  if (dsi & DSI_DestsBody)
1209  {
1210  o << "<tr><td port='dd'>#cnt/dst</td>\n";
1211  unsigned d = 0;
1212  while (d < dend)
1213  {
1214  unsigned cnt = dests_[d];
1215  o << "<td port='d'>#" << cnt << "</td>\n";
1216  ++d;
1217  while (cnt--)
1218  {
1219  o << "<td bgcolor='yellow' port='dd"
1220  << d << "'>" << dests_[d] << "</td>\n";
1221  ++d;
1222  }
1223  }
1224  o << "</tr>\n";
1225  }
1226  if (dsi & DSI_DestsFooter)
1227  o << "</table>>]\n";
1228  }
1229  if (dsi & DSI_GraphFooter)
1230  o << "}\n";
1231  }
1232 
1239  {
1240  if (killed_edge_ == 0)
1241  return;
1242  auto i = std::remove_if(edges_.begin() + 1, edges_.end(),
1243  [this](const edge_storage_t& t) {
1244  return this->is_dead_edge(t);
1245  });
1246  edges_.erase(i, edges_.end());
1247  killed_edge_ = 0;
1248  }
1249 
1255  template<class Predicate = std::less<edge_storage_t>>
1256  void sort_edges_(Predicate p = Predicate())
1257  {
1258  //std::cerr << "\nbefore\n";
1259  //dump_storage(std::cerr);
1260  std::stable_sort(edges_.begin() + 1, edges_.end(), p);
1261  }
1262 
1272  template<class Predicate = std::less<edge_storage_t>>
1273  void sort_edges_srcfirst_(Predicate p = Predicate(),
1274  parallel_policy ppolicy = parallel_policy())
1275  {
1276  SPOT_ASSERT(!edges_.empty());
1277  const unsigned ns = num_states();
1278  std::vector<unsigned> idx_list(ns+1);
1279  edge_vector_t new_edges;
1280  new_edges.reserve(edges_.size());
1281  new_edges.resize(1);
1282  // This causes edge 0 to be considered as dead.
1283  new_edges[0].next_succ = 0;
1284  // Copy all edges so that they are sorted by src
1285  for (unsigned s = 0; s < ns; ++s)
1286  {
1287  idx_list[s] = new_edges.size();
1288  for (const auto& e : out(s))
1289  new_edges.push_back(e);
1290  }
1291  idx_list[ns] = new_edges.size();
1292  // New edge sorted by source
1293  // If we have few edge or only one threads
1294  // Benchmark few?
1295  auto bne = new_edges.begin();
1296 #ifndef SPOT_ENABLE_PTHREAD
1297  (void) ppolicy;
1298 #else
1299  unsigned nthreads = ppolicy.nthreads();
1300  if (nthreads <= 1)
1301 #endif
1302  {
1303  for (unsigned s = 0u; s < ns; ++s)
1304  std::stable_sort(bne + idx_list[s],
1305  bne + idx_list[s+1], p);
1306  }
1307 #ifdef SPOT_ENABLE_PTHREAD
1308  else
1309  {
1310  static std::vector<std::thread> tv;
1311  SPOT_ASSERT(tv.empty());
1312  tv.resize(nthreads);
1313  // FIXME: Due to the way these thread advance into the state
1314  // vector, they access very close memory location. It would
1315  // seems more cache friendly to have threads work on blocks
1316  // of continuous states.
1317  for (unsigned id = 0; id < nthreads; ++id)
1318  tv[id] = std::thread(
1319  [bne, id, ns, &idx_list, p, nthreads]()
1320  {
1321  for (unsigned s = id; s < ns; s += nthreads)
1322  std::stable_sort(bne + idx_list[s],
1323  bne + idx_list[s+1], p);
1324  return;
1325  });
1326  for (auto& t : tv)
1327  t.join();
1328  tv.clear();
1329  }
1330 #endif
1331  std::swap(edges_, new_edges);
1332  // Like after normal sort_edges, they need to be chained before usage
1333  }
1334 
1342  template<bool Stable = false, class Predicate = std::less<edge_storage_t>>
1343  void sort_edges_of_(Predicate p = Predicate(),
1344  const std::vector<bool>* to_sort_ptr = nullptr)
1345  {
1346  SPOT_ASSERT((to_sort_ptr == nullptr)
1347  || (to_sort_ptr->size() == num_states()));
1348  //std::cerr << "\nbefore\n";
1349  //dump_storage(std::cerr);
1350  auto pi = [&](unsigned t1, unsigned t2)
1351  {return p(edges_[t1], edges_[t2]); };
1352 
1353  // Sort the outgoing edges of each selected state according
1354  // to predicate p. Do that in place.
1355  std::vector<unsigned> sort_idx_;
1356  unsigned ns = num_states();
1357  for (unsigned i = 0; i < ns; ++i)
1358  {
1359  if (to_sort_ptr && !(*to_sort_ptr)[i])
1360  continue;
1361  unsigned t = states_[i].succ;
1362  if (t == 0)
1363  continue;
1364  sort_idx_.clear();
1365  do
1366  {
1367  sort_idx_.push_back(t);
1368  t = edges_[t].next_succ;
1369  } while (t != 0);
1370  if constexpr (Stable)
1371  std::stable_sort(sort_idx_.begin(), sort_idx_.end(), pi);
1372  else
1373  std::sort(sort_idx_.begin(), sort_idx_.end(), pi);
1374  // Update the graph
1375  states_[i].succ = sort_idx_.front();
1376  states_[i].succ_tail = sort_idx_.back();
1377  const unsigned n_outs_n1 = sort_idx_.size() - 1;
1378  for (unsigned k = 0; k < n_outs_n1; ++k)
1379  edges_[sort_idx_[k]].next_succ = sort_idx_[k+1];
1380  edges_[sort_idx_.back()].next_succ = 0; // terminal
1381  }
1382  // Done
1383  }
1384 
1390  {
1391  state last_src = -1U;
1392  edge tend = edges_.size();
1393  for (edge t = 1; t < tend; ++t)
1394  {
1395  state src = edges_[t].src;
1396  if (src != last_src)
1397  {
1398  states_[src].succ = t;
1399  if (last_src != -1U)
1400  {
1401  states_[last_src].succ_tail = t - 1;
1402  edges_[t - 1].next_succ = 0;
1403  }
1404  while (++last_src != src)
1405  {
1406  states_[last_src].succ = 0;
1407  states_[last_src].succ_tail = 0;
1408  }
1409  }
1410  else
1411  {
1412  edges_[t - 1].next_succ = t;
1413  }
1414  }
1415  if (last_src != -1U)
1416  {
1417  states_[last_src].succ_tail = tend - 1;
1418  edges_[tend - 1].next_succ = 0;
1419  }
1420  unsigned send = states_.size();
1421  while (++last_src != send)
1422  {
1423  states_[last_src].succ = 0;
1424  states_[last_src].succ_tail = 0;
1425  }
1426  //std::cerr << "\nafter\n";
1427  //dump_storage(std::cerr);
1428  }
1429 
1435  void rename_states_(const std::vector<unsigned>& newst)
1436  {
1437  SPOT_ASSERT(newst.size() == states_.size());
1438  unsigned tend = edges_.size();
1439  for (unsigned t = 1; t < tend; t++)
1440  {
1441  edges_[t].dst = newst[edges_[t].dst];
1442  edges_[t].src = newst[edges_[t].src];
1443  }
1444  }
1445 
1462  void defrag_states(const std::vector<unsigned>& newst, unsigned used_states)
1463  {
1464  SPOT_ASSERT(newst.size() >= states_.size());
1465  SPOT_ASSERT(used_states > 0);
1466 
1467  //std::cerr << "\nbefore defrag\n";
1468  //dump_storage(std::cerr);
1469 
1470  // Permute all states in states_, as indicated by newst.
1471  // This will put erased states after used_states.
1472  permute_vector(states_, newst);
1473  unsigned send = states_.size();
1474  for (state s = used_states; s < send; ++s)
1475  {
1476  // This is an erased state. Mark all its edges as
1477  // dead (i.e., t.next_succ should point to t for each of
1478  // them).
1479  auto t = states_[s].succ;
1480  while (t)
1481  std::swap(t, edges_[t].next_succ);
1482  }
1483  states_.resize(used_states);
1484 
1485  // Shift all edges in edges_. The algorithm is
1486  // similar to remove_if, but it also keeps the correspondence
1487  // between the old and new index as newidx[old] = new.
1488  //
1489  // If you change anything to this logic, you might want to
1490  // double check twa_graph::defrag_states where we need to
1491  // predict the new edges indices in order to update
1492  // highlight-edges.
1493  unsigned tend = edges_.size();
1494  std::vector<edge> newidx(tend);
1495  unsigned dest = 1;
1496  for (edge t = 1; t < tend; ++t)
1497  {
1498  if (is_dead_edge(t))
1499  continue;
1500  if (t != dest)
1501  edges_[dest] = std::move(edges_[t]);
1502  newidx[t] = dest;
1503  ++dest;
1504  }
1505  edges_.resize(dest);
1506  killed_edge_ = 0;
1507 
1508  // Adjust next_succ and dst pointers in all edges.
1509  for (edge t = 1; t < dest; ++t)
1510  {
1511  auto& tr = edges_[t];
1512  tr.src = newst[tr.src];
1513  tr.dst = newst[tr.dst];
1514  tr.next_succ = newidx[tr.next_succ];
1515  }
1516 
1517  // Adjust succ and succ_tails pointers in all states.
1518  for (auto& s: states_)
1519  {
1520  s.succ = newidx[s.succ];
1521  s.succ_tail = newidx[s.succ_tail];
1522  }
1523 
1524  //std::cerr << "\nafter defrag\n";
1525  //dump_storage(std::cerr);
1526  }
1527 
1528  // prototype was changed in Spot 2.10
1529  SPOT_DEPRECATED("use reference version of this method")
1530  void defrag_states(std::vector<unsigned>&& newst, unsigned used_states)
1531  {
1532  return defrag_states(newst, used_states);
1533  }
1535  };
1536 }
A directed graph.
Definition: graph.hh:605
unsigned num_states() const
The number of states in the automaton.
Definition: graph.hh:664
void dump_storage_as_dot(std::ostream &o, int dsi=DSI_All) const
Dump the state and edge storage for debugging.
Definition: graph.hh:1103
bool is_dead_edge(const edge_storage_t &t) const
Tests whether an edge has been erased.
Definition: graph.hh:1020
void dump_storage(std::ostream &o) const
Dump the state and edge storage for debugging.
Definition: graph.hh:1043
const edge_storage_t::data_t & edge_data(edge s) const
return the Edge_Data of an edge.
Definition: graph.hh:778
internal::killer_edge_iterator< digraph > out_iteraser(state_storage_t &src)
Return a fake container with all edges leaving src, allowing erasure.
Definition: graph.hh:937
internal::state_out< digraph > out(state_storage_t &src)
Return a fake container with all edges leaving src.
Definition: graph.hh:914
const state_vector & states() const
Return the vector of states.
Definition: graph.hh:952
void sort_edges_of_(Predicate p=Predicate(), const std::vector< bool > *to_sort_ptr=nullptr)
Sort edges of the given states.
Definition: graph.hh:1343
state new_states(unsigned n, Args &&... args)
Create n new states.
Definition: graph.hh:703
state new_state(Args &&... args)
Create a new states.
Definition: graph.hh:689
edge index_of_edge(const edge_storage_t &tt) const
Convert a storage reference into an edge number.
Definition: graph.hh:899
internal::state_out< const digraph > out(state_storage_t &src) const
Return a fake container with all edges leaving src.
Definition: graph.hh:926
bool is_valid_edge(edge t) const
Test whether the given edge is valid.
Definition: graph.hh:1003
void sort_edges_srcfirst_(Predicate p=Predicate(), parallel_policy ppolicy=parallel_policy())
Sort all edges by src first, then, within edges of the same source use the predicate.
Definition: graph.hh:1273
edge_storage_t::data_t & edge_data(edge s)
return the Edge_Data of an edge.
Definition: graph.hh:772
const dests_vector_t & dests_vector() const
The vector used to store universal destinations.
Definition: graph.hh:1031
dests_vector_t & dests_vector()
The vector used to store universal destinations.
Definition: graph.hh:1036
edge_storage_t & edge_storage(edge s)
return a reference to the storage of an edge
Definition: graph.hh:754
const edge_storage_t & edge_storage(edge s) const
return a reference to the storage of an edge
Definition: graph.hh:760
edge new_univ_edge(state src, const std::initializer_list< state > &dsts, Args &&... args)
Create a new universal edge.
Definition: graph.hh:863
bool is_existential() const
Whether the automaton uses only existential branching.
Definition: graph.hh:678
const state_storage_t & state_storage(state s) const
return a reference to the storage of a state
Definition: graph.hh:724
internal::state_out< digraph > out(state src)
Return a fake container with all edges leaving src.
Definition: graph.hh:908
internal::killer_edge_iterator< digraph > out_iteraser(state src)
Return a fake container with all edges leaving src, allowing erasure.
Definition: graph.hh:943
void remove_dead_edges_()
Remove all dead edges.
Definition: graph.hh:1238
digraph(unsigned max_states=10, unsigned max_trans=0)
Construct an empty graph.
Definition: graph.hh:649
state new_univ_dests(I dst_begin, I dst_end)
Create a new universal destination group.
Definition: graph.hh:815
state_vector & states()
Return the vector of states.
Definition: graph.hh:957
void rename_states_(const std::vector< unsigned > &newst)
Rename all the states in the edge vector.
Definition: graph.hh:1435
void defrag_states(const std::vector< unsigned > &newst, unsigned used_states)
Rename and remove states.
Definition: graph.hh:1462
state_storage_t & state_storage(state s)
return a reference to the storage of a state
Definition: graph.hh:718
internal::state_out< const digraph > out(state src) const
Return a fake container with all edges leaving src.
Definition: graph.hh:920
unsigned num_edges() const
The number of edges in the automaton.
Definition: graph.hh:672
state_storage_t::data_t & state_data(state s)
return the State_Data associated to a state
Definition: graph.hh:736
const edge_vector_t & edge_vector() const
Return the vector of all edges.
Definition: graph.hh:986
state index_of_state(const state_storage_t &ss) const
Convert a storage reference into a state number.
Definition: graph.hh:892
void sort_edges_(Predicate p=Predicate())
Sort all edges according to a predicate.
Definition: graph.hh:1256
bool is_dead_edge(unsigned t) const
Tests whether an edge has been erased.
Definition: graph.hh:1015
edge_vector_t & edge_vector()
Return the vector of all edges.
Definition: graph.hh:991
edge new_univ_edge(state src, I dst_begin, I dst_end, Args &&... args)
Create a new universal edge.
Definition: graph.hh:850
void chain_edges_()
Reconstruct the chain of outgoing edges.
Definition: graph.hh:1389
internal::all_trans< digraph > edges()
Return a fake container with all edges (excluding erased edges)
Definition: graph.hh:972
edge new_edge(state src, state dst, Args &&... args)
Create a new edge.
Definition: graph.hh:791
internal::all_trans< const digraph > edges() const
Return a fake container with all edges (excluding erased edges)
Definition: graph.hh:967
const state_storage_t::data_t & state_data(state s) const
return the State_Data associated to a state
Definition: graph.hh:742
This class is used to tell parallel algorithms what resources they may use.
Definition: common.hh:155
Abstract class for states.
Definition: twa.hh:47
@ tt
True.
@ G
Globally.
SPOT_DEPRECATED("use to_parity() instead") twa_graph_ptr iar(const const_twa_graph_ptr &aut
Turn a Rabin-like or Streett-like automaton into a parity automaton based on the index appearence rec...
Definition: automata.hh:26

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