Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move dag longest path functions to rustworkx-core. (#1192)
* Added `longest_path` to rustworkx (needs verification) 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. * Added tests to `rustworkx-core/src/longest_path.rs` 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 more detailed function description Added function descriptions and comments to `longest_path.rs`. The function description is based off of the format in `rustworkx-core/src/line_graph`. * Style changes * Updated longest_path.rs Slight optimizations for `longest_path.rs` * Changed longest_path to accept any G 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`. * Fixed Test Cases and Adjusted Parameters 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 return time to use usize instead of NodeIndex 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. * Updated `longest_path` in src to use rustworkx_core 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 for the weight function 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 `longest_path.rs` in src to reflect the new type 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. * Added release notes * Documentation changes * Polish up and slight optimizations 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. * Documentation and style Added a function description to `longest_path` in `src/dag_algo/longest_path.rs` * Update releasenotes/notes/migrate-longest_path-7c11cf2c8ac9781f.yaml Co-authored-by: Matthew Treinish <[email protected]> * Rename the module to `dag_algo` 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 return type for the output `Vec` 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. --------- Co-authored-by: Matthew Treinish <[email protected]>
- Loading branch information