Skip to content

Conversation

@silvansievers
Copy link
Contributor

No description provided.

@roeger
Copy link
Contributor

roeger commented Feb 6, 2025

Hi @silvansievers! The integration of PR #243 lead to conflicts for this PR. Can you have a look into this?

@silvansievers
Copy link
Contributor Author

silvansievers commented Feb 9, 2025

Hi @silvansievers! The integration of PR #243 lead to conflicts for this PR. Can you have a look into this?

Done. This is now ready for experiments and reviews.

for (vector<int>::iterator it = affected_cluster.begin();
it != affected_cluster.end();) {
if (*it == next_pair.first || *it == next_pair.second) {
it = affected_cluster.erase(it);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I read https://en.cppreference.com/w/cpp/container/vector/erase correctly, this invalidates the iterator it. I doubt a typical implementation would care, but this is probably still invalid C++. I suggest you access the vector with an index instead, i.e., for (size_t i = 0; i < affected_cluster.size(); ).

Copy link
Contributor Author

@silvansievers silvansievers Aug 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What exactly do you think is the problem? The parameter it gets invalidated, yes, but the return value is a new valid iterator. The example in the link uses the exact same if-else-pattern as the code here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, I missed the assignment to it. So this works. Looking at this a bit more closely now, I am a bit queasy because this is a code smell -- repeatedly calling erase on a vector moves the rest of the vector on every erasure, and in generally this makes such filtering O(n^2) where it is O(n) when using STL algorithms like erase_if, which is what things like the C++ core guidelines would recommend in a case like this. Of course here it's still O(n) because we only remove two things, but erase_if (with a lambda) would still be the more idiomatic C++ approach.

Now that we require c++20, there is a nice special version of erase_if that just takes a vector and a predicate as an argument and does all the resizing etc.

Something like this could replace the for loop and be more efficient as well:

erase_if(affected_cluster, [&](int elem) {
    return elem == next_pair.first || elem == next_pair.second; });```

const FactoredTransitionSystem &fts) const {
vector<int> indices;
indices.reserve(fts.get_num_active_entries());
for (int index: fts) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

space before ":" ?

vector<pair<int, int>> merge_candidates =
compute_merge_candidates(fts, indices_subset);

vector<pair<int, int>> &&merge_candidates) const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why merge_candidates is an rvalue reference here, and the assignment to the rvalue reference confuses me. Can this be a regular reference instead, or is the intended semantics different?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An rvalue reference doesn't make sense indeed. I think I ended up using it because a default value cannot be bound to a non-cost reference. Also, the passed object doesn't need to be modified, i.e., from the caller's perspective, the right interface would indeed be a const reference. To summarize: I'd like to have an optional parameter merge_candidates that can be assigned to within the method in case it is empty (which is the default when the parameter is not specified by the caller).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have any idea of how to achieve what I wrote? Could std::optional help?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about having two separate functions?
One with the parameter and one without. Anyways, the code in compute_merge_candidates is entirely divided in two parts (by an if), so to me it would make sense to split it into two separate functions depending on whether there is already a set of candidates or not, and never have a check of whether the set of candidates is empty.

Also I think it makes more sense semantically, if I call the function
vector<pair<int, int>> MergeSelector::compute_merge_candidates(
const FactoredTransitionSystem &fts,
const vector &indices_subset) const {

with an empty indices_subset, the result should be an empty list because I didn't have a candidate in my set anyway :) The current semantics of (if my list has an element then you are not allowed to generate new candidates but if my list is empty then feel free to add as many candidates as you wish) is slightly more complex.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the suggestion and opened an issue for this. Here is another pull request for that change: #275

add_option<bool>(
"allow_working_on_all_clusters",
"if true, consider as merge candidates the pairs of factors of all SCCs. If "
"false, fully finish dealing with one cluster at a time.",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps simpler: "If false, deal with one cluster at a time."?

…y SCC

Previously, the merge selector used for selecting pairs of M&S factors
only considered one SCC partition after the other. With the new option
set to true, it considers the pairs of factors from any cluster of
factors that has not been fully merged yet.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants