Skip to content

Conversation

@tlively
Copy link
Member

@tlively tlively commented Aug 1, 2024

Use an extension of Kahn's algorithm for finding topological orders that
iteratively makes every possible choice at every step to find all the
topological orders. The order being constructed and the set of possible
choices are managed in-place in the same buffer, so the algorithm takes
linear time and space plus amortized constant time per generated order.

This will be used in an upcoming type optimization.

Use an extension of Kahn's algorithm for finding topological orders that
iteratively makes every possible choice at every step to find all the
topological orders. The order being constructed and the set of possible
choices are managed in-place in the same buffer, so the algorithm takes
linear time and space plus amortized constant time per generated order.

This will be used in an upcoming type optimization.
@tlively tlively requested a review from kripken August 1, 2024 22:57
Iterator begin() { return {this}; }
Iterator end() { return {nullptr}; }

private:
Copy link
Member

Choose a reason for hiding this comment

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

Can all the private parts be moved out of the header? I think it is easier to read in either a single file (no separate cpp; makes sense for short things), or two files with the implementation in one and the interface in the other.

E.g. I think LocalGraph does a good job of this. The header has just the API, and all the flow analysis to compute the graph is in the cpp.

Copy link
Member Author

Choose a reason for hiding this comment

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

Not without introducing a layer of indirection, unfortunately. We could add a unique_ptr to an opaque implementation object, but I'd rather not. Do you think it would be worth it?

Copy link
Member

Choose a reason for hiding this comment

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

Fair enough as it is, I guess. It's reasonably short, and the iteration makes it hard to separate.

// Find the in-degree of each vertex.
for (const auto& vertex : graph) {
for (auto child : vertex) {
++indegrees[child];
Copy link
Member

Choose a reason for hiding this comment

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

Once again I wonder why ++x; instead of x++;, when there is nothing reading the old value... 😄

Copy link
Member Author

@tlively tlively Aug 5, 2024

Choose a reason for hiding this comment

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

For iterators with non-trivial state, preincrement is more efficient, so should clearly be preferred. (Optimizers can't necessarily optimize out copies even if there are no readers because the copy constructors might have arbitrary side effects.) For other iterators it doesn't matter, so we should still choose preincrement for the sake of consistency.

https://stackoverflow.com/questions/30036749/is-it-still-better-to-prefer-pre-increment-over-post-increment

Iterator begin() { return {this}; }
Iterator end() { return {nullptr}; }

private:
Copy link
Member

Choose a reason for hiding this comment

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

Fair enough as it is, I guess. It's reasonably short, and the iteration makes it hard to separate.

// position and return the new selector for the next position. Returns
// nullopt if advancing wraps back around to the original configuration.
std::optional<Selector> advance(TopologicalOrders& ctx);
};
Copy link
Member

Choose a reason for hiding this comment

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

I read Kahn's Algorithm on wikipedia but didn't see mention of "selectors" there. Is this a generally familiar term I am unaware of?

Copy link
Member Author

Choose a reason for hiding this comment

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

Kahn's algorithm finds a single topological sort by selecting one of the next available vertices arbitrarily. Here we systematically select each of the next available vertices in sequence to enumerate all the possible topological sorts. Selector just encapsulates the logic for making one of those iterative selections. It doesn't come from any established literature.

@tlively tlively merged commit 5573cdb into main Aug 5, 2024
@tlively tlively deleted the topo-sorts branch August 5, 2024 19:27
@gkdn gkdn mentioned this pull request Aug 31, 2024
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.

3 participants