spot  2.16
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 an 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:
612  typedef internal::edge_iterator<digraph> iterator;
614  typedef internal::edge_iterator<const digraph> const_iterator;
615 
616  // Extra data to store on each state or edge.
617  typedef State_Data state_data_t;
618  typedef Edge_Data edge_data_t;
619 
620  // State and edges are identified by their indices in some
621  // vector.
622  typedef unsigned state;
623  typedef unsigned edge;
624 
626  typedef internal::distate_storage<edge,
627  internal::boxed_label<State_Data>>
630  typedef internal::edge_storage<state, state, edge,
631  internal::boxed_label<Edge_Data>>
634  typedef std::vector<state_storage_t> state_vector;
636  typedef std::vector<edge_storage_t> edge_vector_t;
637 
638  // A sequence of universal destination groups of the form:
639  // (n state_1 state_2 ... state_n)*
641  typedef std::vector<unsigned> dests_vector_t;
642 
643  protected:
647  unsigned killed_edge_;
648  public:
655  digraph(unsigned max_states = 10, unsigned max_trans = 0)
656  : killed_edge_(0)
657  {
658  states_.reserve(max_states);
659  if (max_trans == 0)
660  max_trans = max_states * 2;
661  edges_.reserve(max_trans + 1);
662  // Edge number 0 is not used, because we use this index
663  // to mark the absence of an edge.
664  edges_.resize(1);
665  // This causes edge 0 to be considered as dead.
666  edges_[0].next_succ = 0;
667  }
668 
670  unsigned num_states() const
671  {
672  return states_.size();
673  }
674 
678  unsigned num_edges() const
679  {
680  return edges_.size() - killed_edge_ - 1;
681  }
682 
684  bool is_existential() const
685  {
686  return dests_.empty();
687  }
688 
694  template <typename... Args>
695  state new_state(Args&&... args)
696  {
697  state s = states_.size();
698  states_.emplace_back(std::forward<Args>(args)...);
699  return s;
700  }
701 
708  template <typename... Args>
709  state new_states(unsigned n, Args&&... args)
710  {
711  state s = states_.size();
712  states_.reserve(s + n);
713  while (n--)
714  states_.emplace_back(std::forward<Args>(args)...);
715  return s;
716  }
717 
725  {
726  return states_[s];
727  }
728 
729  const state_storage_t&
731  {
732  return states_[s];
733  }
735 
741  typename state_storage_t::data_t&
743  {
744  return states_[s].data();
745  }
746 
747  const typename state_storage_t::data_t&
748  state_data(state s) const
749  {
750  return states_[s].data();
751  }
753 
761  {
762  return edges_[s];
763  }
764 
765  const edge_storage_t&
767  {
768  return edges_[s];
769  }
771 
777  typename edge_storage_t::data_t&
779  {
780  return edges_[s].data();
781  }
782 
783  const typename edge_storage_t::data_t&
784  edge_data(edge s) const
785  {
786  return edges_[s].data();
787  }
789 
795  template <typename... Args>
796  edge
797  new_edge(state src, state dst, Args&&... args)
798  {
799  edge t = edges_.size();
800  edges_.emplace_back(dst, 0, src, std::forward<Args>(args)...);
801 
802  edge st = states_[src].succ_tail;
803  SPOT_ASSERT(st < t || !st);
804  if (!st)
805  states_[src].succ = t;
806  else
807  edges_[st].next_succ = t;
808  states_[src].succ_tail = t;
809  return t;
810  }
811 
819  template <typename I>
820  state
821  new_univ_dests(I dst_begin, I dst_end)
822  {
823  unsigned sz = std::distance(dst_begin, dst_end);
824  if (sz == 1)
825  return *dst_begin;
826  SPOT_ASSERT(sz > 1);
827  unsigned d = dests_.size();
828  if (!dests_.empty()
829  && &*dst_begin >= &dests_.front()
830  && &*dst_begin <= &dests_.back()
831  && (dests_.capacity() - dests_.size()) < (sz + 1))
832  {
833  // If dst_begin...dst_end points into dests_ and dests_ risk
834  // being reallocated, we have to save the destination
835  // states before we lose them.
836  std::vector<unsigned> tmp(dst_begin, dst_end);
837  dests_.emplace_back(sz);
838  dests_.insert(dests_.end(), tmp.begin(), tmp.end());
839  }
840  else
841  {
842  dests_.emplace_back(sz);
843  dests_.insert(dests_.end(), dst_begin, dst_end);
844  }
845  return ~d;
846  }
847 
854  template <typename I, typename... Args>
855  edge
856  new_univ_edge(state src, I dst_begin, I dst_end, Args&&... args)
857  {
858  return new_edge(src, new_univ_dests(dst_begin, dst_end),
859  std::forward<Args>(args)...);
860  }
861 
867  template <typename... Args>
868  edge
869  new_univ_edge(state src, const std::initializer_list<state>& dsts,
870  Args&&... args)
871  {
872  return new_univ_edge(src, dsts.begin(), dsts.end(),
873  std::forward<Args>(args)...);
874  }
875 
877  internal::const_universal_dests univ_dests(state src) const
878  {
879  if ((int)src < 0)
880  {
881  unsigned pos = ~src;
882  const unsigned* d = dests_.data();
883  d += pos;
884  unsigned num = *d;
885  return { d + 1, d + num + 1 };
886  }
887  else
888  {
889  return src;
890  }
891  }
892 
894  internal::const_universal_dests univ_dests(const edge_storage_t& e) const
895  {
896  return univ_dests(e.dst);
897  }
898 
901  {
902  SPOT_ASSERT(!states_.empty());
903  return &ss - &states_.front();
904  }
905 
908  {
909  SPOT_ASSERT(!edges_.empty());
910  return &tt - &edges_.front();
911  }
912 
915  internal::state_out<digraph>
916  out(state src)
917  {
918  return {this, states_[src].succ};
919  }
920 
921  internal::state_out<digraph>
923  {
924  return out(index_of_state(src));
925  }
926 
927  internal::state_out<const digraph>
928  out(state src) const
929  {
930  return {this, states_[src].succ};
931  }
932 
933  internal::state_out<const digraph>
934  out(state_storage_t& src) const
935  {
936  return out(index_of_state(src));
937  }
939 
944  internal::killer_edge_iterator<digraph>
946  {
947  return {this, src.succ, src};
948  }
949 
950  internal::killer_edge_iterator<digraph>
952  {
953  return out_iteraser(state_storage(src));
954  }
956 
960  const state_vector& states() const
961  {
962  return states_;
963  }
964 
966  {
967  return states_;
968  }
970 
975  internal::all_trans<const digraph> edges() const
976  {
977  return edges_;
978  }
979 
980  internal::all_trans<digraph> edges()
981  {
982  return edges_;
983  }
985 
994  const edge_vector_t& edge_vector() const
995  {
996  return edges_;
997  }
998 
1000  {
1001  return edges_;
1002  }
1004 
1011  bool is_valid_edge(edge t) const
1012  {
1013  // Erased edges have their next_succ pointing to
1014  // themselves.
1015  return (t < edges_.size() &&
1016  edges_[t].next_succ != t);
1017  }
1018 
1023  bool is_dead_edge(unsigned t) const
1024  {
1025  return edges_[t].next_succ == t;
1026  }
1027 
1028  bool is_dead_edge(const edge_storage_t& t) const
1029  {
1030  return t.next_succ == index_of_edge(t);
1031  }
1033 
1040  {
1041  return dests_;
1042  }
1043 
1045  {
1046  return dests_;
1047  }
1049 
1051  void dump_storage(std::ostream& o) const
1052  {
1053  unsigned tend = edges_.size();
1054  for (unsigned t = 1; t < tend; ++t)
1055  {
1056  o << 't' << t << ": (s"
1057  << edges_[t].src << ", ";
1058  int d = edges_[t].dst;
1059  if (d < 0)
1060  o << 'd' << ~d;
1061  else
1062  o << 's' << d;
1063  o << ") t" << edges_[t].next_succ << '\n';
1064  }
1065  unsigned send = states_.size();
1066  for (unsigned s = 0; s < send; ++s)
1067  {
1068  o << 's' << s << ": t"
1069  << states_[s].succ << " t"
1070  << states_[s].succ_tail << '\n';
1071  }
1072  unsigned dend = dests_.size();
1073  unsigned size = 0;
1074  for (unsigned s = 0; s < dend; ++s)
1075  {
1076  o << 'd' << s << ": ";
1077  if (size == 0)
1078  {
1079  o << '#';
1080  size = dests_[s];
1081  }
1082  else
1083  {
1084  o << 's';
1085  --size;
1086  }
1087  o << dests_[s] << '\n';
1088  }
1089  }
1090 
1093  DSI_GraphHeader = 1,
1094  DSI_GraphFooter = 2,
1095  DSI_StatesHeader = 4,
1096  DSI_StatesBody = 8,
1097  DSI_StatesFooter = 16,
1098  DSI_States = DSI_StatesHeader | DSI_StatesBody | DSI_StatesFooter,
1099  DSI_EdgesHeader = 32,
1100  DSI_EdgesBody = 64,
1101  DSI_EdgesFooter = 128,
1102  DSI_Edges = DSI_EdgesHeader | DSI_EdgesBody | DSI_EdgesFooter,
1103  DSI_DestsHeader = 256,
1104  DSI_DestsBody = 512,
1105  DSI_DestsFooter = 1024,
1106  DSI_Dests = DSI_DestsHeader | DSI_DestsBody | DSI_DestsFooter,
1107  DSI_All =
1108  DSI_GraphHeader | DSI_States | DSI_Edges | DSI_Dests | DSI_GraphFooter,
1109  };
1110 
1112  void dump_storage_as_dot(std::ostream& o, int dsi = DSI_All) const
1113  {
1114  if (dsi & DSI_GraphHeader)
1115  o << "digraph g { \nnode [shape=plaintext]\n";
1116  unsigned send = states_.size();
1117  if (dsi & DSI_StatesHeader)
1118  {
1119  o << ("states [label=<\n"
1120  "<table border='0' cellborder='1' cellspacing='0'>\n"
1121  "<tr><td sides='b' bgcolor='yellow' port='s'>states</td>\n");
1122  for (unsigned s = 0; s < send; ++s)
1123  o << "<td sides='b' bgcolor='yellow' port='s" << s << "'>"
1124  << s << "</td>\n";
1125  o << "</tr>\n";
1126  }
1127  if (dsi & DSI_StatesBody)
1128  {
1129  o << "<tr><td port='ss'>succ</td>\n";
1130  for (unsigned s = 0; s < send; ++s)
1131  {
1132  o << "<td port='ss" << s;
1133  if (states_[s].succ)
1134  o << "' bgcolor='cyan";
1135  o << "'>" << states_[s].succ << "</td>\n";
1136  }
1137  o << "</tr><tr><td port='st'>succ_tail</td>\n";
1138  for (unsigned s = 0; s < send; ++s)
1139  {
1140  o << "<td port='st" << s;
1141  if (states_[s].succ_tail)
1142  o << "' bgcolor='cyan";
1143  o << "'>" << states_[s].succ_tail << "</td>\n";
1144  }
1145  o << "</tr>\n";
1146  }
1147  if (dsi & DSI_StatesFooter)
1148  o << "</table>>]\n";
1149  unsigned eend = edges_.size();
1150  if (dsi & DSI_EdgesHeader)
1151  {
1152  o << ("edges [label=<\n"
1153  "<table border='0' cellborder='1' cellspacing='0'>\n"
1154  "<tr><td sides='b' bgcolor='cyan' port='e'>edges</td>\n");
1155  for (unsigned e = 1; e < eend; ++e)
1156  {
1157  o << "<td sides='b' bgcolor='"
1158  << (e != edges_[e].next_succ ? "cyan" : "gray")
1159  << "' port='e" << e << "'>" << e << "</td>\n";
1160  }
1161  o << "</tr>";
1162  }
1163  if (dsi & DSI_EdgesBody)
1164  {
1165  o << "<tr><td port='ed'>dst</td>\n";
1166  for (unsigned e = 1; e < eend; ++e)
1167  {
1168  o << "<td port='ed" << e;
1169  int d = edges_[e].dst;
1170  if (d < 0)
1171  o << "' bgcolor='pink'>~" << ~d;
1172  else
1173  o << "' bgcolor='yellow'>" << d;
1174  o << "</td>\n";
1175  }
1176  o << "</tr><tr><td port='en'>next_succ</td>\n";
1177  for (unsigned e = 1; e < eend; ++e)
1178  {
1179  o << "<td port='en" << e;
1180  if (edges_[e].next_succ)
1181  {
1182  if (edges_[e].next_succ != e)
1183  o << "' bgcolor='cyan";
1184  else
1185  o << "' bgcolor='gray";
1186  }
1187  o << "'>" << edges_[e].next_succ << "</td>\n";
1188  }
1189  o << "</tr><tr><td port='es'>src</td>\n";
1190  for (unsigned e = 1; e < eend; ++e)
1191  o << "<td port='es" << e << "' bgcolor='yellow'>"
1192  << edges_[e].src << "</td>\n";
1193  o << "</tr>\n";
1194  }
1195  if (dsi & DSI_EdgesFooter)
1196  o << "</table>>]\n";
1197  if (!dests_.empty())
1198  {
1199  unsigned dend = dests_.size();
1200  if (dsi & DSI_DestsHeader)
1201  {
1202  o << ("dests [label=<\n"
1203  "<table border='0' cellborder='1' cellspacing='0'>\n"
1204  "<tr><td sides='b' bgcolor='pink' port='d'>dests</td>\n");
1205  unsigned d = 0;
1206  while (d < dend)
1207  {
1208  o << "<td sides='b' bgcolor='pink' port='d"
1209  << d << "'>~" << d << "</td>\n";
1210  unsigned cnt = dests_[d];
1211  d += cnt + 1;
1212  while (cnt--)
1213  o << "<td sides='b'></td>\n";
1214  }
1215  o << "</tr>\n";
1216  }
1217  if (dsi & DSI_DestsBody)
1218  {
1219  o << "<tr><td port='dd'>#cnt/dst</td>\n";
1220  unsigned d = 0;
1221  while (d < dend)
1222  {
1223  unsigned cnt = dests_[d];
1224  o << "<td port='d'>#" << cnt << "</td>\n";
1225  ++d;
1226  while (cnt--)
1227  {
1228  o << "<td bgcolor='yellow' port='dd"
1229  << d << "'>" << dests_[d] << "</td>\n";
1230  ++d;
1231  }
1232  }
1233  o << "</tr>\n";
1234  }
1235  if (dsi & DSI_DestsFooter)
1236  o << "</table>>]\n";
1237  }
1238  if (dsi & DSI_GraphFooter)
1239  o << "}\n";
1240  }
1241 
1248  {
1249  if (killed_edge_ == 0)
1250  return;
1251  auto i = std::remove_if(edges_.begin() + 1, edges_.end(),
1252  [this](const edge_storage_t& t) {
1253  return this->is_dead_edge(t);
1254  });
1255  edges_.erase(i, edges_.end());
1256  killed_edge_ = 0;
1257  }
1258 
1264  template<class Predicate = std::less<edge_storage_t>>
1265  void sort_edges_(Predicate p = Predicate())
1266  {
1267  //std::cerr << "\nbefore\n";
1268  //dump_storage(std::cerr);
1269  std::stable_sort(edges_.begin() + 1, edges_.end(), p);
1270  }
1271 
1281  template<class Predicate = std::less<edge_storage_t>>
1282  void sort_edges_srcfirst_(Predicate p = Predicate(),
1283  parallel_policy ppolicy = parallel_policy())
1284  {
1285  SPOT_ASSERT(!edges_.empty());
1286  const unsigned ns = num_states();
1287  std::vector<unsigned> idx_list(ns+1);
1288  edge_vector_t new_edges;
1289  new_edges.reserve(edges_.size());
1290  new_edges.resize(1);
1291  // This causes edge 0 to be considered as dead.
1292  new_edges[0].next_succ = 0;
1293  // Copy all edges so that they are sorted by src
1294  for (unsigned s = 0; s < ns; ++s)
1295  {
1296  idx_list[s] = new_edges.size();
1297  for (const auto& e : out(s))
1298  new_edges.push_back(e);
1299  }
1300  idx_list[ns] = new_edges.size();
1301  // New edges sorted by source
1302  // If we have few edges or only one thread
1303  // Benchmark few?
1304  auto bne = new_edges.begin();
1305 #ifndef SPOT_ENABLE_PTHREAD
1306  (void) ppolicy;
1307 #else
1308  unsigned nthreads = ppolicy.nthreads();
1309  if (nthreads <= 1)
1310 #endif
1311  {
1312  for (unsigned s = 0u; s < ns; ++s)
1313  std::stable_sort(bne + idx_list[s],
1314  bne + idx_list[s+1], p);
1315  }
1316 #ifdef SPOT_ENABLE_PTHREAD
1317  else
1318  {
1319  static std::vector<std::thread> tv;
1320  SPOT_ASSERT(tv.empty());
1321  tv.resize(nthreads);
1322  // FIXME: Due to the way these threads advance into the state
1323  // vector, they access very close memory locations. It would
1324  // seem more cache friendly to have threads work on blocks
1325  // of continuous states.
1326  for (unsigned id = 0; id < nthreads; ++id)
1327  tv[id] = std::thread(
1328  [bne, id, ns, &idx_list, p, nthreads]()
1329  {
1330  for (unsigned s = id; s < ns; s += nthreads)
1331  std::stable_sort(bne + idx_list[s],
1332  bne + idx_list[s+1], p);
1333  return;
1334  });
1335  for (auto& t : tv)
1336  t.join();
1337  tv.clear();
1338  }
1339 #endif
1340  std::swap(edges_, new_edges);
1341  // Like after normal sort_edges, they need to be chained before usage
1342  }
1343 
1351  template<bool Stable = false, class Predicate = std::less<edge_storage_t>>
1352  void sort_edges_of_(Predicate p = Predicate(),
1353  const std::vector<bool>* to_sort_ptr = nullptr)
1354  {
1355  SPOT_ASSERT((to_sort_ptr == nullptr)
1356  || (to_sort_ptr->size() == num_states()));
1357  //std::cerr << "\nbefore\n";
1358  //dump_storage(std::cerr);
1359  auto pi = [&](unsigned t1, unsigned t2)
1360  {return p(edges_[t1], edges_[t2]); };
1361 
1362  // Sort the outgoing edges of each selected state according
1363  // to predicate p. Do that in place.
1364  std::vector<unsigned> sort_idx_;
1365  unsigned ns = num_states();
1366  for (unsigned i = 0; i < ns; ++i)
1367  {
1368  if (to_sort_ptr && !(*to_sort_ptr)[i])
1369  continue;
1370  unsigned t = states_[i].succ;
1371  if (t == 0)
1372  continue;
1373  sort_idx_.clear();
1374  do
1375  {
1376  sort_idx_.push_back(t);
1377  t = edges_[t].next_succ;
1378  } while (t != 0);
1379  if constexpr (Stable)
1380  std::stable_sort(sort_idx_.begin(), sort_idx_.end(), pi);
1381  else
1382  std::sort(sort_idx_.begin(), sort_idx_.end(), pi);
1383  // Update the graph
1384  states_[i].succ = sort_idx_.front();
1385  states_[i].succ_tail = sort_idx_.back();
1386  const unsigned n_outs_n1 = sort_idx_.size() - 1;
1387  for (unsigned k = 0; k < n_outs_n1; ++k)
1388  edges_[sort_idx_[k]].next_succ = sort_idx_[k+1];
1389  edges_[sort_idx_.back()].next_succ = 0; // terminal
1390  }
1391  // Done
1392  }
1393 
1399  {
1400  state last_src = -1U;
1401  edge tend = edges_.size();
1402  for (edge t = 1; t < tend; ++t)
1403  {
1404  state src = edges_[t].src;
1405  if (src != last_src)
1406  {
1407  states_[src].succ = t;
1408  if (last_src != -1U)
1409  {
1410  states_[last_src].succ_tail = t - 1;
1411  edges_[t - 1].next_succ = 0;
1412  }
1413  while (++last_src != src)
1414  {
1415  states_[last_src].succ = 0;
1416  states_[last_src].succ_tail = 0;
1417  }
1418  }
1419  else
1420  {
1421  edges_[t - 1].next_succ = t;
1422  }
1423  }
1424  if (last_src != -1U)
1425  {
1426  states_[last_src].succ_tail = tend - 1;
1427  edges_[tend - 1].next_succ = 0;
1428  }
1429  unsigned send = states_.size();
1430  while (++last_src != send)
1431  {
1432  states_[last_src].succ = 0;
1433  states_[last_src].succ_tail = 0;
1434  }
1435  //std::cerr << "\nafter\n";
1436  //dump_storage(std::cerr);
1437  }
1438 
1444  void rename_states_(const std::vector<unsigned>& newst)
1445  {
1446  SPOT_ASSERT(newst.size() == states_.size());
1447  unsigned tend = edges_.size();
1448  for (unsigned t = 1; t < tend; t++)
1449  {
1450  edges_[t].dst = newst[edges_[t].dst];
1451  edges_[t].src = newst[edges_[t].src];
1452  }
1453  }
1454 
1471  void defrag_states(const std::vector<unsigned>& newst, unsigned used_states)
1472  {
1473  SPOT_ASSERT(newst.size() >= states_.size());
1474  SPOT_ASSERT(used_states > 0);
1475 
1476  //std::cerr << "\nbefore defrag\n";
1477  //dump_storage(std::cerr);
1478 
1479  // Permute all states in states_, as indicated by newst.
1480  // This will put erased states after used_states.
1481  permute_vector(states_, newst);
1482  unsigned send = states_.size();
1483  for (state s = used_states; s < send; ++s)
1484  {
1485  // This is an erased state. Mark all its edges as
1486  // dead (i.e., t.next_succ should point to t for each of
1487  // them).
1488  auto t = states_[s].succ;
1489  while (t)
1490  std::swap(t, edges_[t].next_succ);
1491  }
1492  states_.resize(used_states);
1493 
1494  // Shift all edges in edges_. The algorithm is
1495  // similar to remove_if, but it also keeps the correspondence
1496  // between the old and new index as newidx[old] = new.
1497  //
1498  // If you change anything to this logic, you might want to
1499  // double check twa_graph::defrag_states where we need to
1500  // predict the new edges indices in order to update
1501  // highlight-edges.
1502  unsigned tend = edges_.size();
1503  std::vector<edge> newidx(tend);
1504  unsigned dest = 1;
1505  for (edge t = 1; t < tend; ++t)
1506  {
1507  if (is_dead_edge(t))
1508  continue;
1509  if (t != dest)
1510  edges_[dest] = std::move(edges_[t]);
1511  newidx[t] = dest;
1512  ++dest;
1513  }
1514  edges_.resize(dest);
1515  killed_edge_ = 0;
1516 
1517  // Adjust next_succ and dst pointers in all edges.
1518  for (edge t = 1; t < dest; ++t)
1519  {
1520  auto& tr = edges_[t];
1521  tr.src = newst[tr.src];
1522  tr.dst = newst[tr.dst];
1523  tr.next_succ = newidx[tr.next_succ];
1524  }
1525 
1526  // Adjust succ and succ_tails pointers in all states.
1527  for (auto& s: states_)
1528  {
1529  s.succ = newidx[s.succ];
1530  s.succ_tail = newidx[s.succ_tail];
1531  }
1532 
1533  //std::cerr << "\nafter defrag\n";
1534  //dump_storage(std::cerr);
1535  }
1536 
1537  // prototype was changed in Spot 2.10
1538  SPOT_DEPRECATED("use reference version of this method")
1539  void defrag_states(std::vector<unsigned>&& newst, unsigned used_states)
1540  {
1541  return defrag_states(newst, used_states);
1542  }
1544  };
1545 }
A directed graph.
Definition: graph.hh:605
unsigned num_states() const
The number of states in the automaton.
Definition: graph.hh:670
void dump_storage_as_dot(std::ostream &o, int dsi=DSI_All) const
Dump the state and edge storage for debugging.
Definition: graph.hh:1112
internal::distate_storage< edge, internal::boxed_label< State_Data > > state_storage_t
State storage type.
Definition: graph.hh:628
bool is_dead_edge(const edge_storage_t &t) const
Test whether an edge has been erased.
Definition: graph.hh:1028
void dump_storage(std::ostream &o) const
Dump the state and edge storage for debugging.
Definition: graph.hh:1051
const edge_storage_t::data_t & edge_data(edge s) const
Return the Edge_Data of an edge.
Definition: graph.hh:784
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:945
internal::state_out< digraph > out(state_storage_t &src)
Return a fake container with all edges leaving src.
Definition: graph.hh:922
const state_vector & states() const
Return the vector of states.
Definition: graph.hh:960
void sort_edges_of_(Predicate p=Predicate(), const std::vector< bool > *to_sort_ptr=nullptr)
Sort edges of the given states.
Definition: graph.hh:1352
state new_states(unsigned n, Args &&... args)
Create n new states.
Definition: graph.hh:709
state new_state(Args &&... args)
Create a new state.
Definition: graph.hh:695
edge index_of_edge(const edge_storage_t &tt) const
Convert a storage reference into an edge number.
Definition: graph.hh:907
internal::state_out< const digraph > out(state_storage_t &src) const
Return a fake container with all edges leaving src.
Definition: graph.hh:934
dump_storage_items
Flags controlling dump_storage() output.
Definition: graph.hh:1092
dests_vector_t dests_
Used by alternating automata.
Definition: graph.hh:646
bool is_valid_edge(edge t) const
Test whether the given edge is valid.
Definition: graph.hh:1011
std::vector< edge_storage_t > edge_vector_t
Vector of edge storage.
Definition: graph.hh:636
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:1282
edge_storage_t::data_t & edge_data(edge s)
Return the Edge_Data of an edge.
Definition: graph.hh:778
const dests_vector_t & dests_vector() const
The vector used to store universal destinations.
Definition: graph.hh:1039
dests_vector_t & dests_vector()
The vector used to store universal destinations.
Definition: graph.hh:1044
edge_storage_t & edge_storage(edge s)
Return a reference to the storage of an edge.
Definition: graph.hh:760
const edge_storage_t & edge_storage(edge s) const
Return a reference to the storage of an edge.
Definition: graph.hh:766
unsigned edge
Edge number type.
Definition: graph.hh:623
edge new_univ_edge(state src, const std::initializer_list< state > &dsts, Args &&... args)
Create a new universal edge.
Definition: graph.hh:869
bool is_existential() const
Whether the automaton uses only existential branching.
Definition: graph.hh:684
const state_storage_t & state_storage(state s) const
Return a reference to the storage of a state.
Definition: graph.hh:730
internal::state_out< digraph > out(state src)
Return a fake container with all edges leaving src.
Definition: graph.hh:916
internal::edge_iterator< const digraph > const_iterator
Const iterator over edges of a digraph.
Definition: graph.hh:614
internal::killer_edge_iterator< digraph > out_iteraser(state src)
Return a fake container with all edges leaving src, allowing erasure.
Definition: graph.hh:951
state_vector states_
State storage.
Definition: graph.hh:644
void remove_dead_edges_()
Remove all dead edges.
Definition: graph.hh:1247
digraph(unsigned max_states=10, unsigned max_trans=0)
Construct an empty graph.
Definition: graph.hh:655
state new_univ_dests(I dst_begin, I dst_end)
Create a new universal destination group.
Definition: graph.hh:821
internal::const_universal_dests univ_dests(state src) const
Return universal destinations for state src.
Definition: graph.hh:877
state_vector & states()
Return the vector of states.
Definition: graph.hh:965
unsigned killed_edge_
Number of erased edges.
Definition: graph.hh:647
void rename_states_(const std::vector< unsigned > &newst)
Rename all the states in the edge vector.
Definition: graph.hh:1444
void defrag_states(const std::vector< unsigned > &newst, unsigned used_states)
Rename and remove states.
Definition: graph.hh:1471
Edge_Data edge_data_t
Edge data type.
Definition: graph.hh:618
state_storage_t & state_storage(state s)
Return a reference to the storage of a state.
Definition: graph.hh:724
internal::state_out< const digraph > out(state src) const
Return a fake container with all edges leaving src.
Definition: graph.hh:928
State_Data state_data_t
State data type.
Definition: graph.hh:617
internal::const_universal_dests univ_dests(const edge_storage_t &e) const
Return universal destinations for edge e.
Definition: graph.hh:894
unsigned num_edges() const
The number of edges in the automaton.
Definition: graph.hh:678
std::vector< unsigned > dests_vector_t
Vector of universal destinations.
Definition: graph.hh:641
internal::edge_iterator< digraph > iterator
Iterator over edges of a digraph.
Definition: graph.hh:612
state_storage_t::data_t & state_data(state s)
Return the State_Data associated to a state.
Definition: graph.hh:742
const edge_vector_t & edge_vector() const
Return the vector of all edges.
Definition: graph.hh:994
std::vector< state_storage_t > state_vector
Vector of state storage.
Definition: graph.hh:634
state index_of_state(const state_storage_t &ss) const
Convert a storage reference into a state number.
Definition: graph.hh:900
void sort_edges_(Predicate p=Predicate())
Sort all edges according to a predicate.
Definition: graph.hh:1265
bool is_dead_edge(unsigned t) const
Test whether an edge has been erased.
Definition: graph.hh:1023
internal::edge_storage< state, state, edge, internal::boxed_label< Edge_Data > > edge_storage_t
Edge storage type.
Definition: graph.hh:632
edge_vector_t & edge_vector()
Return the vector of all edges.
Definition: graph.hh:999
edge new_univ_edge(state src, I dst_begin, I dst_end, Args &&... args)
Create a new universal edge.
Definition: graph.hh:856
void chain_edges_()
Reconstruct the chain of outgoing edges.
Definition: graph.hh:1398
edge_vector_t edges_
Edge storage.
Definition: graph.hh:645
internal::all_trans< digraph > edges()
Return a fake container with all edges (excluding erased edges)
Definition: graph.hh:980
unsigned state
State number type.
Definition: graph.hh:622
edge new_edge(state src, state dst, Args &&... args)
Create a new edge.
Definition: graph.hh:797
internal::all_trans< const digraph > edges() const
Return a fake container with all edges (excluding erased edges)
Definition: graph.hh:975
const state_storage_t::data_t & state_data(state s) const
Return the State_Data associated to a state.
Definition: graph.hh:748
This class is used to tell parallel algorithms what resources they may use.
Definition: common.hh:157
Abstract class for states.
Definition: twa.hh:49
void permute_vector(std::vector< values > &data, const std::vector< unsigned > &indices)
Reorder data in place according to the permutation indices.
Definition: permute.hh:41
@ tt
True.
@ G
Globally.
Definition: automata.hh:26
constexpr bool operator==(trival a, trival b)
Equality comparison of two trival values.
Definition: trival.hh:134
constexpr bool operator!=(trival a, trival b)
Inequality comparison of two trival values.
Definition: trival.hh:140

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