Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Add first implementation of maximal_subsets_iterator
[simgrid.git] / src / mc / explo / udpor / maximal_subsets_iterator.cpp
diff --git a/src/mc/explo/udpor/maximal_subsets_iterator.cpp b/src/mc/explo/udpor/maximal_subsets_iterator.cpp
new file mode 100644 (file)
index 0000000..a96287d
--- /dev/null
@@ -0,0 +1,116 @@
+#include "src/mc/explo/udpor/maximal_subsets_iterator.hpp"
+#include "src/mc/explo/udpor/UnfoldingEvent.hpp"
+#include "xbt/asserts.h"
+
+#include <algorithm>
+
+namespace simgrid::mc::udpor {
+
+void maximal_subsets_iterator::increment()
+{
+  // Until we discover otherwise, we default to being done
+  auto next_event_ref = topological_ordering.end();
+
+  while (not backtrack_points.empty()) {
+    // This is an iterator which points to the latest event `e` that
+    // was added to what is currently the maximal set
+    const auto latest_event_ref = backtrack_points.top();
+
+    // Look for the next event to test with what we currently
+    // have based on the conflicts we've already kept track of.
+    //
+    // NOTE: We only need to search FROM `e` and not
+    // from the beginning of the topological sort. The fact that the
+    // set is topologically ordered ensures that removing `e`
+    // will not change whether or not to now allow someone before `e`
+    // in the ordering (otherwise, they would have to be in `e`'s history
+    // and therefore would come after `e`)
+    next_event_ref = bookkeeper.find_next_event(latest_event_ref, topological_ordering.end());
+
+    // If we can't find another event to add after `e`,
+    // then we retry after first removing the latest event.
+    // This effectively tests "check now with all combinations that
+    // exclude the latest event".
+    //
+    // Note: it is important to remove the element FIRST before performing
+    // the second search, as removal may enable dependencies of `e` to be selected
+    if (next_event_ref == topological_ordering.end()) {
+      remove_element_from_current_maximal_set(*latest_event_ref);
+      backtrack_points.pop();
+
+      // We begin the search AFTER the event we popped: we only want
+      // to consider those events that could be added AFTER `e` and
+      // not `e` itself again
+      next_event_ref = bookkeeper.find_next_event(latest_event_ref + 1, topological_ordering.end());
+
+      // If we finally found some event, we can stop
+      if (next_event_ref != topological_ordering.end()) {
+        break;
+      }
+    }
+  }
+
+  // If after all of the backtracking we still have no luck, we've finished
+  if (next_event_ref == topological_ordering.end()) {
+    return;
+  }
+
+  // Otherwise we found some other event `e'` which is not in conflict with anything
+  // that currently exists in `current_maximal_set`. Add it in and perform
+  // some more bookkeeping
+  UnfoldingEvent* next_event = *next_event_ref;
+  add_element_to_current_maximal_set(next_event);
+  backtrack_points.push(next_event_ref);
+}
+
+void maximal_subsets_iterator::add_element_to_current_maximal_set(UnfoldingEvent* e)
+{
+  current_maximal_set.insert(e);
+  bookkeeper.mark_included_in_maximal_set(e);
+}
+
+void maximal_subsets_iterator::remove_element_from_current_maximal_set(UnfoldingEvent* e)
+{
+  current_maximal_set.remove(e);
+  bookkeeper.mark_removed_from_maximal_set(e);
+}
+
+maximal_subsets_iterator::topological_order_position
+maximal_subsets_iterator::bookkeeper::find_next_event(topological_order_position first,
+                                                      topological_order_position last) const
+{
+  return std::find_if(first, last, [&](UnfoldingEvent* e) { return is_candidate_event(e); });
+}
+
+bool maximal_subsets_iterator::bookkeeper::is_candidate_event(UnfoldingEvent* e) const
+{
+  if (const auto e_count = event_counts.find(e); e_count != event_counts.end()) {
+    return e_count->second == 0;
+  }
+  return true;
+}
+
+void maximal_subsets_iterator::bookkeeper::mark_included_in_maximal_set(UnfoldingEvent* e)
+{
+  const auto e_history = e->get_history();
+  for (const auto e_hist : e_history) {
+    event_counts[e_hist]++;
+  }
+}
+
+void maximal_subsets_iterator::bookkeeper::mark_removed_from_maximal_set(UnfoldingEvent* e)
+{
+  const auto e_history = e->get_history();
+  for (const auto e_hist : e_history) {
+    xbt_assert(event_counts.find(e_hist) != event_counts.end(),
+               "Invariant Violation: Attempted to remove an event which was not previously added");
+    xbt_assert(event_counts[e_hist] > 0, "Invariant Violation: An event `e` had a count of `0` at this point "
+                                         "of the bookkeeping, which means that it is a candidate maximal event. "
+                                         "Yet some event that `e'` which contains `e` in its history was removed "
+                                         "first. This incidates that the topological sorting of events of the "
+                                         "configuration has failed and should be investigated first");
+    event_counts[e_hist]--;
+  }
+}
+
+} // namespace simgrid::mc::udpor
\ No newline at end of file