-
Notifications
You must be signed in to change notification settings - Fork 153
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
Move dag longest path functions to rustworkx-core. #1192
Conversation
Started to port `longest_path` to rustworkx by first adding `longest_path.rs` to `rustworkx-core/src/lib/rs`. Differences from the original in `rustworkx/src/dag_algo/longest_path.rs`: - Removed Python-specific code: The function no longer uses pyo3 and PyObject. Instead, it uses generic Rust types. - Type flexibility: The function parameters now accept any type N for nodes, E for edges, and a weight function F that maps edges (represented by their node indices and edge reference) to a weight of type T. This allows the function to work with different graph and edge types. - Error handling: Instead of returning a Result, it returns an Option to signify failure only in the case of a cycle, which is preemptively checked at the beginning. - Using Ord and Add: These trait bounds on T allow for ordering and arithmetic, necessary for determining the longest path.
Explanation of Test Cases: - test_empty_graph: Checks that an empty graph returns a path of length zero with no nodes. - test_single_node: Checks that a graph with a single node returns the node itself with zero path length (since there's no edge). - test_simple_path: A simple two-node graph with one edge. Tests if the function correctly identifies the longest path. - test_dag_with_multiple_paths: A Directed Acyclic Graph (DAG) with three nodes and a choice of paths, to test if the function picks the longest one. - test_graph_with_cycle: Ensures that the function returns None for a graph with a cycle, as longest path calculations should only be done on DAGs. These test can be run with `cargo test` or with `cargo test test_longest_path`
Added function descriptions and comments to `longest_path.rs`. The function description is based off of the format in `rustworkx-core/src/line_graph`.
…50/rustworkx into 1168-move-dag-functions
Pull Request Test Coverage Report for Build 9102860038Details
💛 - Coveralls |
Slight optimizations for `longest_path.rs`
Adjusted the function to accept `G` instead of a `DiGraph` where `G` has the mentioned traits to represent any directed graph. This allows the function to be more general and apply to both `DiGraph` and `StableDiGraph`. I also changed some other parts of the code to match the original for consistency. However, there is a current issue with compiling the tests, as for some reason, the tests do not satisfy the trait `EdgeRef`.
Adjusted parameters to that F now represents a function that will pass the `EdgeRef` for the edges in the graph. Before we had an error when passing the `EdgeRef` as a trait of `G`, but it should actually be used for `F`. Adjusted the tests to account for that change and added additional tests of StableDiGraph and negative weights.
Converted the return type from ``Some((Vec<NodeIndex>, T))`` to `Some((Vec<usize>, T))` to match the original function more. May need to change back to `NodeIndex` if it makes more sense to use it, as I can see a couple of cases where that is more ideal.
Changed `longest_path` to call `core_longest_path` (the implementation in rustworkx_core) by creating the `dag` and `edge_cost` parameters for that function. However, there is an issue of unvalid weights, which I am unsure how to handle at the moment. All tests seem to pass except for `digraph.test_depth.TestWeightedLongestPath.test_nan_not_valid_weight`.
Changed the return type to handle the kind of errors that the weight function can return. Made changes so that it is more similar to how we implemented `dijkstra`. Added a small test case for this change.
Updated the edge_cost to return `Result<T, PyErr>` and updated error handling. Now all tests pass. Also updated style and added an example.
Summary of Changes: - Change variable names for clarity: `incoming_path` instead of `us` and `max_path` instead of `maxu` - Simplified while Loop Condition: Replaced complex match statement with map_or for clearer and more concise logic in the backtracking loop. - Optimized HashMap Initialization: Pre-allocated HashMap with capacity equal to the number of nodes to improve performance and memory usage. - Enhanced Maximum Distance Calculation: Used into_iter and unwrap_or to simplify logic and improve readability by directly handling maximum value computation and default cases.
Added a function description to `longest_path` in `src/dag_algo/longest_path.rs`
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 this looks great, thanks for doing this @henryzou50! I left a couple inline comments the only two things are really the return type for the output Vec
when a path is found and the new module's name. Otherwise I think this is good to go.
Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
Rename `longest_path.rs` to `dag_algo` as the `longest_path` function works only for DAGs and now we can include other DAG function here.
Changed the return type from `Some((Vec<usize>, T))` to `Some((Vec<NodeId<G>>, T))`, as it makes it easier for users to use `G::NodeId` (which is the generic form of NodeIndex). Also added aliases for readbility.
@mtreinish thanks for the review and comments! I applied the changes you suggested and it should be ready to go now. |
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.
LGTM, thanks for the quick update and your first contribution to rustworkx!
In the recently merged Qiskit#1192 a new generic DAG longest_path function was added to rustworkx-core. However, the trait bounds on the function were a bit tighter than they needed to be. The traits were forcing NodeId to be of a NodeIndex type and this wasn't really required. The only requirement that the NodeId type can be put on a hashmap and do a partial compare (that implements Hash, Eq, and PartialOrd). Also the IntoNeighborsDirected wasn't required because it's methods weren't ever used. This commit loosens the traits bounds to facilitate this. At the same time this also simplifies the code structure a bit to reduce the separation of the rust code structure in the rustworkx crate using longest_path().
In the recently merged #1192 a new generic DAG longest_path function was added to rustworkx-core. However, the trait bounds on the function were a bit tighter than they needed to be. The traits were forcing NodeId to be of a NodeIndex type and this wasn't really required. The only requirement that the NodeId type can be put on a hashmap and do a partial compare (that implements Hash, Eq, and PartialOrd). Also the IntoNeighborsDirected wasn't required because it's methods weren't ever used. This commit loosens the traits bounds to facilitate this. At the same time this also simplifies the code structure a bit to reduce the separation of the rust code structure in the rustworkx crate using longest_path().
…kit#1195) In the recently merged Qiskit#1192 a new generic DAG longest_path function was added to rustworkx-core. However, the trait bounds on the function were a bit tighter than they needed to be. The traits were forcing NodeId to be of a NodeIndex type and this wasn't really required. The only requirement that the NodeId type can be put on a hashmap and do a partial compare (that implements Hash, Eq, and PartialOrd). Also the IntoNeighborsDirected wasn't required because it's methods weren't ever used. This commit loosens the traits bounds to facilitate this. At the same time this also simplifies the code structure a bit to reduce the separation of the rust code structure in the rustworkx crate using longest_path().
* add hyperbolic random graph model generator * Loosen trait constraints and simplify structure for longest_path (#1195) In the recently merged #1192 a new generic DAG longest_path function was added to rustworkx-core. However, the trait bounds on the function were a bit tighter than they needed to be. The traits were forcing NodeId to be of a NodeIndex type and this wasn't really required. The only requirement that the NodeId type can be put on a hashmap and do a partial compare (that implements Hash, Eq, and PartialOrd). Also the IntoNeighborsDirected wasn't required because it's methods weren't ever used. This commit loosens the traits bounds to facilitate this. At the same time this also simplifies the code structure a bit to reduce the separation of the rust code structure in the rustworkx crate using longest_path(). * use vector references * change to slice (clippy) * generalize to H^D, improve numerical accuracy * allow infinite coordinate * handle infinity in hyperbolic distance * remove unused import (clippy) * fix python stub --------- Co-authored-by: Matthew Treinish <mtreinish@kortar.org> Co-authored-by: Ivan Carvalho <8753214+IvanIsCoding@users.noreply.github.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
* add hyperbolic random graph model generator * Loosen trait constraints and simplify structure for longest_path (#1195) In the recently merged #1192 a new generic DAG longest_path function was added to rustworkx-core. However, the trait bounds on the function were a bit tighter than they needed to be. The traits were forcing NodeId to be of a NodeIndex type and this wasn't really required. The only requirement that the NodeId type can be put on a hashmap and do a partial compare (that implements Hash, Eq, and PartialOrd). Also the IntoNeighborsDirected wasn't required because it's methods weren't ever used. This commit loosens the traits bounds to facilitate this. At the same time this also simplifies the code structure a bit to reduce the separation of the rust code structure in the rustworkx crate using longest_path(). * use vector references * change to slice (clippy) * generalize to H^D, improve numerical accuracy * allow infinite coordinate * handle infinity in hyperbolic distance * remove unused import (clippy) * fix python stub * Rename deprecated cargo config file (#1211) This commit migrates the .cargo/config file which has been deprecated to the new path .cargo/config.toml. This will fix warnings that are emitted when compiling with the latest stable release. This new path has been supported since Rust 1.38 which is much older than our current MSRV of 1.70. * fix hyperbolic distance, swap r and beta, infer time coordinate * use mul_add in hyperbolic distance, remove release note * replace clone with dereference --------- Co-authored-by: Matthew Treinish <mtreinish@kortar.org> Co-authored-by: Ivan Carvalho <8753214+IvanIsCoding@users.noreply.github.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Attempt to Solve #1168
Summary:
We are moving the
longest_path
function torustworkx-core
. The main implementation will be added torustworkx-core/src/longest_path.rs
, utilizing only Rust generics. The existingsrc/dag_algo/longest_path.rs
will be updated to serve as the Python interface. Since we are retaining this file, updates tosrc/dag_algo/mod.rs
, where the four DAG longest path functions reside, are not necessary.Details
longest_path.rs
torustworkx-core/src/
rustworkx-core/src/longest_path.rs
src/dag_algo/longest_path.rs
to utilizerustworkx-core/src/longest_path.rs