-
Notifications
You must be signed in to change notification settings - Fork 162
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
Implement condensation graph generation #1337
base: main
Are you sure you want to change the base?
Implement condensation graph generation #1337
Conversation
eed8af1
to
fada9a1
Compare
Pull Request Test Coverage Report for Build 12451773226Details
💛 - Coveralls |
8754ccd
to
9c026b2
Compare
@IvanIsCoding can anyone review this code? |
57dc3fe
to
5045623
Compare
Sorry I haven’t acknowledged the PR. I haven’t had time to review it. I will do the review after Christmas, it might still make the cut for rustworkx 0.16. |
Thank you for your quick response. |
g: Graph<N, E, Ty, Ix>, | ||
make_acyclic: bool, | ||
sccs: Option<Vec<Vec<usize>>>, | ||
) -> StableGraph<PyObject, PyObject, Ty, Ix> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am wondering if it would make sense to also return a map between the nodes of the condensed graph and the (sets of) nodes of the original graph, so that information computed for the condensed graph could be lifted to the original graph as well?
self.graph.add_edge(self.node_h, self.node_e, "h->e") # サイクル: e -> f -> g -> h -> e | ||
|
||
def test_condensation(self): | ||
# condensation関数を呼び出し |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to translate all comments to English.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not a review, but I was wondering: would it make sense to move this functionality to rustworkx-core? would it make sense to implement this both for directed and undirected graphs? would it make sense to return additional information relating the nodes of the original and the condensed graphs (see in-place comment).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall the condensation logic looks good to me. I left comments that are mostly to simplify the existing code and make it more maintainable in the long run. I don't think you'll need to change much for the algorithm
I think the only debate left is how to return the node mappings. I personally like the short return type signature, so for me a graph attribute with the mapping seems like a good compromise.
check_cycle: false, | ||
node_removed: false, | ||
multigraph: true, | ||
attrs: py.None(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either the attrs
should contain the node mapping like in NetworkX. Or we need to update the signature to return a mapping
py: Python, | ||
graph: &digraph::PyDiGraph, | ||
sccs: Option<Vec<Vec<usize>>>, | ||
) -> digraph::PyDiGraph { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sibling comment to either update the signature or return the node mapping somewhere else
g: Graph<N, E, Ty, Ix>, | ||
make_acyclic: bool, | ||
sccs: Option<Vec<Vec<usize>>>, | ||
) -> StableGraph<PyObject, PyObject, Ty, Ix> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
) -> StableGraph<PyObject, PyObject, Ty, Ix> | |
) -> StablePyGraph<Directed> |
Stick to the shared type alias for consistency with the other methods.
@@ -114,6 +114,79 @@ pub fn strongly_connected_components(graph: &digraph::PyDiGraph) -> Vec<Vec<usiz | |||
.collect() | |||
} | |||
|
|||
fn condensation_inner<N, E, Ty, Ix>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is called only once in the code, so I don't think you need to make it generic. It is fine to accept only one kind of type for each argument.
n.b: the feedback would be different if this wasn't a private function
@@ -192,6 +192,7 @@ def number_connected_components(graph: PyGraph, /) -> int: ... | |||
def number_weakly_connected_components(graph: PyDiGraph, /) -> bool: ... | |||
def node_connected_component(graph: PyGraph, node: int, /) -> set[int]: ... | |||
def strongly_connected_components(graph: PyDiGraph, /) -> list[list[int]]: ... | |||
def condensation(graph: PyDiGraph, /, sccs=None) -> PyDiGraph: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def condensation(graph: PyDiGraph, /, sccs=None) -> PyDiGraph: ... | |
def condensation(graph: PyDiGraph, /, sccs: list[int] | None=...) -> PyDiGraph: ... |
You don't specify the value here, just that the argument is optional because there is already a default
if source != target { | ||
condensed.update_edge(source, target, edge.weight); | ||
} | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is always false, remove this statmenet
|
||
class TestCondensation(unittest.TestCase): | ||
def setUp(self): | ||
# グラフをセットアップ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I appreciate the code comments but before submitting we'll either need to translate them to English
self.graph.add_edge(self.node_a, self.node_b, "a->b") | ||
self.graph.add_edge(self.node_b, self.node_c, "b->c") | ||
self.graph.add_edge(self.node_c, self.node_d, "c->d") | ||
self.graph.add_edge(self.node_d, self.node_a, "d->a") # サイクル: a -> b -> c -> d -> a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self.graph.add_edge(self.node_d, self.node_a, "d->a") # サイクル: a -> b -> c -> d -> a | |
self.graph.add_edge(self.node_d, self.node_a, "d->a") # cycle : a -> b -> c -> d -> a |
self.graph.add_edge(self.node_e, self.node_f, "e->f") | ||
self.graph.add_edge(self.node_f, self.node_g, "f->g") | ||
self.graph.add_edge(self.node_g, self.node_h, "g->h") | ||
self.graph.add_edge(self.node_h, self.node_e, "h->e") # サイクル: e -> f -> g -> h -> e |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self.graph.add_edge(self.node_h, self.node_e, "h->e") # サイクル: e -> f -> g -> h -> e | |
self.graph.add_edge(self.node_h, self.node_e, "h->e") # cycle: e -> f -> g -> h -> e |
@@ -65,3 +65,55 @@ def test_number_strongly_connected_big(self): | |||
node = G.add_node(i) | |||
G.add_child(node, str(i), {}) | |||
self.assertEqual(len(rustworkx.strongly_connected_components(G)), 200000) | |||
|
|||
|
|||
class TestCondensation(unittest.TestCase): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The existing test case is excellent! But we need to add one test covering the case where we pass a list to sccs
as an argument
Some notes to myself:
Feel free to do those on your own too. But I don't mind doing the documentation for you, it is fine to focus on the code. Again, thanks for your repeated contributions to rustworkx |
This pull request adds functionality to generate condensation graphs. The Rust implementation is copied and slightly modified from https://github.com/petgraph/petgraph because the original
petgraph
implementation cannot be applied as it is. Condensation graphs represent strongly connected components of a directed graph as single nodes. The update includes a new implementation and its test to support this feature.