Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Move topological ordering to EventSet
[simgrid.git] / src / mc / explo / udpor / EventSet.cpp
1 /* Copyright (c) 2008-2023. The SimGrid Team. All rights reserved.          */
2
3 /* This program is free software; you can redistribute it and/or modify it
4  * under the terms of the license (GNU LGPL) which comes with this package. */
5
6 #include "src/mc/explo/udpor/EventSet.hpp"
7 #include "src/mc/explo/udpor/Configuration.hpp"
8 #include "src/mc/explo/udpor/History.hpp"
9 #include "src/mc/explo/udpor/UnfoldingEvent.hpp"
10
11 #include <iterator>
12 #include <stack>
13
14 namespace simgrid::mc::udpor {
15
16 EventSet::EventSet(Configuration&& config) : EventSet(config.get_events()) {}
17
18 void EventSet::remove(const UnfoldingEvent* e)
19 {
20   this->events_.erase(e);
21 }
22
23 void EventSet::subtract(const EventSet& other)
24 {
25   this->events_ = std::move(subtracting(other).events_);
26 }
27
28 void EventSet::subtract(const Configuration& config)
29 {
30   subtract(config.get_events());
31 }
32
33 EventSet EventSet::subtracting(const EventSet& other) const
34 {
35   std::unordered_set<const UnfoldingEvent*> result = this->events_;
36
37   for (const UnfoldingEvent* e : other.events_)
38     result.erase(e);
39
40   return EventSet(std::move(result));
41 }
42
43 EventSet EventSet::subtracting(const Configuration& config) const
44 {
45   return subtracting(config.get_events());
46 }
47
48 EventSet EventSet::subtracting(const UnfoldingEvent* e) const
49 {
50   auto result = this->events_;
51   result.erase(e);
52   return EventSet(std::move(result));
53 }
54
55 void EventSet::insert(const UnfoldingEvent* e)
56 {
57   this->events_.insert(e);
58 }
59
60 void EventSet::form_union(const EventSet& other)
61 {
62   this->events_ = std::move(make_union(other).events_);
63 }
64
65 void EventSet::form_union(const Configuration& config)
66 {
67   form_union(config.get_events());
68 }
69
70 EventSet EventSet::make_union(const UnfoldingEvent* e) const
71 {
72   auto result = this->events_;
73   result.insert(e);
74   return EventSet(std::move(result));
75 }
76
77 EventSet EventSet::make_union(const EventSet& other) const
78 {
79   std::unordered_set<const UnfoldingEvent*> result = this->events_;
80
81   for (const UnfoldingEvent* e : other.events_)
82     result.insert(e);
83
84   return EventSet(std::move(result));
85 }
86
87 EventSet EventSet::make_union(const Configuration& config) const
88 {
89   return make_union(config.get_events());
90 }
91
92 size_t EventSet::size() const
93 {
94   return this->events_.size();
95 }
96
97 bool EventSet::empty() const
98 {
99   return this->events_.empty();
100 }
101
102 bool EventSet::contains(const UnfoldingEvent* e) const
103 {
104   return this->events_.find(e) != this->events_.end();
105 }
106
107 bool EventSet::is_subset_of(const EventSet& other) const
108 {
109   // If there is some element not contained in `other`, then
110   // the set difference will contain that element and the
111   // result won't be empty
112   return subtracting(other).empty();
113 }
114
115 bool EventSet::is_valid_configuration() const
116 {
117   /// @invariant: A collection of events `E` is a configuration
118   /// if and only if following while following the history of
119   /// each event `e` of `E` you remain in `E`. In other words, you
120   /// only see events from set `E`
121   ///
122   /// The simple proof is based on the definition of a configuration
123   /// which requires that all events have their history contained
124   /// in the set
125   const History history(*this);
126   return this->contains(history);
127 }
128
129 bool EventSet::contains(const History& history) const
130 {
131   return std::all_of(history.begin(), history.end(), [=](const UnfoldingEvent* e) { return this->contains(e); });
132 }
133
134 bool EventSet::is_maximal() const
135 {
136   const History history(*this);
137   return *this == history.get_all_maximal_events();
138 }
139
140 bool EventSet::is_conflict_free() const
141 {
142   return false;
143 }
144
145 std::vector<const UnfoldingEvent*> EventSet::get_topological_ordering() const
146 {
147   // This is essentially an implementation of detecting cycles
148   // in a graph with coloring, except it makes a topological
149   // ordering out of it
150   if (empty()) {
151     return std::vector<const UnfoldingEvent*>();
152   }
153
154   std::stack<const UnfoldingEvent*> event_stack;
155   std::vector<const UnfoldingEvent*> topological_ordering;
156   EventSet unknown_events = *this;
157   EventSet temporarily_marked_events;
158   EventSet permanently_marked_events;
159
160   while (not unknown_events.empty()) {
161     EventSet discovered_events;
162     event_stack.push(*unknown_events.begin());
163
164     while (not event_stack.empty()) {
165       const UnfoldingEvent* evt = event_stack.top();
166       discovered_events.insert(evt);
167
168       if (not temporarily_marked_events.contains(evt)) {
169         // If this event hasn't yet been marked, do
170         // so now so that if we both see it
171         // again in a child we can detect a cycle
172         temporarily_marked_events.insert(evt);
173
174         EventSet immediate_causes = evt->get_immediate_causes();
175         if (!immediate_causes.empty() && immediate_causes.is_subset_of(temporarily_marked_events)) {
176           throw std::invalid_argument("Attempted to perform a topological sort on a configuration "
177                                       "whose contents contain a cycle. The configuration (and the graph "
178                                       "connecting all of the events) is an invalid event structure");
179         }
180         immediate_causes.subtract(discovered_events);
181         immediate_causes.subtract(permanently_marked_events);
182         std::for_each(immediate_causes.begin(), immediate_causes.end(),
183                       [&event_stack](const UnfoldingEvent* cause) { event_stack.push(cause); });
184       } else {
185         unknown_events.remove(evt);
186         temporarily_marked_events.remove(evt);
187         permanently_marked_events.insert(evt);
188
189         // In moving this event to the end of the list,
190         // we are saying this events "happens before" other
191         // events that are added later.
192         if (this->contains(evt)) {
193           topological_ordering.push_back(evt);
194         }
195
196         // Only now do we remove the event, i.e. once
197         // we've processed the same event twice
198         event_stack.pop();
199       }
200     }
201   }
202   return topological_ordering;
203 }
204
205 std::vector<const UnfoldingEvent*> EventSet::get_topological_ordering_of_reverse_graph() const
206 {
207   // The implementation exploits the property that
208   // a topological sorting S^R of the reverse graph G^R
209   // of some graph G is simply the reverse of any
210   // topological sorting S of G.
211   auto topological_events = get_topological_ordering();
212   std::reverse(topological_events.begin(), topological_events.end());
213   return topological_events;
214 }
215
216 } // namespace simgrid::mc::udpor