-
Notifications
You must be signed in to change notification settings - Fork 155
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
Resolve issue #1029 #1170
base: main
Are you sure you want to change the base?
Resolve issue #1029 #1170
Conversation
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 will try to review this when I have time. I am sorry we mislabeled #1029, maybe we underestimated the effort to implement the algorithm?
Nevertheless, congrats on completing the work and submitting the PR! It is a big accomplishment
For convenience, I think I need to explain my idea why I perform the subgraph creation and the lifted graph construction as in the |
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 left some comments, but overall I think we will need to rewrite most of the hashmap usage because we should avoid the overhead of creating and hashing intermediate strings.
Also, you should replace A* with Dijkstra
IntoNodeIdentifiers, IntoNodeReferences, NodeIndexable, Visitable, | ||
}; | ||
use petgraph::Undirected; | ||
use std::fmt::Debug; |
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.
You should remove std::fmt::Debug
trait from here and the function signatures
let source_name = format!("{}", graph.to_index(edge.source())); | ||
let target_name = format!("{}", graph.to_index(edge.target())); |
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.
Same comment as above about hashing
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'm wondering without the name, how can we know which node is which after putting these into calculations related to the subgraphs and the lifted graph. Imagine NodeIndex(1)
in the original graph (user input), which might be NodeIndex(0)
in a subgraph, and NodeIndex(2)
in the lifted graph. I can only think of mapping these NodeIndex(i)
from one graph with respect to another, and apparently we still need have to use HashMap
. Does it sound good?
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.
You can create a custom struct to store data e.g. a pair of integers. And then implement the hash trait with derive https://doc.rust-lang.org/std/hash/trait.Hash.html#implementing-hash.
Strings contain at least 24 bits even if they are empty. So we should find better representations to store intermediate data in a more compact way. You don’t want string hashing to be the bottleneck of the algorithm
.map(|component| { | ||
let mut subgraph = Graph::<String, i32>::new(); | ||
// Create map index to NodeIndex of the nodes in each component | ||
let mut name_idx_map: HashMap<String, usize> = HashMap::new(); |
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 necessary, you should be able to use the NodeId
directly because it is hashable. Or use the usize as a hash
for &node_id in &component { | ||
let node_index = graph.to_index(node_id); | ||
// Create the name of the node in subgraph from the original graph | ||
let node_name = format!("{}", node_index).trim_matches('"').to_string(); |
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.
Same comment as above about hashing
let mst = min_spanning_tree(&graph); | ||
let mut mst_edges: Vec<(usize, usize)> = Vec::new(); | ||
for element in mst { | ||
// println!("Element: {:?}", element); |
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.
Remove the debug statements
graph: G, | ||
orth: HashSet<(usize, usize)>, | ||
mut weight_fn: F, | ||
name_idx_map: &HashMap<String, usize>, |
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.
We'll need to propagate the hashing changes to _min_cycle
as well
fn _min_cycle_basis<G, F, E>( | ||
graph: G, | ||
weight_fn: F, | ||
name_idx_map: &HashMap<String, usize>, |
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.
We'll need to propagate the hashing changes to _min_cycle as well
for node in path { | ||
let node_name = gi.node_weight(node).unwrap(); | ||
if node_name.contains("_lifted") { | ||
let original_node_name = node_name.replace("_lifted", ""); |
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.
If you need to add additional information to the hash you can also hash tuples in Rust. Or you can come up with a custom struct and write a hasher for it
let nodeidx = NodeIndex::new(node); | ||
let lifted_node = gi_name_to_node_index[&lifted_node_name]; | ||
let lifted_nodeidx = NodeIndex::new(lifted_node); | ||
let result = astar( |
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.
Should probably just be a call for dijkstra given the A* heuristic is a constant
dfbc364
to
f7716ac
Compare
I have added some modifications based on your advice. However, I don't think my approach using the new trait |
src/connectivity/mod.rs
Outdated
#[pyfunction] | ||
#[pyo3(text_signature = "(graph, /)")] | ||
pub fn minimum_cycle_basis(py: Python, graph: &PyGraph) -> PyResult<Vec<Vec<usize>>> { | ||
py.allow_threads(|| { | ||
let result = connectivity::minimum_cycle_basis(&graph.graph); | ||
match result { | ||
Ok(min_cycle_basis) => { | ||
// Convert Rust Vec<Vec<NodeIndex>> to Python Vec<Vec<usize>> | ||
let py_min_cycle_basis = min_cycle_basis | ||
.iter() | ||
.map(|cycle| { | ||
cycle | ||
.iter() | ||
.map(|&node_index| graph.graph.to_index(node_index)) | ||
.collect::<Vec<usize>>() | ||
}) | ||
.collect::<Vec<Vec<usize>>>(); | ||
Ok(py_min_cycle_basis) | ||
} | ||
Err(e) => { | ||
// Handle errors by converting them into Python exceptions | ||
Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(format!( | ||
"An error occurred: {:?}", | ||
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.
My piece of advice is the rustworkx-core
function looks good but we need to do some work on the Python bindings. I think we can just remove the Python bindings and add it later on
I think the code is looking better now, the compiler is complaining about some unused variables + trait missing for Python but overall this was a big improvement! Thanks |
f7716ac
to
1cd2fc4
Compare
I have modified the functions in |
If you know that you need to access the weights multiple times I recommend following this pattern:
If you only need to call this once you can wrap this pattern
|
1cd2fc4
to
8001f6e
Compare
I've added what I think necessary. Thank you a lot! |
…t_fn that returns generic weights
f4f8fbe
to
d5304e6
Compare
Pull Request Test Coverage Report for Build 9597606406Warning: This coverage report may be inaccurate.This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.
Details
💛 - Coveralls |
In fact, I do not want to merge my code straightforwardly, because I need some advice!
minimum_cycle_basis
and some tests. It works, but I believe it it need some modifications to be more well programmed, including the choice of having error handler or not.minimum_cycle_basis
is working fine, we need to add a Python layer to wrap around it. And there are some errors insrc/connectivity/mod.rs
where we use#[pyfunction]
annotation. I have no idea to fix this. Perhaps, to fix it, I need to modify even theminimum_cycle_basis.rs
core inrustworkx-core/src/connectivity/
.Link to the issue https://github.com/Qiskit/rustworkx/issues/1029