def heights(g, scale=-1): """ Compute the order of all vertices at scale `scale`. If scale == -1, the compute the order for vertices at the finer scale. """ heights = {} if scale <= 0: for vid in traversal.iter_mtg2(g, g.root): pid = g.parent(vid) p_height = -1 if pid is None else heights[pid] heights[vid] = p_height+1 else: for rid in g.roots_iter(scale=scale): for vid in traversal.pre_order2(g, rid): pid = g.parent(vid) p_height = -1 if pid is None else heights[pid] heights[vid] = p_height+1 return heights
def orders(g, scale=-1): """ Compute the order of all vertices at scale `scale`. If scale == -1, the compute the order for vertices at the finer scale. """ orders = {} if scale <= 0: for vid in traversal.iter_mtg2(g, g.root): pid = g.parent(vid) p_order = 0 if pid is None else orders[pid] orders[vid] = p_order+1 if g.edge_type(vid) == '+' else p_order else: for rid in g.roots_iter(scale=scale): for vid in traversal.pre_order2(g, rid): pid = g.parent(vid) p_order = 0 if pid is None else orders[pid] orders[vid] = p_order+1 if g.edge_type(vid) == '+' else p_order return orders
def layout2d(g, vid=None, origin=(0, 0), steps=(4, 8), property_name='position'): """ Compute 2d coordinates for each vertex. This method compute a 2D layout of a tree or a MTG at a specific scale. This will allow to plot tree in matplotlib for instance. :Usage: .. code-block:: python >>> g.reindex() >>> g1 = g.reindex(copy=True) >>> mymap = dict(zip(list(traversal.iter_mtg2(g,g.root)), range(len(g)))) >>> g2 = g.reindex(mapping=mymap, copy=True) :Optional Parameters: - `origin` : a 2D point for the root of the tree. - `property` (str) : Name of the property storing the 2D coordinates. :Returns: - a MTG """ if vid is None: vid = g.root if hasattr(g, 'max_scale'): vid = g.component_roots_at_scale_iter(g.root, g.max_scale()).next() # Algorithm # 1. the y is defined by the Height of a node # 2. The x is computed using non intersecting bounding box x_step, y_step = steps y = {} vtxs = traversal.pre_order2(g, vid) vtxs.next() y[vid] = origin[1] for v in vtxs: y[v] = y[g.parent(v)] + y_step bbox = {} for v in traversal.post_order2(g, vid): children = g.children(v) if not children: bbox[v] = 2 * x_step else: has_successor = [ cid for cid in children if g.edge_type(cid) == '<' ] # 2 is for symmetry bbox[v] = sum(bbox[cid] for cid in children) if not has_successor: bbox[v] += 2 * x_step x = {} vtxs = traversal.pre_order2(g, vid) x[vid] = int(bbox[vid] / 2.) for v in vtxs: _width = bbox[v] kids = g.children(v) successor = [cid for cid in kids if g.edge_type(cid) == '<'] ramifs = [cid for cid in kids if g.edge_type(cid) == '+'] _min = x[v] _max = x[v] for cid in successor: _x = x[cid] = x[v] width = bbox[cid] _min = _x - max(1, width / 2) - x_step _max = _x + max(1, width / 2) + x_step weights = [bbox[rid] for rid in ramifs] def mean_ind(weights): left = bool(random() < 0.5) mid = sum(weights) / 2. total = 0 n = 0 for w in weights: if total + w <= mid: total += w else: break n += 1 if total - mid > 1 and left: n += 1 if n == 0 and len(weights) > 1: n += 1 return n n = mean_ind(weights) for rid in reversed(ramifs[:n]): width = bbox[rid] x[rid] = _min - max(1, width / 2) _min -= width for rid in ramifs[n:]: width = bbox[rid] x[rid] = _max + max(1, width / 2) _max += width # TODO: The tree s not well proportionned because we impose a constraint that the # < is aligned to its parent position = dict((k, (x[k], y[k])) for k in y) g.properties()[property_name] = position return g
def simple_layout(g, vid=None, origin=(0, 0), steps=(4, 8), property_name='position', multiscale=True): """ Compute 2d coordinates for each vertex. This method compute a 2D layout of a tree or a MTG at a specific scale. This will allow to plot tree in matplotlib for instance. :Usage: .. code-block:: python >>> g = simple_layout(g) :Optional Parameters: - `origin` : a 2D point for the root of the tree. - `property` (str) : Name of the property storing the 2D coordinates. :Returns: - a MTG """ def shuffle_post_order(tree, vtx_id): ''' Traverse a tree in a postfix way. (from leaves to root) Same algorithm than post_order. The goal is to replace the post_order implementation. ''' edge_type = tree.property('edge_type') def shuffle_children(vid): ''' Internal function to retrieve the children in a correct order: - Branch before successor. ''' kids = tree.children(vid) plus = [] successor = [] for v in kids: if edge_type.get(v) == '<': successor.append(v) else: plus.append(v) n = len(plus) i = n / 2 if n % 2 != 0: left = int(random() < 0.5) i += left child = plus[:i] + successor + plus[i:] return child visited = set([]) queue = [vtx_id] # 1. select first '+' edges while queue: vtx_id = queue[-1] for vid in shuffle_children(vtx_id): if vid not in visited: queue.append(vid) break else: # no child or all have been visited yield vtx_id visited.add(vtx_id) queue.pop() if vid is None: vid = g.root if hasattr(g, 'max_scale'): vid = g.component_roots_at_scale_iter(g.root, g.max_scale()).next() # Algorithm # 1. the y is defined by the Height of a node # 2. The x is computed using non intersecting bounding box x_step, y_step = steps y = {} vtxs = traversal.pre_order2(g, vid) vtxs.next() y[vid] = origin[1] for v in vtxs: y[v] = y[g.parent(v)] + y_step def leaves(g, vid): for v in shuffle_post_order(g, vid): if g.is_leaf(v): yield v def successor(g, vid): for cid in g.children(vid): if g.edge_type(cid) == '<': return cid return x = {} x_pos = origin[0] for v in leaves(g, vid): x[v] = x_pos x_pos += x_step * 10 for v in shuffle_post_order(g, vid): if v in x: continue son_id = successor(g, v) if son_id: x[v] = x[son_id] else: children = g.children(v) x[v] = int( float(sum(x[cid] for cid in children)) / (len(children))) position = dict((k, (x[k], y[k])) for k in y) if multiscale: # get the position at the lower scales max_scale = g.scale(vid) for s in range(max_scale - 1, 0, -1): v_root = g.complex_at_scale(vid, scale=s) vtxs = traversal.pre_order2(g, v_root) for v in vtxs: comp_id = g.component_roots_at_scale_iter( v, scale=max_scale).next() position[v] = position[comp_id] g.properties()[property_name] = position return g
def layout2d(g, vid=None, origin=(0,0), steps=(4,8), property_name='position'): """ Compute 2d coordinates for each vertex. This method compute a 2D layout of a tree or a MTG at a specific scale. This will allow to plot tree in matplotlib for instance. :Usage: .. code-block:: python >>> g.reindex() >>> g1 = g.reindex(copy=True) >>> mymap = dict(zip(list(traversal.iter_mtg2(g,g.root)), range(len(g)))) >>> g2 = g.reindex(mapping=mymap, copy=True) :Optional Parameters: - `origin` : a 2D point for the root of the tree. - `property` (str) : Name of the property storing the 2D coordinates. :Returns: - a MTG """ if vid is None: vid = g.root if hasattr(g, 'max_scale'): vid = g.component_roots_at_scale_iter(g.root, g.max_scale()).next() # Algorithm # 1. the y is defined by the Height of a node # 2. The x is computed using non intersecting bounding box x_step, y_step = steps y= {} vtxs = traversal.pre_order2(g,vid) vtxs.next() y[vid] = origin[1] for v in vtxs: y[v] = y[g.parent(v)]+y_step bbox = {} for v in traversal.post_order2(g,vid): children = g.children(v) if not children: bbox[v] = 2*x_step else: has_successor = [cid for cid in children if g.edge_type(cid)=='<'] # 2 is for symmetry bbox[v] = sum(bbox[cid] for cid in children) if not has_successor: bbox[v]+=2*x_step x ={} vtxs = traversal.pre_order2(g,vid) x[vid] = int(bbox[vid]/2.) for v in vtxs: _width = bbox[v] kids = g.children(v) successor = [cid for cid in kids if g.edge_type(cid)=='<'] ramifs = [cid for cid in kids if g.edge_type(cid)=='+'] _min = x[v]; _max = x[v] for cid in successor: _x = x[cid] = x[v] width = bbox[cid] _min = _x-max(1,width/2)-x_step _max = _x+max(1,width/2)+x_step weights = [bbox[rid] for rid in ramifs] def mean_ind(weights): left = bool(random()<0.5) mid= sum(weights)/2. total = 0 n = 0 for w in weights: if total+w <= mid: total+= w else: break n+=1 if total-mid > 1 and left: n+=1 if n == 0 and len(weights)>1: n+=1 return n n = mean_ind(weights) for rid in reversed(ramifs[:n]): width = bbox[rid] x[rid] = _min - max(1,width/2) _min -= width for rid in ramifs[n:]: width = bbox[rid] x[rid] = _max + max(1,width/2) _max += width # TODO: The tree s not well proportionned because we impose a constraint that the # < is aligned to its parent position = dict((k, (x[k],y[k])) for k in y) g.properties()[property_name] = position return g
def simple_layout(g, vid=None, origin=(0,0), steps=(4,8), property_name='position', multiscale=True): """ Compute 2d coordinates for each vertex. This method compute a 2D layout of a tree or a MTG at a specific scale. This will allow to plot tree in matplotlib for instance. :Usage: .. code-block:: python >>> g = simple_layout(g) :Optional Parameters: - `origin` : a 2D point for the root of the tree. - `property` (str) : Name of the property storing the 2D coordinates. :Returns: - a MTG """ def shuffle_post_order(tree, vtx_id): ''' Traverse a tree in a postfix way. (from leaves to root) Same algorithm than post_order. The goal is to replace the post_order implementation. ''' edge_type = tree.property('edge_type') def shuffle_children(vid): ''' Internal function to retrieve the children in a correct order: - Branch before successor. ''' kids = tree.children(vid) plus = [] successor = [] for v in kids: if edge_type.get(v) == '<': successor.append(v) else: plus.append(v) n = len(plus) i = n/2 if n%2!=0: left = int(random()<0.5) i += left child = plus[:i]+successor+plus[i:] return child visited = set([]) queue = [vtx_id] # 1. select first '+' edges while queue: vtx_id = queue[-1] for vid in shuffle_children(vtx_id): if vid not in visited: queue.append(vid) break else: # no child or all have been visited yield vtx_id visited.add(vtx_id) queue.pop() if vid is None: vid = g.root if hasattr(g, 'max_scale'): vid = g.component_roots_at_scale_iter(g.root, g.max_scale()).next() # Algorithm # 1. the y is defined by the Height of a node # 2. The x is computed using non intersecting bounding box x_step, y_step = steps y= {} vtxs = traversal.pre_order2(g,vid) vtxs.next() y[vid] = origin[1] for v in vtxs: y[v] = y[g.parent(v)]+y_step def leaves(g, vid): for v in shuffle_post_order(g,vid): if g.is_leaf(v): yield v def successor(g, vid): for cid in g.children(vid): if g.edge_type(cid)=='<': return cid return x = {} x_pos = origin[0] for v in leaves(g,vid): x[v] = x_pos x_pos += x_step*10 for v in shuffle_post_order(g,vid): if v in x: continue son_id = successor(g,v) if son_id: x[v] = x[son_id] else: children = g.children(v) x[v] = int(float(sum(x[cid] for cid in children)) / (len(children))) position = dict((k, (x[k],y[k])) for k in y) if multiscale: # get the position at the lower scales max_scale = g.scale(vid) for s in range(max_scale-1,0,-1): v_root = g.complex_at_scale(vid, scale=s) vtxs = traversal.pre_order2(g, v_root) for v in vtxs: comp_id = g.component_roots_at_scale_iter(v, scale=max_scale).next() position[v] = position[comp_id] g.properties()[property_name] = position return g