spot 2.13
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
36namespace 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 {
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
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>
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&
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&
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 save 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
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
1456 void defrag_states(const std::vector<unsigned>& newst, unsigned used_states)
1457 {
1458 SPOT_ASSERT(newst.size() >= states_.size());
1459 SPOT_ASSERT(used_states > 0);
1460
1461 //std::cerr << "\nbefore defrag\n";
1462 //dump_storage(std::cerr);
1463
1464 // Permute all states in states_, as indicated by newst.
1465 // This will put erased states after used_states.
1466 permute_vector(states_, newst);
1467 unsigned send = states_.size();
1468 for (state s = used_states; s < send; ++s)
1469 {
1470 // This is an erased state. Mark all its edges as
1471 // dead (i.e., t.next_succ should point to t for each of
1472 // them).
1473 auto t = states_[s].succ;
1474 while (t)
1475 std::swap(t, edges_[t].next_succ);
1476 continue;
1477 }
1478 states_.resize(used_states);
1479
1480 // Shift all edges in edges_. The algorithm is
1481 // similar to remove_if, but it also keeps the correspondence
1482 // between the old and new index as newidx[old] = new.
1483 //
1484 // If you change anything to this logic, you might want to
1485 // double check twa_graph::defrag_states where we need to
1486 // predict the new edges indices in order to update
1487 // highlight-edges.
1488 unsigned tend = edges_.size();
1489 std::vector<edge> newidx(tend);
1490 unsigned dest = 1;
1491 for (edge t = 1; t < tend; ++t)
1492 {
1493 if (is_dead_edge(t))
1494 continue;
1495 if (t != dest)
1496 edges_[dest] = std::move(edges_[t]);
1497 newidx[t] = dest;
1498 ++dest;
1499 }
1500 edges_.resize(dest);
1501 killed_edge_ = 0;
1502
1503 // Adjust next_succ and dst pointers in all edges.
1504 for (edge t = 1; t < dest; ++t)
1505 {
1506 auto& tr = edges_[t];
1507 tr.src = newst[tr.src];
1508 tr.dst = newst[tr.dst];
1509 tr.next_succ = newidx[tr.next_succ];
1510 }
1511
1512 // Adjust succ and succ_tails pointers in all states.
1513 for (auto& s: states_)
1514 {
1515 s.succ = newidx[s.succ];
1516 s.succ_tail = newidx[s.succ_tail];
1517 }
1518
1519 //std::cerr << "\nafter defrag\n";
1520 //dump_storage(std::cerr);
1521 }
1522
1523 // prototype was changed in Spot 2.10
1524 SPOT_DEPRECATED("use reference version of this method")
1525 void defrag_states(std::vector<unsigned>&& newst, unsigned used_states)
1526 {
1527 return defrag_states(newst, used_states);
1528 }
1530 };
1531}
A directed graph.
Definition: graph.hh:599
unsigned num_states() const
The number of states in the automaton.
Definition: graph.hh:658
const dests_vector_t & dests_vector() const
The vector used to store universal destinations.
Definition: graph.hh:1025
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
internal::state_out< digraph > out(state_storage_t &src)
Return a fake container with all edges leaving src.
Definition: graph.hh:908
internal::all_trans< digraph > edges()
Return a fake container with all edges (excluding erased edges)
Definition: graph.hh:966
dests_vector_t & dests_vector()
The vector used to store universal destinations.
Definition: graph.hh:1030
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
const edge_vector_t & edge_vector() const
Return the vector of all edges.
Definition: graph.hh:980
internal::killer_edge_iterator< digraph > out_iteraser(state src)
Return a fake container with all edges leaving src, allowing erasure.
Definition: graph.hh:937
bool is_valid_edge(edge t) const
Test whether the given edge is valid.
Definition: graph.hh:997
internal::state_out< digraph > out(state src)
Return a fake container with all edges leaving src.
Definition: graph.hh:902
internal::state_out< const digraph > out(state_storage_t &src) const
Return a fake container with all edges leaving src.
Definition: graph.hh:920
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 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
edge_vector_t & edge_vector()
Return the vector of all edges.
Definition: graph.hh:985
const state_storage_t::data_t & state_data(state s) const
return the State_Data associated to a state
Definition: graph.hh:736
internal::state_out< const digraph > out(state src) const
Return a fake container with all edges leaving src.
Definition: graph.hh:914
state_storage_t::data_t & state_data(state s)
return the State_Data associated to a state
Definition: graph.hh:730
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
const edge_storage_t::data_t & edge_data(edge s) const
return the Edge_Data of an edge.
Definition: graph.hh:772
edge_storage_t & edge_storage(edge s)
return a reference to the storage of an edge
Definition: graph.hh:748
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:1456
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
const state_vector & states() const
Return the vector of states.
Definition: graph.hh:946
internal::all_trans< const digraph > edges() const
Return a fake container with all edges (excluding erased edges)
Definition: graph.hh:961
unsigned num_edges() const
The number of edges in the automaton.
Definition: graph.hh:666
edge_storage_t::data_t & edge_data(edge s)
return the Edge_Data of an edge.
Definition: graph.hh:766
state_storage_t & state_storage(state s)
return a reference to the storage of a state
Definition: graph.hh:712
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 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
const edge_storage_t & edge_storage(edge s) const
return a reference to the storage of an edge
Definition: graph.hh:754
edge new_edge(state src, state dst, Args &&... args)
Create a new edge.
Definition: graph.hh:785
state_vector & states()
Return the vector of states.
Definition: graph.hh:951
const state_storage_t & state_storage(state s) const
return a reference to the storage of a state
Definition: graph.hh:718
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: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
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.4