/// <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); }
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)); }
/// <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); }
/// <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); }
/// <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); }
/// <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; }
/// <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); }
/// <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(); }
/// <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); }
/// <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(); }
public List <Neighbour <RecursiveTree <Node> > > GetAllNeighbours(RecursiveTree <Node> data) { throw new NotImplementedException(); // TODO: implement }
/// <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!"); } }
/// <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); }
/// <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; }
/// <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>(); }
/// <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); }