public ImmutableList <ImmutableHashSet <T> > Solve <T>(IDirectedAcyclicGraph <T> graph) where T : IEquatable <T> { var reducedGraph = DirectedAcyclicGraphTransitiveReducer <T> .ReduceGraph(graph); return(internalSolver.Solve(reducedGraph)); }
public ImmutableList <ImmutableHashSet <T> > Solve <T>(IDirectedAcyclicGraph <T> graph) where T : IEquatable <T> { if (graph.Count == 0) { return(ImmutableList <ImmutableHashSet <T> > .Empty); } var ordering = createTopologicalOrdering(graph); return(createLayers(graph, ordering, maxLayerSize)); }
// ReSharper disable once SuggestBaseTypeForParameter private static IEnumerable <T> getAllSuccessorsRecursively(IDirectedAcyclicGraph <T> graph, T start) { yield return(start); foreach (var child in graph.GetDirectSuccessorsOf(start)) { foreach (var descendant in getAllSuccessorsRecursively(graph, child)) { yield return(descendant); } } }
// ReSharper disable once SuggestBaseTypeForParameter private static IList <T> createTopologicalOrdering <T>(IDirectedAcyclicGraph <T> graph) where T : IEquatable <T> { // The topological ordering from low to high. var ordering = new List <T>(graph.Count); var elements = new HashSet <T>(graph.Elements); var orderedElementIndices = new Dictionary <T, int>(); while (ordering.Count < graph.Count) { // All the remaining elements which have all their predecessors added to the topological ordering // already. This includes elements which have no predecessors at all. This is equivalent to the set // of sources in a directed acyclic graph where all already elements and adjacent arrows have been // removed. var sources = elements.Where(allPredecessorsHaveBeenOrdered); // For each considered element, we look at the place of all their predecessors in the partial // topological ordering we have constructed so far. // * We first consider the most recently added predecessor of each element. That is, the predecessor // of said element that is the highest in the current partial topological ordering. // * Of all these predecessors, we pick the predecessor that is lowest in the ordering. If // there is only one element with said predecessor, that element is added to the ordering next. // * Ties are broken by looking at the next highest ordered predecessor of all tied elements. // * If all predecessors of 2 or more elements are the same, we pick the element with the least // predecessors. // This selection process is implemented by representing the predecessors of an element as a // decreasing number sequence of the predecessors' indices, and selecting the lowest of those in a // reflected lexicographic ordering. var next = sources.MinBy(createDecreasingNumberSequenceOfPredecessorIndices); orderedElementIndices.Add(next, ordering.Count); ordering.Add(next); elements.Remove(next); } return(ordering); bool allPredecessorsHaveBeenOrdered(T e) => graph.GetDirectPredecessorsOf(e) .All(n => orderedElementIndices.ContainsKey(n)); DecreasingNumberSequence createDecreasingNumberSequenceOfPredecessorIndices(T e) { var predecessorIndices = graph.GetDirectPredecessorsOf(e).Select(n => orderedElementIndices[n]); return(DecreasingNumberSequence.FromUnsortedNumbers(predecessorIndices)); } }
private static ImmutableList <ImmutableHashSet <T> > createLayers <T>( // ReSharper disable once SuggestBaseTypeForParameter IDirectedAcyclicGraph <T> graph, IList <T> ordering, int maxLayerSize) where T : IEquatable <T> { var layersReversed = new List <ImmutableHashSet <T> .Builder>(); var elementToLayer = new Dictionary <T, int>(); for (var i = ordering.Count - 1; i >= 0; i--) { // We fill in the layers from the last to the first (or bottom to top, if using the traditional // representation where parents go above their children). For each element, we always choose the // layer closest to the last (or lowest), such that all its successors are in a later (or lower) // layer, and the layer has less than W elements. // The list represents the layers in reverse order, meaning that the last layer of our result is the // first element in the list. This allows us to use the automatic growing property of the list. // Combining these two things, the following looks for the lowest integer k such that (1) each // successor is in a set with an index lower lower than k, and (2) the set at index k has less than // W elements. // Requirement (1) var highestSuccessorLevel = graph.GetDirectSuccessorsOf(ordering[i]) .Select(elmt => elementToLayer[elmt] + 1) .Aggregate(0, Math.Max); // Requirement (2) var candidateLayer = highestSuccessorLevel; while ( candidateLayer < layersReversed.Count && layersReversed[candidateLayer].Count == maxLayerSize) { candidateLayer++; } // Expand the list as needed while (candidateLayer >= layersReversed.Count) { layersReversed.Add(ImmutableHashSet.CreateBuilder <T>()); } // Add the element to the layer layersReversed[candidateLayer].Add(ordering[i]); elementToLayer.Add(ordering[i], candidateLayer); } return(ImmutableList.CreateRange(layersReversed.Select(b => b.ToImmutable()).Reverse())); }
public static IDirectedAcyclicGraph <T> ReduceGraph(IDirectedAcyclicGraph <T> graph) { var elementsEnumerable = graph.Elements; var elements = elementsEnumerable as IList <T> ?? elementsEnumerable.ToList(); var graphBuilder = DirectedGraphBuilder <T> .NewBuilder(); foreach (var element in elements) { graphBuilder.AddElement(element); } foreach (var element in elements) { foreach (var child in getOnlyChildrenWithoutIndirectPath(graph, element)) { graphBuilder.AddArrow(element, child); } } return(graphBuilder.CreateAcyclicGraphUnsafe()); }
private static IEnumerable <T> getOnlyChildrenWithoutIndirectPath(IDirectedAcyclicGraph <T> graph, T element) { var childrenEnumerable = graph.GetDirectSuccessorsOf(element); var children = childrenEnumerable as IList <T> ?? childrenEnumerable.ToList(); var reducedEdges = new HashSet <T>(children); foreach (var child in children) { foreach (var descendant in getAllSuccessorsRecursively(graph, child)) { if (descendant.Equals(child)) { continue; } // Every arrow to a successor of our direct child is already reachable. reducedEdges.Remove(descendant); } } return(reducedEdges); }