/// <summary> /// Merges the node x into the fixed target node into. /// /// Edges between these nodes, edges to x and related distance information is updated. /// /// x is marked as to be removed from the search space. If x was the start node, into /// will now be the start node. /// </summary> /// <returns>All neighbors of x before merging. These are the nodes that had their adjacency /// information changed.</returns> protected IEnumerable <int> MergeInto(int x, int into) { if (!NodeStates.IsFixedTarget(into)) { throw new ArgumentException("Nodes can only be merged into fixed target nodes", "into"); } _data.DistanceLookup.IndexToNode(into).MergeWith(_data.DistanceLookup.IndexToNode(x), _data.DistanceLookup.GetShortestPath(x, into)); _data.DistanceLookup.MergeInto(x, into); EdgeSet.Remove(x, into); var intoNeighbors = EdgeSet.NeighborsOf(into); var xNeighbors = EdgeSet.NeighborsOf(x); var neighbors = intoNeighbors.Union(xNeighbors); foreach (var neighbor in xNeighbors) { EdgeSet.Remove(x, neighbor); } foreach (var neighbor in neighbors) { EdgeSet.Add(into, neighbor, _data.DistanceLookup[into, neighbor]); } if (StartNodeIndex == x) { _data.StartNodeIndex = into; } NodeStates.MarkNodeAsRemoved(x); return(xNeighbors); }
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); }
private void RemoveNode(int index) { if (NodeStates.IsTarget(index)) { throw new ArgumentException("Target nodes can't be removed", "index"); } var neighbors = EdgeSet.NeighborsOf(index); switch (neighbors.Count) { case 0: break; case 1: EdgeSet.Remove(index, neighbors[0]); break; case 2: // Merge the two incident edges. var left = neighbors[0]; var right = neighbors[1]; var newWeight = EdgeSet[index, left].Weight + EdgeSet[index, right].Weight; EdgeSet.Remove(index, left); EdgeSet.Remove(index, right); if (newWeight <= DistanceLookup[left, right]) { EdgeSet.Add(left, right, newWeight); } break; default: throw new ArgumentException("Removing nodes with more than 2 neighbors is not supported", "index"); } NodeStates.MarkNodeAsRemoved(index); }
protected override int ExecuteTest() { var edges = new GraphEdge[SearchSpaceSize]; var removedNodes = 0; for (var i = 0; i < SearchSpaceSize; i++) { // This test only checks non-terminals. if (NodeStates.IsTarget(i)) { continue; } // Nodes with less than 3 neighbors are covered by DegreeTest // Nodes are limited to 7 neighbors because this test is exponential in the neighbor count. var neighbors = EdgeSet.NeighborsOf(i); if (neighbors.Count < 3 || neighbors.Count > 7) { continue; } // Cache the edges. They might be removed from EdgeSet when we need them. foreach (var neighbor in neighbors) { edges[neighbor] = EdgeSet[i, neighbor]; } // Check whether each subset satisfies the condition. var canBeRemoved = true; foreach (var subset in GetAllSubsets(neighbors)) { // Only subsets of at least size 3 are relevant. if (subset.Count < 3) { continue; } // Sum up the weights of all edges between the nodes of the subsets and i. var edgeSum = subset.Sum(j => edges[j].Weight); // Build the MST of the fully connected graph of the nodes in the subset with the bottleneck // Steiner distances as edge weights. var mst = new MinimalSpanningTree(subset, SMatrix); mst.Span(subset[0]); // Sum up the edge weights of the MST. var mstSum = mst.SpanningEdges.Sum(e => e.Priority); // The condition is only satisfied if edgeSum >= mstSum. if (edgeSum < mstSum) { canBeRemoved = false; break; } } if (!canBeRemoved) { continue; } // Remove i and replace its edges. foreach (var neighbor in neighbors) { // Remove the old edges between i and its neighbors. var edge = edges[neighbor]; EdgeSet.Remove(edge); // For each pair of neighbors create a new edge. foreach (var neighbor2 in neighbors) { if (neighbor >= neighbor2) { continue; } // The weight of the new edge between the two neighbors is the sum of their edge weights to i. var edge2 = edges[neighbor2]; var newEdgeWeight = edge.Weight + edge2.Weight; // Only add this edge if it wouldn't be removed by the Paths with many terminals test. if (newEdgeWeight <= SMatrix[neighbor, neighbor2]) { EdgeSet.Add(neighbor, neighbor2, newEdgeWeight); } } } NodeStates.MarkNodeAsRemoved(i); removedNodes++; } return(removedNodes); }