private HeapNode GetHeapNode(T oddDegreeNode, IEnumerable <T> oddDegreeNodes, Graph <T> graph) { var adsp = new AllDestinationShortestPaths <T>(oddDegreeNode, oddDegreeNodes, graph, (_, x) => x.Distance); adsp.RunWithNumTargets(_comparisonSetSize); var costDict = adsp.LowestCost; if (costDict.ContainsKey(oddDegreeNode)) { costDict.Remove(oddDegreeNode); } var costs = costDict.Values.ToList(); costs.Sort(); var firstChoiceSavings = costs.Count > 1 ? costs[0] - costs.Skip(1).Average() : 0; var firstChoice = costDict.First(kvp => Math.Abs(kvp.Value - costs.First()) < 1e-12).Key; var path = adsp.GetPath(oddDegreeNode, firstChoice); var heapNode = new LazyGraphAugmenter <T> .HeapNode { Cost = firstChoiceSavings, End = firstChoice, Path = path, Start = oddDegreeNode }; return(heapNode); }
public List <T> GetRoutes(T startingPlace, double maxDistance) { var potentialRoutes = new List <WeightedAdjacencyNode <T>[]>(); // step 1: get all destinations shortest path // get distance-based shortest paths // limit to 0.4 to 0.6 of total distance // get weight-based shortest paths to those nodes Func <T, WeightedAdjacencyNode <T>, double> basicWeightCostFunction = (_, x) => x.Weight; var adspDistance = new AllDestinationShortestPaths <T>(startingPlace, _graph.Neighbors.Keys, _graph, (_, x) => x.Distance); adspDistance.RunWithDistanceCap(maxDistance * MaxDistanceOutbound); var targets = adspDistance.LowestCost.Where(kvp => kvp.Value > maxDistance * MinDistanceOutbound).Select(k => k.Key); var adspWeight = new AllDestinationShortestPaths <T>(startingPlace, targets, _graph, basicWeightCostFunction); adspWeight.Run(); // step 2: for each destination, try to find a return trip. double weight any segment that was previously used. var currentMinDistance = double.MaxValue; List <T> fullPath = new List <T>(); foreach (var potentialDestinationKvp in adspWeight.LowestCost) { // early stop: return trip will always be more expensive, so quit if over half of total best cost. if (currentMinDistance * .5 < potentialDestinationKvp.Value) { break; } // double the weights var endPosition = potentialDestinationKvp.Key; var reversePaths = ReverseWeights(endPosition, adspWeight.TraversalPath); // get weight-based shortest path back var adspReversed = new AllDestinationShortestPaths <T>(endPosition, new[] { startingPlace }, _graph, GetCostFunction(basicWeightCostFunction, reversePaths)); adspReversed.Run(); var reversePathsReturnTrip = ReverseWeights(startingPlace, adspReversed.TraversalPath); var totalCost = potentialDestinationKvp.Value + adspReversed.LowestCost.Values.First(); if (currentMinDistance > totalCost) { currentMinDistance = totalCost; fullPath = adspWeight.GetPath(startingPlace, endPosition); fullPath.Reverse(); var t = adspReversed.GetPath(endPosition, startingPlace); t.Reverse(); fullPath.AddRange(t); } } Console.WriteLine(currentMinDistance); return(fullPath); }
public Graph <T> AugmentGraph(Graph <T> graph) { var extraWeight = 0.0; while (true) { if (!graph.OddDegreeNodes().Any()) { break; } var oddDegreeNodes = graph.OddDegreeNodes(); var startingNode = oddDegreeNodes.First(); var adsp = new AllDestinationShortestPaths <T>(startingNode, graph.OddDegreeNodes().Intersect(graph.MustHitVertices), graph, (_, x) => x.Distance); adsp.Run(); var paths = adsp.TraversalPath; var nodeOrder = adsp.LowestCost.OrderByDescending(kvp => kvp.Value).Select(kvp => kvp.Key); // find pairs of nodes on the shortest path tree var oddNodeLookup = new HashSet <T>(graph.OddDegreeNodes()); var usedNodes = new HashSet <T>(); foreach (var node in nodeOrder) { if (node.Equals(startingNode) || usedNodes.Contains(node)) { continue; } var endNode = paths[node]; var path = new List <T>(new[] { node, endNode }); while (!oddNodeLookup.Contains(endNode) && paths.ContainsKey(endNode)) { endNode = paths[endNode]; path.Add(endNode); } if (!usedNodes.Contains(node) && !usedNodes.Contains(endNode)) { // at this stage, node and endNode are a short path between odd nodes. usedNodes.Add(endNode); usedNodes.Add(node); extraWeight += graph.AddEdgeCardinality(path); } } } Console.WriteLine($"GreedyGraphAugmenter added {extraWeight}"); return(graph); }
/// <summary> /// Construct a set of vertices that are on the shortest path between any two nodes in the required node set. /// These may include non-required nodes. /// </summary> /// <param name="g"></param> /// <returns></returns> private HashSet <Node> GetPathsBetweenRequiredNodes(Graph <Node> g) { var fullVertexSet = new HashSet <Node>(g.MustHitVertices); foreach (var node in g.MustHitVertices) { var adsp = new AllDestinationShortestPaths <Node>(node, g.MustHitVertices, g, (_, x) => x.Distance); adsp.Run(); var traversalPaths = adsp.TraversalPath; foreach (var endNode in g.MustHitVertices) { var localNode = endNode; while (traversalPaths.ContainsKey(localNode) && localNode != node) { fullVertexSet.Add(localNode); localNode = traversalPaths[localNode]; } } } return(fullVertexSet); }