예제 #1
0
        /// <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);
        }
예제 #2
0
        protected override int ExecuteTest()
        {
            // The test only makes sense with at least 2 terminals.
            if (NodeStates.FixedTargetNodeCount <= 1)
            {
                return(0);
            }

            var removedNodes = 0;
            var untested     = new HashSet <int>(NodeStates.FixedTargetNodeIndices);

            // For each terminal z with degree of at least 2
            while (untested.Any())
            {
                var z = untested.First();
                untested.Remove(z);

                var neighbors = EdgeSet.NeighborsOf(z);
                if (neighbors.Count < 2)
                {
                    continue;
                }

                // Determine the shortest and second shortest edge incident to z.
                // For the second shortest, only the weight is of interest.
                var tuple                = ShortestTwoEdgesOf(EdgeSet.EdgesOf(z));
                var shortest             = tuple.Item1;
                var secondShortestWeight = tuple.Item2;
                // v is the node which is connected to z via the shortest edge.
                var v = shortest.N1 == z ? shortest.N2 : shortest.N1;

                // The shortest edge belongs to at least one Steiner minimal tree, if
                // secondShortestWeight >= shortest.Weight + distance(v, y) for any terminal y, y != z
                var canBeContracted = NodeStates.FixedTargetNodeIndices
                                      .Where(y => z != y)
                                      .Any(y => secondShortestWeight >= shortest.Weight + DistanceLookup[v, y]);
                // If such a y exists, we can merge v into z.
                if (canBeContracted)
                {
                    // z was changed and can be tested again.
                    untested.Add(z);
                    // v no longer exists and as such must not be tested.
                    untested.Remove(v);
                    MergeInto(v, z);
                    removedNodes++;
                }
            }
            return(removedNodes);
        }
예제 #3
0
        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);
        }
예제 #5
0
        protected override int ExecuteTest()
        {
            var removedNodes = 0;

            var untested       = new HashSet <int>(Enumerable.Range(0, SearchSpaceSize));
            var dependentNodes = new Dictionary <int, List <int> >();

            while (untested.Any())
            {
                var i = untested.First();
                untested.Remove(i);

                var neighbors = EdgeSet.NeighborsOf(i);

                if (!NodeStates.IsTarget(i))
                {
                    if (neighbors.Count == 1)
                    {
                        // Non target nodes with one neighbor can be removed.
                        untested.Add(neighbors[0]);
                        RemoveNode(i);
                        removedNodes++;
                    }
                    else if (neighbors.Count == 2)
                    {
                        // Non target nodes with two neighbors can be removed and their neighbors
                        // connected directly.
                        untested.Add(neighbors[0]);
                        untested.Add(neighbors[1]);
                        RemoveNode(i);
                        removedNodes++;
                    }
                }
                else if (NodeStates.IsFixedTarget(i))
                {
                    if (neighbors.Count == 1)
                    {
                        var other = neighbors[0];
                        if (EdgeSet.NeighborsOf(other).Count > 2 || NodeStates.IsTarget(other))
                        {
                            // Fixed target nodes with one neighbor can be merged with their neighbor since
                            // it must always be taken.
                            untested.Add(i);
                            untested.Remove(other);
                            untested.UnionWith(MergeInto(other, i));
                            removedNodes++;
                        }
                        else
                        {
                            // Node can only be merged once other has been processed. Other might be a dead end.
                            Debug.Assert(untested.Contains(other));
                            dependentNodes.Add(other, i);
                        }
                    }
                    else if (neighbors.Count > 1)
                    {
                        // Edges from one target node to another that are of minimum cost among the edges of
                        // one of the nodes can be in any optimal solution. Therefore both target nodes can be merged.
                        var minimumEdgeCost        = neighbors.Min(other => DistanceLookup[i, other]);
                        var minimumTargetNeighbors =
                            neighbors.Where(other => DistanceLookup[i, other] == minimumEdgeCost && NodeStates.IsFixedTarget(other));
                        foreach (var other in minimumTargetNeighbors)
                        {
                            untested.Add(i);
                            untested.Remove(other);
                            untested.UnionWith(MergeInto(other, i));
                            removedNodes++;
                        }
                    }
                }

                List <int> dependent;
                if (dependentNodes.TryGetValue(i, out dependent))
                {
                    untested.UnionWith(dependent);
                }
            }

            return(removedNodes);
        }
예제 #6
0
        protected override int ExecuteTest()
        {
            var removedNodes = 0;

            var untested = new HashSet <int>(Enumerable.Range(0, SearchSpaceSize));

            while (untested.Any())
            {
                var i = untested.First();
                untested.Remove(i);

                var neighbors = EdgeSet.NeighborsOf(i);

                if (!NodeStates.IsTarget(i))
                {
                    if (neighbors.Count == 1)
                    {
                        // Non target nodes with one neighbor can be removed.
                        untested.Add(neighbors[0]);
                        RemoveNode(i);
                        removedNodes++;
                    }
                    else if (neighbors.Count == 2)
                    {
                        // Non target nodes with two neighbors can be removed and their neighbors
                        // connected directly.
                        untested.Add(neighbors[0]);
                        untested.Add(neighbors[1]);
                        RemoveNode(i);
                        removedNodes++;
                    }
                }
                else if (NodeStates.IsFixedTarget(i))
                {
                    if (neighbors.Count == 1)
                    {
                        var other = neighbors[0];
                        // Fixed target nodes with one neighbor can be merged with their neighbor since
                        // it must always be taken.
                        // If the neighbor is no target and of degree 1 or 2, it will be removed by the tests above,
                        // after which this node will be processed again.
                        if (EdgeSet.NeighborsOf(other).Count <= 2 && !NodeStates.IsTarget(other))
                        {
                            continue;
                        }
                        // If this node is the only fixed target, the neighbor must not be merged because
                        //  if this is the only target, the neighbor and the rest of the tree will not be in the optimal solution,
                        //  if there are only variable target nodes, the neighbor will not be in the optimal solution if the variable nodes are not taken.
                        if (NodeStates.FixedTargetNodeCount == 1)
                        {
                            continue;
                        }
                        untested.Add(i);
                        untested.Remove(other);
                        untested.UnionWith(MergeInto(other, i));
                        removedNodes++;
                    }
                    else if (neighbors.Count > 1)
                    {
                        // Edges from one target node to another that are of minimum cost among the edges of
                        // one of the nodes can be in any optimal solution. Therefore both target nodes can be merged.
                        var minimumEdgeCost        = neighbors.Min(other => DistanceLookup[i, other]);
                        var minimumTargetNeighbors =
                            neighbors.Where(other => DistanceLookup[i, other] == minimumEdgeCost && NodeStates.IsFixedTarget(other));
                        foreach (var other in minimumTargetNeighbors)
                        {
                            untested.Add(i);
                            untested.Remove(other);
                            untested.UnionWith(MergeInto(other, i));
                            removedNodes++;
                        }
                    }
                }
            }

            return(removedNodes);
        }