Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Add clean up phase to UDPOR
[simgrid.git] / src / mc / explo / UdporChecker.cpp
1 /* Copyright (c) 2016-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/UdporChecker.hpp"
7 #include "src/mc/api/State.hpp"
8 #include "src/mc/explo/udpor/maximal_subsets_iterator.hpp"
9 #include <xbt/asserts.h>
10 #include <xbt/log.h>
11
12 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_udpor, mc, "Logging specific to verification using UDPOR");
13
14 namespace simgrid::mc::udpor {
15
16 UdporChecker::UdporChecker(const std::vector<char*>& args) : Exploration(args)
17 {
18   // Initialize the map
19 }
20
21 void UdporChecker::run()
22 {
23   XBT_INFO("Starting a UDPOR exploration");
24   // NOTE: `A`, `D`, and `C` are derived from the
25   // original UDPOR paper [1], while `prev_exC` arises
26   // from the incremental computation of ex(C) from [3]
27   Configuration C_root;
28
29   // TODO: Move computing the root configuration into a method on the Unfolding
30   auto initial_state      = get_current_state();
31   auto root_event         = std::make_unique<UnfoldingEvent>(EventSet(), std::make_shared<Transition>());
32   auto* root_event_handle = root_event.get();
33   unfolding.insert(std::move(root_event));
34   C_root.add_event(root_event_handle);
35
36   explore(C_root, EventSet(), EventSet(), std::move(initial_state), EventSet());
37
38   XBT_INFO("UDPOR exploration terminated -- model checking completed");
39 }
40
41 void UdporChecker::explore(const Configuration& C, EventSet D, EventSet A, std::unique_ptr<State> stateC,
42                            EventSet prev_exC)
43 {
44   auto exC       = compute_exC(C, *stateC, prev_exC);
45   const auto enC = compute_enC(C, exC);
46
47   // If enC is a subset of D, intuitively
48   // there aren't any enabled transitions
49   // which are "worth" exploring since their
50   // exploration would lead to a so-called
51   // "sleep-set blocked" trace.
52   if (enC.is_subset_of(D)) {
53
54     if (not C.get_events().empty()) {
55       // Report information...
56     }
57
58     // When `en(C)` is empty, intuitively this means that there
59     // are no enabled transitions that can be executed from the
60     // state reached by `C` (denoted `state(C)`), i.e. by some
61     // execution of the transitions in C obeying the causality
62     // relation. Here, then, we would be in a deadlock.
63     if (enC.empty()) {
64       get_remote_app().check_deadlock();
65       DIE_IMPOSSIBLE;
66     }
67
68     return;
69   }
70
71   // TODO: Add verbose logging about which event is being explored
72
73   const UnfoldingEvent* e = select_next_unfolding_event(A, enC);
74   xbt_assert(e != nullptr, "\n\n****** INVARIANT VIOLATION ******\n"
75                            "UDPOR guarantees that an event will be chosen at each point in\n"
76                            "the search, yet no events were actually chosen\n"
77                            "*********************************\n\n");
78
79   // Move the application into stateCe and make note of that state
80   move_to_stateCe(*stateC, *e);
81   auto stateCe = record_current_state();
82
83   // Ce := C + {e}
84   Configuration Ce = C;
85   Ce.add_event(e);
86
87   A.remove(e);
88   exC.remove(e);
89
90   // Explore(C + {e}, D, A \ {e})
91   explore(Ce, D, std::move(A), std::move(stateCe), std::move(exC));
92
93   // D <-- D + {e}
94   D.insert(e);
95
96   // TODO: Determine a value of K to use or don't use it at all
97   constexpr unsigned K = 10;
98   if (auto J = compute_partial_alternative(D, C, K); !J.empty()) {
99     J.subtract(C.get_events());
100
101     // Before searching the "right half", we need to make
102     // sure the program actually reflects the fact
103     // that we are searching again from `stateC` (the recursive
104     // search moved the program into `stateCe`)
105     restore_program_state_to(*stateC);
106
107     // Explore(C, D + {e}, J \ C)
108     explore(C, D, std::move(J), std::move(stateC), std::move(prev_exC));
109   }
110
111   // D <-- D - {e}
112   D.remove(e);
113
114   // Remove(e, C, D)
115   clean_up_explore(e, C, D);
116 }
117
118 EventSet UdporChecker::compute_exC(const Configuration& C, const State& stateC, const EventSet& prev_exC)
119 {
120   // See eqs. 5.7 of section 5.2 of [3]
121   // C = C' + {e_cur}, i.e. C' = C - {e_cur}
122   //
123   // Then
124   //
125   // ex(C) = ex(C' + {e_cur}) = ex(C') / {e_cur} +
126   //    U{<a, K> : K is maximal, `a` depends on all of K, `a` enabled at config(K) }
127   const UnfoldingEvent* e_cur = C.get_latest_event();
128   EventSet exC                = prev_exC;
129   exC.remove(e_cur);
130
131   for (const auto& [aid, actor_state] : stateC.get_actors_list()) {
132     for (const auto& transition : actor_state.get_enabled_transitions()) {
133       // First check for a specialized function that can compute the extension
134       // set "quickly" based on its type. Otherwise, fall back to computing
135       // the set "by hand"
136       const auto specialized_extension_function = incremental_extension_functions.find(transition->type_);
137       if (specialized_extension_function != incremental_extension_functions.end()) {
138         exC.form_union((specialized_extension_function->second)(C, transition));
139       } else {
140         exC.form_union(this->compute_exC_by_enumeration(C, transition));
141       }
142     }
143   }
144   return exC;
145 }
146
147 EventSet UdporChecker::compute_exC_by_enumeration(const Configuration& C, const std::shared_ptr<Transition> action)
148 {
149   // Here we're computing the following:
150   //
151   // U{<a, K> : K is maximal, `a` depends on all of K, `a` enabled at config(K) }
152   //
153   // where `a` is the `action` given to us. Note that `a` is presumed to be enabled
154   EventSet incremental_exC;
155
156   for (auto begin =
157            maximal_subsets_iterator(C, {[&](const UnfoldingEvent* e) { return e->is_dependent_with(action.get()); }});
158        begin != maximal_subsets_iterator(); ++begin) {
159     const EventSet& maximal_subset = *begin;
160
161     // TODO: Determine if `a` is enabled here
162     const bool enabled_at_config_k = false;
163
164     if (enabled_at_config_k) {
165       auto candidate_handle = std::make_unique<UnfoldingEvent>(maximal_subset, action);
166       if (auto candidate_event = candidate_handle.get(); not unfolding.contains_event_equivalent_to(candidate_event)) {
167         // This is a new event (i.e. one we haven't yet seen)
168         unfolding.insert(std::move(candidate_handle));
169         incremental_exC.insert(candidate_event);
170       }
171     }
172   }
173
174   return incremental_exC;
175 }
176
177 EventSet UdporChecker::compute_enC(const Configuration& C, const EventSet& exC) const
178 {
179   EventSet enC;
180   for (const auto e : exC) {
181     if (not e->conflicts_with(C)) {
182       enC.insert(e);
183     }
184   }
185   return enC;
186 }
187
188 void UdporChecker::move_to_stateCe(State& state, const UnfoldingEvent& e)
189 {
190   const aid_t next_actor = e.get_transition()->aid_;
191
192   // TODO: Add the trace if possible for reporting a bug
193   xbt_assert(next_actor >= 0, "\n\n****** INVARIANT VIOLATION ******\n"
194                               "In reaching this execution path, UDPOR ensures that at least one\n"
195                               "one transition of the state of an visited event is enabled, yet no\n"
196                               "state was actually enabled. Please report this as a bug.\n"
197                               "*********************************\n\n");
198   state.execute_next(next_actor);
199 }
200
201 void UdporChecker::restore_program_state_to(const State& stateC)
202 {
203   get_remote_app().restore_initial_state();
204   // TODO: We need to have the stack of past states available at this
205   // point. Since the method is recursive, we'll need to keep track of
206   // this as we progress
207 }
208
209 std::unique_ptr<State> UdporChecker::record_current_state()
210 {
211   auto next_state = this->get_current_state();
212
213   // In UDPOR, we care about all enabled transitions in a given state
214   next_state->mark_all_enabled_todo();
215
216   return next_state;
217 }
218
219 const UnfoldingEvent* UdporChecker::select_next_unfolding_event(const EventSet& A, const EventSet& enC)
220 {
221   if (!enC.empty()) {
222     return *(enC.begin());
223   }
224
225   for (const auto& event : A) {
226     if (enC.contains(event)) {
227       return event;
228     }
229   }
230   return nullptr;
231 }
232
233 EventSet UdporChecker::compute_partial_alternative(const EventSet& D, const Configuration& C, const unsigned k) const
234 {
235   // TODO: Compute k-partial alternatives using [2]
236   return EventSet();
237 }
238
239 void UdporChecker::clean_up_explore(const UnfoldingEvent* e, const Configuration& C, const EventSet& D)
240 {
241   const EventSet C_union_D              = C.get_events().make_union(D);
242   const EventSet es_immediate_conflicts = this->unfolding.get_immediate_conflicts_of(e);
243   const EventSet Q_CDU                  = C_union_D.make_union(es_immediate_conflicts.get_local_config());
244
245   // Move {e} \ Q_CDU from U to G
246   if (Q_CDU.contains(e)) {
247     this->unfolding.remove(e);
248   }
249
250   // foreach ê in #ⁱ_U(e)
251   for (const auto* e_hat : es_immediate_conflicts) {
252     // Move [ê] \ Q_CDU from U to G
253     const EventSet to_remove = e_hat->get_history().subtracting(Q_CDU);
254     this->unfolding.remove(to_remove);
255   }
256 }
257
258 RecordTrace UdporChecker::get_record_trace()
259 {
260   RecordTrace res;
261   return res;
262 }
263
264 std::vector<std::string> UdporChecker::get_textual_trace()
265 {
266   // TODO: Topologically sort the events of the latest configuration
267   // and iterate through that topological sorting
268   std::vector<std::string> trace;
269   return trace;
270 }
271
272 } // namespace simgrid::mc::udpor
273
274 namespace simgrid::mc {
275
276 Exploration* create_udpor_checker(const std::vector<char*>& args)
277 {
278   return new simgrid::mc::udpor::UdporChecker(args);
279 }
280
281 } // namespace simgrid::mc