Exemple #1
0
        /// <summary>
        ///  Converts an MST spanning a set of GraphNodes back into its equivalent
        ///  as a HashSet of SkillNode IDs.
        /// </summary>
        /// <param name="mst">The spanned MinimalSpanningTree.</param>
        /// <param name="visualize">A debug parameter that highlights all used
        /// GraphNodes' SkillNode equivalents in the tree.</param>
        /// <returns>A HashSet containing the node IDs of all SkillNodes spanned
        /// by the MST.</returns>
        HashSet <ushort> SpannedMstToSkillnodes(MinimalSpanningTree mst, bool visualize)
        {
            if (!mst.IsSpanned)
            {
                throw new Exception("The passed MST is not spanned!");
            }

            HashSet <ushort> newSkilledNodes = new HashSet <ushort>();

            foreach (GraphEdge edge in mst.SpanningEdges)
            {
                ushort target = edge.outside.Id;

                // The paths are calculated anyway, so use them here too.
                newSkilledNodes.UnionWith(distances.GetShortestPath(edge));
                newSkilledNodes.Add(target);
            }

            if (visualize)
            {
                tree._nodeHighlighter.UnhighlightAllNodes(NodeHighlighter.HighlightState.FromAttrib);
                foreach (GraphNode steinerNode in mst.mstNodes)
                {
                    tree._nodeHighlighter.HighlightNode(SkillTree.Skillnodes[steinerNode.Id], NodeHighlighter.HighlightState.FromAttrib);
                }
            }

            //tree.DrawHighlights(tree._nodeHighlighter);
            return(newSkilledNodes);
        }
Exemple #2
0
        double fitnessFunction(BitArray representation)
        {
            MinimalSpanningTree mst = dnaToMst(representation);

            // This is the bottleneck, quite obviously.
            mst.Span(startFrom: startNodes);

            int usedNodes = mst.UsedNodeCount;

            // TODO: Investigate fitness function
            return(1500 - usedNodes);
        }
Exemple #3
0
        /// <summary>
        ///  Tells the genetic algorithm to advance one generation and processes
        ///  the resulting (possibly) improved solution.
        /// </summary>
        public void EvolutionStep()
        {
            if (!_initialized)
            {
                throw new InvalidOperationException("Solver not initialized!");
            }

            ga.NewGeneration();

            if ((_bestDNA == null) || (GeneticAnnealingAlgorithm.SetBits(ga.GetBestDNA().Xor(_bestDNA)) != 0))
            {
                _bestDNA = ga.GetBestDNA();
                MinimalSpanningTree bestMst = dnaToMst(_bestDNA);
                bestMst.Span(startFrom: startNodes);

                // #DEBUG#: Pass true to show the used steiner nodes.
                BestSolution = SpannedMstToSkillnodes(bestMst, false);
            }
        }
        public void TestMST()
        {
            /// 0 -- 1 -- 2 -- 3
            ///  \        |   /
            ///    \      | /
            ///      4 -- 5 -- 6 -- 7
            bool[,] graph1 = {
                                 { false, true,  false, false, true,  false, false, false },
                                 { true,  false, true,  false, false, false, false, false },
                                 { false, true,  false, true,  false, true,  false, false },
                                 { false, false, true,  false, false, true,  false, false },
                                 { true,  false, false, false, false, true,  false, false },
                                 { false, false, true,  true,  true,  false, true,  false },
                                 { false, false, false, false, false, true,  false, true  },
                                 { false, false, false, false, false, false, true,  false },
                             };

            SearchGraph searchGraph1 = SearchGraphFromData(graph1);

            Dictionary<int, GraphNode> graphNodes1 = GetGraphNodesIdIndex(searchGraph1);
            DistanceLookup distanceLookup = new DistanceLookup();

            HashSet<GraphNode> mstNodes1 = new HashSet<GraphNode>
                { graphNodes1[3], graphNodes1[5], graphNodes1[7] };
            MinimalSpanningTree mst1 = new MinimalSpanningTree(mstNodes1);
            mst1.Span(startFrom: graphNodes1[0]);

            Assert.IsTrue(mst1.SpanningEdges.Count == 3, "Wrong amount of spanning edges");
            /// This can fail even if the mst would be valid! The test only works for
            /// the current implementation of the mst algorithm, but I can't find a
            /// better way to do this with the current data structures...
            Assert.IsTrue(mst1.SpanningEdges[0].inside.Id == 0 && mst1.SpanningEdges[0].outside.Id == 5,
                "First edge is wrong");
            Assert.IsTrue(mst1.SpanningEdges[1].inside.Id == 5 && mst1.SpanningEdges[1].outside.Id == 3,
                "Second edge is wrong");
            Assert.IsTrue(mst1.SpanningEdges[2].inside.Id == 5 && mst1.SpanningEdges[2].outside.Id == 7,
                "Third edge is wrong");
            Assert.IsTrue(mst1.UsedNodeCount == 5, "Wrong MST length");

            /// Test unconnected graph

            /// 0 -- 1    2 -- 3
            ///           |   /
            ///           | /
            ///      4 -- 5
            bool[,] graph2 = {
                                 { false, true,  false, false, false, false },
                                 { true,  false, false, false, false, false },
                                 { false, false, false, true,  false, true  },
                                 { false, false, true,  false, false, true  },
                                 { false, false, false, false, false, true  },
                                 { false, false, true,  true,  true,  false },
                             };
            SearchGraph searchGraph2 = SearchGraphFromData(graph2);
            Dictionary<int, GraphNode> graphNodes2 = GetGraphNodesIdIndex(searchGraph2);
            HashSet<GraphNode> mstNodes2 = new HashSet<GraphNode> { graphNodes2[0], graphNodes2[2], graphNodes2[4] };

            bool pass = false;
            try
            {
                MinimalSpanningTree mst = new MinimalSpanningTree(mstNodes2);
                mst.Span(graphNodes2[3]);
            }
            catch (DistanceLookup.GraphNotConnectedException)
            {
                pass = true;
            }
            Assert.IsTrue(pass, "No exception thrown for disconnected graph");
        }
Exemple #5
0
        /// <summary>
        ///  Converts an MST spanning a set of GraphNodes back into its equivalent
        ///  as a HashSet of SkillNode IDs.
        /// </summary>
        /// <param name="mst">The spanned MinimalSpanningTree.</param>
        /// <param name="visualize">A debug parameter that highlights all used
        /// GraphNodes' SkillNode equivalents in the tree.</param>
        /// <returns>A HashSet containing the node IDs of all SkillNodes spanned
        /// by the MST.</returns>
        HashSet<ushort> SpannedMstToSkillnodes(MinimalSpanningTree mst, bool visualize)
        {
            if (!mst.IsSpanned)
                throw new Exception("The passed MST is not spanned!");

            HashSet<ushort> newSkilledNodes = new HashSet<ushort>();
            foreach (GraphEdge edge in mst.SpanningEdges)
            {
                ushort target = edge.outside.Id;

                HashSet<ushort> start;
                if (edge.inside is Supernode)
                    start = tree.SkilledNodes;
                else
                    start = new HashSet<ushort>() { edge.inside.Id };

                var path = tree.GetShortestPathTo(target, start);

                newSkilledNodes = new HashSet<ushort>(newSkilledNodes.Concat(path));
            }

            if (visualize)
            {
                tree._nodeHighlighter.UnhighlightAllNodes(NodeHighlighter.HighlightState.FromAttrib);
                foreach (GraphNode steinerNode in mst.mstNodes)
                    tree._nodeHighlighter.HighlightNode(SkillTree.Skillnodes[steinerNode.Id], NodeHighlighter.HighlightState.FromAttrib);
            }

            //tree.DrawHighlights(tree._nodeHighlighter);
            return newSkilledNodes;
        }
Exemple #6
0
        /// <summary>
        ///  Finds the nodes in the search graph that can be potential steiner
        ///  nodes. Those form the search space base.
        /// </summary>
        private void buildSearchSpaceBase()
        {
            searchSpaceBase = new List<GraphNode>();

            MinimalSpanningTree leastSolution = new MinimalSpanningTree(targetNodes, distances);
            leastSolution.Span(startFrom: startNodes);

            int maxEdgeDistance = 0;
            foreach (GraphEdge edge in leastSolution.SpanningEdges)
            {
                int edgeDistance = distances.GetDistance(edge);
                if (edgeDistance > maxEdgeDistance)
                    maxEdgeDistance = edgeDistance;
            }
            /*
            int maxTargetDistance = 0;
            foreach (GraphNode targetNode in targetNodes)
            {
                int targetDistance = distances.GetDistance(targetNode, startNodes);
                if (targetDistance > maxTargetDistance)
                    maxTargetDistance = targetDistance;
            }*/

            // Find potential steiner points that are in reasonable vicinity.
            /// TODO: This can surely be improved in some shape or form, but I
            /// can't figure it out right now. Since the GA also has to work well
            /// with larger input sizes, I won't investigate this right now.
            foreach (GraphNode node in searchGraph.nodeDict.Values)
            {
                // This can be a steiner node only if it has more than 2 neighbors.
                if (node.Adjacent.Count > 2)
                {
                    /*
                     * While this would mathematically be correct, it's not a
                     * good criterium for the skill tree. I don't think the
                     * relevant cases can appear and this permits way too many
                     * nodes to be considered that will never be included in an
                     * actual solution.
                     *
                    /// If every target node is closer to the start than to a certain
                    /// steiner node, that node can't be needed for the steiner tree.
                    bool add = false;
                    foreach (GraphNode targetNode in targetNodes)
                        if (distances.GetDistance(targetNode, node) < distances.GetDistance(targetNode, startNodes))
                            add = true;
                    if (add)
                        searchSpaceBase.Add(node);
                     */

                    /*
                    /// This is a pretty handwavy approach... If anybody figures
                    /// out a case that causes this to fail, let me know please!
                    if (distances.GetDistance(node, startNodes) < 1.2 * maxTargetDistance)
                        searchSpaceBase.Add(node);
                     */

                    // This should be a reasonable approach.
                    bool add = false;
                    foreach (GraphNode targetNode in targetNodes)
                        if (distances.GetDistance(targetNode, node) < maxEdgeDistance)
                            add = true;
                    if (add)
                        searchSpaceBase.Add(node);

                }
            }
            /* ONLY FOR WHEN NODES HAVE INDIVIDUAL WEIGHTS
             * foreach (ushort nodeId in targetSkillnodes)
            {
                searchSpaceBase.Add(SkillTree.Skillnodes[nodeId]);
            }*/
        }
Exemple #7
0
        /// <summary>
        ///  Converts an MST spanning a set of GraphNodes back into its equivalent
        ///  as a HashSet of SkillNode IDs.
        /// </summary>
        /// <param name="mst">The spanned MinimalSpanningTree.</param>
        /// <param name="visualize">A debug parameter that highlights all used
        /// GraphNodes' SkillNode equivalents in the tree.</param>
        /// <returns>A HashSet containing the node IDs of all SkillNodes spanned
        /// by the MST.</returns>
        HashSet <ushort> SpannedMstToSkillnodes(MinimalSpanningTree mst, bool visualize)
        {
            if (!mst.IsSpanned)
            {
                throw new Exception("The passed MST is not spanned!");
            }

            HashSet <ushort> newSkilledNodes = new HashSet <ushort>();

            foreach (GraphEdge edge in mst.SpanningEdges)
            {
                ushort target = edge.outside.Id;

                HashSet <ushort> start;
                if (edge.inside is Supernode)
                {
                    start = tree.SkilledNodes;
                }
                else
                {
                    start = new HashSet <ushort>()
                    {
                        edge.inside.Id
                    }
                };

                var path = tree.GetShortestPathTo(target, start);

                newSkilledNodes = new HashSet <ushort>(newSkilledNodes.Concat(path));
            }

            if (visualize)
            {
                tree._nodeHighlighter.UnhighlightAllNodes(NodeHighlighter.HighlightState.FromAttrib);
                foreach (GraphNode steinerNode in mst.mstNodes)
                {
                    tree._nodeHighlighter.HighlightNode(SkillTree.Skillnodes[steinerNode.Id], NodeHighlighter.HighlightState.FromAttrib);
                }
            }

            //tree.DrawHighlights(tree._nodeHighlighter);
            return(newSkilledNodes);
        }

        MinimalSpanningTree dnaToMst(BitArray dna)
        {
            List <GraphNode> usedSteinerPoints = new List <GraphNode>();

            for (int i = 0; i < dna.Length; i++)
            {
                if (dna[i])
                {
                    usedSteinerPoints.Add(searchSpaceBase[i]);
                }
            }

            HashSet <GraphNode> mstNodes = new HashSet <GraphNode>(usedSteinerPoints);

            mstNodes.Add(startNodes);

            foreach (GraphNode targetNode in targetNodes)
            {
                mstNodes.Add(targetNode);
            }

            return(new MinimalSpanningTree(mstNodes, distances));
        }

        double fitnessFunction(BitArray representation)
        {
            MinimalSpanningTree mst = dnaToMst(representation);

            // This is the bottleneck, quite obviously.
            mst.Span(startFrom: startNodes);

            int usedNodes = mst.UsedNodeCount;

            // TODO: Investigate fitness function
            return(1500 - usedNodes);
        }
    }
Exemple #8
0
        /// <summary>
        ///  Finds the nodes in the search graph that can be potential steiner
        ///  nodes. Those form the search space base.
        /// </summary>
        private void buildSearchSpaceBase()
        {
            searchSpaceBase = new List <GraphNode>();

            MinimalSpanningTree leastSolution = new MinimalSpanningTree(targetNodes, distances);

            leastSolution.Span(startFrom: startNodes);

            int maxEdgeDistance = 0;

            foreach (GraphEdge edge in leastSolution.SpanningEdges)
            {
                int edgeDistance = distances.GetDistance(edge);
                if (edgeDistance > maxEdgeDistance)
                {
                    maxEdgeDistance = edgeDistance;
                }
            }

            /*
             * int maxTargetDistance = 0;
             * foreach (GraphNode targetNode in targetNodes)
             * {
             *  int targetDistance = distances.GetDistance(targetNode, startNodes);
             *  if (targetDistance > maxTargetDistance)
             *      maxTargetDistance = targetDistance;
             * }*/

            // Find potential steiner points that are in reasonable vicinity.
            /// TODO: This can surely be improved in some shape or form, but I
            /// can't figure it out right now. Since the GA also has to work well
            /// with larger input sizes, I won't investigate this right now.
            foreach (GraphNode node in searchGraph.nodeDict.Values)
            {
                // This can be a steiner node only if it has more than 2 neighbors.
                if (node.Adjacent.Count > 2)
                {
                    /*
                     * While this would mathematically be correct, it's not a
                     * good criterium for the skill tree. I don't think the
                     * relevant cases can appear and this permits way too many
                     * nodes to be considered that will never be included in an
                     * actual solution.
                     *
                     * /// If every target node is closer to the start than to a certain
                     * /// steiner node, that node can't be needed for the steiner tree.
                     * bool add = false;
                     * foreach (GraphNode targetNode in targetNodes)
                     *  if (distances.GetDistance(targetNode, node) < distances.GetDistance(targetNode, startNodes))
                     *      add = true;
                     * if (add)
                     *  searchSpaceBase.Add(node);
                     */

                    /*
                     * /// This is a pretty handwavy approach... If anybody figures
                     * /// out a case that causes this to fail, let me know please!
                     * if (distances.GetDistance(node, startNodes) < 1.2 * maxTargetDistance)
                     *  searchSpaceBase.Add(node);
                     */

                    // This should be a reasonable approach.
                    bool add = false;
                    foreach (GraphNode targetNode in targetNodes)
                    {
                        if (distances.GetDistance(targetNode, node) < maxEdgeDistance)
                        {
                            add = true;
                        }
                    }
                    if (add)
                    {
                        searchSpaceBase.Add(node);
                    }
                }
            }

            /* ONLY FOR WHEN NODES HAVE INDIVIDUAL WEIGHTS
             * foreach (ushort nodeId in targetSkillnodes)
             * {
             *  searchSpaceBase.Add(SkillTree.Skillnodes[nodeId]);
             * }*/
        }
Exemple #9
0
        /// <summary>
        ///  Converts an MST spanning a set of GraphNodes back into its equivalent
        ///  as a HashSet of SkillNode IDs.
        /// </summary>
        /// <param name="mst">The spanned MinimalSpanningTree.</param>
        /// <param name="visualize">A debug parameter that highlights all used
        /// GraphNodes' SkillNode equivalents in the tree.</param>
        /// <returns>A HashSet containing the node IDs of all SkillNodes spanned
        /// by the MST.</returns>
        HashSet<ushort> SpannedMstToSkillnodes(MinimalSpanningTree mst, bool visualize)
        {
            if (!mst.IsSpanned)
                throw new Exception("The passed MST is not spanned!");

            HashSet<ushort> newSkilledNodes = new HashSet<ushort>();
            foreach (GraphEdge edge in mst.SpanningEdges)
            {
                ushort target = edge.outside.Id;

                // The paths are calculated anyway, so use them here too.
                newSkilledNodes.UnionWith(distances.GetShortestPath(edge));
                newSkilledNodes.Add(target);
            }

            if (visualize)
            {
                tree._nodeHighlighter.UnhighlightAllNodes(NodeHighlighter.HighlightState.FromAttrib);
                foreach (GraphNode steinerNode in mst.mstNodes)
                    tree._nodeHighlighter.HighlightNode(SkillTree.Skillnodes[steinerNode.Id], NodeHighlighter.HighlightState.FromAttrib);
            }

            //tree.DrawHighlights(tree._nodeHighlighter);
            return newSkilledNodes;
        }