Skip to content

Conversation

kazuki0824
Copy link
Contributor

@kazuki0824 kazuki0824 commented Dec 8, 2024

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.

  • I ran rustfmt locally
  • I have added the tests to cover my changes.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.

@kazuki0824 kazuki0824 force-pushed the feat/generate_condensation_graph branch from eed8af1 to fada9a1 Compare December 8, 2024 17:38
@coveralls
Copy link

coveralls commented Dec 9, 2024

Pull Request Test Coverage Report for Build 14777358700

Details

  • 80 of 112 (71.43%) changed or added relevant lines in 2 files are covered.
  • 2 unchanged lines in 1 file lost coverage.
  • Overall coverage decreased (-0.1%) to 95.236%

Changes Missing Coverage Covered Lines Changed/Added Lines %
src/connectivity/mod.rs 78 110 70.91%
Files with Coverage Reduction New Missed Lines %
rustworkx-core/src/generators/random_graph.rs 2 70.57%
Totals Coverage Status
Change from base Build 14760218172: -0.1%
Covered Lines: 18730
Relevant Lines: 19667

💛 - Coveralls

@kazuki0824 kazuki0824 marked this pull request as ready for review December 15, 2024 08:01
@kazuki0824 kazuki0824 force-pushed the feat/generate_condensation_graph branch from 8754ccd to 9c026b2 Compare December 15, 2024 08:06
@kazuki0824
Copy link
Contributor Author

@IvanIsCoding can anyone review this code?

@kazuki0824 kazuki0824 force-pushed the feat/generate_condensation_graph branch from 57dc3fe to 5045623 Compare December 22, 2024 06:03
@IvanIsCoding
Copy link
Collaborator

@IvanIsCoding can anyone review this code?

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.

@kazuki0824
Copy link
Contributor Author

@IvanIsCoding can anyone review this code?

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>
Copy link
Member

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関数を呼び出し
Copy link
Member

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.

Copy link
Member

@alexanderivrii alexanderivrii left a 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).

Copy link
Collaborator

@IvanIsCoding IvanIsCoding left a 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(),
Copy link
Collaborator

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 {
Copy link
Collaborator

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

@IvanIsCoding
Copy link
Collaborator

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

@kazuki0824 kazuki0824 force-pushed the feat/generate_condensation_graph branch 3 times, most recently from 162ef11 to dc8b154 Compare April 20, 2025 13:19
@kazuki0824
Copy link
Contributor Author

@IvanIsCoding Now ready to review. I also added Rust doc and release notes. Please check them too.

@kazuki0824 kazuki0824 requested a review from IvanIsCoding April 20, 2025 15:59
kazuki0824 and others added 12 commits April 26, 2025 13:11
thanks @IvanIsCoding

Co-authored-by: Ivan Carvalho <8753214+IvanIsCoding@users.noreply.github.com>
thanks @IvanIsCoding

Co-authored-by: Ivan Carvalho <8753214+IvanIsCoding@users.noreply.github.com>
Update mod.rs
Update mod.rs

Update mod.rs

Update mod.rs
Update mod.rs

ok

ok

wip

Update mod.rs

Reformat

Reformat
@kazuki0824 kazuki0824 force-pushed the feat/generate_condensation_graph branch from c8feb98 to 571cd81 Compare April 26, 2025 04:11
@IvanIsCoding
Copy link
Collaborator

I plan to take a look at this soon, sorry for the delay

@kazuki0824
Copy link
Contributor Author

I plan to take a look at this soon, sorry for the delay

Thank you for your response.

Copy link
Collaborator

@IvanIsCoding IvanIsCoding left a comment

Choose a reason for hiding this comment

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

Some comments. Overall this works but there are some things that can be improved.

The tests look good though. You can keep them as they are.

#[pyo3(text_signature = "(graph, /, sccs=None)", signature=(graph, sccs=None))]
pub fn condensation(
py: Python,
graph: PyObject,
Copy link
Collaborator

Choose a reason for hiding this comment

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

We generally have the split at __init__.py with a Python function with @_rustworkx_dispatch and then two Rust functions with digraph_* and graph_*:

def distance_matrix(graph, parallel_threshold=300, as_undirected=False, null_value=0.0):

I'd rather keep the existing pattern, of course this one works but consistency in the library is important

Copy link
Contributor Author

@kazuki0824 kazuki0824 May 1, 2025

Choose a reason for hiding this comment

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

We generally have the split at __init__.py with a Python function with @_rustworkx_dispatch and then two Rust functions with digraph_* and graph_*:

def distance_matrix(graph, parallel_threshold=300, as_undirected=False, null_value=0.0):

I'd rather keep the existing pattern, of course this one works but consistency in the library is important

@IvanIsCoding I got it, and where do we place two Rust functions with digraph_* and graph_*, and a new python function with decorator?

Rust functions with digraph_* and graph_*: maybe 'src/connectivity/mod.rs', replacing current 'condensation' function
Python function with decorator: sorry but no idea

Copy link
Collaborator

Choose a reason for hiding this comment

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

The Python function goes here:

def complement(graph):

It’s in __init__.py, there’s not a lot of code there just the doc string essentially.

The Rust code can stay where it is right now

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 just tried to address this issue following other code.

Comment on lines 326 to 327
let node_map_py = node_map.into_pyobject(py)?;
let attrs = PyDict::new(py);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I believe you can use a DictMap instead of a PyDict and just call into_pyobject() at the end. In general, working with Rust primitives might be easier. In general the Python APIs force you to handle errors that don't happen very often. For Rust, putting an item in the hashmap would "just work"

let attrs = PyDict::new(py);
attrs.set_item("node_map", node_map_py)?;
result.attrs = attrs.into();
Ok(result.into_pyobject(py)?.into())
Copy link
Collaborator

Choose a reason for hiding this comment

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

If you split the Rust methods in digraph_condensation and graph_condensation, you'll be able to just return the PyDiGraph or PyGraph object without needing this conversion

StableGraph::with_capacity(components_usize.len(), g.edge_count());

// Build a map from old indices to new ones.
let mut node_map = vec![usize::MAX; g.node_count()];
Copy link
Collaborator

Choose a reason for hiding this comment

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

Prefer using None instead of usize::MAX to represent missing objects

let source = node_map
.get(edge.source().index())
.copied()
.unwrap_or(usize::MAX);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same comment about prefering Option<usize> instead of using usize::MAX as a replacement for None

Co-authored-by: Ivan Carvalho <8753214+IvanIsCoding@users.noreply.github.com>
@kazuki0824 kazuki0824 force-pushed the feat/generate_condensation_graph branch 2 times, most recently from 8e7beb2 to 2f7f62a Compare May 1, 2025 13:07
@kazuki0824 kazuki0824 force-pushed the feat/generate_condensation_graph branch from 2f7f62a to bc221d6 Compare May 1, 2025 14:09
@kazuki0824 kazuki0824 force-pushed the feat/generate_condensation_graph branch from bc221d6 to 3e5b320 Compare May 1, 2025 14:13
@kazuki0824 kazuki0824 force-pushed the feat/generate_condensation_graph branch from 3e5b320 to 3da19fe Compare May 1, 2025 14:41
@kazuki0824 kazuki0824 requested a review from IvanIsCoding May 1, 2025 14:56
Copy link
Collaborator

@IvanIsCoding IvanIsCoding left a comment

Choose a reason for hiding this comment

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

LGTM, thanks for addressing the comments

@IvanIsCoding IvanIsCoding added this pull request to the merge queue May 1, 2025
Merged via the queue into Qiskit:main with commit 62181fb May 1, 2025
31 checks passed
SILIZ4 pushed a commit to SILIZ4/rustworkx that referenced this pull request Jul 4, 2025
* Implement condensation tentatively

* Update test_strongly_connected.py

* Update src/connectivity/mod.rs

thanks @IvanIsCoding

Co-authored-by: Ivan Carvalho <8753214+IvanIsCoding@users.noreply.github.com>

* Update rustworkx/rustworkx.pyi

thanks @IvanIsCoding

Co-authored-by: Ivan Carvalho <8753214+IvanIsCoding@users.noreply.github.com>

* Update mod.rs

Update mod.rs

* Update mod.rs

Update mod.rs

Update mod.rs

Update mod.rs

* Update test_strongly_connected.py

* Create condensation-undirected-support-apr2025.yaml

* Use pyobject and bound

Update mod.rs

ok

ok

wip

Update mod.rs

Reformat

Reformat

* Update rustworkx.pyi

* Update test_strongly_connected.py

* Update test_strongly_connected.py

* Replace MAX with None

Co-authored-by: Ivan Carvalho <8753214+IvanIsCoding@users.noreply.github.com>

* Separate digraph and graph function

Update lib.rs

* Add stub definition

Reformat

* Update __init__.py

* Replace MAX with None (2)

Reformat

Update __init__.py

* Reformat

---------

Co-authored-by: Ivan Carvalho <8753214+IvanIsCoding@users.noreply.github.com>
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