protected override int ExecuteTest() { if (NodeStates.FixedTargetNodeCount <= 1 || NodeStates.VariableTargetNodeCount > 0) { return(0); } var removedNodes = 0; var mst = new MinimalSpanningTree(NodeStates.FixedTargetNodeIndices.ToList(), DistanceLookup); mst.Span(StartNodeIndex); var maxEdgeDistance = mst.SpanningEdges.Max(e => e.Priority); for (var i = 0; i < SearchSpaceSize; i++) { if (NodeStates.IsTarget(i) || NodeStates.IsRemoved(i)) { continue; } // Theoretically, this can be sped up by using Voronoi partitions. The Voronoi base of i is the // terminal with the smallest distance to i by definition, so only the distance to that terminal // has to be checked. if (NodeStates.FixedTargetNodeIndices.All(t => DistanceLookup[i, t] >= maxEdgeDistance)) { EdgeSet.EdgesOf(i).ForEach(EdgeSet.Remove); NodeStates.MarkNodeAsRemoved(i); removedNodes++; } } return(removedNodes); }
/// <summary> /// Removes all nodes that were marked as removed from the search space and updates the distance lookup and the edge set. /// </summary> private void ContractSearchSpace() { var distanceLookup = _data.DistanceLookup; // Save all edges by the GraphNodes they are connecting. (the indices are worthless after the search space is contracted) var edges = _data.EdgeSet.Select( e => Tuple.Create(distanceLookup.IndexToNode(e.N1), distanceLookup.IndexToNode(e.N2), e.Weight)) .ToList(); // Contract the search space and update _distanceLookup. _nodeStates.SearchSpace = distanceLookup.RemoveNodes(_nodeStates.SearchSpace.Where(n => _nodeStates.IsRemoved(n.DistancesIndex))); // Add all edges back. _data.EdgeSet = new GraphEdgeSet(distanceLookup.CacheSize); foreach (var tuple in edges) { _data.EdgeSet.Add(tuple.Item1.DistancesIndex, tuple.Item2.DistancesIndex, tuple.Item3); } }