spot 2.16
Loading...
Searching...
No Matches
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
41namespace 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>
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&
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&
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>
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>
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
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
const dests_vector_t & dests_vector() const
The vector used to store universal destinations.
Definition graph.hh:1039
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
internal::state_out< digraph > out(state_storage_t &src)
Return a fake container with all edges leaving src.
Definition graph.hh:922
internal::all_trans< digraph > edges()
Return a fake container with all edges (excluding erased edges)
Definition graph.hh:980
dests_vector_t & dests_vector()
The vector used to store universal destinations.
Definition graph.hh:1044
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
dump_storage_items
Flags controlling dump_storage() output.
Definition graph.hh:1092
const edge_vector_t & edge_vector() const
Return the vector of all edges.
Definition graph.hh:994
internal::killer_edge_iterator< digraph > out_iteraser(state src)
Return a fake container with all edges leaving src, allowing erasure.
Definition graph.hh:951
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
internal::state_out< digraph > out(state src)
Return a fake container with all edges leaving src.
Definition graph.hh:916
std::vector< edge_storage_t > edge_vector_t
Vector of edge storage.
Definition graph.hh:636
internal::state_out< const digraph > out(state_storage_t &src) const
Return a fake container with all edges leaving src.
Definition graph.hh:934
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
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
edge_vector_t & edge_vector()
Return the vector of all edges.
Definition graph.hh:999
const state_storage_t::data_t & state_data(state s) const
Return the State_Data associated to a state.
Definition graph.hh:748
internal::edge_iterator< const digraph > const_iterator
Const iterator over edges of a digraph.
Definition graph.hh:614
internal::state_out< const digraph > out(state src) const
Return a fake container with all edges leaving src.
Definition graph.hh:928
state_storage_t::data_t & state_data(state s)
Return the State_Data associated to a state.
Definition graph.hh:742
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
const edge_storage_t::data_t & edge_data(edge s) const
Return the Edge_Data of an edge.
Definition graph.hh:784
unsigned killed_edge_
Number of erased edges.
Definition graph.hh:647
edge_storage_t & edge_storage(edge s)
Return a reference to the storage of an edge.
Definition graph.hh:760
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
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
const state_vector & states() const
Return the vector of states.
Definition graph.hh:960
Edge_Data edge_data_t
Edge data type.
Definition graph.hh:618
internal::all_trans< const digraph > edges() const
Return a fake container with all edges (excluding erased edges)
Definition graph.hh:975
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
edge_storage_t::data_t & edge_data(edge s)
Return the Edge_Data of an edge.
Definition graph.hh:778
internal::edge_iterator< digraph > iterator
Iterator over edges of a digraph.
Definition graph.hh:612
state_storage_t & state_storage(state s)
Return a reference to the storage of a state.
Definition graph.hh:724
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 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
unsigned state
State number type.
Definition graph.hh:622
const edge_storage_t & edge_storage(edge s) const
Return a reference to the storage of an edge.
Definition graph.hh:766
edge new_edge(state src, state dst, Args &&... args)
Create a new edge.
Definition graph.hh:797
state_vector & states()
Return the vector of states.
Definition graph.hh:965
const state_storage_t & state_storage(state s) const
Return a reference to the storage of a state.
Definition graph.hh:730
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.8