public static IReadOnlyList <Cycle <TVertex> > GetCycles <TVertex, TEdge>( this BidirectionalGraph <TVertex, TEdge> graph) where TEdge : IEdge <TVertex> { // Initialize vertex, and current stack tracking var visited = new HashSet <TVertex>(); var stack = new List <TVertex>(); var cycles = new List <Cycle <TVertex> >(); // Call the recursive helper function to detect cycle in different DFS trees foreach (var vertex in graph.Vertices) { _logger.Debug($"Probing node '{vertex}' for cyclical dependencies..."); graph.FindCycles(vertex, visited, stack, cycles); } return(cycles); }
private static void FindCycles <TVertex, TEdge>( this BidirectionalGraph <TVertex, TEdge> executionGraph, TVertex vertex, HashSet <TVertex> visited, List <TVertex> stack, List <Cycle <TVertex> > cycles) where TEdge : IEdge <TVertex> { // Do we have a circular dependency? if (stack.Contains(vertex)) { var cycle = new Cycle <TVertex> { Vertex = vertex.ToString(), Path = stack.SkipWhile(x => !x.Equals(vertex)) .Concat(new[] { vertex }) .ToList() }; if (_logger.IsDebugEnabled) { _logger.Debug($"Cycle found for vertex '{cycle.Vertex}': {string.Join(" --> ", cycle.Path.Select(x => x.ToString()))}"); } cycles.Add(cycle); visited.Add(vertex); return; } // If we've already evaluated this vertex, stop now. if (visited.Contains(vertex)) { return; } // Mark the current node as visited and part of recursion stack visited.Add(vertex); stack.Add(vertex); try { var children = executionGraph .OutEdges(vertex) .Select(x => x.Target) .ToList(); if (_logger.IsDebugEnabled) { if (children.Any()) { _logger.Debug($"Children of {vertex}: {string.Join(" => ", children.Select(x => x.ToString()))}"); } } foreach (var child in children) { executionGraph.FindCycles(child, visited, stack, cycles); } } finally { stack.Remove(vertex); } }