res = 0 for i in xrange(1, t+1): binom = 1 for j in xrange(1, i+1): binom *= (c - i + j) binom /= j res += binom return res # end def CtdColor = recordtype('CtdColor', 'isInSet number nodes') # bool isInSet: for each color, save whether the color is in the combination # int number: store the frequency of each color # set<int> nodes CtdData = recordtype('CtdData', 'numColors treeDepth currentDepth combi ' + 'lastColor responsible color unionFind max_id') # int numColors: number of all colors, whose frequency are at least 2 # int treeDepth: the tree depth, which is checked # int currentDepth: current number of colors in the combination # set<int> combi: store the colors, which are in the combination, # without the last color # int lastColor: store the last color, which is in the combination
# import sys import os from lib.graph.graph import Graph, Coloring from lib.util.recordtype import * import itertools # Define a record type of information about the vertices # Attributes: # parent: vertex that is the parent in the decomposition. None iff vertex # is the root # children: list of vertices that are children of this vertex # depth: integer of the depth of the vertex. Root has depth 0. VertexInfo = recordtype('VertexInfo', 'parent children depth') class TDDecomposition(Graph): """ A subclass of Graph with additional data structures to describe a treedepth decomposition. Attributes: maxDepth: The depth of the decomposition vertexRecords: A dictionary mapping a vertex to its VertexInfo record (see above) root: the root of the decomposition """ def __init__(self):
# import sys import os import itertools from lib.graph.graph import Graph, Coloring from lib.util.recordtype import * # Define a record type of information about the vertices # Attributes: # parent: vertex that is the parent in the decomposition. None iff vertex # is the root # children: list of vertices that are children of this vertex # depth: integer of the depth of the vertex. Root has depth 0. VertexInfo = recordtype('VertexInfo', 'parent children depth') class TDDecomposition(Graph): """ A subclass of Graph with additional data structures to describe a treedepth decomposition. Attributes: maxDepth: The depth of the decomposition vertexRecords: A dictionary mapping a vertex to its VertexInfo record (see above) root: the root of the decomposition """ def __init__(self): super(TDDecomposition, self).__init__()
class DFSSweep(DecompGenerator): # max: number of vertices in graph # current_depth: Number of colors we are looking at (0 indexed, so 0 is 1st # color, -1 is no color) # colors_in_combi: set containing colors that we are currently processing # component_store: A stack of dictionaries for reverse component lookups # wscolorlist: A list of WCSColors (one for each color used to color our # parent graph G # union_find: A stack of union find structures for fast merging of # components # union find structure: # every vertex stores information in one mint64 # every mint64 contains following bits: # ------------------------------------------ # | parent index | type | # | number of vertices | type | # ------------------------------------------ # | ... | 5 | 4 | 3 | 2 | 1 | 0 | # ------------------------------------------ # # type can be # 00 = the vertex will be ignored (the color is not in the # combination) # 01 = the vertex is a root in a connected component (stores number of # vertices) # 10 = the vertex is a child (the parent-bits are used) WCSData = recordtype( 'WCSData', 'max current_depth colors_in_combi ' 'component_store wscolorlist union_find') # number: Number of vertices of this color # nodes: A set of vertices that have this color WCSColor = recordtype('WCSColor', 'number nodes') UFS_TYPE_MASK = 0b11 # Mask for determining if vertex is root or child UFS_TYPE_ROOT = 0b01 # Constant for type root UFS_TYPE_CHILD = 0b10 # Constant for type child def __iter__(self): """ Return an iterator based on size of color sets we are looking at :return: An iterator for decompositions """ return self.induced_color_components() def walk_color_space(self): """ A function that walks our color space using a recursive helper function. """ # Make a WCSData recordtype to store information about # components data = self.WCSData(max=self.G.get_max_id(), current_depth=-1, colors_in_combi=set(), component_store=[], wscolorlist={}, union_find=[]) # Make a new recordtype for each color in our graph for color in self.coloring.usedcols: data.wscolorlist[color] = self.WCSColor(number=0, nodes=set()) # Record which vertices belong to what # color. Also record their count for v in self.G: data.wscolorlist[self.coloring[v]].number += 1 data.wscolorlist[self.coloring[v]].nodes.add(v) # Generate components by recursively walking through color space for component in self.recursive_walk(data, list(self.coloring.usedcols)): yield component def recursive_walk(self, data, colors, index_of_last=-1): """ Method to help walk through color space :param data: The WCSData object :param colors: A list of all colors used for coloring G :param index_of_last (optional): Keeps track of index of last color added to combination """ # Check to see if we are at p-1 colors, if so generate all combinations # with added pth color if len(data.colors_in_combi) == self.p - 1: for color in colors[index_of_last + 1:]: # Add color to stack self.add(data, color) # Generate components with added color and yield them # If the verbose flag is provided if self.verbose: sys.stdout.write("\t" + str(data.colors_in_combi) + "\n") # Before yielding components with this color set, run all the # callbacks in our before_color_set list. for fn in self.before_color_set: fn(data.colors_in_combi) # Yield components in order to generate TDDs for c in self.component_generator(data): yield c # After yielding components with this color set, run all the # callbacks in our after_color_set list. for fn in self.after_color_set: fn(data.colors_in_combi) # Pop color from stack since we don't need it self.remove(data, color) else: # We need a separate last index for the recursive call # which will be incremented for each color we add last_index = index_of_last # For every remaining color in the list add and process for color in colors[index_of_last + 1:]: last_index += 1 # At this point we are not looking at sets that have # less than td_H elements self.add(data, color) if len(data.colors_in_combi) >= self.td_H: # Generate components with added color and yield them # If the verbose flag is provided if self.verbose: sys.stdout.write("\t" + str(data.colors_in_combi) + "\n") # Before yielding components with this color set, run all # the callbacks in our before_color_set list. for fn in self.before_color_set: fn(data.colors_in_combi) # Yield the components to generate decompositions for c in self.component_generator(data): yield c # After yielding components with this color set, run all # the callbacks in our after_color_set list. for fn in self.after_color_set: fn(data.colors_in_combi) # Call this method again to add additional color to stack for component in self.recursive_walk(data, colors, last_index): yield component # Pop the last color self.remove(data, color) def ufs_find(self, ufs, node): """ Find operation for our union find structure Find the root of a vertex in our union find structure Implementation used from check_tree_depth.py :param ufs: The union-find structure :param node: The node whose root we are trying to find """ # Save a copy of our node since we want to do path compression save = node depth = 0 # traverse through the tree up until we find a root # while (ufs_isChild(ufs, node)): while (ufs[node] & self.UFS_TYPE_MASK) == self.UFS_TYPE_CHILD: node = (ufs[node] >> 2) # ufs_getParent(ufs, node) depth += 1 # If our original node was a child, make it point to # its farthest parent (root) for finding it faster the next time if ((ufs[save] & self.UFS_TYPE_MASK) == self.UFS_TYPE_CHILD and depth > 1): # (ufs_isChild(ufs, save)): # ufs[save] = ufs_setParent(ufs, save, node) ufs[save] = self.UFS_TYPE_CHILD | (node << 2) # return our root return node def print_union_find(self, ufs): """ A nice representation for our union find structure. Useful for debugging. :param ufs: The union find structure that we want to print """ ufs_str = "[" # For each entry for idx, entry in enumerate(ufs): # Ignore this entry if entry == 0: ufs_str += 'none' # This is a root, print -> vertex r - num vertices in component elif entry % 2 != 0: ufs_str += str(idx) + 'r - ' + str(entry >> 2) # This is a child, print -> vertex cof(child of) root else: ufs_str += str(idx) + ' cof ' + str(self.ufs_find(ufs, idx)) # Add a comma after each entry ufs_str += ', ' # Cut out extra ', ' at the end and add closing bracket print '\n' + ufs_str[0:len(ufs_str) - 2] + ']' + '\n' def remove(self, data, color): """ Remove a color from the current combination :param data: The WCSData object :param color: The color we want to remove """ # Drop one level data.component_store.pop() # We are done processing 'color', so remove it data.colors_in_combi.remove(color) # Drop one level in the union find data.union_find.pop() # Decrease current depth by 1 data.current_depth -= 1 def alt_component_generator(self, data): """ An alternate implementation for generating components which is memory efficient :param data: WCSData :return: A generator for components """ # Initialize an empty dictionary to store the components comps = [] # Build the dictionary ufs = data.union_find[data.current_depth] for idx, elem in enumerate(ufs): # Ignore this element if elem == 0: continue # This is a root, so make new dictionary entry for this root elif elem % 2 != 0: # Flexible comps[idx] = {idx} if idx >= len(comps): comps.extend([{x} for x in range(len(comps), idx)]) comps.insert(idx, {idx}) # This is a child, find its root, and add to appropriate set else: root = self.ufs_find(ufs, idx) # Flexible comps[root] = {root} if root >= len(comps): comps.extend([{x} for x in range(len(comps), root)]) comps.insert(root, {root}) comps[root].add(idx) # Generate components while also pruning ones we don't need for item in comps: if not self.prune(item, self.multi_pat_min_p): yield item def component_generator(self, data): """ Generator for components at current level :param data: The WCSData object :return A generator for components at current_depth in data """ # Get the dictionary at the current depth comps_at_level = data.component_store[data.current_depth] # Iterate over its keys to get all components for component in comps_at_level.itervalues(): # Prune component if it has less than p vertices if not self.prune(component, self.multi_pat_min_p): yield component def add(self, data, color): """ Add one color to the color combination and find all new connected components using a union find data structure :param G: The parent graph :param data: The WCSData object :param color: The color to add """ # Add the color to the combination data.colors_in_combi.add(color) # Increment current depth since we have added a color data.current_depth += 1 # Find how many vertices we have n = data.max + 1 # If we are at looking at the first color if data.current_depth == 0: # Make a ufs structure and initialize it with 0s ufs = [0] * n # Initialize a component store to store our components # Components are stored as sets in a dictionary comps = {} # For all the vertices of that color for v in data.wscolorlist[color].nodes: # Initialize the union find entry # Make each vertex a root ufs[v] = (1 << 2) | self.UFS_TYPE_ROOT # Since each vertex is a component by itself # Make a set containing only v and add it to the dictionary comps[v] = frozenset([v]) # We are looking at at least two colors else: # Copy the previous dictionary of components comps = copy(data.component_store[data.current_depth - 1]) # Copy the previous union find structure ufs = copy(data.union_find[data.current_depth - 1]) # Initialize new colors in the UFS and comps dictionary for v in data.wscolorlist[color].nodes: ufs[v] = (1 << 2) | self.UFS_TYPE_ROOT comps[v] = frozenset([v]) for v in data.wscolorlist[color].nodes: # For each newly added vertex, find its neighbors # and check to see if it already belongs to some component for u in self.G.neighbours(v): if self.coloring[u] in data.colors_in_combi: root1 = self.ufs_find(ufs, v) root2 = self.ufs_find(ufs, u) if root1 != root2: # Get the UFS entries of these roots ufs_root1 = ufs[root1] ufs_root2 = ufs[root2] # Find which root to append to and which root gets # appended. 'a' is the one that 'd' will be # appended to. a, d = (root1, root2) if ((ufs_root1 >> 2) > (ufs_root2 >> 2)) \ else (root2, root1) # Increment the count of vertices in 'a' total_count = (ufs_root1 >> 2) + (ufs_root2 >> 2) ufs[a] = (total_count << 2) | self.UFS_TYPE_ROOT # Set 'd' as a child of a # union operation (set parent) ufs[d] = self.UFS_TYPE_CHILD | (a << 2) # Move all of "d's" elements to "a" comps[a] |= comps[d] # Delete key since we don't need it anymore del comps[d] # Append the new union find to our stack of union finds data.union_find.append(ufs) # Append the new component dictionary to our # stack of component dictionaries data.component_store.append(comps) def induced_color_components(self): def buildTDD(vertices, colorClasses, decomp, parent=None): """ Creates a treedepth decomposition for the subtree rooted at parent Arguments: vertices (iterable): Vertices that have yet to be assigned a level in the decomposition colorClasses (dict): Dictionary mapping a color to a set of vertices that have that color parent (vertex, optional): Parent of the subtrees for the remaining vertices. Should only be set to None when decomp is None. decomp (TDDecomposition, optional): Treedepth decomposition being created from the vertices. If None, the function will create a new TDDecomposition for each component. """ component_color_freqs = self.coloring.frequencies(vertices) if len(component_color_freqs) == 2: # pop some vertex to check some_vertex = vertices.pop() # add it back because we don't want to lose it vertices.add(some_vertex) # Check to see if this is the center if (component_color_freqs[self.coloring[some_vertex]] == 1): # it is the center, so build tdDecomposition without making # recursive calls decomp.update_parent_child(some_vertex, parent) vertices.remove(some_vertex) decomp.update_parent_children(vertices, some_vertex) # not the center else: # delete entry in dictionary for the color of that vertex, # which leaves us with the color of the center del component_color_freqs[self.coloring[some_vertex]] # get that vertex to add to the decomposition vertex = (colorClasses[component_color_freqs.keys()[0]] & vertices).pop() decomp.update_parent_child(vertex, parent) vertices.remove(vertex) decomp.update_parent_children(vertices, vertex) else: # find a center for this subtree for color, v_set in colorClasses.iteritems(): remainingInColor = vertices & v_set # if the color is a center if len(remainingInColor) == 1: nextVertices = vertices - remainingInColor v = remainingInColor.pop() # update parent/child relations decomp.update_parent_child(v, parent) for comp in decomp.get_components(nextVertices): # recursively build the decomposition in the # subtrees buildTDD(comp, colorClasses, decomp, v) # stop searching break # if we are at the root of the decomposition, we should # save it if parent is None: return decomp # For each component yielded by walk_color_space for component in self.walk_color_space(): # Computer color classes colorClasses = self.G.get_color_classes(component, self.coloring) # Make a new set since we don't want to alter the one in our # component store vertices = set(component) # Make a new TDDecomposition object decomposition = TDDecomposition.fromSubgraph( self.G, vertices, self.coloring) # Yield the tree depth decomposition for this component yield buildTDD(vertices, colorClasses, decomposition)