-
Notifications
You must be signed in to change notification settings - Fork 150
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
adding line_graph
and greedy_edge_color
for undirected graphs
#870
Merged
Merged
Changes from 24 commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
49d8594
trying to implement line_graph and edge_coloring
alexanderivrii cbcc239
minor refactoring
alexanderivrii 6c44a7a
more refactoring
alexanderivrii 93bad32
moving greedy_color to core
alexanderivrii 38422e1
running fmt
alexanderivrii d3b4238
running clippy
alexanderivrii 3cacbe3
adapting tests
alexanderivrii 7921827
merge with main
alexanderivrii 3980a14
fixes
alexanderivrii 07c5ba7
first pass on implementing line_graph in rustworkx-core
alexanderivrii 3c4513e
cleanup
alexanderivrii d475d08
docs
alexanderivrii e78d069
docs fix
alexanderivrii 79b7b4d
exposing line_graph to rustworkx python interface
alexanderivrii 7dec989
moving graph_line_graph to a separate package
alexanderivrii 99f0224
minor cleanup
alexanderivrii 97e32ca
more cleanup
alexanderivrii 370407c
more cleanup
alexanderivrii 02e3691
tests for graph_greedy_edge_color
alexanderivrii ecdbe16
additional test + docs
alexanderivrii 715f270
line graph tests
alexanderivrii 1456573
docs
alexanderivrii 46d1350
release notes
alexanderivrii bbacc33
minor
alexanderivrii c324acb
implementing greedy_edge_color in rustworkx-core
alexanderivrii 2e9fcf5
changing test to be different from the one in docs
alexanderivrii d0c3ee3
using greedy_edge_color from core
alexanderivrii 71f2de6
Merge branch 'main' into edge-coloring
alexanderivrii 5e3af3f
Merge branch 'main' into edge-coloring
mergify[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
releasenotes/notes/add-graph-greedy-edge-color-7ef35785e5768419.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
--- | ||
features: | ||
- | | ||
Added a new function, :func:`~.graph_line_graph` to construct a line | ||
graph of a :class:`~.PyGraph` object. | ||
|
||
The line graph `L(G)` of a graph `G` represents the adjacencies between edges of G. | ||
`L(G)` contains a vertex for every edge in `G`, and `L(G)` contains an edge between two | ||
vertices if the corresponding edges in `G` have a vertex in common. | ||
|
||
.. jupyter-execute:: | ||
|
||
import rustworkx as rx | ||
|
||
graph = rx.PyGraph() | ||
node_a = graph.add_node("a") | ||
node_b = graph.add_node("b") | ||
node_c = graph.add_node("c") | ||
node_d = graph.add_node("d") | ||
edge_ab = graph.add_edge(node_a, node_b, 1) | ||
edge_ac = graph.add_edge(node_a, node_c, 1) | ||
edge_bc = graph.add_edge(node_b, node_c, 1) | ||
edge_ad = graph.add_edge(node_a, node_d, 1) | ||
|
||
out_graph, out_edge_map = rx.graph_line_graph(graph) | ||
assert out_graph.node_indices() == [0, 1, 2, 3] | ||
assert out_graph.edge_list() == [(3, 1), (3, 0), (1, 0), (2, 0), (2, 1)] | ||
assert out_edge_map == {edge_ab: 0, edge_ac: 1, edge_bc: 2, edge_ad: 3} | ||
|
||
- | | ||
Added a new function, :func:`~.graph_greedy_edge_color` to color edges | ||
of a :class:`~.PyGraph` object using a greedy approach. | ||
|
||
This function works by greedily coloring the line graph of the given graph. | ||
|
||
.. jupyter-execute:: | ||
|
||
import rustworkx as rx | ||
|
||
graph = rx.generators.cycle_graph(7) | ||
edge_colors = rx.graph_greedy_edge_color(graph) | ||
assert edge_colors == {0: 0, 1: 1, 2: 0, 3: 1, 4: 0, 5: 1, 6: 2} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. More a note for myself when we release 0.14.0 it'll be good to have this visualized by calling |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
// Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
// not use this file except in compliance with the License. You may obtain | ||
// a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
// License for the specific language governing permissions and limitations | ||
// under the License. | ||
|
||
use std::hash::Hash; | ||
|
||
use hashbrown::HashMap; | ||
use petgraph::data::Create; | ||
use petgraph::visit::{Data, EdgeCount, EdgeRef, IntoEdges, IntoNodeIdentifiers}; | ||
|
||
/// Constructs the line graph of an undirected graph. | ||
/// | ||
/// The line graph `L(G)` of a graph `G` represents the adjacencies between edges of G. | ||
/// `L(G)` contains a vertex for every edge in `G`, and `L(G)` contains an edge between two | ||
/// vertices if the corresponding edges in `G` have a vertex in common. | ||
/// | ||
/// Arguments: | ||
/// | ||
/// * `input_graph` - The input graph `G`. | ||
/// * `default_node_weight` - A callable that will return the weight to use | ||
/// for newly created nodes. | ||
/// * `default_edge_weight` - A callable that will return the weight object | ||
/// to use for newly created edges. | ||
/// | ||
/// Returns the constructed line graph `L(G)`, and the map from the edges of `L(G)` to | ||
/// the vertices of `G`. | ||
/// | ||
/// # Example | ||
/// ```rust | ||
/// use rustworkx_core::line_graph::line_graph; | ||
/// use rustworkx_core::petgraph::visit::EdgeRef; | ||
/// use rustworkx_core::petgraph::Graph; | ||
/// use hashbrown::HashMap; | ||
/// use petgraph::graph::{EdgeIndex, NodeIndex}; | ||
/// use petgraph::Undirected; | ||
/// | ||
/// let input_graph = | ||
/// Graph::<(), (), Undirected>::from_edges(&[(0, 1), (0, 2), (1, 2), (0, 3)]); | ||
/// | ||
/// let (output_graph, output_edge_map): ( | ||
/// petgraph::graph::UnGraph<(), ()>, | ||
/// HashMap<petgraph::prelude::EdgeIndex, petgraph::prelude::NodeIndex>, | ||
/// ) = line_graph(&input_graph, || (), || ()); | ||
/// | ||
/// let output_edge_list = output_graph | ||
/// .edge_references() | ||
/// .map(|edge| (edge.source().index(), edge.target().index())) | ||
/// .collect::<Vec<(usize, usize)>>(); | ||
/// | ||
/// let expected_edge_list = vec![(3, 1), (3, 0), (1, 0), (2, 0), (2, 1)]; | ||
/// let expected_edge_map: HashMap<EdgeIndex, NodeIndex> = [ | ||
/// (EdgeIndex::new(0), NodeIndex::new(0)), | ||
/// (EdgeIndex::new(1), NodeIndex::new(1)), | ||
/// (EdgeIndex::new(2), NodeIndex::new(2)), | ||
/// (EdgeIndex::new(3), NodeIndex::new(3)), | ||
/// ] | ||
/// .into_iter() | ||
/// .collect(); | ||
/// | ||
/// assert_eq!(output_edge_list, expected_edge_list); | ||
/// assert_eq!(output_edge_map, expected_edge_map); | ||
/// ``` | ||
pub fn line_graph<K, G, T, F, H, M>( | ||
input_graph: K, | ||
mut default_node_weight: F, | ||
mut default_edge_weight: H, | ||
) -> (G, HashMap<K::EdgeId, G::NodeId>) | ||
where | ||
K: EdgeCount + IntoNodeIdentifiers + IntoEdges, | ||
G: Create + Data<NodeWeight = T, EdgeWeight = M>, | ||
F: FnMut() -> T, | ||
H: FnMut() -> M, | ||
K::EdgeId: Hash + Eq, | ||
{ | ||
let num_edges = input_graph.edge_count(); | ||
let mut output_graph = G::with_capacity(num_edges, 0); | ||
let mut output_edge_map = | ||
HashMap::<K::EdgeId, G::NodeId>::with_capacity(input_graph.edge_count()); | ||
|
||
for edge in input_graph.edge_references() { | ||
let new_node = output_graph.add_node(default_node_weight()); | ||
output_edge_map.insert(edge.id(), new_node); | ||
} | ||
|
||
for node in input_graph.node_identifiers() { | ||
let edges: Vec<K::EdgeRef> = input_graph.edges(node).collect(); | ||
for i in 0..edges.len() { | ||
for j in i + 1..edges.len() { | ||
let node0 = output_edge_map.get(&edges[i].id()).unwrap(); | ||
let node1 = output_edge_map.get(&edges[j].id()).unwrap(); | ||
output_graph.add_edge(*node0, *node1, default_edge_weight()); | ||
} | ||
} | ||
} | ||
(output_graph, output_edge_map) | ||
} | ||
|
||
#[cfg(test)] | ||
|
||
mod test_line_graph { | ||
use crate::line_graph::line_graph; | ||
use crate::petgraph::visit::EdgeRef; | ||
use crate::petgraph::Graph; | ||
use hashbrown::HashMap; | ||
use petgraph::graph::{EdgeIndex, NodeIndex}; | ||
use petgraph::Undirected; | ||
|
||
#[test] | ||
fn test_greedy_node_color_simple_graph() { | ||
// Simple graph | ||
let input_graph = | ||
Graph::<(), (), Undirected>::from_edges(&[(0, 1), (0, 2), (1, 2), (0, 3)]); | ||
|
||
let (output_graph, output_edge_map): ( | ||
petgraph::graph::UnGraph<(), ()>, | ||
HashMap<petgraph::prelude::EdgeIndex, petgraph::prelude::NodeIndex>, | ||
) = line_graph(&input_graph, || (), || ()); | ||
|
||
let output_edge_list = output_graph | ||
.edge_references() | ||
.map(|edge| (edge.source().index(), edge.target().index())) | ||
.collect::<Vec<(usize, usize)>>(); | ||
|
||
let expected_edge_list = vec![(3, 1), (3, 0), (1, 0), (2, 0), (2, 1)]; | ||
let expected_edge_map: HashMap<EdgeIndex, NodeIndex> = [ | ||
(EdgeIndex::new(0), NodeIndex::new(0)), | ||
(EdgeIndex::new(1), NodeIndex::new(1)), | ||
(EdgeIndex::new(2), NodeIndex::new(2)), | ||
(EdgeIndex::new(3), NodeIndex::new(3)), | ||
] | ||
.into_iter() | ||
.collect(); | ||
|
||
assert_eq!(output_edge_list, expected_edge_list); | ||
assert_eq!(output_edge_map, expected_edge_map); | ||
} | ||
mtreinish marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
// not use this file except in compliance with the License. You may obtain | ||
// a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
// License for the specific language governing permissions and limitations | ||
// under the License. | ||
|
||
use crate::{graph, StablePyGraph}; | ||
|
||
use hashbrown::HashMap; | ||
|
||
use petgraph::graph::{EdgeIndex, NodeIndex}; | ||
use petgraph::visit::{EdgeRef, IntoEdgeReferences}; | ||
use petgraph::Undirected; | ||
use rustworkx_core::dictmap::*; | ||
use rustworkx_core::line_graph::line_graph; | ||
|
||
use pyo3::prelude::*; | ||
use pyo3::Python; | ||
|
||
/// Constructs the line graph of a :class:`~.PyGraph` object. | ||
/// | ||
/// The line graph `L(G)` of a graph `G` represents the adjacencies between edges of G. | ||
/// `L(G)` contains a vertex for every edge in `G`, and `L(G)` contains an edge between two | ||
/// vertices if the corresponding edges in `G` have a vertex in common. | ||
/// | ||
/// :param PyGraph: The input PyGraph object | ||
/// | ||
/// :returns: A new PyGraph object that is the line graph of ``graph``, and the dictionary | ||
/// where the keys are indices of edges in``graph`` and the values are the corresponding | ||
/// indices of nodes in the linear graph. | ||
/// :rtype: Tuple[:class:`~rustworkx.PyGraph`, dict] | ||
/// | ||
/// .. jupyter-execute:: | ||
/// | ||
/// import rustworkx as rx | ||
/// | ||
/// graph = rx.PyGraph() | ||
/// node_a = graph.add_node("a") | ||
/// node_b = graph.add_node("b") | ||
/// node_c = graph.add_node("c") | ||
/// node_d = graph.add_node("d") | ||
/// edge_ab = graph.add_edge(node_a, node_b, 1) | ||
/// edge_ac = graph.add_edge(node_a, node_c, 1) | ||
/// edge_bc = graph.add_edge(node_b, node_c, 1) | ||
/// edge_ad = graph.add_edge(node_a, node_d, 1) | ||
/// | ||
/// out_graph, out_edge_map = rx.graph_line_graph(graph) | ||
/// assert out_graph.node_indices() == [0, 1, 2, 3] | ||
/// assert out_graph.edge_list() == [(3, 1), (3, 0), (1, 0), (2, 0), (2, 1)] | ||
/// assert out_edge_map == {edge_ab: 0, edge_ac: 1, edge_bc: 2, edge_ad: 3} | ||
/// | ||
#[pyfunction] | ||
#[pyo3(text_signature = "(graph, /)")] | ||
pub fn graph_line_graph( | ||
py: Python, | ||
graph: &graph::PyGraph, | ||
) -> (graph::PyGraph, DictMap<usize, usize>) { | ||
let default_fn = || py.None(); | ||
|
||
let (output_graph, output_edge_to_node_map): ( | ||
StablePyGraph<Undirected>, | ||
HashMap<EdgeIndex, NodeIndex>, | ||
) = line_graph(&graph.graph, default_fn, default_fn); | ||
|
||
let output_graph_py = graph::PyGraph { | ||
graph: output_graph, | ||
node_removed: false, | ||
multigraph: false, | ||
attrs: py.None(), | ||
}; | ||
|
||
let mut output_edge_to_node_map_py: DictMap<usize, usize> = DictMap::new(); | ||
|
||
for edge in graph.graph.edge_references() { | ||
let edge_id = edge.id(); | ||
let node_id = output_edge_to_node_map.get(&edge_id).unwrap(); | ||
output_edge_to_node_map_py.insert(edge_id.index(), node_id.index()); | ||
} | ||
|
||
(output_graph_py, output_edge_to_node_map_py) | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
More a note for myself when we release 0.14.0 it'll be good to have this visualized by calling
mpl_draw()
in the release notes instead of asserts. But I'll just do this as part of the release prep.