public void TarjanStronglyConnected_Smoke_Test() { var graph = new DiGraph <char>(); graph.AddVertex('A'); graph.AddVertex('B'); graph.AddVertex('C'); graph.AddVertex('D'); graph.AddVertex('E'); graph.AddVertex('F'); graph.AddVertex('G'); graph.AddVertex('H'); graph.AddEdge('A', 'B'); graph.AddEdge('B', 'C'); graph.AddEdge('C', 'A'); graph.AddEdge('C', 'D'); graph.AddEdge('D', 'E'); graph.AddEdge('E', 'F'); graph.AddEdge('F', 'G'); graph.AddEdge('G', 'E'); graph.AddEdge('F', 'H'); var algorithm = new TarjansStronglyConnected <char>(); var result = algorithm.FindStronglyConnectedComponents(graph); Assert.AreEqual(4, result.Count); var expectedResult = new List <List <char> >() { new char[] { 'H' }.ToList(), new char[] { 'E', 'F', 'G' }.ToList(), new char[] { 'D' }.ToList(), new char[] { 'A', 'B', 'C' }.ToList() }; for (int i = 0; i < expectedResult.Count; i++) { var expectation = expectedResult[i]; var actual = result[i]; Assert.IsTrue(expectation.Count == actual.Count); foreach (var vertex in expectation) { Assert.IsTrue(actual.Contains(vertex)); } } }
/// <summary> /// Breaks cycles in the graph, returning all edges that changed their orientation. /// </summary> /// <param name="graph"></param> /// <returns></returns> public List <(Operation Operation1, Operation Operation2)> BreakCycles(DiGraph <Operation> graph) { var changedOrientationEdges = new List <(Operation Operation1, Operation Operation2)>(); var algorithm = new TarjansStronglyConnected <Operation>(); List <List <Operation> > components; do { // find strongly connected components components = algorithm.FindStronglyConnectedComponents(graph).Where(x => x.Count > 1).ToList(); foreach (List <Operation> component in components.AsShuffledEnumerable()) { var componentHashSet = component.ToHashSet(); foreach (var operation1 in component.AsShuffledEnumerable()) { // get all neighbor operations that are in the same component var neighborOperations = GetNeighborComponentOperations(graph, operation1, componentHashSet); foreach (var operation2 in neighborOperations.AsShuffledEnumerable()) { // two operations are not on the same job // but are on the same machines (=> can switch their edge orientation if I need to) if (operation1.JobId != operation2.JobId && operation1.MachineId == operation2.MachineId) { double probability = RandomizationProvider.Current.GetDouble(); // switch directions of edge with prob for back edge, forward edge or same level edge if ((operation1.Order > operation2.Order && probability <= backEdgeBreakProbability) || (operation1.Order < operation2.Order && probability <= forwardEdgeBreakProbability) || (operation1.Order == operation2.Order && probability <= sameLevelEdgeBreakProbability)) { // switch edge orientation graph.RemoveEdge(operation1, operation2); graph.AddEdge(operation2, operation1); changedOrientationEdges.Add((operation1, operation2)); goto cycleOut; } } } } } cycleOut :; } while (components.Count > 0); return(changedOrientationEdges); }