Skip to content

Commit dadb482

Browse files
committed
Feature: merge basis and basis_edges into one
- Cycle basis edges is now within the codebase of cycle basis. - The `EdgesOrNodes` enum handles the different return data types.
1 parent 23f2759 commit dadb482

File tree

4 files changed

+143
-211
lines changed

4 files changed

+143
-211
lines changed

rustworkx-core/src/connectivity/cycle_basis.rs

Lines changed: 121 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ use std::hash::Hash;
3535
/// * `graph` - The graph in which to find the basis.
3636
/// * `root` - Optional node index for starting the basis search. If not
3737
/// specified, an arbitrary node is chosen.
38+
/// * `edges` - Optional bool for when the user requests the edges instead
39+
/// of the nodes of the cycles.
3840
///
3941
/// # Example
4042
/// ```rust
@@ -43,112 +45,14 @@ use std::hash::Hash;
4345
///
4446
/// let edge_list = [(0, 1), (0, 3), (0, 5), (1, 2), (2, 3), (3, 4), (4, 5)];
4547
/// let graph = UnGraph::<i32, i32>::from_edges(&edge_list);
46-
/// let mut res: Vec<Vec<NodeIndex>> = cycle_basis(&graph, Some(NodeIndex::new(0)));
48+
/// let mut res: Vec<Vec<NodeIndex>> = cycle_basis(&graph, Some(NodeIndex::new(0)), Some(false)).unwrap_nodes();
4749
/// ```
48-
pub fn cycle_basis<G>(graph: G, root: Option<G::NodeId>) -> Vec<Vec<G::NodeId>>
49-
where
50-
G: NodeCount,
51-
G: IntoNeighbors,
52-
G: IntoNodeIdentifiers,
53-
G::NodeId: Eq + Hash,
54-
{
55-
let mut root_node = root;
56-
let mut graph_nodes: HashSet<G::NodeId> = graph.node_identifiers().collect();
57-
let mut cycles: Vec<Vec<G::NodeId>> = Vec::new();
58-
while !graph_nodes.is_empty() {
59-
let temp_value: G::NodeId;
60-
// If root_node is not set get an arbitrary node from the set of graph
61-
// nodes we've not "examined"
62-
let root_index = match root_node {
63-
Some(root_node) => root_node,
64-
None => {
65-
temp_value = *graph_nodes.iter().next().unwrap();
66-
graph_nodes.remove(&temp_value);
67-
temp_value
68-
}
69-
};
70-
// Stack (ie "pushdown list") of vertices already in the spanning tree
71-
let mut stack: Vec<G::NodeId> = vec![root_index];
72-
// Map of node index to predecessor node index
73-
let mut pred: HashMap<G::NodeId, G::NodeId> = HashMap::new();
74-
pred.insert(root_index, root_index);
75-
// Set of examined nodes during this iteration
76-
let mut used: HashMap<G::NodeId, HashSet<G::NodeId>> = HashMap::new();
77-
used.insert(root_index, HashSet::new());
78-
// Walk the spanning tree
79-
// Use the last element added so that cycles are easier to find
80-
while let Some(z) = stack.pop() {
81-
for neighbor in graph.neighbors(z) {
82-
// A new node was encountered:
83-
if !used.contains_key(&neighbor) {
84-
pred.insert(neighbor, z);
85-
stack.push(neighbor);
86-
let mut temp_set: HashSet<G::NodeId> = HashSet::new();
87-
temp_set.insert(z);
88-
used.insert(neighbor, temp_set);
89-
// A self loop:
90-
} else if z == neighbor {
91-
let cycle: Vec<G::NodeId> = vec![z];
92-
cycles.push(cycle);
93-
// A cycle was found:
94-
} else if !used.get(&z).unwrap().contains(&neighbor) {
95-
let pn = used.get(&neighbor).unwrap();
96-
let mut cycle: Vec<G::NodeId> = vec![neighbor, z];
97-
let mut p = pred.get(&z).unwrap();
98-
while !pn.contains(p) {
99-
cycle.push(*p);
100-
p = pred.get(p).unwrap();
101-
}
102-
cycle.push(*p);
103-
cycles.push(cycle);
104-
let neighbor_set = used.get_mut(&neighbor).unwrap();
105-
neighbor_set.insert(z);
106-
}
107-
}
108-
}
109-
let mut temp_hashset: HashSet<G::NodeId> = HashSet::new();
110-
for key in pred.keys() {
111-
temp_hashset.insert(*key);
112-
}
113-
graph_nodes = graph_nodes.difference(&temp_hashset).copied().collect();
114-
root_node = None;
115-
}
116-
cycles
117-
}
11850
119-
/// Return a list of edge indices representing cycles which form a basis for
120-
/// cycles of a given graph.
121-
///
122-
/// A basis for cycles of a graph is a minimal collection of
123-
/// cycles such that any cycle in the graph can be written
124-
/// as a sum of cycles in the basis. Here summation of cycles
125-
/// is defined as the exclusive-or of the edges.
126-
///
127-
/// This is adapted from
128-
/// Paton, K. An algorithm for finding a fundamental set of
129-
/// cycles of a graph. Comm. ACM 12, 9 (Sept 1969), 514-518.
130-
///
131-
/// The function implicitly assumes that there are no parallel edges.
132-
/// It may produce incorrect/unexpected results if the input graph has
133-
/// parallel edges.
134-
///
135-
///
136-
/// Arguments:
137-
///
138-
/// * `graph` - The graph in which to find the basis.
139-
/// * `root` - Optional node index for starting the basis search. If not
140-
/// specified, an arbitrary node is chosen.
141-
///
142-
/// # Example
143-
/// ```rust
144-
/// use petgraph::prelude::*;
145-
/// use rustworkx_core::connectivity::cycle_basis_edges;
146-
///
147-
/// let edge_list = [(0, 1), (0, 3), (0, 5), (1, 2), (2, 3), (3, 4), (4, 5)];
148-
/// let graph = UnGraph::<i32, i32>::from_edges(&edge_list);
149-
/// let mut res: Vec<Vec<EdgeIndex>> = cycle_basis_edges(&graph, Some(NodeIndex::new(0)));
150-
/// ```
151-
pub fn cycle_basis_edges<G>(graph: G, root: Option<G::NodeId>) -> Vec<Vec<G::EdgeId>>
51+
pub fn cycle_basis<G>(
52+
graph: G,
53+
root: Option<G::NodeId>,
54+
edges: Option<bool>,
55+
) -> EdgesOrNodes<G::NodeId, G::EdgeId>
15256
where
15357
G: NodeCount,
15458
G: IntoNeighbors,
@@ -157,9 +61,11 @@ where
15761
G::NodeId: Eq + Hash,
15862
G::EdgeId: Eq + Hash,
15963
{
64+
let want_edges = edges == Some(true);
16065
let mut root_node = root;
16166
let mut graph_nodes: HashSet<G::NodeId> = graph.node_identifiers().collect();
162-
let mut cycles: Vec<Vec<G::EdgeId>> = Vec::new();
67+
let mut cycles_edges: Vec<Vec<G::EdgeId>> = Vec::new();
68+
let mut cycles_nodes: Vec<Vec<G::NodeId>> = Vec::new();
16369

16470
// Method used to retrieve all the edges between an origin node and a target node.
16571
// Can be used to check if a node loops back to itself by enabling equiv
@@ -220,43 +126,59 @@ where
220126
used.insert(neighbor, temp_set);
221127
// A self loop:
222128
} else if z == neighbor {
223-
let cycle_edge: Vec<G::EdgeId> = get_edge_between(graph, z, z, true);
224-
cycles.push(cycle_edge);
129+
if want_edges {
130+
let cycle_edge: Vec<G::EdgeId> = get_edge_between(graph, z, z, true);
131+
cycles_edges.push(cycle_edge);
132+
} else {
133+
let cycle: Vec<G::NodeId> = vec![z];
134+
cycles_nodes.push(cycle);
135+
}
225136
// A cycle was found:
226137
} else if !used.get(&z).unwrap().contains(&neighbor) {
227138
let pn = used.get(&neighbor).unwrap();
228-
let mut cycle: Vec<G::EdgeId> = vec![];
229139
let mut p = pred.get(&z).unwrap();
230-
// Retreive all edges from z to neighbor and vice versa
231-
let mut neigh_edge: Vec<G::EdgeId> =
232-
get_edge_between(graph, z, neighbor, false);
233-
// Append to cycle
234-
cycle.append(&mut neigh_edge);
235-
// Make last p_node == z
236-
let mut prev_p: &G::NodeId = &z;
237-
// While p is in the neighborhood of neighbor
238-
while !pn.contains(p) {
239-
// Retrieve all edges from prev_p to p and vice versa
140+
if want_edges {
141+
let mut cycle: Vec<G::EdgeId> = Vec::new();
142+
// Retreive all edges from z to neighbor.
240143
let mut neigh_edge: Vec<G::EdgeId> =
241-
get_edge_between(graph, *prev_p, *p, false);
144+
get_edge_between(graph, z, neighbor, false);
242145
// Append to cycle
243146
cycle.append(&mut neigh_edge);
244-
// Update prev_p to p
245-
prev_p = p;
246-
// Retreive a new predecessor node from p and replace p
247-
p = pred.get(p).unwrap();
248-
}
249-
// When loop ends add remaining edges from prev_p to p.
250-
let mut neigh_edge: Vec<G::EdgeId> =
251-
get_edge_between(graph, *prev_p, *p, false);
252-
cycle.append(&mut neigh_edge);
253-
// Also retreive all edges between the last p and neighbor
254-
let mut neigh_edge: Vec<G::EdgeId> =
255-
get_edge_between(graph, *p, neighbor, false);
256-
cycle.append(&mut neigh_edge);
257147

258-
// Once all edges within cycle have been found, push to cycle list.
259-
cycles.push(cycle);
148+
// Make last p_node == z
149+
let mut prev_p: &G::NodeId = &z;
150+
// While p is in the neighborhood of neighbor
151+
while !pn.contains(p) {
152+
// Retrieve all edges from prev_p to p and vice versa
153+
let mut neigh_edge: Vec<G::EdgeId> =
154+
get_edge_between(graph, *prev_p, *p, false);
155+
// Append to cycle
156+
cycle.append(&mut neigh_edge);
157+
// Update prev_p to p
158+
prev_p = p;
159+
// Retreive a new predecessor node from p and replace p
160+
p = pred.get(p).unwrap();
161+
}
162+
// When loop ends add remaining edges from prev_p to p.
163+
let mut neigh_edge: Vec<G::EdgeId> =
164+
get_edge_between(graph, *prev_p, *p, false);
165+
cycle.append(&mut neigh_edge);
166+
// Also retreive all edges between the last p and neighbor
167+
let mut neigh_edge: Vec<G::EdgeId> =
168+
get_edge_between(graph, *p, neighbor, false);
169+
cycle.append(&mut neigh_edge);
170+
// Once all edges within cycle have been found, push to cycle list.
171+
cycles_edges.push(cycle);
172+
} else {
173+
// Append neighbor and z to cycle.
174+
let mut cycle: Vec<G::NodeId> = vec![neighbor, z];
175+
while !pn.contains(p) {
176+
cycle.push(*p);
177+
p = pred.get(p).unwrap();
178+
}
179+
cycle.push(*p);
180+
cycles_nodes.push(cycle);
181+
}
260182
let neighbor_set: &mut HashSet<G::NodeId> = used.get_mut(&neighbor).unwrap();
261183
neighbor_set.insert(z);
262184
}
@@ -269,39 +191,67 @@ where
269191
graph_nodes = graph_nodes.difference(&temp_hashset).copied().collect();
270192
root_node = None;
271193
}
272-
cycles
194+
if want_edges {
195+
EdgesOrNodes::Edges(cycles_edges)
196+
} else {
197+
EdgesOrNodes::Nodes(cycles_nodes)
198+
}
199+
}
200+
201+
/// Enum for custom return types of `cycle_basis()`.
202+
pub enum EdgesOrNodes<N, E> {
203+
Nodes(Vec<Vec<N>>),
204+
Edges(Vec<Vec<E>>),
205+
}
206+
/// Functions used to unwrap the desired datatype of `EdgesOrNodes`.
207+
impl<N, E> EdgesOrNodes<N, E> {
208+
pub fn unwrap_nodes(self) -> Vec<Vec<N>> {
209+
use EdgesOrNodes::*;
210+
match self {
211+
Nodes(x) => x,
212+
Edges(_) => vec![],
213+
}
214+
}
215+
pub fn unwrap_edges(self) -> Vec<Vec<E>> {
216+
use EdgesOrNodes::*;
217+
match self {
218+
Edges(x) => x,
219+
Nodes(_) => vec![],
220+
}
221+
}
273222
}
274223

275224
#[cfg(test)]
276225
mod tests {
277226
use crate::connectivity::cycle_basis;
278-
use crate::connectivity::cycle_basis_edges;
279227
use petgraph::prelude::*;
280228

281-
fn sorted_cycle(cycles: Vec<Vec<NodeIndex>>) -> Vec<Vec<usize>> {
282-
let mut sorted_cycles: Vec<Vec<usize>> = vec![];
283-
for cycle in cycles {
284-
let mut cycle: Vec<usize> = cycle.iter().map(|x| x.index()).collect();
285-
cycle.sort();
286-
sorted_cycles.push(cycle);
287-
}
288-
sorted_cycles.sort();
289-
sorted_cycles
290-
}
229+
use super::EdgesOrNodes;
291230

292-
fn sorted_cycle_edges(cycles: Vec<Vec<EdgeIndex>>) -> Vec<Vec<usize>> {
231+
fn sorted_cycle(cycles: EdgesOrNodes<NodeIndex, EdgeIndex>, edges: bool) -> Vec<Vec<usize>> {
293232
let mut sorted_cycles: Vec<Vec<usize>> = vec![];
294-
for cycle in cycles {
295-
let mut cycle: Vec<usize> = cycle.iter().map(|x| x.index()).collect();
296-
cycle.sort();
297-
sorted_cycles.push(cycle);
298-
}
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+
};
299248
sorted_cycles.sort();
300249
sorted_cycles
301250
}
302251

303252
#[test]
304253
fn test_cycle_basis_source() {
254+
let edges: bool = false;
305255
let edge_list = vec![
306256
(0, 1),
307257
(0, 3),
@@ -318,16 +268,17 @@ mod tests {
318268
];
319269
let graph = UnGraph::<i32, i32>::from_edges(&edge_list);
320270
let expected = vec![vec![0, 1, 2, 3], vec![0, 1, 6, 7, 8], vec![0, 3, 4, 5]];
321-
let res_0 = cycle_basis(&graph, Some(NodeIndex::new(0)));
322-
assert_eq!(sorted_cycle(res_0), expected);
323-
let res_1 = cycle_basis(&graph, Some(NodeIndex::new(1)));
324-
assert_eq!(sorted_cycle(res_1), expected);
325-
let res_9 = cycle_basis(&graph, Some(NodeIndex::new(9)));
326-
assert_eq!(sorted_cycle(res_9), expected);
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);
327277
}
328278

329279
#[test]
330280
fn test_cycle_edge_basis_source() {
281+
let edges: bool = true;
331282
let edge_list = vec![
332283
(0, 0),
333284
(0, 1),
@@ -340,16 +291,17 @@ mod tests {
340291
];
341292
let graph = UnGraph::<i32, i32>::from_edges(&edge_list);
342293
let expected = vec![vec![0], vec![3, 4, 5, 6]];
343-
let res_0 = cycle_basis_edges(&graph, Some(NodeIndex::new(0)));
344-
assert_eq!(sorted_cycle_edges(res_0), expected);
345-
let res_1 = cycle_basis_edges(&graph, Some(NodeIndex::new(2)));
346-
assert_eq!(sorted_cycle_edges(res_1), expected);
347-
let res_9 = cycle_basis_edges(&graph, Some(NodeIndex::new(6)));
348-
assert_eq!(sorted_cycle_edges(res_9), expected);
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);
349300
}
350301

351302
#[test]
352303
fn test_self_loop() {
304+
let edges: bool = false;
353305
let edge_list = vec![
354306
(0, 1),
355307
(0, 3),
@@ -366,9 +318,9 @@ mod tests {
366318
];
367319
let mut graph = UnGraph::<i32, i32>::from_edges(&edge_list);
368320
graph.add_edge(NodeIndex::new(1), NodeIndex::new(1), 0);
369-
let res_0 = cycle_basis(&graph, Some(NodeIndex::new(0)));
321+
let res_0 = cycle_basis(&graph, Some(NodeIndex::new(0)), Some(edges));
370322
assert_eq!(
371-
sorted_cycle(res_0),
323+
sorted_cycle(res_0, edges),
372324
vec![
373325
vec![0, 1, 2, 3],
374326
vec![0, 1, 6, 7, 8],
@@ -380,6 +332,7 @@ mod tests {
380332

381333
#[test]
382334
fn test_self_loop_edges() {
335+
let edges: bool = true;
383336
let edge_list = vec![
384337
(0, 1),
385338
(0, 3),
@@ -396,9 +349,9 @@ mod tests {
396349
];
397350
let mut graph = UnGraph::<i32, i32>::from_edges(&edge_list);
398351
graph.add_edge(NodeIndex::new(1), NodeIndex::new(1), 0);
399-
let res_0 = cycle_basis_edges(&graph, Some(NodeIndex::new(0)));
352+
let res_0 = cycle_basis(&graph, Some(NodeIndex::new(0)), Some(edges));
400353
assert_eq!(
401-
sorted_cycle_edges(res_0),
354+
sorted_cycle(res_0, edges),
402355
vec![
403356
vec![0, 1, 4, 6],
404357
vec![0, 3, 5, 9, 10],

0 commit comments

Comments
 (0)