Beispiel #1
0
        /// <summary>
        /// Creates a subtree in the form of a single line from a list of nodes
        /// </summary>
        /// <param name="nodes">The nodes to be in the subtree</param>
        /// <returns>The root of a subtree with the nodes in a single line</returns>
        private RecursiveTree <Node> CreateLine(List <Node> nodes)
        {
            RecursiveTree <Node> root   = new RecursiveTree <Node>(nodes[0]);
            RecursiveTree <Node> parent = root;

            for (int i = 1; i < nodes.Count; i++)
            {
                RecursiveTree <Node> node = new RecursiveTree <Node>(nodes[i])
                {
                    Parent = parent
                };
                parent.AddChild(node);
                parent = node;
            }
            return(root);
        }
Beispiel #2
0
        public Neighbour <RecursiveTree <Node> > GetRandomNeighbour(RecursiveTree <Node> data)
        {
            List <Neighbour <RecursiveTree <Node> > > neighbourlist = new List <Neighbour <RecursiveTree <Node> > >();

            // Create a random MoveUpNeighbour
            Neighbour <RecursiveTree <Node> > move = MoveUpNeighbourSpace.GetRandomNeighbour(data);

            neighbourlist.Add(move);

            // Generate Splits amount of random SplitNeighbours
            for (int i = 0; i < Splits; i++)
            {
                Neighbour <RecursiveTree <Node> > split = SplitNeighbourSpace.GetRandomNeighbour(data);
                neighbourlist.Add(split);
            }

            // Return the list with the single Move and multiple SplitNeighbours
            return(new MultipleNeighbourNeighbour <RecursiveTree <Node> >(neighbourlist));
        }
Beispiel #3
0
        /// <summary>
        /// Recursive method used for computing the heuristic tree, computes the tree for a subset of nodes
        /// </summary>
        /// <param name="nodes">All nodes in the entire tree</param>
        /// <param name="ancestors">The list of ancestors for the current subtree</param>
        /// <param name="left">The INCLUSIVE index in nodes where the current subtree starts</param>
        /// <param name="right">The EXCLUSIVE index in nodes where the current subtree ends</param>
        /// <param name="timer">A RUNNING! timer to keep track of how long this computation is allowed to take</param>
        /// <param name="maxTimeAllowed">The maximum time this computation is allowed to take</param>
        /// <param name="fast">Whether heuristics should be updated during the computation. Fast means they are not updated</param>
        /// <returns>The resulting heuristic tree for this subset of nodes</returns>
        private RecursiveTree <Node> RecGetHeuristicTree(Node[] nodes, HashSet <Node> ancestors, int left, int right, Stopwatch timer, double maxTimeAllowed, bool fast) // Left inclusive, right exclusive
        {
            // Compute the array for this subset of nodes
            subArray = nodes.Skip(left).Take(right - left);

            // If the time is up, return the nodes in a single line with for this subtree
            if (timer.Elapsed.TotalSeconds >= maxTimeAllowed)
            {
                return(CreateLine(subArray.ToList()));
            }

            // If the new tree has only one node, return this node
            if (left - right == 1)
            {
                return(new RecursiveTree <Node>(nodes[left]));
            }

            // Select a node as the root of this subtree based on the heuristics of the nodes in this subgraph and compute the connected component when this node is removed
            // These components are the subtrees that are children of the selected node
            Node selectedNode            = GetHeuristicNode(subArray, fast);
            RecursiveTree <Node> newTree = new RecursiveTree <Node>(selectedNode);

            ComputeConnectedComponents(nodes, ancestors, selectedNode, left, right);
            Tuple <int, int>[] borders = ComputeNewSubgraphBorders(nodes, left);

            // For each of the new subtrees, do a recursive call to this method to compute it
            for (int i = 0; i < borders.Length; i++)
            {
                RecursiveTree <Node> ChildTree = RecGetHeuristicTree(nodes, ancestors, borders[i].Item1, borders[i].Item2, timer, maxTimeAllowed, fast);
                ChildTree.Parent = newTree;
                newTree.AddChild(ChildTree);
            }

            // Remove the selected node from the ancestors so the possible other subtrees of the parent of this subtree do not accidentaly use it.
            // We do this to avoid having to copy the entire ancestors list for each recursive call
            ancestors.Remove(selectedNode);
            return(newTree);
        }
Beispiel #4
0
        /// <summary>
        /// Create a RecursiveTree with all nodes in a single line (each tree has exactly one child, except for the single leaf)
        /// </summary>
        /// <returns>The root of a RecursiveTree in a line</returns>
        public static RecursiveTree <Node> LineRecursiveTree(ref List <RecursiveTree <Node> > allRecTreeNodes, Node[] allNodes)
        {
            allRecTreeNodes = new List <RecursiveTree <Node> >();
            int numberOfNodes = allNodes.Length;

            // Create the root of the line
            RecursiveTree <Node> recTree = new RecursiveTree <Node>(allNodes[0]);

            allRecTreeNodes.Add(recTree);

            // Loop through all other nodes and create trees for them with the correct parent and cildren references
            RecursiveTree <Node> child = recTree;

            for (int i = 1; i < numberOfNodes; i++)
            {
                Node newNode = allNodes[i];
                RecursiveTree <Node> parent = new RecursiveTree <Node>(newNode);
                child.Parent = parent;
                parent.AddChild(child);
                child = parent;
                allRecTreeNodes.Add(parent);
            }
            return(recTree);
        }
Beispiel #5
0
 /// <summary>
 /// Operation that removes a certain child from a node
 /// </summary>
 /// <param name="node">The node from which the child is removed</param>
 /// <param name="child">The child that is removed</param>
 public RemoveChildOperation(RecursiveTree <Node> node, RecursiveTree <Node> child)
 {
     this.node  = node;
     this.child = child;
     node.RemoveChild(child);
 }
Beispiel #6
0
 /// <summary>
 /// Changes the parent of a node
 /// </summary>
 /// <param name="node">The node that needs a new parent</param>
 /// <param name="newParent">The new parent of the node</param>
 public ChangeParentOperation(RecursiveTree <Node> node, RecursiveTree <Node> newParent)
 {
     this.node   = node;
     oldParent   = node.Parent;
     node.Parent = newParent;
 }
Beispiel #7
0
 /// <summary>
 /// Operation that adds multiple children to a node
 /// </summary>
 /// <param name="node">The node to add the children to</param>
 /// <param name="children">The collection of children to be added</param>
 public AddChildrenOperation(RecursiveTree <Node> node, IEnumerable <RecursiveTree <Node> > children)
 {
     this.node     = node;
     this.children = children;
     node.AddChildren(children);
 }
Beispiel #8
0
 /// <summary>
 /// Operation that removes all children from a node
 /// </summary>
 /// <param name="node">The node from which the children are removed</param>
 public RemoveAllChildrenOperation(RecursiveTree <Node> node)
 {
     this.node = node;
     children  = node.Children;
     node.RemoveAllChildren();
 }
Beispiel #9
0
 /// <summary>
 /// Operation that removes a list of children from a node
 /// </summary>
 /// <param name="node">The node from which the children are removed</param>
 /// <param name="children">The children that are removed from the node</param>
 public RemoveChildrenOperation(RecursiveTree <Node> node, IEnumerable <RecursiveTree <Node> > children)
 {
     this.node     = node;
     this.children = node.Children;
     node.RemoveChildren(children);
 }
Beispiel #10
0
        /// <summary>
        /// Execute the program using simulated annealing
        /// </summary>
        /// <param name="fileName">The name of the file to be run, or empty if the console is used</param>
        /// <param name="console">Whether input form the console is used. If so, the program runs in quiet mode</param>
        private static void SimulatedAnnealing(string fileName = "", bool console = true)
        {
            if (!console)
            {
                Console.WriteLine($"Starting file {fileName}...");
            }

            // The stopwatch keeps track of the total running time of the program
            stopwatchSingleRun.Start();

            // Read the input, depending on whether the console is used a file is being read or console input
            Node[] inputAsNodes;
            if (!console)
            {
                inputAsNodes = IO.ReadInputAsNodes($"..\\..\\..\\..\\..\\Testcases\\{fileName}.gr", CENTER_RESEMBLANCE_CAP, ARTICULATION_POINT_MINIMUM);
            }
            else
            {
                inputAsNodes = IO.ReadInputAsNodes(CENTER_RESEMBLANCE_CAP, ARTICULATION_POINT_MINIMUM);
            }
            allNodes = inputAsNodes;

            // Create instances for the simunlated annealing and recursivesplit classes
            SimulatedAnnealing <State <RecursiveTree <Node> >, RecursiveTree <Node> > simulatedAnnealing =
                new SimulatedAnnealing <State <RecursiveTree <Node> >, RecursiveTree <Node> >(random, START_TEMP, RESET_TEMPERATURE_THRESHOLD, TEMP_MULTIPLIER, MULTIPLY_TEMP_PER_ITERATIONS, stopwatchSingleRun, MAX_TIME_TOTAL_SECONDS);
            RecursiveSplit recursiveSplit = new RecursiveSplit(inputAsNodes);

            // Find an initial solution where all nodes are in a single line
            RecursiveTreeState heurInitState = new RecursiveTreeState(BaseSolutionGenerator.LineRecursiveTree(ref allRecTreeNodes, allNodes), random, allRecTreeNodes);

            bestStateSoFar = heurInitState.Data.ToString();
            if (!console)
            {
                Console.WriteLine($"Line found with depth {heurInitState.Data.Root.Depth}.");
            }

            initialSolutionsTimer.Start();
            RecursiveTree <Node> bestTree = null;

            // Find an initial solution using the RecursiveSplit using a fast but bad heuristic
            if (allNodes.Length <= FASTER_HEURISTIC_CAP)
            {
                double maxTime = MAX_TIME_INITIAL_SOLUTIONS_SECONDS;
                if (allNodes.Length > INDEPENDENT_SET_CAP)
                {
                    if (allNodes.Length > FAST_HEURISTIC_CAP)
                    {
                        maxTime *= 3;
                    }
                    else
                    {
                        maxTime *= 1.5;
                    }
                }
                else if (allNodes.Length > FAST_HEURISTIC_CAP)
                {
                    maxTime *= 1.5;
                }

                bestTree = recursiveSplit.GetHeuristicTree(initialSolutionsTimer, maxTime, true);
                if (!console)
                {
                    Console.WriteLine($"Fastest heuristic tree found with depth {bestTree.Depth}.");
                }
            }

            // Find an initial solution using the RecursiveSplit using a decent but pretty slow heuristic
            if (allNodes.Length <= FAST_HEURISTIC_CAP)
            {
                double maxTime = MAX_TIME_INITIAL_SOLUTIONS_SECONDS;
                if (allNodes.Length > INDEPENDENT_SET_CAP)
                {
                    maxTime *= 3;
                }
                else if (allNodes.Length > FASTER_HEURISTIC_CAP)
                {
                    maxTime *= 1.5;
                }
                else
                {
                    maxTime *= 2;
                }

                RecursiveTree <Node> treeFromFastHeuristic = recursiveSplit.GetHeuristicTree(initialSolutionsTimer, maxTime, false);
                if (bestTree == null || treeFromFastHeuristic.Depth < bestTree.Depth)
                {
                    bestTree = treeFromFastHeuristic;
                }
                if (!console)
                {
                    Console.WriteLine($"Fast heuristic tree found with depth {treeFromFastHeuristic.Depth}.");
                }
            }

            // If the problem instance is small enough, we would like to try to find an initial solution using independent sets
            if (allNodes.Length <= INDEPENDENT_SET_CAP)
            {
                double maxTime = MAX_TIME_INITIAL_SOLUTIONS_SECONDS * 3;
                RecursiveTree <Node> treeFromIndependentSet = IndependentSet.TreeFromIndependentSets(allNodes, initialSolutionsTimer, maxTime);
                if (bestTree == null || treeFromIndependentSet.Depth < bestTree.Depth)
                {
                    bestTree = treeFromIndependentSet;
                }
                if (!console)
                {
                    Console.WriteLine($"Independent set tree found with depth {treeFromIndependentSet.Depth}.");
                }
            }

            // Recreate the tree and save the best found initial solution as the initial solution for simulated annealing
            initialSolutionsTimer.Reset();
            if (bestTree != null)
            {
                bestStateSoFar = bestTree.ToString();
                RecreateTree(bestStateSoFar);
                heurInitState = new RecursiveTreeState(allRecTreeNodes[0].Root, random, allRecTreeNodes);
            }

            if (allNodes.Length <= SIMULATED_ANNEALING_CAP)
            {
                simulatedAnnealing.Search(heurInitState, ref bestStateSoFar);
            }

            // If the input from the console is not used, write the output to a file, otherwise write it back to the console
            if (!console)
            {
                using (StreamWriter sw = new StreamWriter($"..\\..\\..\\..\\..\\Results\\{fileName}.tree", false))
                {
                    sw.Write(bestStateSoFar);
                }
            }
            else
            {
                Console.Write(bestStateSoFar);
            }

            // Print the result and total time thus far
            if (!console)
            {
                // Stop the total of this problem instace
                stopwatchSingleRun.Stop();
                Console.WriteLine($"Tree found with depth {bestStateSoFar.Split('\n')[0]} in {stopwatchSingleRun.Elapsed} seconds. (Total time: {allRunsTotalStopwatch.Elapsed})");
                Console.WriteLine();
            }

            // Reset the stopwatch for the next time the program is run
            stopwatchSingleRun.Reset();
        }
Beispiel #11
0
 public List <Neighbour <RecursiveTree <Node> > > GetAllNeighbours(RecursiveTree <Node> data)
 {
     throw new NotImplementedException(); // TODO: implement
 }
Beispiel #12
0
 /// <summary>
 /// Remove a child from this RecursiveTree
 /// </summary>
 /// <param name="child">The child to be removed</param>
 public void RemoveChild(RecursiveTree <V> child)
 {
     try { ChildrenList.Remove(child); }
     catch { throw new Exception("Child is no child of this node!"); }
 }
Beispiel #13
0
 /// <summary>
 /// Add a child to the children of this tree
 /// </summary>
 /// <param name="child">The child to be added</param>
 public void AddChild(RecursiveTree <V> child)
 {
     ChildrenList.Add(child);
 }
Beispiel #14
0
 /// <summary>
 /// Constructor for the RecursiveTree
 /// </summary>
 /// <param name="original">The original RecursiveTree this tree is supposed to be made off</param>
 public RecursiveTree(RecursiveTree <V> original)
 {
     Value        = original.Value;
     ChildrenList = original.ChildrenList;
 }
Beispiel #15
0
 /// <summary>
 /// Splits a subtree into multiple subtrees
 /// </summary>
 /// <param name="splitNode">The root of the subtree that is going to be split</param>
 public SplitNeighbour(RecursiveTree <Node> splitNode)
 {
     this.splitNode   = splitNode;
     componentIndices = new Dictionary <Node, int>();
 }
Beispiel #16
0
        /// <summary>
        /// Recursive method for calculating the best heuristic tree
        /// </summary>
        /// <param name="bestFoundSolution">The optimal solution thus far from this level of the tree, used for early stopping</param>
        /// <param name="nodes">A list of all nodes in this subtree</param>
        /// <param name="ancestors">The list of all ancestors of the current subtree</param>
        /// <param name="checkedSubsets">A dictionary with checked subsets, used for memoization</param>
        /// <returns>The optimal exact tree for the list of input nodes</returns>
        private RecursiveTree <Node> RecGetBestTree(int bestFoundSolution, List <Node> nodes, HashSet <Node> ancestors, Dictionary <string, RecursiveTree <Node> > checkedSubsets)
        {
            // If the currently best found solution is smaller than the list of ancestors here, we cannot possibly improve it. Thus, we return an empty tree
            if (bestFoundSolution < ancestors.Count + 1)
            {
                return(null);
            }

            // Check if we have already computed a subtree for this set of nodes, if so, return that tree
            string asBits = NodeSubsetRepresentation(nodes);

            if (checkedSubsets.ContainsKey(asBits) && checkedSubsets[asBits] != null)
            {
                RecursiveTree <Node> computedTree = new RecursiveTree <Node>(checkedSubsets[asBits]);
                return(computedTree);
            }

            // Sort the nodes on their remaining degree value (descending) and recursively compute the best trees
            HashSet <Node> nodesAsHash = new HashSet <Node>(nodes);

            nodes = nodes.OrderByDescending(n => n.RemainingDegree(nodesAsHash)).ToList();
            RecursiveTree <Node> bestTree = null;

            foreach (Node selectedNode in nodes)
            {
                RecursiveTree <Node> newTree  = new RecursiveTree <Node>(selectedNode);
                HashSet <Node>       beenList = new HashSet <Node>(ancestors)
                {
                    selectedNode
                };
                bool broken = false;

                foreach (Node n in nodes)
                {
                    // Find the connected component this node is in
                    if (beenList.Contains(n))
                    {
                        continue;
                    }
                    List <Node>    connectedNodes = DFS.All(n, (nn) => { return(true); }, beenList);
                    HashSet <Node> newHash        = new HashSet <Node>(ancestors)
                    {
                        selectedNode
                    };

                    // Compute the best possible subtree for this connected component
                    RecursiveTree <Node> ChildTree = RecGetBestTree(bestFoundSolution, connectedNodes, newHash, checkedSubsets);
                    if (ChildTree == null)
                    {
                        // If the resulting tree is null, it is not a viable solution
                        broken = true;
                        break;
                    }
                    ChildTree.Parent = newTree;
                    newTree.AddChild(ChildTree);
                }

                // If we found a viable solution, update the best found solution thus far
                if (!broken)
                {
                    int newDepth = newTree.Depth;
                    if (newDepth + ancestors.Count < bestFoundSolution)
                    {
                        bestFoundSolution = newDepth + ancestors.Count;
                        bestTree          = newTree;
                    }
                }
            }

            // Save the tree in the memoization dictionary and return it
            checkedSubsets[asBits] = bestTree;
            return(bestTree);
        }