Skip to content

Commit c0f9f94

Browse files
committed
Fix: Make cycle_basis and EdgeOrNode private
- A previous commit inadvertedly made cycle_basis prublic. - To address this, two new public-facing functions were added: - `cycle_basis` for NodeId returns. - `cycle_basis_edges` for EdgeId returns. - When customizing cycle_basis a new enum EdgesOrNodes was made. - Enum should not be have been shared by definition. - Correction makes it private. - Removed redundant import from unwrap methods. - Tests have been adjusted accordingly.
1 parent 7c45634 commit c0f9f94

File tree

5 files changed

+178
-86
lines changed

5 files changed

+178
-86
lines changed

docs/source/api.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ Connectivity and Cycles
164164
rustworkx.weakly_connected_components
165165
rustworkx.is_weakly_connected
166166
rustworkx.cycle_basis
167+
rustworkx.cycle_basis_edges
167168
rustworkx.simple_cycles
168169
rustworkx.digraph_find_cycle
169170
rustworkx.articulation_points

rustworkx-core/src/connectivity/cycle_basis.rs

Lines changed: 135 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ use hashbrown::{HashMap, HashSet};
1414
use petgraph::visit::{EdgeRef, IntoEdges, IntoNeighbors, IntoNodeIdentifiers, NodeCount};
1515
use std::hash::Hash;
1616

17-
/// Return a list of cycles which form a basis for cycles of a given graph.
17+
/// Inner private function for `cycle_basis` and `cycle_basis_edges`.
18+
/// Returns a list of cycles which forms a basis of cycles of a given
19+
/// graph.
1820
///
1921
/// A basis for cycles of a graph is a minimal collection of
2022
/// cycles such that any cycle in the graph can be written
@@ -29,29 +31,18 @@ use std::hash::Hash;
2931
/// It may produce incorrect/unexpected results if the input graph has
3032
/// parallel edges.
3133
///
32-
///
3334
/// Arguments:
3435
///
3536
/// * `graph` - The graph in which to find the basis.
3637
/// * `root` - Optional node index for starting the basis search. If not
3738
/// specified, an arbitrary node is chosen.
38-
/// * `edges` - Optional bool for when the user requests the edges instead
39+
/// * `edges` - bool for when the user requests the edges instead
3940
/// of the nodes of the cycles.
40-
///
41-
/// # Example
42-
/// ```rust
43-
/// use petgraph::prelude::*;
44-
/// use rustworkx_core::connectivity::cycle_basis;
45-
///
46-
/// let edge_list = [(0, 1), (0, 3), (0, 5), (1, 2), (2, 3), (3, 4), (4, 5)];
47-
/// let graph = UnGraph::<i32, i32>::from_edges(&edge_list);
48-
/// let mut res: Vec<Vec<NodeIndex>> = cycle_basis(&graph, Some(NodeIndex::new(0)), Some(false)).unwrap_nodes();
49-
/// ```
5041
51-
pub fn cycle_basis<G>(
42+
fn inner_cycle_basis<G>(
5243
graph: G,
5344
root: Option<G::NodeId>,
54-
edges: Option<bool>,
45+
edges: bool,
5546
) -> EdgesOrNodes<G::NodeId, G::EdgeId>
5647
where
5748
G: NodeCount,
@@ -61,7 +52,6 @@ where
6152
G::NodeId: Eq + Hash,
6253
G::EdgeId: Eq + Hash,
6354
{
64-
let want_edges = edges == Some(true);
6555
let mut root_node = root;
6656
let mut graph_nodes: HashSet<G::NodeId> = graph.node_identifiers().collect();
6757
let mut cycles_edges: Vec<Vec<G::EdgeId>> = Vec::new();
@@ -126,7 +116,7 @@ where
126116
used.insert(neighbor, temp_set);
127117
// A self loop:
128118
} else if z == neighbor {
129-
if want_edges {
119+
if edges {
130120
let cycle_edge: Vec<G::EdgeId> = get_edge_between(graph, z, z, true);
131121
cycles_edges.push(cycle_edge);
132122
} else {
@@ -137,7 +127,7 @@ where
137127
} else if !used.get(&z).unwrap().contains(&neighbor) {
138128
let pn = used.get(&neighbor).unwrap();
139129
let mut p = pred.get(&z).unwrap();
140-
if want_edges {
130+
if edges {
141131
let mut cycle: Vec<G::EdgeId> = Vec::new();
142132
// Retreive all edges from z to neighbor.
143133
let mut neigh_edge: Vec<G::EdgeId> =
@@ -191,67 +181,149 @@ where
191181
graph_nodes = graph_nodes.difference(&temp_hashset).copied().collect();
192182
root_node = None;
193183
}
194-
if want_edges {
184+
if edges {
195185
EdgesOrNodes::Edges(cycles_edges)
196186
} else {
197187
EdgesOrNodes::Nodes(cycles_nodes)
198188
}
199189
}
200190

201191
/// Enum for custom return types of `cycle_basis()`.
202-
pub enum EdgesOrNodes<N, E> {
192+
enum EdgesOrNodes<N, E> {
203193
Nodes(Vec<Vec<N>>),
204194
Edges(Vec<Vec<E>>),
205195
}
206196
/// Functions used to unwrap the desired datatype of `EdgesOrNodes`.
207197
impl<N, E> EdgesOrNodes<N, E> {
208-
pub fn unwrap_nodes(self) -> Vec<Vec<N>> {
209-
use EdgesOrNodes::*;
198+
fn unwrap_nodes(self) -> Vec<Vec<N>> {
210199
match self {
211-
Nodes(x) => x,
212-
Edges(_) => vec![],
200+
Self::Nodes(x) => x,
201+
Self::Edges(_) => unreachable!(
202+
"Function should only return instances of {}.",
203+
std::any::type_name::<N>()
204+
),
213205
}
214206
}
215-
pub fn unwrap_edges(self) -> Vec<Vec<E>> {
216-
use EdgesOrNodes::*;
207+
fn unwrap_edges(self) -> Vec<Vec<E>> {
217208
match self {
218-
Edges(x) => x,
219-
Nodes(_) => vec![],
209+
Self::Edges(x) => x,
210+
Self::Nodes(_) => unreachable!(
211+
"Function should only return instances of {}.",
212+
std::any::type_name::<E>()
213+
),
220214
}
221215
}
222216
}
223217

218+
/// Returns lists of `NodeIndex` representing cycles which form
219+
/// a basis for cycles of a given graph.
220+
///
221+
/// A basis for cycles of a graph is a minimal collection of
222+
/// cycles such that any cycle in the graph can be written
223+
/// as a sum of cycles in the basis. Here summation of cycles
224+
/// is defined as the exclusive-or of the edges.
225+
///
226+
/// This is adapted from
227+
/// Paton, K. An algorithm for finding a fundamental set of
228+
/// cycles of a graph. Comm. ACM 12, 9 (Sept 1969), 514-518.
229+
///
230+
/// The function implicitly assumes that there are no parallel edges.
231+
/// It may produce incorrect/unexpected results if the input graph has
232+
/// parallel edges.
233+
///
234+
///
235+
/// Arguments:
236+
///
237+
/// * `graph` - The graph in which to find the basis.
238+
/// * `root` - Optional node index for starting the basis search. If not
239+
/// specified, an arbitrary node is chosen.
240+
///
241+
/// # Example
242+
/// ```rust
243+
/// use petgraph::prelude::*;
244+
/// use rustworkx_core::connectivity::cycle_basis;
245+
///
246+
/// let edge_list = [(0, 1), (0, 3), (0, 5), (1, 2), (2, 3), (3, 4), (4, 5)];
247+
/// let graph = UnGraph::<i32, i32>::from_edges(&edge_list);
248+
/// let mut res: Vec<Vec<NodeIndex>> = cycle_basis(&graph, Some(NodeIndex::new(0)));
249+
/// ```
250+
pub fn cycle_basis<G>(graph: G, root: Option<G::NodeId>) -> Vec<Vec<G::NodeId>>
251+
where
252+
G: NodeCount,
253+
G: IntoEdges,
254+
G: IntoNodeIdentifiers,
255+
G::NodeId: Eq + Hash,
256+
G::EdgeId: Eq + Hash,
257+
{
258+
inner_cycle_basis(graph, root, false).unwrap_nodes()
259+
}
260+
261+
/// Returns lists of `EdgeIndex` representing cycles which form
262+
/// a basis for cycles of a given graph.
263+
///
264+
/// A basis for cycles of a graph is a minimal collection of
265+
/// cycles such that any cycle in the graph can be written
266+
/// as a sum of cycles in the basis. Here summation of cycles
267+
/// is defined as the exclusive-or of the edges.
268+
///
269+
/// This is adapted from
270+
/// Paton, K. An algorithm for finding a fundamental set of
271+
/// cycles of a graph. Comm. ACM 12, 9 (Sept 1969), 514-518.
272+
///
273+
/// The function implicitly assumes that there are no parallel edges.
274+
/// It may produce incorrect/unexpected results if the input graph has
275+
/// parallel edges.
276+
///
277+
///
278+
/// Arguments:
279+
///
280+
/// * `graph` - The graph in which to find the basis.
281+
/// * `root` - Optional node index for starting the basis search. If not
282+
/// specified, an arbitrary node is chosen.
283+
///
284+
/// # Example
285+
/// ```rust
286+
/// use petgraph::prelude::*;
287+
/// use rustworkx_core::connectivity::cycle_basis_edges;
288+
///
289+
/// let edge_list = [(0, 1), (0, 3), (0, 5), (1, 2), (2, 3), (3, 4), (4, 5)];
290+
/// let graph = UnGraph::<i32, i32>::from_edges(&edge_list);
291+
/// let mut res: Vec<Vec<EdgeIndex>> = cycle_basis_edges(&graph, Some(NodeIndex::new(0)));
292+
/// ```
293+
pub fn cycle_basis_edges<G>(graph: G, root: Option<G::NodeId>) -> Vec<Vec<G::EdgeId>>
294+
where
295+
G: NodeCount,
296+
G: IntoEdges,
297+
G: IntoNodeIdentifiers,
298+
G::NodeId: Eq + Hash,
299+
G::EdgeId: Eq + Hash,
300+
{
301+
inner_cycle_basis(graph, root, true).unwrap_edges()
302+
}
303+
224304
#[cfg(test)]
225305
mod tests {
226306
use crate::connectivity::cycle_basis;
307+
use crate::connectivity::cycle_basis_edges;
227308
use petgraph::prelude::*;
309+
use petgraph::stable_graph::GraphIndex;
228310

229-
use super::EdgesOrNodes;
230-
231-
fn sorted_cycle(cycles: EdgesOrNodes<NodeIndex, EdgeIndex>, edges: bool) -> Vec<Vec<usize>> {
311+
fn sorted_cycle<T>(cycles: Vec<Vec<T>>) -> Vec<Vec<usize>>
312+
where
313+
T: GraphIndex,
314+
{
232315
let mut sorted_cycles: Vec<Vec<usize>> = vec![];
233-
if edges {
234-
let cycles: Vec<Vec<EdgeIndex>> = cycles.unwrap_edges();
235-
for cycle in cycles {
236-
let mut cycle: Vec<usize> = cycle.iter().map(|x: &EdgeIndex| x.index()).collect();
237-
cycle.sort();
238-
sorted_cycles.push(cycle);
239-
}
240-
} else {
241-
let cycles: Vec<Vec<NodeIndex>> = cycles.unwrap_nodes();
242-
for cycle in cycles {
243-
let mut cycle: Vec<usize> = cycle.iter().map(|x: &NodeIndex| x.index()).collect();
244-
cycle.sort();
245-
sorted_cycles.push(cycle);
246-
}
247-
};
316+
for cycle in cycles {
317+
let mut cycle: Vec<usize> = cycle.iter().map(|x: &T| x.index()).collect();
318+
cycle.sort();
319+
sorted_cycles.push(cycle);
320+
}
248321
sorted_cycles.sort();
249322
sorted_cycles
250323
}
251324

252325
#[test]
253326
fn test_cycle_basis_source() {
254-
let edges: bool = false;
255327
let edge_list = vec![
256328
(0, 1),
257329
(0, 3),
@@ -268,17 +340,16 @@ mod tests {
268340
];
269341
let graph = UnGraph::<i32, i32>::from_edges(&edge_list);
270342
let expected = vec![vec![0, 1, 2, 3], vec![0, 1, 6, 7, 8], vec![0, 3, 4, 5]];
271-
let res_0 = cycle_basis(&graph, Some(NodeIndex::new(0)), Some(edges));
272-
assert_eq!(sorted_cycle(res_0, false), expected);
273-
let res_1 = cycle_basis(&graph, Some(NodeIndex::new(1)), Some(edges));
274-
assert_eq!(sorted_cycle(res_1, false), expected);
275-
let res_9 = cycle_basis(&graph, Some(NodeIndex::new(9)), Some(edges));
276-
assert_eq!(sorted_cycle(res_9, false), expected);
343+
let res_0 = cycle_basis(&graph, Some(NodeIndex::new(0)));
344+
assert_eq!(sorted_cycle(res_0), expected);
345+
let res_1 = cycle_basis(&graph, Some(NodeIndex::new(1)));
346+
assert_eq!(sorted_cycle(res_1), expected);
347+
let res_9 = cycle_basis(&graph, Some(NodeIndex::new(9)));
348+
assert_eq!(sorted_cycle(res_9), expected);
277349
}
278350

279351
#[test]
280352
fn test_cycle_edge_basis_source() {
281-
let edges: bool = true;
282353
let edge_list = vec![
283354
(0, 0),
284355
(0, 1),
@@ -291,17 +362,16 @@ mod tests {
291362
];
292363
let graph = UnGraph::<i32, i32>::from_edges(&edge_list);
293364
let expected = vec![vec![0], vec![3, 4, 5, 6]];
294-
let res_0 = cycle_basis(&graph, Some(NodeIndex::new(0)), Some(edges));
295-
assert_eq!(sorted_cycle(res_0, true), expected);
296-
let res_1 = cycle_basis(&graph, Some(NodeIndex::new(2)), Some(edges));
297-
assert_eq!(sorted_cycle(res_1, true), expected);
298-
let res_9 = cycle_basis(&graph, Some(NodeIndex::new(6)), Some(edges));
299-
assert_eq!(sorted_cycle(res_9, true), expected);
365+
let res_0 = cycle_basis_edges(&graph, Some(NodeIndex::new(0)));
366+
assert_eq!(sorted_cycle(res_0), expected);
367+
let res_1 = cycle_basis_edges(&graph, Some(NodeIndex::new(2)));
368+
assert_eq!(sorted_cycle(res_1), expected);
369+
let res_9 = cycle_basis_edges(&graph, Some(NodeIndex::new(6)));
370+
assert_eq!(sorted_cycle(res_9), expected);
300371
}
301372

302373
#[test]
303374
fn test_self_loop() {
304-
let edges: bool = false;
305375
let edge_list = vec![
306376
(0, 1),
307377
(0, 3),
@@ -318,9 +388,9 @@ mod tests {
318388
];
319389
let mut graph = UnGraph::<i32, i32>::from_edges(&edge_list);
320390
graph.add_edge(NodeIndex::new(1), NodeIndex::new(1), 0);
321-
let res_0 = cycle_basis(&graph, Some(NodeIndex::new(0)), Some(edges));
391+
let res_0 = cycle_basis(&graph, Some(NodeIndex::new(0)));
322392
assert_eq!(
323-
sorted_cycle(res_0, edges),
393+
sorted_cycle(res_0),
324394
vec![
325395
vec![0, 1, 2, 3],
326396
vec![0, 1, 6, 7, 8],
@@ -332,7 +402,6 @@ mod tests {
332402

333403
#[test]
334404
fn test_self_loop_edges() {
335-
let edges: bool = true;
336405
let edge_list = vec![
337406
(0, 1),
338407
(0, 3),
@@ -349,9 +418,9 @@ mod tests {
349418
];
350419
let mut graph = UnGraph::<i32, i32>::from_edges(&edge_list);
351420
graph.add_edge(NodeIndex::new(1), NodeIndex::new(1), 0);
352-
let res_0 = cycle_basis(&graph, Some(NodeIndex::new(0)), Some(edges));
421+
let res_0 = cycle_basis_edges(&graph, Some(NodeIndex::new(0)));
353422
assert_eq!(
354-
sorted_cycle(res_0, edges),
423+
sorted_cycle(res_0),
355424
vec![
356425
vec![0, 1, 4, 6],
357426
vec![0, 3, 5, 9, 10],

rustworkx-core/src/connectivity/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,6 @@ pub use conn_components::connected_components;
3131
pub use conn_components::number_connected_components;
3232
pub use core_number::core_number;
3333
pub use cycle_basis::cycle_basis;
34+
pub use cycle_basis::cycle_basis_edges;
3435
pub use find_cycle::find_cycle;
3536
pub use min_cut::stoer_wagner_min_cut;

0 commit comments

Comments
 (0)