Пример #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
        /// <summary>
        /// Creates a new object that can reduce the problem instance described by the given parameters.
        /// </summary>
        /// <param name="searchSpace">Problem solutions must be a subset of these nodes. They also describe the initial edges.
        /// Must contain all nodes from the other parameters.</param>
        /// <param name="fixedTargetNodes">All nodes that must be included in solutions. The nodes for which the minimal
        /// Steiner tree has to be found.</param>
        /// <param name="startNode">The node from which solution trees are built. Must be a fixed target node.
        /// If null, the first fixed target node will be taken.</param>
        /// <param name="variableTargetNodes">Finding the subset of these nodes (unioned with the fixed target nodes) whose
        /// minimal Steiner tree optimizes the constraints formulates the extended Steiner problem solved by the AdvancedSolver.
        /// These nodes are in a special state between normal nodes and fixed target nodes for reductions.
        /// If null, no nodes are considered as variable target nodes and the problem is an instance of the normal Steiner tree problem.</param>
        /// <remarks>
        /// The fixed target nodes are removed from the search space and then added to the end of it so they always get the last distance
        /// indices. This improves the quality of solutions because the MST algorithm considers edges involving these after same priority edges
        /// not involving them.
        /// </remarks>
        public SteinerPreprocessor(IEnumerable <GraphNode> searchSpace, IEnumerable <GraphNode> fixedTargetNodes,
                                   GraphNode startNode = null, IEnumerable <GraphNode> variableTargetNodes = null)
        {
            var fixedTargetNodesSet = new HashSet <GraphNode>(fixedTargetNodes);

            if (!fixedTargetNodesSet.Any())
            {
                throw new ArgumentException("At least one fixed target node must be provided.", "fixedTargetNodes");
            }

            // Fixed target nodes are added last into the search space to get the last distance indices.
            _nodeStates = new NodeStates(searchSpace.Except(fixedTargetNodesSet).Union(fixedTargetNodesSet),
                                         fixedTargetNodesSet, variableTargetNodes ?? new GraphNode[0]);
            _data = new Data(startNode ?? _nodeStates.FixedTargetNodes.First());
            if (startNode != null && !_nodeStates.IsFixedTarget(startNode))
            {
                throw new ArgumentException("Start node must be a fixed target node if specified.", "startNode");
            }
        }
Пример #3
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);
        }
Пример #4
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);
        }