spot  2.11.6
graph.hh
1 // -*- coding: utf-8 -*-
2 // Copyright (C) 2014-2018, 2020-2023 Laboratoire de Recherche et
3 // Développement de l'Epita.
4 //
5 // This file is part of Spot, a model checking library.
6 //
7 // Spot is free software; you can redistribute it and/or modify it
8 // under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // Spot is distributed in the hope that it will be useful, but WITHOUT
13 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
15 // License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 
20 #pragma once
21 
22 #include <spot/misc/common.hh>
23 #include <spot/misc/_config.h>
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 
36 namespace spot
37 {
38  template <typename State_Data, typename Edge_Data>
39  class SPOT_API digraph;
40 
41  namespace internal
42  {
43 #ifndef SWIG
44  template <typename Of, typename ...Args>
46  {
47  static const bool value = false;
48  };
49 
50  template <typename Of, typename Arg1, typename ...Args>
51  struct first_is_base_of<Of, Arg1, Args...>
52  {
53  static const bool value =
54  std::is_base_of<Of, typename std::decay<Arg1>::type>::value;
55  };
56 #endif
57 
58  // The boxed_label class stores Data as an attribute called
59  // "label" if boxed is true. It is an empty class if Data is
60  // void, and it simply inherits from Data if boxed is false.
61  //
62  // The data() method offers an homogeneous access to the Data
63  // instance.
64  template <typename Data, bool boxed = !std::is_class<Data>::value>
65  struct SPOT_API boxed_label
66  {
67  typedef Data data_t;
68  Data label;
69 
70 #ifndef SWIG
71  template <typename... Args,
72  typename = typename std::enable_if<
73  !first_is_base_of<boxed_label, Args...>::value>::type>
74  boxed_label(Args&&... args)
75  noexcept(std::is_nothrow_constructible<Data, Args...>::value)
76  : label{std::forward<Args>(args)...}
77  {
78  }
79 #endif
80 
81  // if Data is a POD type, G++ 4.8.2 wants default values for all
82  // label fields unless we define this default constructor here.
83  explicit boxed_label()
84  noexcept(std::is_nothrow_constructible<Data>::value)
85  {
86  }
87 
88  Data& data()
89  {
90  return label;
91  }
92 
93  const Data& data() const
94  {
95  return label;
96  }
97 
98  bool operator<(const boxed_label& other) const
99  {
100  return label < other.label;
101  }
102  };
103 
104  template <>
105  struct SPOT_API boxed_label<void, true>: public std::tuple<>
106  {
107  typedef std::tuple<> data_t;
108  std::tuple<>& data()
109  {
110  return *this;
111  }
112 
113  const std::tuple<>& data() const
114  {
115  return *this;
116  }
117 
118  };
119 
120  template <typename Data>
121  struct SPOT_API boxed_label<Data, false>: public Data
122  {
123  typedef Data data_t;
124 
125 #ifndef SWIG
126  template <typename... Args,
127  typename = typename std::enable_if<
128  !first_is_base_of<boxed_label, Args...>::value>::type>
129  boxed_label(Args&&... args)
130  noexcept(std::is_nothrow_constructible<Data, Args...>::value)
131  : Data{std::forward<Args>(args)...}
132  {
133  }
134 #endif
135 
136  // if Data is a POD type, G++ 4.8.2 wants default values for all
137  // label fields unless we define this default constructor here.
138  explicit boxed_label()
139  noexcept(std::is_nothrow_constructible<Data>::value)
140  {
141  }
142 
143  Data& data()
144  {
145  return *this;
146  }
147 
148  const Data& data() const
149  {
150  return *this;
151  }
152  };
153 
155  // State storage for digraphs
157 
158  // We have two implementations, one with attached State_Data, and
159  // one without.
160 
161  template <typename Edge, typename State_Data>
162  struct SPOT_API distate_storage final: public State_Data
163  {
164  Edge succ = 0; // First outgoing edge (used when iterating)
165  Edge succ_tail = 0; // Last outgoing edge (used for
166  // appending new edges)
167 #ifndef SWIG
168  template <typename... Args,
169  typename = typename std::enable_if<
170  !first_is_base_of<distate_storage, Args...>::value>::type>
171  distate_storage(Args&&... args)
172  noexcept(std::is_nothrow_constructible<State_Data, Args...>::value)
173  : State_Data{std::forward<Args>(args)...}
174  {
175  }
176 #endif
177  };
178 
180  // Edge storage
182 
183  // Again two implementation: one with label, and one without.
184 
185  template <typename StateIn,
186  typename StateOut, typename Edge, typename Edge_Data>
187  struct SPOT_API edge_storage final: public Edge_Data
188  {
189  typedef Edge edge;
190 
191  StateOut dst; // destination
192  Edge next_succ; // next outgoing edge with same
193  // source, or 0
194  StateIn src; // source
195 
196  explicit edge_storage()
197  noexcept(std::is_nothrow_constructible<Edge_Data>::value)
198  : Edge_Data{}
199  {
200  }
201 
202 #ifndef SWIG
203  template <typename... Args>
204  edge_storage(StateOut dst, Edge next_succ,
205  StateIn src, Args&&... args)
206  noexcept(std::is_nothrow_constructible<Edge_Data, Args...>::value
207  && std::is_nothrow_constructible<StateOut, StateOut>::value
208  && std::is_nothrow_constructible<Edge, Edge>::value)
209  : Edge_Data{std::forward<Args>(args)...},
210  dst(dst), next_succ(next_succ), src(src)
211  {
212  }
213 #endif
214 
215  bool operator<(const edge_storage& other) const
216  {
217  if (src < other.src)
218  return true;
219  if (src > other.src)
220  return false;
221  // This might be costly if the destination is a vector
222  if (dst < other.dst)
223  return true;
224  if (dst > other.dst)
225  return false;
226  return this->data() < other.data();
227  }
228 
229  bool operator==(const edge_storage& other) const
230  {
231  return src == other.src &&
232  dst == other.dst &&
233  this->data() == other.data();
234  }
235  };
236 
238  // Edge iterator
240 
241  // This holds a graph and a edge number that is the start of
242  // a list, and it iterates over all the edge_storage_t elements
243  // of that list.
244 
245  template <typename Graph>
246  class SPOT_API edge_iterator
247  {
248  public:
249  typedef typename std::conditional<std::is_const<Graph>::value,
250  const typename Graph::edge_storage_t,
251  typename Graph::edge_storage_t>::type
252  value_type;
253  typedef value_type& reference;
254  typedef value_type* pointer;
255  typedef std::ptrdiff_t difference_type;
256  typedef std::forward_iterator_tag iterator_category;
257 
258  typedef typename Graph::edge edge;
259 
260  edge_iterator() noexcept
261  : g_(nullptr), t_(0)
262  {
263  }
264 
265  edge_iterator(Graph* g, edge t) noexcept
266  : g_(g), t_(t)
267  {
268  }
269 
270  bool operator==(edge_iterator o) const
271  {
272  return t_ == o.t_;
273  }
274 
275  bool operator!=(edge_iterator o) const
276  {
277  return t_ != o.t_;
278  }
279 
280  reference operator*() const
281  {
282  return g_->edge_storage(t_);
283  }
284 
285  pointer operator->() const
286  {
287  return &g_->edge_storage(t_);
288  }
289 
290  edge_iterator operator++()
291  {
292  t_ = operator*().next_succ;
293  return *this;
294  }
295 
296  edge_iterator operator++(int)
297  {
298  edge_iterator ti = *this;
299  t_ = operator*().next_succ;
300  return ti;
301  }
302 
303  operator bool() const
304  {
305  return t_;
306  }
307 
308  edge trans() const
309  {
310  return t_;
311  }
312 
313  protected:
314  Graph* g_;
315  edge t_;
316  };
317 
318  template <typename Graph>
319  class SPOT_API killer_edge_iterator: public edge_iterator<Graph>
320  {
321  typedef edge_iterator<Graph> super;
322  public:
323  typedef typename Graph::state_storage_t state_storage_t;
324  typedef typename Graph::edge edge;
325 
326  killer_edge_iterator(Graph* g, edge t, state_storage_t& src) noexcept
327  : super(g, t), src_(src), prev_(0)
328  {
329  }
330 
331  killer_edge_iterator operator++()
332  {
333  prev_ = this->t_;
334  this->t_ = this->operator*().next_succ;
335  return *this;
336  }
337 
338  killer_edge_iterator operator++(int)
339  {
340  killer_edge_iterator ti = *this;
341  ++*this;
342  return ti;
343  }
344 
345  // Erase the current edge and advance the iterator.
346  void erase()
347  {
348  edge next = this->operator*().next_succ;
349 
350  // Update source state and previous edges
351  if (prev_)
352  {
353  this->g_->edge_storage(prev_).next_succ = next;
354  }
355  else
356  {
357  if (src_.succ == this->t_)
358  src_.succ = next;
359  }
360  if (src_.succ_tail == this->t_)
361  {
362  src_.succ_tail = prev_;
363  SPOT_ASSERT(next == 0);
364  }
365 
366  // Erased edges have themselves as next_succ.
367  this->operator*().next_succ = this->t_;
368 
369  // Advance iterator to next edge.
370  this->t_ = next;
371 
372  ++this->g_->killed_edge_;
373  }
374 
375  protected:
376  state_storage_t& src_;
377  edge prev_;
378  };
379 
380 
382  // State OUT
384 
385  // Fake container listing the outgoing edges of a state.
386 
387  template <typename Graph>
388  class SPOT_API state_out
389  {
390  public:
391  typedef typename Graph::edge edge;
392  state_out(Graph* g, edge t) noexcept
393  : g_(g), t_(t)
394  {
395  }
396 
397  edge_iterator<Graph> begin() const
398  {
399  return {g_, t_};
400  }
401 
402  edge_iterator<Graph> end() const
403  {
404  return {};
405  }
406 
407  void recycle(edge t)
408  {
409  t_ = t;
410  }
411 
412  protected:
413  Graph* g_;
414  edge t_;
415  };
416 
418  // all_trans
420 
421  template <typename Graph>
422  class SPOT_API all_edge_iterator
423  {
424  public:
425  typedef typename std::conditional<std::is_const<Graph>::value,
426  const typename Graph::edge_storage_t,
427  typename Graph::edge_storage_t>::type
428  value_type;
429  typedef value_type& reference;
430  typedef value_type* pointer;
431  typedef std::ptrdiff_t difference_type;
432  typedef std::forward_iterator_tag iterator_category;
433 
434  protected:
435  typedef typename std::conditional<std::is_const<Graph>::value,
436  const typename Graph::edge_vector_t,
437  typename Graph::edge_vector_t>::type
438  tv_t;
439 
440  unsigned t_;
441  tv_t& tv_;
442 
443  void skip_()
444  {
445  unsigned s = tv_.size();
446  do
447  ++t_;
448  while (t_ < s && tv_[t_].next_succ == t_);
449  }
450 
451  public:
452  all_edge_iterator(unsigned pos, tv_t& tv) noexcept
453  : t_(pos), tv_(tv)
454  {
455  skip_();
456  }
457 
458  all_edge_iterator(tv_t& tv) noexcept
459  : t_(tv.size()), tv_(tv)
460  {
461  }
462 
463  all_edge_iterator& operator++()
464  {
465  skip_();
466  return *this;
467  }
468 
469  all_edge_iterator operator++(int)
470  {
471  all_edge_iterator old = *this;
472  ++*this;
473  return old;
474  }
475 
476  bool operator==(all_edge_iterator o) const
477  {
478  return t_ == o.t_;
479  }
480 
481  bool operator!=(all_edge_iterator o) const
482  {
483  return t_ != o.t_;
484  }
485 
486  reference operator*() const
487  {
488  return tv_[t_];
489  }
490 
491  pointer operator->() const
492  {
493  return &tv_[t_];
494  }
495  };
496 
497 
498  template <typename Graph>
499  class SPOT_API all_trans
500  {
501  public:
502  typedef typename std::conditional<std::is_const<Graph>::value,
503  const typename Graph::edge_vector_t,
504  typename Graph::edge_vector_t>::type
505  tv_t;
507  private:
508  tv_t& tv_;
509  public:
510 
511  all_trans(tv_t& tv) noexcept
512  : tv_(tv)
513  {
514  }
515 
516  iter_t begin() const
517  {
518  return {0, tv_};
519  }
520 
521  iter_t end() const
522  {
523  return {tv_};
524  }
525  };
526 
527  class SPOT_API const_universal_dests
528  {
529  private:
530  const unsigned* begin_;
531  const unsigned* end_;
532  unsigned tmp_;
533  public:
534  const_universal_dests(const unsigned* begin, const unsigned* end) noexcept
535  : begin_(begin), end_(end)
536  {
537  }
538 
539  const_universal_dests(unsigned state) noexcept
540  : begin_(&tmp_), end_(&tmp_ + 1), tmp_(state)
541  {
542  }
543 
544  const unsigned* begin() const
545  {
546  return begin_;
547  }
548 
549  const unsigned* end() const
550  {
551  return end_;
552  }
553  };
554 
555  template<class G>
557  {
558  std::map<std::vector<unsigned>, unsigned> uniq_;
559  G& g_;
560  public:
561 
562  univ_dest_mapper(G& graph)
563  : g_(graph)
564  {
565  }
566 
567  template<class I>
568  unsigned new_univ_dests(I begin, I end)
569  {
570  std::vector<unsigned> tmp(begin, end);
571  std::sort(tmp.begin(), tmp.end());
572  tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end());
573  auto p = uniq_.emplace(tmp, 0);
574  if (p.second)
575  p.first->second = g_.new_univ_dests(tmp.begin(), tmp.end());
576  return p.first->second;
577  }
578 
579  unsigned new_univ_dests(std::vector<unsigned>&& tmp)
580  {
581  std::sort(tmp.begin(), tmp.end());
582  tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end());
583  auto p = uniq_.emplace(tmp, 0);
584  if (p.second)
585  p.first->second = g_.new_univ_dests(tmp.begin(), tmp.end());
586  return p.first->second;
587  }
588  };
589 
590  } // namespace internal
591 
592 
597  template <typename State_Data, typename Edge_Data>
598  class digraph
599  {
600  friend class internal::edge_iterator<digraph>;
601  friend class internal::edge_iterator<const digraph>;
603 
604  public:
607 
608  // Extra data to store on each state or edge.
609  typedef State_Data state_data_t;
610  typedef Edge_Data edge_data_t;
611 
612  // State and edges are identified by their indices in some
613  // vector.
614  typedef unsigned state;
615  typedef unsigned edge;
616 
617  typedef internal::distate_storage<edge,
620  typedef internal::edge_storage<state, state, edge,
623  typedef std::vector<state_storage_t> state_vector;
624  typedef std::vector<edge_storage_t> edge_vector_t;
625 
626  // A sequence of universal destination groups of the form:
627  // (n state_1 state_2 ... state_n)*
628  typedef std::vector<unsigned> dests_vector_t;
629 
630  protected:
631  state_vector states_;
632  edge_vector_t edges_;
633  dests_vector_t dests_; // Only used by alternating automata.
634  // Number of erased edges.
635  unsigned killed_edge_;
636  public:
643  digraph(unsigned max_states = 10, unsigned max_trans = 0)
644  : killed_edge_(0)
645  {
646  states_.reserve(max_states);
647  if (max_trans == 0)
648  max_trans = max_states * 2;
649  edges_.reserve(max_trans + 1);
650  // Edge number 0 is not used, because we use this index
651  // to mark the absence of a edge.
652  edges_.resize(1);
653  // This causes edge 0 to be considered as dead.
654  edges_[0].next_succ = 0;
655  }
656 
658  unsigned num_states() const
659  {
660  return states_.size();
661  }
662 
666  unsigned num_edges() const
667  {
668  return edges_.size() - killed_edge_ - 1;
669  }
670 
672  bool is_existential() const
673  {
674  return dests_.empty();
675  }
676 
682  template <typename... Args>
683  state new_state(Args&&... args)
684  {
685  state s = states_.size();
686  states_.emplace_back(std::forward<Args>(args)...);
687  return s;
688  }
689 
696  template <typename... Args>
697  state new_states(unsigned n, Args&&... args)
698  {
699  state s = states_.size();
700  states_.reserve(s + n);
701  while (n--)
702  states_.emplace_back(std::forward<Args>(args)...);
703  return s;
704  }
705 
711  state_storage_t&
712  state_storage(state s)
713  {
714  return states_[s];
715  }
716 
717  const state_storage_t&
718  state_storage(state s) const
719  {
720  return states_[s];
721  }
723 
729  typename state_storage_t::data_t&
730  state_data(state s)
731  {
732  return states_[s].data();
733  }
734 
735  const typename state_storage_t::data_t&
736  state_data(state s) const
737  {
738  return states_[s].data();
739  }
741 
747  edge_storage_t&
748  edge_storage(edge s)
749  {
750  return edges_[s];
751  }
752 
753  const edge_storage_t&
754  edge_storage(edge s) const
755  {
756  return edges_[s];
757  }
759 
765  typename edge_storage_t::data_t&
766  edge_data(edge s)
767  {
768  return edges_[s].data();
769  }
770 
771  const typename edge_storage_t::data_t&
772  edge_data(edge s) const
773  {
774  return edges_[s].data();
775  }
777 
783  template <typename... Args>
784  edge
785  new_edge(state src, state dst, Args&&... args)
786  {
787  edge t = edges_.size();
788  edges_.emplace_back(dst, 0, src, std::forward<Args>(args)...);
789 
790  edge st = states_[src].succ_tail;
791  SPOT_ASSERT(st < t || !st);
792  if (!st)
793  states_[src].succ = t;
794  else
795  edges_[st].next_succ = t;
796  states_[src].succ_tail = t;
797  return t;
798  }
799 
807  template <typename I>
808  state
809  new_univ_dests(I dst_begin, I dst_end)
810  {
811  unsigned sz = std::distance(dst_begin, dst_end);
812  if (sz == 1)
813  return *dst_begin;
814  SPOT_ASSERT(sz > 1);
815  unsigned d = dests_.size();
816  if (!dests_.empty()
817  && &*dst_begin >= &dests_.front()
818  && &*dst_begin <= &dests_.back()
819  && (dests_.capacity() - dests_.size()) < (sz + 1))
820  {
821  // If dst_begin...dst_end points into dests_ and dests_ risk
822  // being reallocated, we have to savea the destination
823  // states before we lose them.
824  std::vector<unsigned> tmp(dst_begin, dst_end);
825  dests_.emplace_back(sz);
826  dests_.insert(dests_.end(), tmp.begin(), tmp.end());
827  }
828  else
829  {
830  dests_.emplace_back(sz);
831  dests_.insert(dests_.end(), dst_begin, dst_end);
832  }
833  return ~d;
834  }
835 
842  template <typename I, typename... Args>
843  edge
844  new_univ_edge(state src, I dst_begin, I dst_end, Args&&... args)
845  {
846  return new_edge(src, new_univ_dests(dst_begin, dst_end),
847  std::forward<Args>(args)...);
848  }
849 
855  template <typename... Args>
856  edge
857  new_univ_edge(state src, const std::initializer_list<state>& dsts,
858  Args&&... args)
859  {
860  return new_univ_edge(src, dsts.begin(), dsts.end(),
861  std::forward<Args>(args)...);
862  }
863 
864  internal::const_universal_dests univ_dests(state src) const
865  {
866  if ((int)src < 0)
867  {
868  unsigned pos = ~src;
869  const unsigned* d = dests_.data();
870  d += pos;
871  unsigned num = *d;
872  return { d + 1, d + num + 1 };
873  }
874  else
875  {
876  return src;
877  }
878  }
879 
880  internal::const_universal_dests univ_dests(const edge_storage_t& e) const
881  {
882  return univ_dests(e.dst);
883  }
884 
886  state index_of_state(const state_storage_t& ss) const
887  {
888  SPOT_ASSERT(!states_.empty());
889  return &ss - &states_.front();
890  }
891 
893  edge index_of_edge(const edge_storage_t& tt) const
894  {
895  SPOT_ASSERT(!edges_.empty());
896  return &tt - &edges_.front();
897  }
898 
902  out(state src)
903  {
904  return {this, states_[src].succ};
905  }
906 
909  {
910  return out(index_of_state(src));
911  }
912 
914  out(state src) const
915  {
916  return {this, states_[src].succ};
917  }
918 
920  out(state_storage_t& src) const
921  {
922  return out(index_of_state(src));
923  }
925 
932  {
933  return {this, src.succ, src};
934  }
935 
937  out_iteraser(state src)
938  {
939  return out_iteraser(state_storage(src));
940  }
942 
946  const state_vector& states() const
947  {
948  return states_;
949  }
950 
951  state_vector& states()
952  {
953  return states_;
954  }
956 
962  {
963  return edges_;
964  }
965 
967  {
968  return edges_;
969  }
971 
980  const edge_vector_t& edge_vector() const
981  {
982  return edges_;
983  }
984 
985  edge_vector_t& edge_vector()
986  {
987  return edges_;
988  }
990 
997  bool is_valid_edge(edge t) const
998  {
999  // Erased edges have their next_succ pointing to
1000  // themselves.
1001  return (t < edges_.size() &&
1002  edges_[t].next_succ != t);
1003  }
1004 
1009  bool is_dead_edge(unsigned t) const
1010  {
1011  return edges_[t].next_succ == t;
1012  }
1013 
1014  bool is_dead_edge(const edge_storage_t& t) const
1015  {
1016  return t.next_succ == index_of_edge(t);
1017  }
1019 
1025  const dests_vector_t& dests_vector() const
1026  {
1027  return dests_;
1028  }
1029 
1030  dests_vector_t& dests_vector()
1031  {
1032  return dests_;
1033  }
1035 
1037  void dump_storage(std::ostream& o) const
1038  {
1039  unsigned tend = edges_.size();
1040  for (unsigned t = 1; t < tend; ++t)
1041  {
1042  o << 't' << t << ": (s"
1043  << edges_[t].src << ", ";
1044  int d = edges_[t].dst;
1045  if (d < 0)
1046  o << 'd' << ~d;
1047  else
1048  o << 's' << d;
1049  o << ") t" << edges_[t].next_succ << '\n';
1050  }
1051  unsigned send = states_.size();
1052  for (unsigned s = 0; s < send; ++s)
1053  {
1054  o << 's' << s << ": t"
1055  << states_[s].succ << " t"
1056  << states_[s].succ_tail << '\n';
1057  }
1058  unsigned dend = dests_.size();
1059  unsigned size = 0;
1060  for (unsigned s = 0; s < dend; ++s)
1061  {
1062  o << 'd' << s << ": ";
1063  if (size == 0)
1064  {
1065  o << '#';
1066  size = dests_[s];
1067  }
1068  else
1069  {
1070  o << 's';
1071  --size;
1072  }
1073  o << dests_[s] << '\n';
1074  }
1075  }
1076 
1077  enum dump_storage_items {
1078  DSI_GraphHeader = 1,
1079  DSI_GraphFooter = 2,
1080  DSI_StatesHeader = 4,
1081  DSI_StatesBody = 8,
1082  DSI_StatesFooter = 16,
1083  DSI_States = DSI_StatesHeader | DSI_StatesBody | DSI_StatesFooter,
1084  DSI_EdgesHeader = 32,
1085  DSI_EdgesBody = 64,
1086  DSI_EdgesFooter = 128,
1087  DSI_Edges = DSI_EdgesHeader | DSI_EdgesBody | DSI_EdgesFooter,
1088  DSI_DestsHeader = 256,
1089  DSI_DestsBody = 512,
1090  DSI_DestsFooter = 1024,
1091  DSI_Dests = DSI_DestsHeader | DSI_DestsBody | DSI_DestsFooter,
1092  DSI_All =
1093  DSI_GraphHeader | DSI_States | DSI_Edges | DSI_Dests | DSI_GraphFooter,
1094  };
1095 
1097  void dump_storage_as_dot(std::ostream& o, int dsi = DSI_All) const
1098  {
1099  if (dsi & DSI_GraphHeader)
1100  o << "digraph g { \nnode [shape=plaintext]\n";
1101  unsigned send = states_.size();
1102  if (dsi & DSI_StatesHeader)
1103  {
1104  o << ("states [label=<\n"
1105  "<table border='0' cellborder='1' cellspacing='0'>\n"
1106  "<tr><td sides='b' bgcolor='yellow' port='s'>states</td>\n");
1107  for (unsigned s = 0; s < send; ++s)
1108  o << "<td sides='b' bgcolor='yellow' port='s" << s << "'>"
1109  << s << "</td>\n";
1110  o << "</tr>\n";
1111  }
1112  if (dsi & DSI_StatesBody)
1113  {
1114  o << "<tr><td port='ss'>succ</td>\n";
1115  for (unsigned s = 0; s < send; ++s)
1116  {
1117  o << "<td port='ss" << s;
1118  if (states_[s].succ)
1119  o << "' bgcolor='cyan";
1120  o << "'>" << states_[s].succ << "</td>\n";
1121  }
1122  o << "</tr><tr><td port='st'>succ_tail</td>\n";
1123  for (unsigned s = 0; s < send; ++s)
1124  {
1125  o << "<td port='st" << s;
1126  if (states_[s].succ_tail)
1127  o << "' bgcolor='cyan";
1128  o << "'>" << states_[s].succ_tail << "</td>\n";
1129  }
1130  o << "</tr>\n";
1131  }
1132  if (dsi & DSI_StatesFooter)
1133  o << "</table>>]\n";
1134  unsigned eend = edges_.size();
1135  if (dsi & DSI_EdgesHeader)
1136  {
1137  o << ("edges [label=<\n"
1138  "<table border='0' cellborder='1' cellspacing='0'>\n"
1139  "<tr><td sides='b' bgcolor='cyan' port='e'>edges</td>\n");
1140  for (unsigned e = 1; e < eend; ++e)
1141  {
1142  o << "<td sides='b' bgcolor='"
1143  << (e != edges_[e].next_succ ? "cyan" : "gray")
1144  << "' port='e" << e << "'>" << e << "</td>\n";
1145  }
1146  o << "</tr>";
1147  }
1148  if (dsi & DSI_EdgesBody)
1149  {
1150  o << "<tr><td port='ed'>dst</td>\n";
1151  for (unsigned e = 1; e < eend; ++e)
1152  {
1153  o << "<td port='ed" << e;
1154  int d = edges_[e].dst;
1155  if (d < 0)
1156  o << "' bgcolor='pink'>~" << ~d;
1157  else
1158  o << "' bgcolor='yellow'>" << d;
1159  o << "</td>\n";
1160  }
1161  o << "</tr><tr><td port='en'>next_succ</td>\n";
1162  for (unsigned e = 1; e < eend; ++e)
1163  {
1164  o << "<td port='en" << e;
1165  if (edges_[e].next_succ)
1166  {
1167  if (edges_[e].next_succ != e)
1168  o << "' bgcolor='cyan";
1169  else
1170  o << "' bgcolor='gray";
1171  }
1172  o << "'>" << edges_[e].next_succ << "</td>\n";
1173  }
1174  o << "</tr><tr><td port='es'>src</td>\n";
1175  for (unsigned e = 1; e < eend; ++e)
1176  o << "<td port='es" << e << "' bgcolor='yellow'>"
1177  << edges_[e].src << "</td>\n";
1178  o << "</tr>\n";
1179  }
1180  if (dsi & DSI_EdgesFooter)
1181  o << "</table>>]\n";
1182  if (!dests_.empty())
1183  {
1184  unsigned dend = dests_.size();
1185  if (dsi & DSI_DestsHeader)
1186  {
1187  o << ("dests [label=<\n"
1188  "<table border='0' cellborder='1' cellspacing='0'>\n"
1189  "<tr><td sides='b' bgcolor='pink' port='d'>dests</td>\n");
1190  unsigned d = 0;
1191  while (d < dend)
1192  {
1193  o << "<td sides='b' bgcolor='pink' port='d"
1194  << d << "'>~" << d << "</td>\n";
1195  unsigned cnt = dests_[d];
1196  d += cnt + 1;
1197  while (cnt--)
1198  o << "<td sides='b'></td>\n";
1199  }
1200  o << "</tr>\n";
1201  }
1202  if (dsi & DSI_DestsBody)
1203  {
1204  o << "<tr><td port='dd'>#cnt/dst</td>\n";
1205  unsigned d = 0;
1206  while (d < dend)
1207  {
1208  unsigned cnt = dests_[d];
1209  o << "<td port='d'>#" << cnt << "</td>\n";
1210  ++d;
1211  while (cnt--)
1212  {
1213  o << "<td bgcolor='yellow' port='dd"
1214  << d << "'>" << dests_[d] << "</td>\n";
1215  ++d;
1216  }
1217  }
1218  o << "</tr>\n";
1219  }
1220  if (dsi & DSI_DestsFooter)
1221  o << "</table>>]\n";
1222  }
1223  if (dsi & DSI_GraphFooter)
1224  o << "}\n";
1225  }
1226 
1233  {
1234  if (killed_edge_ == 0)
1235  return;
1236  auto i = std::remove_if(edges_.begin() + 1, edges_.end(),
1237  [this](const edge_storage_t& t) {
1238  return this->is_dead_edge(t);
1239  });
1240  edges_.erase(i, edges_.end());
1241  killed_edge_ = 0;
1242  }
1243 
1249  template<class Predicate = std::less<edge_storage_t>>
1250  void sort_edges_(Predicate p = Predicate())
1251  {
1252  //std::cerr << "\nbefore\n";
1253  //dump_storage(std::cerr);
1254  std::stable_sort(edges_.begin() + 1, edges_.end(), p);
1255  }
1256 
1266  template<class Predicate = std::less<edge_storage_t>>
1267  void sort_edges_srcfirst_(Predicate p = Predicate(),
1268  parallel_policy ppolicy = parallel_policy())
1269  {
1270  SPOT_ASSERT(!edges_.empty());
1271  const unsigned ns = num_states();
1272  std::vector<unsigned> idx_list(ns+1);
1273  edge_vector_t new_edges;
1274  new_edges.reserve(edges_.size());
1275  new_edges.resize(1);
1276  // This causes edge 0 to be considered as dead.
1277  new_edges[0].next_succ = 0;
1278  // Copy all edges so that they are sorted by src
1279  for (unsigned s = 0; s < ns; ++s)
1280  {
1281  idx_list[s] = new_edges.size();
1282  for (const auto& e : out(s))
1283  new_edges.push_back(e);
1284  }
1285  idx_list[ns] = new_edges.size();
1286  // New edge sorted by source
1287  // If we have few edge or only one threads
1288  // Benchmark few?
1289  auto bne = new_edges.begin();
1290 #ifndef SPOT_ENABLE_PTHREAD
1291  (void) ppolicy;
1292 #else
1293  unsigned nthreads = ppolicy.nthreads();
1294  if (nthreads <= 1)
1295 #endif
1296  {
1297  for (unsigned s = 0u; s < ns; ++s)
1298  std::stable_sort(bne + idx_list[s],
1299  bne + idx_list[s+1], p);
1300  }
1301 #ifdef SPOT_ENABLE_PTHREAD
1302  else
1303  {
1304  static std::vector<std::thread> tv;
1305  SPOT_ASSERT(tv.empty());
1306  tv.resize(nthreads);
1307  // FIXME: Due to the way these thread advance into the state
1308  // vector, they access very close memory location. It would
1309  // seems more cache friendly to have threads work on blocks
1310  // of continuous states.
1311  for (unsigned id = 0; id < nthreads; ++id)
1312  tv[id] = std::thread(
1313  [bne, id, ns, &idx_list, p, nthreads]()
1314  {
1315  for (unsigned s = id; s < ns; s += nthreads)
1316  std::stable_sort(bne + idx_list[s],
1317  bne + idx_list[s+1], p);
1318  return;
1319  });
1320  for (auto& t : tv)
1321  t.join();
1322  tv.clear();
1323  }
1324 #endif
1325  std::swap(edges_, new_edges);
1326  // Like after normal sort_edges, they need to be chained before usage
1327  }
1328 
1336  template<bool Stable = false, class Predicate = std::less<edge_storage_t>>
1337  void sort_edges_of_(Predicate p = Predicate(),
1338  const std::vector<bool>* to_sort_ptr = nullptr)
1339  {
1340  SPOT_ASSERT((to_sort_ptr == nullptr)
1341  || (to_sort_ptr->size() == num_states()));
1342  //std::cerr << "\nbefore\n";
1343  //dump_storage(std::cerr);
1344  auto pi = [&](unsigned t1, unsigned t2)
1345  {return p(edges_[t1], edges_[t2]); };
1346 
1347  // Sort the outgoing edges of each selected state according
1348  // to predicate p. Do that in place.
1349  std::vector<unsigned> sort_idx_;
1350  unsigned ns = num_states();
1351  for (unsigned i = 0; i < ns; ++i)
1352  {
1353  if (to_sort_ptr && !(*to_sort_ptr)[i])
1354  continue;
1355  unsigned t = states_[i].succ;
1356  if (t == 0)
1357  continue;
1358  sort_idx_.clear();
1359  do
1360  {
1361  sort_idx_.push_back(t);
1362  t = edges_[t].next_succ;
1363  } while (t != 0);
1364  if constexpr (Stable)
1365  std::stable_sort(sort_idx_.begin(), sort_idx_.end(), pi);
1366  else
1367  std::sort(sort_idx_.begin(), sort_idx_.end(), pi);
1368  // Update the graph
1369  states_[i].succ = sort_idx_.front();
1370  states_[i].succ_tail = sort_idx_.back();
1371  const unsigned n_outs_n1 = sort_idx_.size() - 1;
1372  for (unsigned k = 0; k < n_outs_n1; ++k)
1373  edges_[sort_idx_[k]].next_succ = sort_idx_[k+1];
1374  edges_[sort_idx_.back()].next_succ = 0; // terminal
1375  }
1376  // Done
1377  }
1378 
1384  {
1385  state last_src = -1U;
1386  edge tend = edges_.size();
1387  for (edge t = 1; t < tend; ++t)
1388  {
1389  state src = edges_[t].src;
1390  if (src != last_src)
1391  {
1392  states_[src].succ = t;
1393  if (last_src != -1U)
1394  {
1395  states_[last_src].succ_tail = t - 1;
1396  edges_[t - 1].next_succ = 0;
1397  }
1398  while (++last_src != src)
1399  {
1400  states_[last_src].succ = 0;
1401  states_[last_src].succ_tail = 0;
1402  }
1403  }
1404  else
1405  {
1406  edges_[t - 1].next_succ = t;
1407  }
1408  }
1409  if (last_src != -1U)
1410  {
1411  states_[last_src].succ_tail = tend - 1;
1412  edges_[tend - 1].next_succ = 0;
1413  }
1414  unsigned send = states_.size();
1415  while (++last_src != send)
1416  {
1417  states_[last_src].succ = 0;
1418  states_[last_src].succ_tail = 0;
1419  }
1420  //std::cerr << "\nafter\n";
1421  //dump_storage(std::cerr);
1422  }
1423 
1429  void rename_states_(const std::vector<unsigned>& newst)
1430  {
1431  SPOT_ASSERT(newst.size() == states_.size());
1432  unsigned tend = edges_.size();
1433  for (unsigned t = 1; t < tend; t++)
1434  {
1435  edges_[t].dst = newst[edges_[t].dst];
1436  edges_[t].src = newst[edges_[t].src];
1437  }
1438  }
1439 
1457  void defrag_states(const std::vector<unsigned>& newst, unsigned used_states)
1458  {
1459  SPOT_ASSERT(newst.size() >= states_.size());
1460  SPOT_ASSERT(used_states > 0);
1461 
1462  //std::cerr << "\nbefore defrag\n";
1463  //dump_storage(std::cerr);
1464 
1465  // Shift all states in states_, as indicated by newst.
1466  unsigned send = states_.size();
1467  for (state s = 0; s < send; ++s)
1468  {
1469  state dst = newst[s];
1470  if (dst == s)
1471  continue;
1472  if (dst == -1U)
1473  {
1474  // This is an erased state. Mark all its edges as
1475  // dead (i.e., t.next_succ should point to t for each of
1476  // them).
1477  auto t = states_[s].succ;
1478  while (t)
1479  std::swap(t, edges_[t].next_succ);
1480  continue;
1481  }
1482  states_[dst] = std::move(states_[s]);
1483  }
1484  states_.resize(used_states);
1485 
1486  // Shift all edges in edges_. The algorithm is
1487  // similar to remove_if, but it also keeps the correspondence
1488  // between the old and new index as newidx[old] = new.
1489  unsigned tend = edges_.size();
1490  std::vector<edge> newidx(tend);
1491  unsigned dest = 1;
1492  for (edge t = 1; t < tend; ++t)
1493  {
1494  if (is_dead_edge(t))
1495  continue;
1496  if (t != dest)
1497  edges_[dest] = std::move(edges_[t]);
1498  newidx[t] = dest;
1499  ++dest;
1500  }
1501  edges_.resize(dest);
1502  killed_edge_ = 0;
1503 
1504  // Adjust next_succ and dst pointers in all edges.
1505  for (edge t = 1; t < dest; ++t)
1506  {
1507  auto& tr = edges_[t];
1508  tr.src = newst[tr.src];
1509  tr.dst = newst[tr.dst];
1510  tr.next_succ = newidx[tr.next_succ];
1511  }
1512 
1513  // Adjust succ and succ_tails pointers in all states.
1514  for (auto& s: states_)
1515  {
1516  s.succ = newidx[s.succ];
1517  s.succ_tail = newidx[s.succ_tail];
1518  }
1519 
1520  //std::cerr << "\nafter defrag\n";
1521  //dump_storage(std::cerr);
1522  }
1523 
1524  // prototype was changed in Spot 2.10
1525  SPOT_DEPRECATED("use reference version of this method")
1526  void defrag_states(std::vector<unsigned>&& newst, unsigned used_states)
1527  {
1528  return defrag_states(newst, used_states);
1529  }
1531  };
1532 }
A directed graph.
Definition: graph.hh:599
unsigned num_states() const
The number of states in the automaton.
Definition: graph.hh:658
void dump_storage_as_dot(std::ostream &o, int dsi=DSI_All) const
Dump the state and edge storage for debugging.
Definition: graph.hh:1097
bool is_dead_edge(const edge_storage_t &t) const
Tests whether an edge has been erased.
Definition: graph.hh:1014
void dump_storage(std::ostream &o) const
Dump the state and edge storage for debugging.
Definition: graph.hh:1037
const edge_storage_t::data_t & edge_data(edge s) const
return the Edgeg_Data of an edge.
Definition: graph.hh:772
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:931
internal::state_out< digraph > out(state_storage_t &src)
Return a fake container with all edges leaving src.
Definition: graph.hh:908
const state_vector & states() const
Return the vector of states.
Definition: graph.hh:946
void sort_edges_of_(Predicate p=Predicate(), const std::vector< bool > *to_sort_ptr=nullptr)
Sort edges of the given states.
Definition: graph.hh:1337
state new_states(unsigned n, Args &&... args)
Create n new states.
Definition: graph.hh:697
state new_state(Args &&... args)
Create a new states.
Definition: graph.hh:683
edge index_of_edge(const edge_storage_t &tt) const
Convert a storage reference into an edge number.
Definition: graph.hh:893
internal::state_out< const digraph > out(state_storage_t &src) const
Return a fake container with all edges leaving src.
Definition: graph.hh:920
bool is_valid_edge(edge t) const
Test whether the given edge is valid.
Definition: graph.hh:997
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:1267
edge_storage_t::data_t & edge_data(edge s)
return the Edgeg_Data of an edge.
Definition: graph.hh:766
const dests_vector_t & dests_vector() const
The vector used to store universal destinations.
Definition: graph.hh:1025
dests_vector_t & dests_vector()
The vector used to store universal destinations.
Definition: graph.hh:1030
edge_storage_t & edge_storage(edge s)
return a reference to the storage of an edge
Definition: graph.hh:748
const edge_storage_t & edge_storage(edge s) const
return a reference to the storage of an edge
Definition: graph.hh:754
edge new_univ_edge(state src, const std::initializer_list< state > &dsts, Args &&... args)
Create a new universal edge.
Definition: graph.hh:857
bool is_existential() const
Whether the automaton uses only existential branching.
Definition: graph.hh:672
const state_storage_t & state_storage(state s) const
return a reference to the storage of a state
Definition: graph.hh:718
internal::state_out< digraph > out(state src)
Return a fake container with all edges leaving src.
Definition: graph.hh:902
internal::killer_edge_iterator< digraph > out_iteraser(state src)
Return a fake container with all edges leaving src, allowing erasure.
Definition: graph.hh:937
void remove_dead_edges_()
Remove all dead edges.
Definition: graph.hh:1232
digraph(unsigned max_states=10, unsigned max_trans=0)
Construct an empty graph.
Definition: graph.hh:643
state new_univ_dests(I dst_begin, I dst_end)
Create a new universal destination group.
Definition: graph.hh:809
state_vector & states()
Return the vector of states.
Definition: graph.hh:951
void rename_states_(const std::vector< unsigned > &newst)
Rename all the states in the edge vector.
Definition: graph.hh:1429
void defrag_states(const std::vector< unsigned > &newst, unsigned used_states)
Rename and remove states.
Definition: graph.hh:1457
state_storage_t & state_storage(state s)
return a reference to the storage of a state
Definition: graph.hh:712
internal::state_out< const digraph > out(state src) const
Return a fake container with all edges leaving src.
Definition: graph.hh:914
unsigned num_edges() const
The number of edges in the automaton.
Definition: graph.hh:666
state_storage_t::data_t & state_data(state s)
return the State_Data associated to a state
Definition: graph.hh:730
const edge_vector_t & edge_vector() const
Return the vector of all edges.
Definition: graph.hh:980
state index_of_state(const state_storage_t &ss) const
Convert a storage reference into a state number.
Definition: graph.hh:886
void sort_edges_(Predicate p=Predicate())
Sort all edges according to a predicate.
Definition: graph.hh:1250
bool is_dead_edge(unsigned t) const
Tests whether an edge has been erased.
Definition: graph.hh:1009
edge_vector_t & edge_vector()
Return the vector of all edges.
Definition: graph.hh:985
edge new_univ_edge(state src, I dst_begin, I dst_end, Args &&... args)
Create a new universal edge.
Definition: graph.hh:844
void chain_edges_()
Reconstruct the chain of outgoing edges.
Definition: graph.hh:1383
internal::all_trans< digraph > edges()
Return a fake container with all edges (exluding erased edges)
Definition: graph.hh:966
edge new_edge(state src, state dst, Args &&... args)
Create a new edge.
Definition: graph.hh:785
internal::all_trans< const digraph > edges() const
Return a fake container with all edges (exluding erased edges)
Definition: graph.hh:961
const state_storage_t::data_t & state_data(state s) const
return the State_Data associated to a state
Definition: graph.hh:736
Definition: graph.hh:423
Definition: graph.hh:500
Definition: graph.hh:247
Definition: graph.hh:320
Definition: graph.hh:389
Definition: graph.hh:557
This class is used to tell parallel algorithms what resources they may use.
Definition: common.hh:156
Abstract class for states.
Definition: twa.hh:51
@ 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:27
Definition: graph.hh:66
Definition: graph.hh:163
Definition: graph.hh:188
Definition: graph.hh:46

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