1111// under the License.
1212
1313use hashbrown:: { HashMap , HashSet } ;
14+ use petgraph:: algo;
1415use petgraph:: visit:: {
15- EdgeCount , GraphBase , IntoNeighborsDirected , IntoNodeIdentifiers , NodeCount ,
16+ EdgeCount , GraphBase , IntoNeighborsDirected , IntoNodeIdentifiers , NodeCount , Visitable ,
1617} ;
1718use petgraph:: Direction :: Outgoing ;
1819use std:: hash:: Hash ;
@@ -57,22 +58,22 @@ where
5758 G : GraphBase ,
5859 G : NodeCount ,
5960 G : EdgeCount ,
60- for < ' b > & ' b G : GraphBase < NodeId = G :: NodeId > + IntoNodeIdentifiers + IntoNeighborsDirected ,
61+ for < ' b > & ' b G :
62+ GraphBase < NodeId = G :: NodeId > + IntoNodeIdentifiers + IntoNeighborsDirected + Visitable ,
6163 G :: NodeId : Eq + Hash ,
6264{
6365 // Find a cycle in the given graph and return it as a list of edges
64- let mut graph_nodes: HashSet < G :: NodeId > = graph. node_identifiers ( ) . collect ( ) ;
6566 let mut cycle: Vec < ( G :: NodeId , G :: NodeId ) > = Vec :: with_capacity ( graph. edge_count ( ) ) ;
66- let temp_value: G :: NodeId ;
67- // If source is not set get an arbitrary node from the set of graph
68- // nodes we've not "examined"
67+ // If source is not set get a node in an arbitrary cycle if it exists,
68+ // otherwise return that there is no cycle
6969 let source_index = match source {
7070 Some ( source_value) => source_value,
71- None => {
72- temp_value = * graph_nodes. iter ( ) . next ( ) . unwrap ( ) ;
73- graph_nodes. remove ( & temp_value) ;
74- temp_value
75- }
71+ None => match find_node_in_arbitrary_cycle ( & graph) {
72+ Some ( node_in_cycle) => node_in_cycle,
73+ None => {
74+ return Vec :: new ( ) ;
75+ }
76+ } ,
7677 } ;
7778 // Stack (ie "pushdown list") of vertices already in the spanning tree
7879 let mut stack: Vec < G :: NodeId > = vec ! [ source_index] ;
@@ -119,11 +120,47 @@ where
119120 cycle
120121}
121122
123+ fn find_node_in_arbitrary_cycle < G > ( graph : & G ) -> Option < G :: NodeId >
124+ where
125+ G : GraphBase ,
126+ G : NodeCount ,
127+ G : EdgeCount ,
128+ for < ' b > & ' b G :
129+ GraphBase < NodeId = G :: NodeId > + IntoNodeIdentifiers + IntoNeighborsDirected + Visitable ,
130+ G :: NodeId : Eq + Hash ,
131+ {
132+ for scc in algo:: kosaraju_scc ( & graph) {
133+ if scc. len ( ) > 1 {
134+ return Some ( scc[ 0 ] ) ;
135+ }
136+ }
137+ for node in graph. node_identifiers ( ) {
138+ for neighbor in graph. neighbors_directed ( node, Outgoing ) {
139+ if neighbor == node {
140+ return Some ( node) ;
141+ }
142+ }
143+ }
144+ None
145+ }
146+
122147#[ cfg( test) ]
123148mod tests {
124149 use crate :: connectivity:: find_cycle;
125150 use petgraph:: prelude:: * ;
126151
152+ // Utility to assert cycles in the response
153+ macro_rules! assert_cycle {
154+ ( $g: expr, $cycle: expr) => { {
155+ for i in 0 ..$cycle. len( ) {
156+ let ( s, t) = $cycle[ i] ;
157+ assert!( $g. contains_edge( s, t) ) ;
158+ let ( next_s, _) = $cycle[ ( i + 1 ) % $cycle. len( ) ] ;
159+ assert_eq!( t, next_s) ;
160+ }
161+ } } ;
162+ }
163+
127164 #[ test]
128165 fn test_find_cycle_source ( ) {
129166 let edge_list = vec ! [
@@ -141,20 +178,13 @@ mod tests {
141178 ( 8 , 9 ) ,
142179 ] ;
143180 let graph = DiGraph :: < i32 , i32 > :: from_edges ( edge_list) ;
144- let mut res: Vec < ( usize , usize ) > = find_cycle ( & graph, Some ( NodeIndex :: new ( 0 ) ) )
145- . iter ( )
146- . map ( |( s, t) | ( s. index ( ) , t. index ( ) ) )
147- . collect ( ) ;
148- assert_eq ! ( res, [ ( 0 , 1 ) , ( 1 , 2 ) , ( 2 , 3 ) , ( 3 , 0 ) ] ) ;
149- res = find_cycle ( & graph, Some ( NodeIndex :: new ( 1 ) ) )
150- . iter ( )
151- . map ( |( s, t) | ( s. index ( ) , t. index ( ) ) )
152- . collect ( ) ;
153- assert_eq ! ( res, [ ( 1 , 2 ) , ( 2 , 3 ) , ( 3 , 0 ) , ( 0 , 1 ) ] ) ;
154- res = find_cycle ( & graph, Some ( NodeIndex :: new ( 5 ) ) )
155- . iter ( )
156- . map ( |( s, t) | ( s. index ( ) , t. index ( ) ) )
157- . collect ( ) ;
181+ for i in [ 0 , 1 , 2 , 3 ] . iter ( ) {
182+ let idx = NodeIndex :: new ( * i) ;
183+ let res = find_cycle ( & graph, Some ( idx) ) ;
184+ assert_cycle ! ( graph, res) ;
185+ assert_eq ! ( res[ 0 ] . 0 , idx) ;
186+ }
187+ let res = find_cycle ( & graph, Some ( NodeIndex :: new ( 5 ) ) ) ;
158188 assert_eq ! ( res, [ ] ) ;
159189 }
160190
@@ -176,10 +206,32 @@ mod tests {
176206 ] ;
177207 let mut graph = DiGraph :: < i32 , i32 > :: from_edges ( edge_list) ;
178208 graph. add_edge ( NodeIndex :: new ( 1 ) , NodeIndex :: new ( 1 ) , 0 ) ;
179- let res: Vec < ( usize , usize ) > = find_cycle ( & graph, Some ( NodeIndex :: new ( 0 ) ) )
180- . iter ( )
181- . map ( |( s, t) | ( s. index ( ) , t. index ( ) ) )
182- . collect ( ) ;
183- assert_eq ! ( res, [ ( 1 , 1 ) ] ) ;
209+ let res = find_cycle ( & graph, Some ( NodeIndex :: new ( 0 ) ) ) ;
210+ assert_eq ! ( res[ 0 ] . 0 , NodeIndex :: new( 1 ) ) ;
211+ assert_cycle ! ( graph, res) ;
212+ }
213+
214+ #[ test]
215+ fn test_self_loop_no_source ( ) {
216+ let edge_list = vec ! [ ( 0 , 1 ) , ( 1 , 2 ) , ( 2 , 3 ) , ( 2 , 2 ) ] ;
217+ let graph = DiGraph :: < i32 , i32 > :: from_edges ( edge_list) ;
218+ let res = find_cycle ( & graph, None ) ;
219+ assert_cycle ! ( graph, res) ;
220+ }
221+
222+ #[ test]
223+ fn test_cycle_no_source ( ) {
224+ let edge_list = vec ! [ ( 0 , 1 ) , ( 1 , 2 ) , ( 2 , 3 ) , ( 3 , 4 ) , ( 4 , 2 ) ] ;
225+ let graph = DiGraph :: < i32 , i32 > :: from_edges ( edge_list) ;
226+ let res = find_cycle ( & graph, None ) ;
227+ assert_cycle ! ( graph, res) ;
228+ }
229+
230+ #[ test]
231+ fn test_no_cycle_no_source ( ) {
232+ let edge_list = vec ! [ ( 0 , 1 ) , ( 1 , 2 ) , ( 2 , 3 ) ] ;
233+ let graph = DiGraph :: < i32 , i32 > :: from_edges ( edge_list) ;
234+ let res = find_cycle ( & graph, None ) ;
235+ assert_eq ! ( res, [ ] ) ;
184236 }
185237}
0 commit comments