namespace simgrid::mc {
-UdporChecker::UdporChecker(const std::vector<char*>& args) : Exploration(args) {}
+UdporChecker::UdporChecker(const std::vector<char*>& args) : Exploration(args)
+{
+ /* Create initial data structures, if any ...*/
+ XBT_INFO("Starting a UDPOR exploration");
+
+ // TODO: Initialize state structures for the search
+}
+
+void UdporChecker::run()
+{
+ // NOTE: `A`, `D`, and `C` are derived from the
+ // original UDPOR paper [1], while
+ // `prev_exC` arises from the incremental computation
+ // of ex(C) from the former paper described in [3]
+ EventSet A, D;
+ Configuration C;
+ EventSet prev_exC;
+
+ /**
+ * Maintains the mapping between handles referenced by events in
+ * the current state of the unfolding
+ */
+ StateManager state_manager_;
+
+ const auto initial_state = std::make_unique<State>(get_remote_app());
+ const auto initial_state_id = state_manager_.record_state(std::move(initial_state));
+ const auto root_event = std::make_unique<UnfoldingEvent>(-1, "", EventSet(), initial_state_id);
+ explore(std::move(C), {EventSet()}, std::move(D), std::move(A), root_event.get(), std::move(prev_exC));
+}
+
+void UdporChecker::explore(Configuration C, std::list<EventSet> max_evt_history, EventSet D, EventSet A,
+ UnfoldingEvent* cur_evt, EventSet prev_exC)
+{
+ // Perform the incremental computation of exC
+ auto [exC, enC] = this->extend(C, max_evt_history, *cur_evt, prev_exC);
+
+ // If enC is a subset of D, intuitively
+ // there aren't any enabled transitions
+ // which are "worth" exploring since their
+ // exploration would lead to a so-called
+ // "sleep-set blocked" trace.
+ if (enC.is_subset_of(D)) {
+
+ // Log traces
+ if (C.events_.size() > 0) {
+
+ // g_var::nb_traces++;
+
+ // TODO: Log here correctly
+ // XBT_DEBUG("\n Exploring executions: %d : \n", g_var::nb_traces);
+ // ...
+ // ...
+ }
+
+ // When `en(C)` is empty, intuitively this means that there
+ // are no enabled transitions that can be executed from the
+ // state reached by `C` (denoted `state(C)`) (i.e. by some
+ // execution of the transitions in C obeying the causality
+ // relation). Hence, it is at this point we should check for a deadlock
+ if (enC.empty()) {
+ get_remote_app().check_deadlock();
+ }
+
+ return;
+ }
+
+ UnfoldingEvent* e = this->select_next_unfolding_event(A, enC);
+ if (e == nullptr) {
+ XBT_ERROR("\n\n****** CRITICAL ERROR ****** \n"
+ "UDPOR guarantees that an event will be chosen here, yet no events were actually chosen...\n"
+ "******************");
+ DIE_IMPOSSIBLE;
+ }
+
+ // TODO: Add verbose logging about which event is being explored
+
+ // TODO: Execute the transition associated with the event
+ // and map the new state
+
+ // const auto cur_state_id = cur_evt->get_state_id();
+ // auto curEv_StateId = currentEvt->get_state_id();
+ // auto nextState_id = App::app_side_->execute_transition(curEv_StateId, e->get_transition_tag());
+ // e->set_state_id(nextState_id);
+
+ // Configuration is the same + event e
+ // C1 = C + {e}
+ Configuration C1 = C;
+ C1.events_.insert(e);
+ C1.updateMaxEvent(e);
-void UdporChecker::run() {}
+ max_evt_history.push_back(C1.maxEvent);
+
+ // A <-- A \ {e}, ex(C) <-- ex(C) \ {e}
+ A.remove(e);
+ exC.remove(e);
+
+ // Explore(C + {e}, D, A \ {e})
+ this->explore(C1, max_evt_history, D, A, e, exC);
+
+ // D <-- D + {e}
+ D.insert(e);
+
+ // TODO: Determine a value of K to use or don't use it at all
+ constexpr unsigned K = 10;
+ auto J = this->compute_partial_alternative(D, C, K);
+ if (!J.empty()) {
+ J.subtract(C.events_);
+ max_evt_history.pop_back();
+ explore(C, max_evt_history, D, J, cur_evt, prev_exC);
+ }
+
+ // D <-- D - {e}
+ D.remove(e);
+ this->clean_up_explore(e, C, D);
+}
+
+std::tuple<EventSet, EventSet> UdporChecker::extend(const Configuration& C, const std::list<EventSet>& max_evt_history,
+ const UnfoldingEvent& cur_event, const EventSet& prev_exC) const
+{
+ // exC.remove(cur_event);
+
+ // TODO: Compute extend() as it exists in tiny_simgrid
+
+ // exC.subtract(C);
+ return std::tuple<EventSet, EventSet>();
+}
+
+UnfoldingEvent* UdporChecker::select_next_unfolding_event(const EventSet& A, const EventSet& enC)
+{
+ // TODO: Actually select an event here
+ return nullptr;
+}
+
+EventSet UdporChecker::compute_partial_alternative(const EventSet& D, const EventSet& C, const unsigned k) const
+{
+ // TODO: Compute k-partial alternatives using [2]
+ return EventSet();
+}
+
+void UdporChecker::clean_up_explore(const UnfoldingEvent* e, const EventSet& C, const EventSet& D)
+{
+ // TODO: Perform clean up here
+}
RecordTrace UdporChecker::get_record_trace()
{
#include "src/mc/mc_record.hpp"
#include "src/mc/udpor_global.hpp"
+#include <optional>
+
namespace simgrid::mc {
+/**
+ * @brief Performs exploration of a concurrent system via the
+ * UDPOR algorithm
+ *
+ * The `UdporChecker` implementation is based primarily off three papers,
+ * herein referred to as [1], [2], and [3] respectively, as well as the
+ * current implementation of `tiny_simgrid`:
+ *
+ * 1. "Unfolding-based Partial Order Reduction" by Rodriguez et al.
+ * 2. Quasi-Optimal Partial Order Reduction by Nguyen et al.
+ * 3. The Anh Pham's Thesis "Exploration efficace de l'espace ..."
+ */
class XBT_PRIVATE UdporChecker : public Exploration {
public:
explicit UdporChecker(const std::vector<char*>& args);
uint32_t nb_traces = 0;
/**
- * The "relevant" portions of the unfolding that must be kept around to ensure that
+ * @brief The "relevant" portions of the unfolding that must be kept around to ensure that
* UDPOR properly searches the state space
*
* The set `U` is a global variable which is maintained by UDPOR
*
* @invariant: When a new event is created by UDPOR, it is inserted into
* this set. All new events that are created by UDPOR have causes that
- * also exist in U and are valid for the duration of the search
+ * also exist in U and are valid for the duration of the search.
+ *
+ * If an event is discarded instead of moved from set `U` to set `G`,
+ * the event and its contents will be discarded.
*/
EventSet U;
+
+ /**
+ * @brief The "irrelevant" portions of the unfolding that do not need to be kept
+ * around to ensure that UDPOR functions correctly
+ *
+ * The set `G` is another global variable maintained by the UDPOR algorithm which
+ * is used to keep track of all events which used to be important to UDPOR
+ */
EventSet G;
private:
*
* TODO: Explain what this means here
*/
- void explore(Configuration C, std::list<EventSet> maxEvtHistory, EventSet D, EventSet A, UnfoldingEvent* currentEvt,
+ void explore(Configuration C, std::list<EventSet> max_evt_history, EventSet D, EventSet A, UnfoldingEvent* cur_event,
EventSet prev_exC);
+
+ /**
+ * @brief Computes the sets `ex(C)` and `en(C)` of the given configuration
+ * `C` as an incremental computation from the the previous computation of `ex(C)`
+ *
+ * A central component to UDPOR is the computation of the set `ex(C)`. The
+ * extension set `ex(C)` of a configuration `C` is defined as the set of events
+ * outside of `C` whose full dependency chain is contained in `C` (see [1]
+ * for more details).
+ *
+ * In general, computing `ex(C)` is very expensive. In paper [3], The Anh Pham
+ * shows a method of incremental computation of the set `ex(C)` under the
+ * conclusions afforded under the computation model in consideration, of which
+ * SimGrid is apart, which allow for `ex(C)` to be computed much more efficiently.
+ * Intuitively, the idea is to take advantage of the fact that you can avoid a lot
+ * of repeated computation by exploiting the aforementioned properties (in [3]) in
+ * what is effectively a dynamic programming optimization. See [3] for more details
+ *
+ * @param C the configuration based on which the two sets `ex(C)` and `en(C)` are
+ * computed
+ * @param cur_event the event where UDPOR currently "rests", viz. the event UDPOR
+ * is now currently considering
+ * @param prev_exC the previous value of `ex(C)`, viz. that which was computed for
+ * the configuration `C' := C - {cur_event}`
+ * @returns a tuple containing the pair of sets `ex(C)` and `en(C)` respectively
+ */
+ std::tuple<EventSet, EventSet> extend(const Configuration& C, const std::list<EventSet>& max_evt_history,
+ const UnfoldingEvent& cur_event, const EventSet& prev_exC) const;
+
+ /**
+ * @brief Identifies the next event from the unfolding of the concurrent system
+ * that should next be explored as an extension of a configuration with
+ * enabled events `enC`
+ *
+ * @param A The set of events `A` maintained by the UDPOR algorithm to help
+ * determine how events should be selected. See the original paper [1] for more details
+ *
+ * @param enC The set `enC` of enabled events from the extension set `exC` used
+ * by the UDPOR algorithm to select new events to search. See the original
+ * paper [1] for more details
+ */
+ UnfoldingEvent* select_next_unfolding_event(const EventSet& A, const EventSet& enC);
+
+ EventSet compute_partial_alternative(const EventSet& D, const EventSet& C, const unsigned k) const;
+
+ /**
+ *
+ */
+ void clean_up_explore(const UnfoldingEvent* e, const EventSet& C, const EventSet& D);
};
} // namespace simgrid::mc