Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Begin filling in computations of ex(C) and en(C)
authorMaxwell Pirtle <maxwellpirtle@gmail.com>
Wed, 8 Mar 2023 14:27:43 +0000 (15:27 +0100)
committerMaxwell Pirtle <maxwellpirtle@gmail.com>
Wed, 8 Mar 2023 14:27:43 +0000 (15:27 +0100)
This commit introduces the first steps towards
computing ex(C) and en(C) using many of the
additions of the previous phases. An important
part of the computation is so far missing, however,
viz. the determination of whether or not an action
`a` is enabled at some point in the past. We'll
need to decipher what's going on in tiny_simgrid
to fully determine what to do here; for now, we
simply ignore adding an event entirely and just
say that `a` was disabled at all points in the past.

src/mc/api/ActorState.hpp
src/mc/explo/UdporChecker.cpp
src/mc/explo/UdporChecker.hpp
src/mc/explo/udpor/Unfolding.cpp
src/mc/explo/udpor/Unfolding.hpp
src/mc/explo/udpor/UnfoldingEvent.cpp
src/mc/explo/udpor/UnfoldingEvent.hpp

index 946971a..97cf05a 100644 (file)
@@ -40,7 +40,7 @@ class ActorState {
    * such transitions such that `pending_transitions_[i]` represents
    * the variation of the transition with `times_considered = i`.
    *
-   * TODO: If only a subset of transitions of an actor that can
+   * @note: If only a subset of transitions of an actor that can
    * take multiple transitions in some state are truly enabled,
    * we would instead need to map `times_considered` to a transition,
    * as the map is currently implicit in the ordering of the transitions
index 02452aa..4d89514 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "src/mc/explo/UdporChecker.hpp"
 #include "src/mc/api/State.hpp"
+#include "src/mc/explo/udpor/maximal_subsets_iterator.hpp"
 #include <xbt/asserts.h>
 #include <xbt/log.h>
 
@@ -40,13 +41,8 @@ void UdporChecker::run()
 void UdporChecker::explore(const Configuration& C, EventSet D, EventSet A, std::unique_ptr<State> stateC,
                            EventSet prev_exC)
 {
-  // Perform the incremental computation of exC
-  //
-  // TODO: This method will have side effects on
-  // the unfolding, but the naming of the method
-  // suggests it is doesn't have side effects. We should
-  // reconcile this in the future
-  auto [exC, enC] = compute_extension(C, *stateC, prev_exC);
+  auto exC       = compute_exC(C, *stateC, prev_exC);
+  const auto enC = compute_enC(C, exC);
 
   // If enC is a subset of D, intuitively
   // there aren't any enabled transitions
@@ -118,8 +114,7 @@ void UdporChecker::explore(const Configuration& C, EventSet D, EventSet A, std::
   clean_up_explore(e, C, D);
 }
 
-std::tuple<EventSet, EventSet> UdporChecker::compute_extension(const Configuration& C, const State& stateC,
-                                                               const EventSet& prev_exC) const
+EventSet UdporChecker::compute_exC(const Configuration& C, const State& stateC, const EventSet& prev_exC)
 {
   // See eqs. 5.7 of section 5.2 of [3]
   // C = C' + {e_cur}, i.e. C' = C - {e_cur}
@@ -127,7 +122,7 @@ std::tuple<EventSet, EventSet> UdporChecker::compute_extension(const Configurati
   // Then
   //
   // ex(C) = ex(C' + {e_cur}) = ex(C') / {e_cur} +
-  //    U{<a, K> : K is maximal, `a` depends on all of K, `a` enabled at K }
+  //    U{<a, K> : K is maximal, `a` depends on all of K, `a` enabled at config(K) }
   const UnfoldingEvent* e_cur = C.get_latest_event();
   EventSet exC                = prev_exC;
   exC.remove(e_cur);
@@ -139,33 +134,56 @@ std::tuple<EventSet, EventSet> UdporChecker::compute_extension(const Configurati
       // the set "by hand"
       const auto specialized_extension_function = incremental_extension_functions.find(transition->type_);
       if (specialized_extension_function != incremental_extension_functions.end()) {
-        exC.form_union((specialized_extension_function->second)(C, *transition));
+        exC.form_union((specialized_extension_function->second)(C, transition));
       } else {
-        exC.form_union(this->compute_extension_by_enumeration(C, *transition));
+        exC.form_union(this->compute_exC_by_enumeration(C, transition));
       }
     }
   }
-
-  EventSet enC;
-  // TODO: Compute enC based on conflict relations
-
-  return std::tuple<EventSet, EventSet>(exC, enC);
+  return exC;
 }
 
-EventSet UdporChecker::compute_extension_by_enumeration(const Configuration& C, const Transition& action) const
+EventSet UdporChecker::compute_exC_by_enumeration(const Configuration& C, const std::shared_ptr<Transition> action)
 {
   // Here we're computing the following:
   //
-  // U{<a, K> : K is maximal, `a` depends on all of K, `a` enabled at K }
+  // U{<a, K> : K is maximal, `a` depends on all of K, `a` enabled at config(K) }
   //
-  // where `a` is the `action` given to us.
+  // where `a` is the `action` given to us. Note that `a` is presumed to be enabled
   EventSet incremental_exC;
 
-  // Compute the extension set here using the algorithm Martin described...
+  for (auto begin =
+           maximal_subsets_iterator(C, {[&](const UnfoldingEvent* e) { return e->is_dependent_with(action.get()); }});
+       begin != maximal_subsets_iterator(); ++begin) {
+    const EventSet& maximal_subset = *begin;
+
+    // TODO: Determine if `a` is enabled here
+    const bool enabled_at_config_k = false;
+
+    if (enabled_at_config_k) {
+      auto candidate_handle = std::make_unique<UnfoldingEvent>(maximal_subset, action);
+      if (auto candidate_event = candidate_handle.get(); not unfolding.contains_event_equivalent_to(candidate_event)) {
+        // This is a new event (i.e. one we haven't yet seen)
+        unfolding.insert(std::move(candidate_handle));
+        incremental_exC.insert(candidate_event);
+      }
+    }
+  }
 
   return incremental_exC;
 }
 
+EventSet UdporChecker::compute_enC(const Configuration& C, const EventSet& exC) const
+{
+  EventSet enC;
+  for (const auto e : exC) {
+    if (not e->conflicts_with(C)) {
+      enC.insert(e);
+    }
+  }
+  return enC;
+}
+
 void UdporChecker::move_to_stateCe(State& state, const UnfoldingEvent& e)
 {
   const aid_t next_actor = e.get_transition()->aid_;
index 5e0b575..2bcdeb4 100644 (file)
@@ -71,12 +71,11 @@ private:
   /// @brief UDPOR's current "view" of the program it is exploring
   Unfolding unfolding = Unfolding();
 
-  using ExtensionFunction = std::function<EventSet(const Configuration&, const Transition&)>;
-
   /**
    * @brief A collection of specialized functions which can incrementally
    * compute the extension of a configuration based on the action taken
    */
+  using ExtensionFunction = std::function<EventSet(const Configuration&, const std::shared_ptr<Transition>)>;
   std::unordered_map<Transition::Type, ExtensionFunction> incremental_extension_functions =
       std::unordered_map<Transition::Type, ExtensionFunction>();
 
@@ -139,12 +138,17 @@ private:
    * @param stateC the state of the program after having executed C (viz. `state(C)`)
    * @param prev_exC the previous value of `ex(C)`, viz. that which was computed for
    * the configuration `C' := C - {e}`
-   * @returns a tuple containing the pair of sets `ex(C)` and `en(C)` respectively
+   * @returns the extension set `ex(C)` of `C`
+   */
+  EventSet compute_exC(const Configuration& C, const State& stateC, const EventSet& prev_exC);
+
+  /**
+   * @brief Computes a portion of the extension set of a configuration given
+   * some action `action`
    */
-  std::tuple<EventSet, EventSet> compute_extension(const Configuration& C, const State& stateC,
-                                                   const EventSet& prev_exC) const;
+  EventSet compute_exC_by_enumeration(const Configuration& C, const std::shared_ptr<Transition> action);
 
-  EventSet compute_extension_by_enumeration(const Configuration& C, const Transition& action) const;
+  EventSet compute_enC(const Configuration& C, const EventSet& exC) const;
 
   /**
    *
index abe9f26..84ad822 100644 (file)
@@ -9,7 +9,7 @@
 
 namespace simgrid::mc::udpor {
 
-void Unfolding::remove(UnfoldingEvent* e)
+void Unfolding::remove(const UnfoldingEvent* e)
 {
   if (e == nullptr) {
     throw std::invalid_argument("Expected a non-null pointer to an event, but received NULL");
@@ -19,9 +19,8 @@ void Unfolding::remove(UnfoldingEvent* e)
 
 void Unfolding::insert(std::unique_ptr<UnfoldingEvent> e)
 {
-  UnfoldingEvent* handle = e.get();
-  auto loc               = this->global_events_.find(handle);
-  if (loc != this->global_events_.end()) {
+  const UnfoldingEvent* handle = e.get();
+  if (auto loc = this->global_events_.find(handle); loc != this->global_events_.end()) {
     // This is bad: someone wrapped the raw event address twice
     // in two different unique ptrs and attempted to
     // insert it into the unfolding...
@@ -33,4 +32,16 @@ void Unfolding::insert(std::unique_ptr<UnfoldingEvent> e)
   this->global_events_[handle] = std::move(e);
 }
 
+bool Unfolding::contains_event_equivalent_to(const UnfoldingEvent* e) const
+{
+  // Notice the use of `==` equality here. `e` may not be contained in the
+  // unfolding; but some event which is "equivalent" to it could be.
+  for (const auto& [event, _] : global_events_) {
+    if (*event == *e) {
+      return true;
+    }
+  }
+  return false;
+}
+
 } // namespace simgrid::mc::udpor
index 471e043..01c7663 100644 (file)
@@ -24,15 +24,16 @@ private:
    * of the addresses that are referenced by EventSet instances and Configuration
    * instances. UDPOR guarantees that events are persisted for as long as necessary
    */
-  std::unordered_map<UnfoldingEvent*, std::unique_ptr<UnfoldingEvent>> global_events_;
+  std::unordered_map<const UnfoldingEvent*, std::unique_ptr<UnfoldingEvent>> global_events_;
 
 public:
   Unfolding()                       = default;
   Unfolding& operator=(Unfolding&&) = default;
   Unfolding(Unfolding&&)            = default;
 
-  void remove(UnfoldingEvent* e);
+  void remove(const UnfoldingEvent* e);
   void insert(std::unique_ptr<UnfoldingEvent> e);
+  bool contains_event_equivalent_to(const UnfoldingEvent* e) const;
 
   size_t size() const { return this->global_events_.size(); }
   bool empty() const { return this->global_events_.empty(); }
index fe1a3a3..50c1e22 100644 (file)
@@ -20,10 +20,13 @@ UnfoldingEvent::UnfoldingEvent(EventSet immediate_causes, std::shared_ptr<Transi
 
 bool UnfoldingEvent::operator==(const UnfoldingEvent& other) const
 {
-  const bool same_actor = associated_transition->aid_ == other.associated_transition->aid_;
-  if (!same_actor)
+  // Must be run by the same actor
+  if (associated_transition->aid_ != other.associated_transition->aid_)
     return false;
 
+  // If run by the same actor, must be the same _step_ of that actor's
+  // execution
+
   // TODO: Add in information to determine which step in the sequence this actor was executed
 
   // All unfolding event objects are created in reference to
@@ -85,9 +88,14 @@ bool UnfoldingEvent::conflicts_with(const Configuration& config) const
                      [&](const UnfoldingEvent* e) { return this->has_dependent_transition_with(e); });
 }
 
+bool UnfoldingEvent::is_dependent_with(const Transition* t) const
+{
+  return associated_transition->depends(t);
+}
+
 bool UnfoldingEvent::has_dependent_transition_with(const UnfoldingEvent* other) const
 {
-  return associated_transition->depends(other->associated_transition.get());
+  return is_dependent_with(other->associated_transition.get());
 }
 
 } // namespace simgrid::mc::udpor
index 3ef7d9f..b72e13c 100644 (file)
@@ -33,6 +33,7 @@ public:
   bool conflicts_with(const UnfoldingEvent* other) const;
   bool conflicts_with(const Configuration& config) const;
   bool immediately_conflicts_with(const UnfoldingEvent* other) const;
+  bool is_dependent_with(const Transition*) const;
   bool has_dependent_transition_with(const UnfoldingEvent* other) const;
 
   const EventSet& get_immediate_causes() const { return this->immediate_causes; }