#include "src/mc/explo/udpor/Configuration.hpp"
#include "src/mc/explo/udpor/History.hpp"
#include "src/mc/explo/udpor/UnfoldingEvent.hpp"
+#include "src/mc/explo/udpor/maximal_subsets_iterator.hpp"
#include "xbt/asserts.h"
#include <algorithm>
event_stack.push(cause);
}
} else {
- // Mark this event as:
- // 1. discovered across all DFSs performed
- // 2. permanently marked
- // 3. part of the topological search
unknown_events.remove(evt);
temporarily_marked_events.remove(evt);
permanently_marked_events.insert(evt);
topological_ordering.push_back(evt);
// Only now do we remove the event, i.e. once
- // we've processed the same event again
+ // we've processed the same event twice
event_stack.pop();
}
}
std::vector<const UnfoldingEvent*> Configuration::get_topologically_sorted_events_of_reverse_graph() const
{
- // The method exploits the property that
+ // The implementation exploits the property that
// a topological sorting S^R of the reverse graph G^R
// of some graph G is simply the reverse of any
// topological sorting S of G.
return topological_events;
}
+EventSet Configuration::get_minimally_reproducible_events() const
+{
+ // The implementation exploits the following observations:
+ //
+ // To select the smallest reproducible set of events, we want
+ // to pick events that "knock out" a lot of others. Furthermore,
+ // we need to ensure that the events furthest down in the
+ // causality graph are also selected. If you combine these ideas,
+ // you're basically left with traversing the set of maximal
+ // subsets of C! And we have an iterator for that already!
+ //
+ // The next observation is that the moment we don't increase in size
+ // the current maximal set (or decrease the number of events),
+ // we know that the prior set `S` covered the entire history of C and
+ // was maximal. Subsequent sets will miss events earlier in the
+ // topological ordering that appear in `S`
+ EventSet minimally_reproducible_events = EventSet();
+
+ for (const auto& maximal_set : maximal_subsets_iterator_wrapper(*this)) {
+ if (maximal_set.size() > minimally_reproducible_events.size()) {
+ minimally_reproducible_events = maximal_set;
+ } else {
+ // The moment we see the iterator generate a set of size
+ // that is not monotonically increasing, we can stop:
+ // the set prior was the minimally-reproducible one
+ return minimally_reproducible_events;
+ }
+ }
+ return minimally_reproducible_events;
+}
+
} // namespace simgrid::mc::udpor
*/
std::vector<const UnfoldingEvent*> get_topologically_sorted_events_of_reverse_graph() const;
+ /**
+ * @brief Computes the smallest set of events whose collective histories
+ * capture all events of this configuration
+ *
+ * @invariant The set of all events in the collective histories
+ * of the events returned by this method is equal to the set of events
+ * in this configuration
+ *
+ * @returns the smallest set of events whose events generates this configuration
+ * (denoted `config(E)`)
+ */
+ EventSet get_minimally_reproducible_events() const;
+
private:
/**
* @brief The most recent event added to the configuration
friend class boost::iterator_core_access;
};
+/**
+ * @brief A collection whose contents consist of
+ * the maximal event sets of some configuration
+ *
+ * @note You should treat this class as a small
+ * wrapper that is more convenient and readable
+ * than creating iterators directly, and thus should
+ * not e.g. store instanca
+ */
+class maximal_subsets_iterator_wrapper {
+public:
+ using node_filter_function = maximal_subsets_iterator::node_filter_function;
+
+ maximal_subsets_iterator_wrapper(const maximal_subsets_iterator_wrapper&) = delete;
+ maximal_subsets_iterator_wrapper& operator=(const maximal_subsets_iterator_wrapper&) = delete;
+ maximal_subsets_iterator_wrapper(const maximal_subsets_iterator_wrapper&&) = delete;
+ maximal_subsets_iterator_wrapper& operator=(const maximal_subsets_iterator_wrapper&&) = delete;
+
+ explicit maximal_subsets_iterator_wrapper(const Configuration& config) : config(config) {}
+ maximal_subsets_iterator_wrapper(const Configuration& config, node_filter_function filter)
+ : config(config), filter({filter})
+ {
+ }
+
+ auto begin() const { return maximal_subsets_iterator(config, filter); }
+ auto end() const { return maximal_subsets_iterator(); }
+
+private:
+ const Configuration& config;
+ std::optional<node_filter_function> filter = std::nullopt;
+};
+
} // namespace simgrid::mc::udpor
#endif