/// <summary> /// Find the connected components in the subtree /// </summary> /// <returns></returns> private List <List <Node> > FindConnectedComponents() { List <List <Node> > connectedComponents = new List <List <Node> >(); List <Node> subNodes = splitNode.AllNodesInSubtree; subNodes.Remove(splitNode.Value); HashSet <Node> beenList = new HashSet <Node>(splitNode.AncestorNodes) { splitNode.Value }; // Use DFS to find all connected components for (int i = 0; i < subNodes.Count; i++) { if (beenList.Contains(subNodes[i])) { continue; } List <Node> connectedNodes = DFS.All(subNodes[i], (n) => { // When a node is explored, add the index of the component it is in to the dictionary componentIndices[n] = connectedComponents.Count; return(true); }, beenList); connectedComponents.Add(connectedNodes); } return(connectedComponents); }
public override void Apply() { // Find all the connected components in this subtree List <List <Node> > connectedComponents = FindConnectedComponents(); // If the number of connected components is equal to the number of children, we cannot split the tree even further, so return if (connectedComponents.Count == splitNode.Children.Count) { return; } // Consider a subtree where the middle part is a connected component. We want that part as a new child, and the rest as a new child, but connected to each other // Thus, if we remove the middle part of a connected component, we want to reconnect this component // In each component, find the new children of the splitnode, and possibly new parents for nodes that were cut off bool[] foundComponent = new bool[connectedComponents.Count]; List <Tuple <RecursiveTree <Node>, RecursiveTree <Node> > > newParents = new List <Tuple <RecursiveTree <Node>, RecursiveTree <Node> > >(); List <RecursiveTree <Node> > newChildren = DFS.All(splitNode, (n) => { if (n == splitNode) { return(false); } int index = componentIndices[n.Value]; if (foundComponent[index]) { // If we have already found this component, find the first parent that is also part of this component. This node is now namely divided from the rest of this component RecursiveTree <Node> parent = n.Parent; while (componentIndices[parent.Value] != index) { parent = parent.Parent; } newParents.Add(new Tuple <RecursiveTree <Node>, RecursiveTree <Node> >(n, parent)); return(false); } foundComponent[index] = true; return(true); }); // Reconnect the divided components foreach (Tuple <RecursiveTree <Node>, RecursiveTree <Node> > nodePair in newParents) { operations.Push(new RemoveChildOperation(nodePair.Item1.Parent, nodePair.Item1)); operations.Push(new ChangeParentOperation(nodePair.Item1, nodePair.Item2)); operations.Push(new AddChildOperation(nodePair.Item2, nodePair.Item1)); } // Connect the new children to the split node foreach (RecursiveTree <Node> newChild in newChildren) { if (newChild.Parent == splitNode) { continue; } operations.Push(new RemoveChildOperation(newChild.Parent, newChild)); operations.Push(new ChangeParentOperation(newChild, splitNode)); operations.Push(new AddChildOperation(splitNode, newChild)); } }
/// <summary> /// Compute the connected components in a set of nodes and fills these in the list connectedComponents /// </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="selectedNode">The node that is the root of 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> private void ComputeConnectedComponents(Node[] nodes, HashSet <Node> ancestors, Node selectedNode, int left, int right) { ancestors.Add(selectedNode); beenList.Clear(); beenList.UnionWith(ancestors); connectedComponents.Clear(); // For all nodes in the current subtree, do a DFS to find the connected components for (int i = left; i < right; i++) { if (beenList.Contains(nodes[i])) { continue; } List <Node> connectedNodes = DFS.All(nodes[i], node => { return(true); }, beenList); connectedComponents.Add(connectedNodes); } }
/// <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); }