Пример #1
0
        private static IEnumerable<List<Edge>> AllTriangles(Graph graph)
        {
            List<Vertex> vertices = new List<Vertex>(graph.Vertices);
            vertices.Sort((x, y) => graph.GetDegree(x).CompareTo(graph.GetDegree(y)));
            vertices.Reverse(); // From highest to lowest
            Dictionary<Vertex, int> index = new Dictionary<Vertex, int>();
            Dictionary<Vertex, List<Vertex>> A = new Dictionary<Vertex, List<Vertex>>();

            for (int i = 0; i < vertices.Count; i++)
            {
                index.Add(vertices[i], i);
                A.Add(graph.Vertices[i], new List<Vertex>());
            }

            foreach (var firstVertex in vertices)
            {
                foreach (var firstEdge in graph.GetEdgesForVertex(firstVertex))
                {
                    var secondVertex = firstEdge.Other(firstVertex);
                    if (index[firstVertex] < index[secondVertex])
                    {
                        foreach (var thirdVertex in A[firstVertex].Intersect(A[secondVertex]))
                        {
                            var secondEdge =
                                graph.GetEdgesForVertex(firstVertex).Single(x => x.Other(firstVertex) == thirdVertex);
                            var thirdEdge =
                                graph.GetEdgesForVertex(secondVertex).Single(x => x.Other(secondVertex) == thirdVertex);
                            yield return new List<Edge>(new [] { firstEdge, secondEdge, thirdEdge });
                        }

                        A[secondVertex].Add(firstVertex);
                    }
                }
            }
        }
Пример #2
0
        /// <summary>
        /// Runs the Degree Test to reduce the graph.
        /// </summary>
        /// <param name="graph">The graph on which to run the test.</param>
        /// <returns>The reduced graph.</returns>
        public static ReductionResult RunTest(Graph graph)
        {
            // Use some simple rules to reduce the problem.
            // The rules are: - if a vertex v has degree 1 and is not part of the required nodes, remove the vertex.
            //                - if a vertex v has degree 1 and is part of the required nodes, the one edge it is
            //                  connected to has to be in the solution, and as a consequence, the node at the other side
            //                  is either a Steiner node or also required.
            //                - (not implemented yet) if a vertex v is required and has degree 2, and thus is connected to 2 edges,
            //                  namely e1 and e2, and cost(e1) < cost(e2) and e1 = (u, v) and u is
            //                  also a required vertex, then every solution must contain e1

            var result = new ReductionResult();

            // Remove leaves as long as there are any
            var leaves = graph.Vertices.Where(v => graph.GetDegree(v) == 1 && !graph.Terminals.Contains(v)).ToList();
            while (leaves.Count > 0)
            {
                foreach (var leaf in leaves)
                    graph.RemoveVertex(leaf);
                result.RemovedVertices.AddRange(leaves);
                leaves = graph.Vertices.Where(v => graph.GetDegree(v) == 1 && !graph.Terminals.Contains(v)).ToList();
            }

            // When a leaf is required, add the node on the other side of its one edge to required nodes
            var requiredLeaves = graph.Vertices.Where(v => graph.GetDegree(v) == 1 && graph.Terminals.Contains(v)).ToList();
            foreach (var requiredLeaf in requiredLeaves)
            {
                // Find the edge this leaf is connected to
                var alsoRequired = graph.GetEdgesForVertex(requiredLeaf).Single().Other(requiredLeaf);
                if (!graph.Terminals.Contains(alsoRequired))
                    graph.RequiredSteinerNodes.Add(alsoRequired);
            }

            return result;
        }
Пример #3
0
        public static int Dijkstra(Vertex from, Vertex to, Graph graph)
        {
            HashSet<Vertex> visited = new HashSet<Vertex>();
            Dictionary<Vertex, FibonacciHeap<int, Vertex>.Node> nodes = new Dictionary<Vertex, FibonacciHeap<int, Vertex>.Node>();
            FibonacciHeap<int, Vertex> labels = new FibonacciHeap<int, Vertex>();
            // Initialize labels.
            foreach (var vertex in graph.Vertices)
            {
                var n = labels.Add(vertex == from ? 0 : int.MaxValue, vertex);
                nodes.Add(vertex, n);
            }

            int currentLabel = int.MaxValue;
            while (!visited.Contains(to))
            {
                var currentNode = labels.ExtractMin();
                var current = currentNode.Value;
                currentLabel = currentNode.Key;

                // Consider all edges ending in unvisited neighbours
                var edges =
                    graph.GetEdgesForVertex(current).Where(x => !visited.Contains(x.Other(current)));
                // Update labels on the other end.
                foreach (var edge in edges)
                {
                    if (currentNode.Key + edge.Cost < nodes[edge.Other(current)].Key)
                        labels.DecreaseKey(nodes[edge.Other(current)], currentNode.Key + edge.Cost);
                }

                visited.Add(current);
            }

            return currentLabel;
        }
Пример #4
0
        public static Path DijkstraPath(Vertex from, Vertex to, Graph graph)
        {
            HashSet<Vertex> visited = new HashSet<Vertex>();
            Dictionary<Vertex, FibonacciHeap<int, Vertex>.Node> nodes =
                new Dictionary<Vertex, FibonacciHeap<int, Vertex>.Node>();
            Dictionary<Vertex, Edge> comingFrom = new Dictionary<Vertex, Edge>();
            FibonacciHeap<int, Vertex> labels = new FibonacciHeap<int, Vertex>();
            // Initialize labels.
            foreach (var vertex in graph.Vertices)
            {
                var n = labels.Add(vertex == from ? 0 : int.MaxValue, vertex);
                nodes.Add(vertex, n);
                comingFrom.Add(vertex, null);
            }

            while (!visited.Contains(to))
            {
                var currentNode = labels.ExtractMin();
                var current = currentNode.Value;

                // Consider all edges ending in unvisited neighbours
                var edges =
                    graph.GetEdgesForVertex(current).Where(x => !visited.Contains(x.Other(current)));
                // Update labels on the other end.
                foreach (var edge in edges)
                {
                    if (currentNode.Key + edge.Cost < nodes[edge.Other(current)].Key)
                    {
                        labels.DecreaseKey(nodes[edge.Other(current)], currentNode.Key + edge.Cost);
                        comingFrom[edge.Other(current)] = edge;
                    }
                }

                visited.Add(current);
            }

            // Now travel back, to find the actual path
            List<Edge> pathEdges = new List<Edge>();
            Vertex pathVertex = to;
            while (pathVertex != from)
            {
                pathEdges.Add(comingFrom[pathVertex]);
                pathVertex = comingFrom[pathVertex].Other(pathVertex);
            }

            pathEdges.Reverse();
            Path path = new Path(from);
            path.Edges.AddRange(pathEdges);
            return path;
        }
Пример #5
0
        private void ReconnectTerminals(Graph workingSolution, Graph problemInstance, Graph problemInstanceDistance)
        {
            Stopwatch reconnectStopwatch = new Stopwatch();
            reconnectStopwatch.Start();

            var components = workingSolution.CreateComponentTable();
            var componentsToConnect =
                components.Where(x => problemInstance.Terminals.Contains(x.Key))
                    .Select(x => x.Value)
                    .Distinct()
                    .ToList();

            if (componentsToConnect.Count <= 1) return;

            MultiDictionary<int, Tuple<Vertex, Vertex>> componentConnectingPathDictionary = new MultiDictionary<int, Tuple<Vertex, Vertex>>();
            List<Vertex> componentGraphVertices = new List<Vertex>();
            foreach (var i in componentsToConnect)
                componentGraphVertices.Add(new Vertex(i));
            Graph componentGraph = new Graph(componentGraphVertices);

            for (int i = 0; i < componentsToConnect.Count; i++)
            {
                int fromComponent = componentsToConnect[i];
                for (int j = i + 1; j < componentsToConnect.Count; j++)
                {
                    int toComponent = componentsToConnect[j];
                    int minDistance = int.MaxValue;

                    foreach (var fromVertex in components.Where(x => x.Value == fromComponent)
                                                         .Select(x => x.Key))
                    {
                        var distanceEdges = problemInstanceDistance.GetEdgesForVertex(fromVertex);
                        foreach (var toVertexEdge in distanceEdges)
                        {
                            var toVertex = toVertexEdge.Other(fromVertex);
                            if (components[toVertex] != toComponent)
                                continue;

                            int distance = toVertexEdge.Cost;
                            if (!componentConnectingPathDictionary.ContainsKey(fromComponent, toComponent))
                            {
                                componentConnectingPathDictionary.Add(fromComponent, toComponent, new Tuple<Vertex, Vertex>(fromVertex, toVertex));
                                componentGraph.AddEdge(new Edge(componentGraphVertices[i], componentGraphVertices[j], minDistance));
                            }

                            if (distance < minDistance)
                            {
                                minDistance = distance;
                                componentConnectingPathDictionary[fromComponent, toComponent] = new Tuple<Vertex, Vertex>(fromVertex, toVertex);
                                componentGraph.GetEdgesForVertex(componentGraphVertices[i])
                                    .Single(x => x.Other(componentGraphVertices[i]) == componentGraphVertices[j])
                                    .Cost = minDistance;
                            }
                        }
                    }
                }
            }
            componentGraph = Algorithms.Kruskal(componentGraph);
            foreach (var edge in componentGraph.Edges)
            {
                var v1 = edge.Either();
                var v2 = edge.Other(v1);
                var vertices = componentConnectingPathDictionary[v1.VertexName, v2.VertexName];
                var path = Algorithms.DijkstraPath(vertices.Item1, vertices.Item2, problemInstance);
                foreach (var pathEdge in path.Edges)
                    workingSolution.AddEdge(pathEdge);
            }

            reconnectStopwatch.Stop();
        }
Пример #6
0
        /// <summary>
        /// Runs the Special Distance Test to reduce the graph.
        /// </summary>
        /// <param name="graph">The graph on which to run the test.</param>
        /// <returns>The reduced graph.</returns>
        public static ReductionResult RunTest(Graph graph)
        {
            List<Edge> redundant = new List<Edge>();
            var result = new ReductionResult();

            var distanceGraph = graph.CreateDistanceGraph();
            var specialDistanceGraph = CreateInitialSpecialDistanceGraph(graph);
            for (int i = 0; i < graph.NumberOfVertices; i++)
            {
                Console.Write("SD Test: {0}/{1}   \r", i, graph.NumberOfVertices);
                // Step 1. L := { i }
                //         for all j set delta_j = d_ij
                // In each iteration, delta_j means the current special distance from the start vertex to j.
                var vFrom = graph.Vertices[i];
                List<Vertex> L = new List<Vertex>(new [] { vFrom }); // Initially only the start veretx is labeled.
                // Initially set special distance equal to distance
                foreach (var edge in specialDistanceGraph.GetEdgesForVertex(vFrom))
                {
                    edge.Cost =
                        distanceGraph.GetEdgesForVertex(vFrom).Single(x => x.Other(vFrom) == edge.Other(vFrom)).Cost;
                    //edge.Cost = Math.Min(edge.Cost,
                    //    distanceGraph.GetEdgesForVertex(vFrom).Single(x => x.Other(vFrom) == edge.Other(vFrom)).Cost);
                }

                List<Vertex> unhandledTerminals = null; // K \ L
                while ((unhandledTerminals = graph.Terminals.Where(x => !L.Contains(x)).ToList()).Count > 0)
                { // While K \ L is not empty
                    // Find the terminal which minimizes delta(j) for all j in K \ L
                    int currentMinimum = int.MaxValue;
                    Vertex k = null;
                    var edgesFrom = specialDistanceGraph.GetEdgesForVertex(vFrom);
                    foreach (var terminal in unhandledTerminals)
                    {
                        var deltaEdge = edgesFrom.First(x => x.Other(vFrom) == terminal);
                        if (deltaEdge.Cost < currentMinimum)
                        {
                            currentMinimum = deltaEdge.Cost;
                            k = terminal;
                        }
                    }

                    L.Add(k);

                    // Re-lable all vertices that haven't gotten a definitive label yet.
                    var delta_k = edgesFrom.First(x => x.Other(vFrom) == k).Cost;
                    foreach (var unlabeled in graph.Vertices.Where(x => !L.Contains(x)))
                    {
                        var d_kj = distanceGraph.GetEdgesForVertex(k).First(x => x.Other(k) == unlabeled).Cost;
                        var deltaEdge = edgesFrom.First(x => x.Other(vFrom) == unlabeled);
                        deltaEdge.Cost = Math.Min(deltaEdge.Cost, Math.Max(delta_k, d_kj));
                    }
                }

                var specialEdges = specialDistanceGraph.GetEdgesForVertex(vFrom);
                var distanceEdges = distanceGraph.GetEdgesForVertex(vFrom);
                var edges = graph.GetEdgesForVertex(vFrom);
                foreach (var redundantEdge in specialEdges.Where(x => x.Cost < distanceEdges.First(y => y.Other(vFrom) == x.Other(vFrom)).Cost))
                {
                    // Special distance is smaller than distance. Edge is redundant.
                    var edge = edges.FirstOrDefault(x => x.Other(vFrom) == redundantEdge.Other(vFrom));
                    if (edge != null)
                        redundant.Add(edge);
                }
            }

            foreach (var edge in redundant)
            {
                graph.RemoveEdge(edge);
                result.RemovedEdges.Add(edge);
            }

            Console.Write("                                  \r");

            return result;
        }
Пример #7
0
        public static Dictionary<Vertex, Path> DijkstraPathToAll(Vertex from, Graph graph, bool onlyTerminals)
        {
            Dictionary<Vertex, Edge> comingFrom = new Dictionary<Vertex, Edge>();
            HashSet<Vertex> visited = new HashSet<Vertex>();
            Dictionary<Vertex, FibonacciHeap<int, Vertex>.Node> nodes =
                new Dictionary<Vertex, FibonacciHeap<int, Vertex>.Node>();
            Dictionary<Vertex, Path> paths = new Dictionary<Vertex, Path>();
            FibonacciHeap<int, Vertex> labels = new FibonacciHeap<int, Vertex>();
            // Initialize labels.
            foreach (var vertex in graph.Vertices)
            {
                var n = labels.Add(vertex == from ? 0 : int.MaxValue, vertex);
                nodes.Add(vertex, n);
            }

            while (paths.Count < (onlyTerminals ? graph.Terminals.Count - 1 : graph.NumberOfVertices - 1))
            {
                var currentNode = labels.ExtractMin();
                var current = currentNode.Value;

                // Consider all edges ending in unvisited neighbours
                var edges =
                    graph.GetEdgesForVertex(current).Where(x => !visited.Contains(x.Other(current)));
                // Update labels on the other end.
                foreach (var edge in edges)
                {
                    if (currentNode.Key + edge.Cost < nodes[edge.Other(current)].Key)
                    {
                        labels.DecreaseKey(nodes[edge.Other(current)], currentNode.Key + edge.Cost);
                        comingFrom[edge.Other(current)] = edge;
                    }
                }

                visited.Add(current);
                if (current != from && (!onlyTerminals || graph.Terminals.Contains(current)))
                {
                    // Travel back the path
                    List<Edge> pathEdges = new List<Edge>();
                    Vertex pathVertex = current;
                    while (pathVertex != from)
                    {
                        pathEdges.Add(comingFrom[pathVertex]);
                        pathVertex = comingFrom[pathVertex].Other(pathVertex);
                    }

                    pathEdges.Reverse();
                    Path path = new Path(from);
                    path.Edges.AddRange(pathEdges);
                    paths[current] = path;
                }
            }

            return paths;
        }
Пример #8
0
        private static bool ExistsPath(Vertex v1, Vertex v2, Graph graph, List<Vertex> visited)
        {
            foreach (var edge in graph.GetEdgesForVertex(v1))
            {
                if (edge.Other(v1) == v2)
                {
                    return true;
                }

                if (!visited.Contains(edge.Other(v1)))
                {
                    var visited2 = visited.ToList();
                    visited2.Add(v1);
                    if (ExistsPath(edge.Other(v1), v2, graph, visited2))
                        return true;
                }
            }
            return false;
        }
Пример #9
0
        public static List<Path> NearestTerminals(Vertex from, Graph graph, int n)
        {
            List<Path> foundPaths = new List<Path>();
            HashSet<Vertex> visited = new HashSet<Vertex>();
            Dictionary<Vertex, FibonacciHeap<int, Vertex>.Node> nodes = new Dictionary<Vertex, FibonacciHeap<int, Vertex>.Node>();
            FibonacciHeap<int, Vertex> labels = new FibonacciHeap<int, Vertex>();
            Dictionary<Vertex, Edge> comingFrom = new Dictionary<Vertex, Edge>();

            if (graph.Terminals.Contains(from))
                foundPaths.Add(new Path(from));

            // Initialize labels.
            foreach (var vertex in graph.Vertices)
            {
                var node = labels.Add(vertex == from ? 0 : int.MaxValue, vertex);
                nodes.Add(vertex, node);
                comingFrom.Add(vertex, null);
            }

            while (!labels.IsEmpty() && foundPaths.Count < n)
            {
                var currentNode = labels.ExtractMin();
                var current = currentNode.Value;

                // Consider all edges ending in unvisited neighbours
                var edges =
                    graph.GetEdgesForVertex(current).Where(x => !visited.Contains(x.Other(current)));
                // Update labels on the other end.
                foreach (var edge in edges)
                {
                    if (currentNode.Key + edge.Cost < nodes[edge.Other(current)].Key)
                    {
                        labels.DecreaseKey(nodes[edge.Other(current)], currentNode.Key + edge.Cost);
                        comingFrom[edge.Other(current)] = edge;
                    }
                }

                visited.Add(current);

                if (graph.Terminals.Contains(current) && current != from)
                {
                    // Now travel back, to find the actual path
                    List<Edge> pathEdges = new List<Edge>();
                    Vertex pathVertex = current;
                    while (pathVertex != from)
                    {
                        pathEdges.Add(comingFrom[pathVertex]);
                        pathVertex = comingFrom[pathVertex].Other(pathVertex);
                    }

                    pathEdges.Reverse();
                    Path path = new Path(from);
                    path.Edges.AddRange(pathEdges);
                    foundPaths.Add(path);
                }
            }

            return foundPaths;
        }
Пример #10
0
        private static Dictionary<int, double> CalculateComponentFunctionValues(Graph graph,
            Dictionary<Vertex, int> components)
        {
            Dictionary<int, double> fValues = new Dictionary<int, double>();
            foreach (var componentNumber in components.Values.Distinct())
            {
                //Get all edges in this component
                HashSet<Edge> edgesInComponent = new HashSet<Edge>();
                var verticesInComponent = components.Where(x => x.Value == componentNumber).Select(x => x.Key);
                var nrOfTerminals = verticesInComponent.Count(x => graph.Terminals.Contains(x));
                foreach (var v in verticesInComponent)
                {
                    foreach (var e in graph.GetEdgesForVertex(v))
                        edgesInComponent.Add(e);
                }

                if (edgesInComponent.Count > 0)
                {
                    double fVal = edgesInComponent.Sum(x => x.Cost)/Math.Pow(nrOfTerminals, 2);
                    fValues.Add(componentNumber, fVal);
                }
                else
                    fValues.Add(componentNumber, double.MaxValue);
            }
            return fValues;
        }
Пример #11
0
        public static Graph RunSolver(Graph graph)
        {
            var solution = new Graph(graph.Vertices);

            DijkstraState state = new DijkstraState();
            // Create the states needed for every execution of the Dijkstra algorithm
            foreach (var terminal in graph.Terminals)
                state.AddVertexToInterleavingDijkstra(terminal, graph);

            // Initialize
            Vertex currentVertex = state.GetNextVertex();
            FibonacciHeap<int, Vertex> labels = state.GetLabelsFibonacciHeap();
            HashSet<Vertex> visited = state.GetVisitedHashSet();
            Dictionary<Vertex, Path> paths = state.GetPathsFound();
            Dictionary<Vertex, FibonacciHeap<int, Vertex>.Node> nodes = state.GetNodeMapping();
            Dictionary<Vertex, Edge> comingFrom = state.GetComingFromDictionary();

            Dictionary<Vertex, int> components = solution.CreateComponentTable();
            Dictionary<Vertex, double> terminalFValues = CreateInitialFValuesTable(graph);

            int maxLoopsNeeded = graph.Terminals.Count * graph.NumberOfVertices;
            int loopsDone = 0;
            int updateInterval = 100;

            int longestPath = graph.Terminals.Max(x => Algorithms.DijkstraToAll(x, graph).Max(y => y.Value));

            while (state.GetLowestLabelVertex() != null)
            {
                if (loopsDone % updateInterval == 0)
                    Console.Write("\rRunning IDA... {0:0.0}%                           \r", 100.0 * loopsDone / maxLoopsNeeded);
                loopsDone++;

                if (state.GetLowestLabelVertex() != currentVertex)
                {
                    // Interleave. Switch to the Dijkstra procedure of the vertex which currently has the lowest distance.
                    state.SetLabelsFibonacciHeap(labels);
                    state.SetVisitedHashSet(visited);
                    state.SetPathsFound(paths);
                    state.SetComingFromDictionary(comingFrom);

                    currentVertex = state.GetNextVertex();
                    labels = state.GetLabelsFibonacciHeap();
                    visited = state.GetVisitedHashSet();
                    paths = state.GetPathsFound();
                    nodes = state.GetNodeMapping();
                    comingFrom = state.GetComingFromDictionary();
                }

                // Do one loop in Dijkstra algorithm
                var currentNode = labels.ExtractMin();
                var current = currentNode.Value;

                if (currentNode.Key > longestPath / 2)
                    break; //Travelled across the half of longest distance. No use in going further.

                // Consider all edges ending in unvisited neighbours
                var edges = graph.GetEdgesForVertex(current).Where(x => !visited.Contains(x.Other(current)));
                // Update labels on the other end
                foreach (var edge in edges)
                {
                    if (currentNode.Key + edge.Cost < nodes[edge.Other(current)].Key)
                    {
                        labels.DecreaseKey(nodes[edge.Other(current)], currentNode.Key + edge.Cost);
                        comingFrom[edge.Other(current)] = edge;
                    }
                }

                visited.Add(current);
                if (current != currentVertex)
                {
                    // Travel back the new path
                    List<Edge> pathEdges = new List<Edge>();
                    Vertex pathVertex = current;
                    while (pathVertex != currentVertex)
                    {
                        pathEdges.Add(comingFrom[pathVertex]);
                        pathVertex = comingFrom[pathVertex].Other(pathVertex);
                    }

                    pathEdges.Reverse();
                    Path path = new Path(currentVertex);
                    path.Edges.AddRange(pathEdges);
                    paths[current] = path;
                }

                // Find matching endpoints from two different terminals
                var mutualEnd = state.FindPathsEndingInThisVertex(current);
                if (mutualEnd.Count() > 1)
                {
                    var terminals = mutualEnd.Select(x => x.Start).ToList();

                    // Step 1. Calculate new heuristic function value for this shared point.
                    // f(x) = (Cost^2)/(NumberOfTerminals^3)
                    var f1 = Math.Pow(mutualEnd.Sum(p => p.TotalCost), 2) / Math.Pow(terminals.Count, 3);
                    var f2 = Math.Pow(mutualEnd.Sum(p => p.TotalCost), 1) / Math.Pow(terminals.Count, 2);
                    var f3 = Math.Pow(mutualEnd.Sum(p => p.TotalCost), 3) / Math.Pow(terminals.Count, 2);
                    var terminalsAvgF = terminals.Select(x => terminalFValues[x]).Average();
                    var terminalsMinF = terminals.Select(x => terminalFValues[x]).Min();
                    var f = (new[] { f1, f2, f3 }).Max();
                    Debug.WriteLine("F value: {0}, Fmin: {3} - Connecting terminals: {1} via {2}", f, string.Join(", ", terminals.Select(x => x.VertexName)), current.VertexName, terminalsMinF);

                    // Do not proceed if f > avgF AND working in same component
                    if (terminals.Select(x => components[x]).Distinct().Count() == 1 && f > terminalsMinF)
                        continue;

                    Debug.WriteLine("Proceeding with connection...");

                    // Step 2. Disconnect terminals in mutual component.
                    foreach (var group in terminals.GroupBy(x => components[x]))
                    {
                        if (group.Count() <= 1)
                            continue;

                        HashSet<Edge> remove = new HashSet<Edge>();
                        var sameComponentTerminals = group.ToList();
                        for (int i = 0; i < sameComponentTerminals.Count-1; i++)
                        {
                            for (int j = i+1; j< sameComponentTerminals.Count; j++)
                            {
                                var removePath = Algorithms.DijkstraPath(sameComponentTerminals[i], sameComponentTerminals[j], solution);
                                foreach (var e in removePath.Edges)
                                    remove.Add(e);
                            }
                        }

                        foreach (var e in remove)
                            solution.RemoveEdge(e, false);
                    }

                    components = solution.CreateComponentTable();

                    // Step 3. Reconnect all now disconnected terminals via shared endpoint
                    foreach (var t in terminals)
                    {
                        var path = Algorithms.DijkstraPath(t, current, graph);
                        foreach (var edge in path.Edges)
                            solution.AddEdge(edge);
                        // Update f value
                        terminalFValues[t] = f;
                    }

                    components = solution.CreateComponentTable();
                }
            }

            // If this solution is connected, take MST
            if (graph.Terminals.Select(x => components[x]).Distinct().Count() == 1)
            {
                // Clean up!
                foreach (var vertex in solution.Vertices.Where(x => solution.GetDegree(x) == 0).ToList())
                    solution.RemoveVertex(vertex);

                int componentNumber = graph.Terminals.Select(x => components[x]).Distinct().Single();
                foreach (var vertex in components.Where(x => x.Value != componentNumber).Select(x => x.Key).ToList())
                    solution.RemoveVertex(vertex);

                solution = Algorithms.Kruskal(solution);
                return solution;
            }

            // If the solution is not connected, it is not a good solution.
            return null;
        }