Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to split e-classes for easier visualization #14

Merged
merged 5 commits into from
Sep 9, 2024

Conversation

saulshanabrook
Copy link
Member

This PR adds the ability to "split" e-classes to assist with visualization.

This feature was previously implemented as part of the egglog export process, to allow splitting primitive nodes into their own classes.

For example, this is an unsplit e-class:

fibonacci

And after splitting primitive nodes:

fibonacci-split

By moving this ability to the serialize library and extending it to support arbitrary nodes, we can use it to split other nodes besides primitives. For example, we can choose to split Num nodes or other nodes that are contr. I implemented this to help when debugging e-graphs through visualization, by reducing the cyclic nature of the graphs.

For example, here is an e-graph with just primitives split and then inlined (old behavior):
old

And then with this PR, it is a bit easier to follow the top down flow of the nodes (ignoring the isolated nodes in the top right):

new

for Class { id, nodes } in self.classes().clone().values() {
let mut other_nodes = Vec::new();
let mut unique_node = None;
for node_id in nodes {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nits: the logic can be probably simplified with parition

Copy link
Member Author

Choose a reason for hiding this comment

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

Thank you, yep that was much clearer.

///
/// This can be used for example to make multiple e-classes for all nodes equivalent to i64(0), to make it easier
/// to visualize this.
pub fn split_e_classes(&mut self, should_split: impl Fn(&NodeId, &Node) -> bool) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I found myself reading the description and the implementation several times before understanding what this does. The "unique node" is essentially a representative that every duplicated e-class would contain, and that's why it needs to be unique, is this right? I think the documentation might be clearer if it is narrated from an application point of view (e.g., what it tries to achieve at a high level) or describes some end-to-end use scenarios.

Copy link
Contributor

Choose a reason for hiding this comment

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

nits: eclasses or classes seems to be more consistent with the rest of the library than e_classes

Copy link
Member Author

Choose a reason for hiding this comment

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

Thank you! I updated the comment to try to explain it a bit more. Happy for more feedback about parts that are confusing.

}
// split out other nodes if there are multiple of them.
// Leave one node in this e-class and make new e-classes for remaining nodes
for other_node_id in other_nodes.into_iter().skip(1) {
Copy link
Contributor

Choose a reason for hiding this comment

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

For other_nodes[0], which is skipped, while you don't need to create a new e-class for it, don't you need to merge it with a clone of unique_node?

Copy link
Member Author

Choose a reason for hiding this comment

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

No, I don't believe so because we leave the original unique node there with it.

// split out other nodes if there are multiple of them.
// Leave one node in this e-class and make new e-classes for remaining nodes
for other_node_id in other_nodes.into_iter().skip(1) {
changed = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is changed set to true here (in addition to below where parent E-nodes are updated).

Copy link
Member Author

Choose a reason for hiding this comment

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

I updated the logic to only trigger changed if there are any parents and we make a new node. Changed so only be true if the e-graph was mutated.

@saulshanabrook
Copy link
Member Author

@mwillsey Could you take a look at this? egraphs-good/egglog#407 is blocked on it

@mwillsey mwillsey merged commit 9ce2812 into egraphs-good:main Sep 9, 2024
2 checks passed
@yihozhang yihozhang mentioned this pull request Sep 13, 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