// Call with `paths` being a family of paths from startVertex to some end point. This family should // be vertex-disjoint and maximal. // Returns a cut. public static List <T> FindCutVDP <T>(this IGraphEdges <T> graph, T startVertex, List <List <T> > paths) { var usedEdges = new HashSet <Tuple <T, T> >(); foreach (var path in paths) { for (int i = 0; i < path.Count() - 1; ++i) { usedEdges.Add(Tuple.Create(path[i], path[i + 1])); } } var accessible = new HashSet <T>(from pair in graph.AccessibleVerticesVDP(startVertex, usedEdges) select pair.Item1); var cut = new HashSet <T>( from vertex in accessible from v in graph.GetNeighbours(vertex) where !accessible.Contains(v) select vertex); if (cut.Remove(startVertex)) { foreach (var v in graph.GetNeighbours(startVertex)) { if (!accessible.Contains(v)) { cut.Add(v); } } } return(new List <T>(cut)); }
// Depth first search for path from `from` and to `to`. // Returns an empty List if no path. public static List <T> FindPath <T>(this IGraphEdges <T> graph, T startVertex, T to) { if (Eq(startVertex, to)) { var singlePath = new List <T>(); singlePath.Add(startVertex); return(singlePath); } // Dictionary to store vertices we can reach and the vertex we visited before. var lookup = new Dictionary <T, T>(); // Stack of vertices to explore next var toVisit = new Stack <Tuple <T, T> >(); var willVisit = new HashSet <T>(); foreach (T vertex in graph.GetNeighbours(startVertex)) { toVisit.Push(Tuple.Create(vertex, startVertex)); willVisit.Add(vertex); } while (toVisit.Count() > 0) { Tuple <T, T> PairWhereFrom = toVisit.Pop(); T vertex = PairWhereFrom.Item1; T vertexFrom = PairWhereFrom.Item2; lookup[vertex] = vertexFrom; willVisit.Remove(vertex); if (Eq(vertex, to)) { break; // Found the end point, so can end } foreach (T v in graph.GetNeighbours(vertex)) { if (!lookup.ContainsKey(v) && !willVisit.Contains(v)) { toVisit.Push(Tuple.Create(v, vertex)); willVisit.Add(v); } } } // Extract path from `to` to `startVertex`, working backwards var path = new List <T>(); if (!lookup.ContainsKey(to)) { return(path); } var currentVertex = to; while (!Eq(currentVertex, startVertex)) { path.Add(currentVertex); currentVertex = lookup[currentVertex]; } path.Add(startVertex); path.Reverse(); return(path); }
/** Finding vertex-disjoint paths * First version uses an auxiliary directed graph, and the edge-disjoint path finding from above. **/ // Find maximal set of vertex disjoint paths: returns the set of edges used. public static HashSet <Tuple <T, T> > VertexDisjointPaths <T>(this IGraphEdges <T> graph, T startVertex, T to) { if (Eq(startVertex, to) || graph.GetNeighbours(startVertex).Contains(to)) { return(new HashSet <Tuple <T, T> >()); } // So `startVertex` and `to` are not the same, and not neighbours // Build new directed graph; (t,0) is the "in" vertex for t, and (t,1) the "out" vertex. var transformed = new DirectedGraph <Tuple <T, int> >(); // Make "vertex edges", except start/finish foreach (var v in graph.DepthFirstSearch(startVertex)) { if (!Eq(startVertex, v) && !Eq(to, v)) { transformed.AddEdge(Tuple.Create(v, 0), Tuple.Create(v, 1)); } } // Link them all up foreach (var vout in graph.DepthFirstSearch(startVertex)) { foreach (var vin in graph.GetNeighbours(vout)) { transformed.AddEdge(Tuple.Create(vout, 1), Tuple.Create(vin, 0)); } } var transUsedEdges = transformed.EdgeDisjointPaths(Tuple.Create(startVertex, 1), Tuple.Create(to, 0)); // Check foreach (var edge in transUsedEdges) { if (Eq(edge.Item1.Item1, edge.Item2.Item1)) { if (edge.Item1.Item2 != 0 || edge.Item2.Item2 != 1) { throw new Exception(String.Format("Backwards edge: {0}", edge)); } } } return(new HashSet <Tuple <T, T> >( from edge in transUsedEdges where !Eq(edge.Item1.Item1, edge.Item2.Item1) select Tuple.Create(edge.Item1.Item1, edge.Item2.Item1))); }
/** Find edge-disjoint paths * Uses variant of Ford-Fulkerson algorithm. * See http://matthewdaws.github.io/Paths.html **/ // Actually does the work, and returns a Set of directed edges which form the paths private static HashSet <Tuple <T, T> > EdgeDisjointPaths <T>(this IGraphEdges <T> graph, T startVertex, T to) { // We store the current paths as simply a list of edges used. var currentPaths = new HashSet <Tuple <T, T> >(); if (Eq(startVertex, to)) { return(currentPaths); } var edges = new List <Tuple <T, T> >(); foreach (T vertex in graph.DepthFirstSearch(startVertex)) { edges.Add(from end in graph.GetNeighbours(vertex) select Tuple.Create(vertex, end)); } // Now apply the FF algorithm // Rather than build a new residual graph each time, keep a copy and update var residual = new DirectedGraph <T>(); residual.AddEdges(edges); while (true) { var newPath = residual.FindPath(startVertex, to); if (newPath.Count() == 0) { return(currentPaths); // End, so return number of paths found } // Now merge new paths into both residual graph and collection of used edges for (int index = 0; index < newPath.Count() - 1; ++index) { var edge = Tuple.Create(newPath[index], newPath[index + 1]); var revEdge = Tuple.Create(newPath[index + 1], newPath[index]); if (currentPaths.Contains(revEdge)) { currentPaths.Remove(revEdge); foreach (var e in graph.AsDirectedEdges(edge.Item1, edge.Item2)) { residual.RemoveEdge(e.Item1, e.Item2); } foreach (var e in graph.AsDirectedEdges(revEdge.Item1, revEdge.Item2)) { residual.AddEdge(e.Item1, e.Item2); } } else { currentPaths.Add(edge); foreach (var e in graph.AsDirectedEdges(edge.Item1, edge.Item2)) { residual.RemoveEdge(e.Item1, e.Item2); } residual.AddEdge(revEdge.Item1, revEdge.Item2); } } } }
/** Finding vertex-disjoint paths * Second version implements the algorithm directly without building residual graphs etc. **/ // Main algorithm for Vertex-Disjoint Path finding. // Call with `usedEdges` being a set of vertex-disjoint paths. // Yield Returns pairs (vertex, parent) where vertex is any `vertex` accessible from startVertex, and `parent` // is the vertex before. As a special case yields (startVertex, default(T)). // For variety, this use a breadth-first search. // The only reason to ever re-visit a vertex `a` is if: // - Before we came along an edge `b`-->`a` which is not on a reversed path // - Now we are coming `c`-->`a` which is on a reversed path // - A better way would be to log when we've visited `a` in a "full" capacity. internal static IEnumerable <Tuple <T, T> > AccessibleVerticesVDP <T>(this IGraphEdges <T> graph, T startVertex, HashSet <Tuple <T, T> > usedEdges) { // All vertices on a path, except startVertex var verticesOnPaths = new Dictionary <T, bool>(); var predOnPaths = new Dictionary <T, T>(); foreach (var edge in usedEdges) { verticesOnPaths[edge.Item2] = false; predOnPaths[edge.Item2] = edge.Item1; } var toVisit = new Queue <Tuple <T, T> >(); var visited = new HashSet <T>(); toVisit.Enqueue(Tuple.Create(startVertex, default(T))); visited.Add(startVertex); while (toVisit.Count() > 0) { var vertexPair = toVisit.Dequeue(); var vertex = vertexPair.Item1; var parent = vertexPair.Item2; yield return(vertexPair); // Where can we go next? if (verticesOnPaths.ContainsKey(vertex)) { // On path; where can we go to following path backwards? Allow us to revisit if // haven't been to this vertex "from the path" before. T next = predOnPaths[vertex]; if ((!Eq(next, startVertex) && verticesOnPaths[next] == false) || !visited.Contains(next)) { toVisit.Enqueue(Tuple.Create(next, vertex)); visited.Add(next); verticesOnPaths[next] = true; // Will now visit `next` from path, so don't do this again! } if (!usedEdges.Contains(Tuple.Create(vertex, parent))) { continue; // Didn't come from path, so no other options. } } // So "normal": look at neighbours foreach (T nhbr in graph.GetNeighbours(vertex)) { if (!visited.Contains(nhbr) && !usedEdges.Contains(Tuple.Create(vertex, nhbr))) { toVisit.Enqueue(Tuple.Create(nhbr, vertex)); visited.Add(nhbr); } } } }
// Pass in a maximal set of edge-disjoint paths from `startVertex` to some end point // Returns a cut: a minimal list of (directed) edges to delete to disconnect `startVertex` from the end point. public static HashSet <Tuple <T, T> > FindCutEDP <T>(this IGraphEdges <T> graph, T startVertex, HashSet <Tuple <T, T> > pathEdges) { var accessible = new HashSet <T>( from pair in graph.AccessibleVerticesEDP(startVertex, pathEdges) select pair.Item1); return(new HashSet <Tuple <T, T> >( from start in accessible from end in graph.GetNeighbours(start) where !accessible.Contains(end) select Tuple.Create(start, end))); }
// Perform a depth-first search from `startVertex` static public IEnumerable <T> DepthFirstSearch <T>(this IGraphEdges <T> graph, T startVertex) { var vertices = new HashSet <T>(); // Vertices we've visited already or are on the stack. var stack = new Stack <T>(); stack.Push(startVertex); vertices.Add(startVertex); while (stack.Count() > 0) { T currentVertex = stack.Pop(); yield return(currentVertex); foreach (T v in graph.GetNeighbours(currentVertex)) { if (!vertices.Contains(v)) { stack.Push(v); vertices.Add(v); } } } }
/** Second implementation, using a direct implementation of the "residual graph" **/ // Pass in a set of directed edges which form edge-disjoint paths from `startVertex` to end. // Yield returns pairs (vertex, parent) when walking the "residual" graph. // Special case: (startVertex, default(T)) public static IEnumerable <Tuple <T, T> > AccessibleVerticesEDP <T>(this IGraphEdges <T> graph, T startVertex, HashSet <Tuple <T, T> > pathEdges) { // Pairs (next, parent) var toVisit = new Stack <Tuple <T, T> >(); toVisit.Push(Tuple.Create(startVertex, default(T))); var visited = new HashSet <T>(); visited.Add(startVertex); while (toVisit.Count() > 0) { var vertexPair = toVisit.Pop(); var vertex = vertexPair.Item1; yield return(vertexPair); // Check neighbours; can't walk on current path foreach (var v in graph.GetNeighbours(vertex)) { var edge = Tuple.Create(vertex, v); if (!visited.Contains(v) && !pathEdges.Contains(edge)) { toVisit.Push(Tuple.Create(v, vertex)); visited.Add(v); } } // In the _directed_ case need to also consider that we can walk "backwards" on pathEdges // so need to search. This is slow... foreach (var edge in pathEdges) { if (Eq(vertex, edge.Item2) && !visited.Contains(edge.Item1)) { toVisit.Push(Tuple.Create(edge.Item1, vertex)); visited.Add(edge.Item1); } } } }