def simplify_graph(infr, graph=None, copy=True): if graph is None: graph = infr.graph simple = graph.copy() if copy else graph ut.nx_delete_edge_attr(simple, infr.visual_edge_attrs) ut.nx_delete_node_attr(simple, infr.visual_node_attrs + ['pin']) return simple
def initialize_visual_node_attrs(infr, graph=None): infr.print('initialize_visual_node_attrs!!!') infr.print('initialize_visual_node_attrs', 3) # import networkx as nx if graph is None: graph = infr.graph # nx.set_node_attributes(graph, name='framewidth', values=3.0) # nx.set_node_attributes(graph, name='shape', values=ut.dzip(annot_nodes, ['rect'])) ut.nx_delete_node_attr(graph, 'size') ut.nx_delete_node_attr(graph, 'width') ut.nx_delete_node_attr(graph, 'height') ut.nx_delete_node_attr(graph, 'radius') infr._viz_init_nodes = True infr._viz_image_config_dirty = False
def show_arch_nx_graph(layers, fnum=None, fullinfo=True): r""" CommandLine: python -m ibeis_cnn.draw_net show_arch_nx_graph:0 --show python -m ibeis_cnn.draw_net show_arch_nx_graph:1 --show Example0: >>> # ENABLE_DOCTEST >>> from ibeis_cnn.draw_net import * # NOQA >>> from ibeis_cnn import models >>> model = models.mnist.MNISTModel(batch_size=128, output_dims=10, >>> data_shape=(24, 24, 3)) >>> model.init_arch() >>> layers = model.get_all_layers() >>> show_arch_nx_graph(layers) >>> ut.quit_if_noshow() >>> import plottool as pt >>> ut.show_if_requested() Example1: >>> # ENABLE_DOCTEST >>> from ibeis_cnn.draw_net import * # NOQA >>> from ibeis_cnn import models >>> model = models.SiameseCenterSurroundModel(autoinit=True) >>> layers = model.get_all_layers() >>> show_arch_nx_graph(layers) >>> ut.quit_if_noshow() >>> import plottool as pt >>> ut.show_if_requested() """ import networkx as nx import plottool as pt import ibeis_cnn.__LASAGNE__ as lasange #from matplotlib import offsetbox #import matplotlib as mpl REMOVE_BATCH_SIZE = True from ibeis_cnn import net_strs def get_hex_color(layer_type): if 'Input' in layer_type: return '#A2CECE' if 'Conv2D' in layer_type: return '#7C9ABB' if 'Dense' in layer_type: return '#6CCF8D' if 'Pool' in layer_type: return '#9D9DD2' if 'SoftMax' in layer_type: return '#7E9FD9' else: return '#{0:x}'.format(hash(layer_type + 'salt') % 2 ** 24) node_dict = {} edge_list = [] edge_attrs = ut.ddict(dict) # Make layer ids (ensure no duplicates) layer_to_id = { l: repr(l) if l.name is None else l.name for l in set(layers) } keys_ = layer_to_id.keys() dups = ut.find_duplicate_items(layer_to_id.values()) for dupval, dupidxs in dups.items(): newval_fmt = dupval + '_%d' for layer in ut.take(keys_, dupidxs): newid = ut.get_nonconflicting_string(newval_fmt, layer_to_id.values()) layer_to_id[layer] = newid def layerid(layer): return layer_to_id[layer] main_nodes = [] for i, layer in enumerate(layers): layer_info = net_strs.get_layer_info(layer) layer_type = layer_info['classalias'] key = layerid(layer) color = get_hex_color(layer_info['classalias']) # Make label lines = [] if layer_info['name'] is not None: lines.append(layer_info['name']) if fullinfo: lines.append(layer_info['classalias']) for attr, val in layer_info['layer_attrs'].items(): if attr == 'shape' and REMOVE_BATCH_SIZE: val = val[1:] if attr == 'output_shape' and REMOVE_BATCH_SIZE: val = val[1:] lines.append('{0}: {1}'.format(attr, val)) nonlinearity = layer_info.get('nonlinearity') if nonlinearity is not None: alias_map = { 'LeakyRectify': 'LReLU', } val = layer_info['nonlinearity']['type'] val = alias_map.get(val, val) lines.append('nonlinearity:\n{0}'.format(val)) label = '\n'.join(lines) # append node is_main_layer = len(layer.params) > 0 #is_main_layer = len(lasange.layers.get_all_params(layer, trainable=True)) > 0 if layer_info['classname'] in lasange.layers.normalization.__all__: is_main_layer = False if layer_info['classname'] in lasange.layers.special.__all__: is_main_layer = False if layer_info['classname'].startswith('BatchNorm'): is_main_layer = False if layer_info['classname'].startswith('ElemwiseSum'): is_main_layer = True if layer_type == 'Input': is_main_layer = True if hasattr(layer, '_is_main_layer'): is_main_layer = layer._is_main_layer #if getattr(layer, 'name', '') is not None and getattr(layer, 'name', '') .endswith('/sum'): # is_main_layer = True node_attr = dict(name=key, label=label, color=color, fillcolor=color, style='filled', is_main_layer=is_main_layer) node_attr['is_main_layer'] = is_main_layer if is_main_layer: main_nodes.append(key) node_attr['classalias'] = layer_info['classalias'] if is_main_layer or node_attr['classalias'].startswith('Conv'): if hasattr(layer, 'shape'): if len(layer.shape) == 3: node_attr['out_size'] = (layer.shape[2], layer.shape[1]) node_attr['depth'] = layer.output_shape[0] if hasattr(layer, 'output_shape'): if len(layer.output_shape) == 4: depth = layer.output_shape[1] width, height = (layer.output_shape[3], layer.output_shape[2]) xshift = -width * (.1 / (depth ** (1 / 3))) / 3 yshift = height * (.1 / (depth ** (1 / 3))) / 2 node_attr['depth'] = depth node_attr['xshift'] = xshift node_attr['yshift'] = yshift node_attr['out_size'] = (width, height) if len(layer.output_shape) == 2: node_attr['out_size'] = (1, layer.output_shape[1]) node_dict[key] = node_attr _input_layers = [] if hasattr(layer, 'input_layers'): _input_layers += layer.input_layers if hasattr(layer, 'input_layer'): _input_layers += [layer.input_layer] for input_layer in _input_layers: parent_key = layerid(input_layer) edge = (parent_key, key) edge_list.append(edge) main_size_ = np.array((100, 100)) * 4 sub_size = np.array((75, 50)) * 4 # Setup scaled width and heights out_size_list = [v['out_size'] for v in node_dict.values() if 'out_size' in v] out_size_list = np.array(out_size_list) #out_size_list = out_size_list[out_size_list.T[0] > 1] area_arr = np.prod(out_size_list, axis=1) main_outsize = np.array(out_size_list[area_arr.argmax()]) #main_outsize = np.array(out_size_list[area_arr.argmin()]) scale = main_size_ / main_outsize scale_dense_max = .25 scale_dense_min = 8 for k, v in node_dict.items(): if v['is_main_layer'] or v['classalias'].startswith('Conv'): if 'out_size' in v: # Make dense layers more visible if v['classalias'] == 'Dense': v['shape'] = 'rect' v['width'] = scale_dense_min if v['out_size'][1] > main_outsize[1]: v['height'] = v['out_size'][1] * scale[1] * scale_dense_max elif v['out_size'][1] < scale_dense_min: v['height'] = scale_dense_min * v['out_size'][1] else: v['height'] = v['out_size'][1] elif v['classalias'].startswith('Conv'): v['shape'] = 'stack' #v['shape'] = 'rect' v['width'] = v['out_size'][0] * scale[0] v['height'] = v['out_size'][1] * scale[1] else: v['shape'] = 'rect' v['width'] = v['out_size'][0] * scale[0] v['height'] = v['out_size'][1] * scale[1] else: v['shape'] = 'rect' v['width'] = main_size_[0] v['height'] = main_size_[1] else: #v['shape'] = 'ellipse' v['shape'] = 'rect' v['style'] = 'rounded' v['width'] = sub_size[0] v['height'] = sub_size[1] key_order = ut.take(layer_to_id, layers) node_dict = ut.dict_subset(node_dict, key_order) #print('node_dict = ' + ut.repr3(node_dict)) # Create the networkx graph structure G = nx.DiGraph() G.add_nodes_from(node_dict.items()) G.add_edges_from(edge_list) for key, val in edge_attrs.items(): nx.set_edge_attributes(G, key, val) # Add invisible structure #main_nodes = [key for key, val in # nx.get_node_attributes(G, 'is_main_layer').items() if val] main_children = ut.odict() #for n1, n2 in ut.itertwo(main_nodes): # print('n1, n2 = %r %r' % (n1, n2)) # import utool # utool.embed() # children = ut.nx_all_nodes_between(G, n1, n2) # if n1 in children: # children.remove(n1) # if n2 in children: # children.remove(n2) # main_children[n1] = children # #pass #main_children[main_nodes[-1]] = [] for n1 in main_nodes: main_children[n1] = [] # Main nodes only place constraints on nodes in the next main group. # Not their own next_main = None G.node[n1]['group'] = n1 for (_, n2) in nx.bfs_edges(G, n1): if next_main is None: if n2 in main_nodes: next_main = n2 else: G.node[n2]['group'] = n1 main_children[n1].append(n2) else: if n2 not in list(nx.descendants(G, next_main)): G.node[n2]['group'] = n1 main_children[n1].append(n2) # Custom positioning x = 0 y = 1000 #print('main_children = %s' % (ut.repr3(main_children),)) #main_nodes = ut.isect(list(nx.topological_sort(G)), main_nodes) xpad = main_size_[0] * .3 ypad = main_size_[1] * .3 # Draw each main node, and then put its children under it # Then move to the left and draw the next main node. cumwidth = 0 for n1 in main_nodes: cumheight = 0 maxwidth = G.node[n1]['width'] for n2 in main_children[n1]: maxwidth = max(maxwidth, G.node[n2]['width']) cumwidth += xpad cumwidth += maxwidth / 2 pos = np.array([x + cumwidth, y - cumheight]) G.node[n1]['pos'] = pos G.node[n1]['pin'] = 'true' height = G.node[n1]['height'] cumheight += height / 2 for n2 in main_children[n1]: height = G.node[n2]['height'] cumheight += ypad cumheight += height / 2 pos = np.array([x + cumwidth, y - cumheight]) G.node[n2]['pos'] = pos G.node[n2]['pin'] = 'true' cumheight += height / 2 cumwidth += maxwidth / 2 # Pin everybody nx.set_node_attributes(G, 'pin', 'true') layoutkw = dict(prog='neato', splines='line') #layoutkw = dict(prog='neato', splines='spline') layoutkw = dict(prog='neato', splines='ortho') G_ = G.copy() # delete lables for positioning _labels = nx.get_node_attributes(G_, 'label') ut.nx_delete_node_attr(G_, 'label') nx.set_node_attributes(G_, 'label', '') nolayout = False if nolayout: G_.remove_edges_from(list(G_.edges())) else: layout_info = pt.nx_agraph_layout(G_, inplace=True, **layoutkw) # NOQA # reset labels if not nolayout: nx.set_node_attributes(G_, 'label', _labels) _ = pt.show_nx(G_, fontsize=8, arrow_width=.3, layout='custom', fnum=fnum) # NOQA #pt.adjust_subplots(top=1, bot=0, left=0, right=1) pt.plt.tight_layout()
def intraoccurrence_connected(): r""" CommandLine: python -m ibeis.scripts.specialdraw intraoccurrence_connected --show python -m ibeis.scripts.specialdraw intraoccurrence_connected --show --postcut python -m ibeis.scripts.specialdraw intraoccurrence_connected --show --smaller Example: >>> # DISABLE_DOCTEST >>> from ibeis.scripts.specialdraw import * # NOQA >>> result = intraoccurrence_connected() >>> print(result) >>> ut.quit_if_noshow() >>> import plottool as pt >>> ut.show_if_requested() """ import ibeis import plottool as pt from ibeis.viz import viz_graph import networkx as nx pt.ensure_pylab_qt4() ibs = ibeis.opendb(defaultdb='PZ_Master1') nid2_aid = { #4880: [3690, 3696, 3703, 3706, 3712, 3721], 4880: [3690, 3696, 3703], 6537: [3739], 6653: [7671], 6610: [7566, 7408], #6612: [7664, 7462, 7522], #6624: [7465, 7360], #6625: [7746, 7383, 7390, 7477, 7376, 7579], 6630: [7586, 7377, 7464, 7478], #6677: [7500] } nid2_dbaids = {4880: [33, 6120, 7164], 6537: [7017, 7206], 6653: [7660]} if ut.get_argflag('--small') or ut.get_argflag('--smaller'): del nid2_aid[6630] del nid2_aid[6537] del nid2_dbaids[6537] if ut.get_argflag('--smaller'): nid2_dbaids[4880].remove(33) nid2_aid[4880].remove(3690) nid2_aid[6610].remove(7408) #del nid2_aid[4880] #del nid2_dbaids[4880] aids = ut.flatten(nid2_aid.values()) temp_nids = [1] * len(aids) postcut = ut.get_argflag('--postcut') aids_list = ibs.group_annots_by_name(aids)[0] ensure_edges = 'all' if True or not postcut else None unlabeled_graph = viz_graph.make_netx_graph_from_aid_groups( ibs, aids_list, #invis_edges=invis_edges, ensure_edges=ensure_edges, temp_nids=temp_nids) viz_graph.color_by_nids(unlabeled_graph, unique_nids=[1] * len(list(unlabeled_graph.nodes()))) viz_graph.ensure_node_images(ibs, unlabeled_graph) nx.set_node_attributes(unlabeled_graph, 'shape', 'rect') #unlabeled_graph = unlabeled_graph.to_undirected() # Find the "database exemplars for these annots" if False: gt_aids = ibs.get_annot_groundtruth(aids) gt_aids = [ut.setdiff(s, aids) for s in gt_aids] dbaids = ut.unique(ut.flatten(gt_aids)) dbaids = ibs.filter_annots_general(dbaids, minqual='good') ibs.get_annot_quality_texts(dbaids) else: dbaids = ut.flatten(nid2_dbaids.values()) exemplars = nx.DiGraph() #graph = exemplars # NOQA exemplars.add_nodes_from(dbaids) def add_clique(graph, nodes, edgeattrs={}, nodeattrs={}): edge_list = ut.upper_diag_self_prodx(nodes) graph.add_edges_from(edge_list, **edgeattrs) return edge_list for aids_, nid in zip(*ibs.group_annots_by_name(dbaids)): add_clique(exemplars, aids_) viz_graph.ensure_node_images(ibs, exemplars) viz_graph.color_by_nids(exemplars, ibs=ibs) nx.set_node_attributes(unlabeled_graph, 'framewidth', False) nx.set_node_attributes(exemplars, 'framewidth', 4.0) nx.set_node_attributes(unlabeled_graph, 'group', 'unlab') nx.set_node_attributes(exemplars, 'group', 'exemp') #big_graph = nx.compose_all([unlabeled_graph]) big_graph = nx.compose_all([exemplars, unlabeled_graph]) # add sparse connections from unlabeled to exemplars import numpy as np rng = np.random.RandomState(0) if True or not postcut: for aid_ in unlabeled_graph.nodes(): flags = rng.rand(len(exemplars)) > .5 nid_ = ibs.get_annot_nids(aid_) exnids = np.array(ibs.get_annot_nids(list(exemplars.nodes()))) flags = np.logical_or(exnids == nid_, flags) exmatches = ut.compress(list(exemplars.nodes()), flags) big_graph.add_edges_from(list(ut.product([aid_], exmatches)), color=pt.ORANGE, implicit=True) else: for aid_ in unlabeled_graph.nodes(): flags = rng.rand(len(exemplars)) > .5 exmatches = ut.compress(list(exemplars.nodes()), flags) nid_ = ibs.get_annot_nids(aid_) exnids = np.array(ibs.get_annot_nids(exmatches)) exmatches = ut.compress(exmatches, exnids == nid_) big_graph.add_edges_from(list(ut.product([aid_], exmatches))) pass nx.set_node_attributes(big_graph, 'shape', 'rect') #if False and postcut: # ut.nx_delete_node_attr(big_graph, 'nid') # ut.nx_delete_edge_attr(big_graph, 'color') # viz_graph.ensure_graph_nid_labels(big_graph, ibs=ibs) # viz_graph.color_by_nids(big_graph, ibs=ibs) # big_graph = big_graph.to_undirected() layoutkw = { 'sep': 1 / 5, 'prog': 'neato', 'overlap': 'false', #'splines': 'ortho', 'splines': 'spline', } as_directed = False #as_directed = True #hacknode = True hacknode = 0 graph = big_graph ut.nx_ensure_agraph_color(graph) if hacknode: nx.set_edge_attributes(graph, 'taillabel', {e: str(e[0]) for e in graph.edges()}) nx.set_edge_attributes(graph, 'headlabel', {e: str(e[1]) for e in graph.edges()}) explicit_graph = pt.get_explicit_graph(graph) _, layout_info = pt.nx_agraph_layout(explicit_graph, orig_graph=graph, inplace=True, **layoutkw) if ut.get_argflag('--smaller'): graph.node[7660]['pos'] = np.array([550, 350]) graph.node[6120]['pos'] = np.array([200, 600]) + np.array([350, -400]) graph.node[7164]['pos'] = np.array([200, 480]) + np.array([350, -400]) nx.set_node_attributes(graph, 'pin', 'true') _, layout_info = pt.nx_agraph_layout(graph, inplace=True, **layoutkw) elif ut.get_argflag('--small'): graph.node[7660]['pos'] = np.array([750, 350]) graph.node[33]['pos'] = np.array([300, 600]) + np.array([350, -400]) graph.node[6120]['pos'] = np.array([500, 600]) + np.array([350, -400]) graph.node[7164]['pos'] = np.array([410, 480]) + np.array([350, -400]) nx.set_node_attributes(graph, 'pin', 'true') _, layout_info = pt.nx_agraph_layout(graph, inplace=True, **layoutkw) if not postcut: #pt.show_nx(graph.to_undirected(), layout='agraph', layoutkw=layoutkw, # as_directed=False) #pt.show_nx(graph, layout='agraph', layoutkw=layoutkw, # as_directed=as_directed, hacknode=hacknode) pt.show_nx(graph, layout='custom', layoutkw=layoutkw, as_directed=as_directed, hacknode=hacknode) else: #explicit_graph = pt.get_explicit_graph(graph) #_, layout_info = pt.nx_agraph_layout(explicit_graph, orig_graph=graph, # **layoutkw) #layout_info['edge']['alpha'] = .8 #pt.apply_graph_layout_attrs(graph, layout_info) #graph_layout_attrs = layout_info['graph'] ##edge_layout_attrs = layout_info['edge'] ##node_layout_attrs = layout_info['node'] #for key, vals in layout_info['node'].items(): # #print('[special] key = %r' % (key,)) # nx.set_node_attributes(graph, key, vals) #for key, vals in layout_info['edge'].items(): # #print('[special] key = %r' % (key,)) # nx.set_edge_attributes(graph, key, vals) #nx.set_edge_attributes(graph, 'alpha', .8) #graph.graph['splines'] = graph_layout_attrs.get('splines', 'line') #graph.graph['splines'] = 'polyline' # graph_layout_attrs.get('splines', 'line') #graph.graph['splines'] = 'line' cut_graph = graph.copy() edge_list = list(cut_graph.edges()) edge_nids = np.array(ibs.unflat_map(ibs.get_annot_nids, edge_list)) cut_flags = edge_nids.T[0] != edge_nids.T[1] cut_edges = ut.compress(edge_list, cut_flags) cut_graph.remove_edges_from(cut_edges) ut.nx_delete_node_attr(cut_graph, 'nid') viz_graph.ensure_graph_nid_labels(cut_graph, ibs=ibs) #ut.nx_get_default_node_attributes(exemplars, 'color', None) ut.nx_delete_node_attr(cut_graph, 'color', nodes=unlabeled_graph.nodes()) aid2_color = ut.nx_get_default_node_attributes(cut_graph, 'color', None) nid2_colors = ut.group_items(aid2_color.values(), ibs.get_annot_nids(aid2_color.keys())) nid2_colors = ut.map_dict_vals(ut.filter_Nones, nid2_colors) nid2_colors = ut.map_dict_vals(ut.unique, nid2_colors) #for val in nid2_colors.values(): # assert len(val) <= 1 # Get initial colors nid2_color_ = { nid: colors_[0] for nid, colors_ in nid2_colors.items() if len(colors_) == 1 } graph = cut_graph viz_graph.color_by_nids(cut_graph, ibs=ibs, nid2_color_=nid2_color_) nx.set_node_attributes(cut_graph, 'framewidth', 4) pt.show_nx(cut_graph, layout='custom', layoutkw=layoutkw, as_directed=as_directed, hacknode=hacknode) pt.zoom_factory()
def nx_agraph_layout(graph, orig_graph=None, inplace=False, verbose=None, **kwargs): r""" orig_graph = graph graph = layout_graph References: http://www.graphviz.org/content/attrs http://www.graphviz.org/doc/info/attrs.html """ import networkx as nx import pygraphviz kwargs = kwargs.copy() prog = kwargs.pop('prog', 'dot') if prog != 'dot': kwargs['overlap'] = kwargs.get('overlap', 'false') kwargs['splines'] = kwargs.get('splines', 'spline') kwargs['notranslate'] = 'true' # for neato postprocessing argparts = ['-G%s=%s' % (key, str(val)) for key, val in kwargs.items()] args = ' '.join(argparts) splines = kwargs['splines'] if verbose is None: verbose = ut.VERBOSE if verbose: print('args = %r' % (args,)) # Convert to agraph format graph_ = graph.copy() ut.nx_ensure_agraph_color(graph_) # Reduce size to be in inches not pixels # FIXME: make robust to param settings # Hack to make the w/h of the node take thae max instead of # dot which takes the minimum shaped_nodes = [n for n, d in graph_.nodes(data=True) if 'width' in d] node_attrs = ut.dict_take(graph_.node, shaped_nodes) width_px = np.array(ut.take_column(node_attrs, 'width')) height_px = np.array(ut.take_column(node_attrs, 'height')) scale = np.array(ut.dict_take_column(node_attrs, 'scale', default=1.0)) width_in = width_px / 72.0 * scale height_in = height_px / 72.0 * scale width_in_dict = dict(zip(shaped_nodes, width_in)) height_in_dict = dict(zip(shaped_nodes, height_in)) nx.set_node_attributes(graph_, 'width', width_in_dict) nx.set_node_attributes(graph_, 'height', height_in_dict) ut.nx_delete_node_attr(graph_, 'scale') # Check for any nodes with groupids node_to_groupid = nx.get_node_attributes(graph_, 'groupid') if node_to_groupid: groupid_to_nodes = ut.group_items(*zip(*node_to_groupid.items())) else: groupid_to_nodes = {} # Initialize agraph format #import utool #utool.embed() ut.nx_delete_None_edge_attr(graph_) agraph = nx.nx_agraph.to_agraph(graph_) # Add subgraphs labels # TODO: subgraph attrs group_attrs = graph.graph.get('groupattrs', {}) for groupid, nodes in groupid_to_nodes.items(): # subgraph_attrs = {} subgraph_attrs = group_attrs.get(groupid, {}).copy() cluster_flag = True # FIXME: make this more natural to specify if 'cluster' in subgraph_attrs: cluster_flag = subgraph_attrs['cluster'] del subgraph_attrs['cluster'] # subgraph_attrs = dict(rankdir='LR') # subgraph_attrs = dict(rankdir='LR') # subgraph_attrs['rank'] = 'min' # subgraph_attrs['rank'] = 'source' name = groupid if cluster_flag: # graphviz treast subgraphs labeld with cluster differently name = 'cluster_' + groupid else: name = groupid agraph.add_subgraph(nodes, name, **subgraph_attrs) for node in graph_.nodes(): # force pinning of node points anode = pygraphviz.Node(agraph, node) if anode.attr['pin'] == 'true': if anode.attr['pos'] is not None and len(anode.attr['pos']) > 0 and not anode.attr['pos'].endswith('!'): import re #utool.embed() ptstr_ = anode.attr['pos'] #print('ptstr_ = %r' % (ptstr_,)) ptstr = ptstr_.strip('[]').strip(' ').strip('()') #print('ptstr = %r' % (ptstr,)) ptstr_list = [x.rstrip(',') for x in re.split(r'\s+', ptstr)] #print('ptstr_list = %r' % (ptstr_list,)) pt_list = list(map(float, ptstr_list)) #print('pt_list = %r' % (pt_list,)) pt_arr = np.array(pt_list) / 72.0 #print('pt_arr = %r' % (pt_arr,)) new_ptstr_list = list(map(str, pt_arr)) new_ptstr = ','.join(new_ptstr_list) + '!' #print('new_ptstr = %r' % (new_ptstr,)) anode.attr['pos'] = new_ptstr # Run layout #print('prog = %r' % (prog,)) if ut.VERBOSE or verbose > 0: print('BEFORE LAYOUT\n' + str(agraph)) agraph.layout(prog=prog, args=args) agraph.draw(ut.truepath('~/test_graphviz_draw.png')) if ut.VERBOSE or verbose > 1: print('AFTER LAYOUT\n' + str(agraph)) # TODO: just replace with a single dict of attributes node_layout_attrs = ut.ddict(dict) edge_layout_attrs = ut.ddict(dict) #for node in agraph.nodes(): for node in graph_.nodes(): anode = pygraphviz.Node(agraph, node) node_attrs = parse_anode_layout_attrs(anode) for key, val in node_attrs.items(): node_layout_attrs[key][node] = val edges = list(ut.nx_edges(graph_, keys=True)) for edge in edges: aedge = pygraphviz.Edge(agraph, *edge) edge_attrs = parse_aedge_layout_attrs(aedge) for key, val in edge_attrs.items(): edge_layout_attrs[key][edge] = val if orig_graph is not None and kwargs.get('draw_implicit', True): # ADD IN IMPLICIT EDGES layout_edges = set(ut.nx_edges(graph_, keys=True)) orig_edges = set(ut.nx_edges(orig_graph, keys=True)) implicit_edges = list(orig_edges - layout_edges) #all_edges = list(set.union(orig_edges, layout_edges)) needs_implicit = len(implicit_edges) > 0 if needs_implicit: # Pin down positions for node in agraph.nodes(): anode = pygraphviz.Node(agraph, node) anode.attr['pin'] = 'true' anode.attr['pos'] += '!' # Add new edges to route for iedge in implicit_edges: data = orig_graph.get_edge_data(*iedge) agraph.add_edge(*iedge, **data) if ut.VERBOSE or verbose: print('BEFORE IMPLICIT LAYOUT\n' + str(agraph)) # Route the implicit edges (must use neato) control_node = pygraphviz.Node(agraph, node) #print('control_node = %r' % (control_node,)) node1_attr1 = parse_anode_layout_attrs(control_node) #print('node1_attr1 = %r' % (node1_attr1,)) implicit_kw = kwargs.copy() implicit_kw['overlap'] = 'true' #del implicit_kw['overlap'] # can cause node positions to change argparts = ['-G%s=%s' % (key, str(val)) for key, val in implicit_kw.items()] args = ' '.join(argparts) #print('args = %r' % (args,)) #import utool #utool.embed() agraph.layout(prog='neato', args='-n ' + args) agraph.draw(ut.truepath('~/implicit_test_graphviz_draw.png')) if ut.VERBOSE or verbose: print('AFTER IMPLICIT LAYOUT\n' + str(agraph)) control_node = pygraphviz.Node(agraph, node) print('control_node = %r' % (control_node,)) node1_attr2 = parse_anode_layout_attrs(control_node) print('node1_attr2 = %r' % (node1_attr2,)) # graph positions shifted # This is not the right place to divide by 72 translation = (node1_attr1['pos'] - node1_attr2['pos'] ) #print('translation = %r' % (translation,)) #translation = np.array([0, 0]) print('translation = %r' % (translation,)) #for iedge in all_edges: for iedge in implicit_edges: aedge = pygraphviz.Edge(agraph, *iedge) iedge_attrs = parse_aedge_layout_attrs(aedge, translation) for key, val in iedge_attrs.items(): edge_layout_attrs[key][iedge] = val graph_layout_attrs = dict( splines=splines ) layout_info = { 'graph': graph_layout_attrs, 'edge': dict(edge_layout_attrs), 'node': dict(node_layout_attrs), } if inplace: if orig_graph is not None: graph = orig_graph apply_graph_layout_attrs(graph, layout_info) return graph, layout_info
def make_agraph(graph): # FIXME; use this in nx_agraph_layout instead to comparementalize more import networkx as nx import pygraphviz # Convert to agraph format graph_ = graph.copy() ut.nx_ensure_agraph_color(graph_) # Reduce size to be in inches not pixels # FIXME: make robust to param settings # Hack to make the w/h of the node take thae max instead of # dot which takes the minimum shaped_nodes = [n for n, d in graph_.nodes(data=True) if 'width' in d] node_attrs = ut.dict_take(graph_.node, shaped_nodes) width_px = np.array(ut.take_column(node_attrs, 'width')) height_px = np.array(ut.take_column(node_attrs, 'height')) scale = np.array(ut.dict_take_column(node_attrs, 'scale', default=1.0)) width_in = width_px / 72.0 * scale height_in = height_px / 72.0 * scale width_in_dict = dict(zip(shaped_nodes, width_in)) height_in_dict = dict(zip(shaped_nodes, height_in)) nx.set_node_attributes(graph_, 'width', width_in_dict) nx.set_node_attributes(graph_, 'height', height_in_dict) ut.nx_delete_node_attr(graph_, 'scale') # Check for any nodes with groupids node_to_groupid = nx.get_node_attributes(graph_, 'groupid') if node_to_groupid: groupid_to_nodes = ut.group_items(*zip(*node_to_groupid.items())) else: groupid_to_nodes = {} # Initialize agraph format #import utool #utool.embed() ut.nx_delete_None_edge_attr(graph_) agraph = nx.nx_agraph.to_agraph(graph_) # Add subgraphs labels # TODO: subgraph attrs for groupid, nodes in groupid_to_nodes.items(): subgraph_attrs = {} #subgraph_attrs = dict(rankdir='LR') #subgraph_attrs['rank'] = 'min' subgraph_attrs['rank'] = 'same' name = groupid name = 'cluster_' + groupid agraph.add_subgraph(nodes, name, **subgraph_attrs) for node in graph_.nodes(): # force pinning of node points anode = pygraphviz.Node(agraph, node) if anode.attr['pin'] == 'true': if anode.attr['pos'] is not None and not anode.attr['pos'].endswith('!'): import re #utool.embed() ptstr = anode.attr['pos'].strip('[]').strip(' ') ptstr_list = re.split(r'\s+', ptstr) pt_arr = np.array(list(map(float, ptstr_list))) / 72.0 #print('pt_arr = %r' % (pt_arr,)) new_ptstr_list = list(map(str, pt_arr)) new_ptstr = ','.join(new_ptstr_list) + '!' #print('new_ptstr = %r' % (new_ptstr,)) anode.attr['pos'] = new_ptstr return agraph
def intraoccurrence_connected(): r""" CommandLine: python -m ibeis.scripts.specialdraw intraoccurrence_connected --show python -m ibeis.scripts.specialdraw intraoccurrence_connected --show --postcut python -m ibeis.scripts.specialdraw intraoccurrence_connected --show --smaller Example: >>> # DISABLE_DOCTEST >>> from ibeis.scripts.specialdraw import * # NOQA >>> result = intraoccurrence_connected() >>> print(result) >>> ut.quit_if_noshow() >>> import plottool as pt >>> ut.show_if_requested() """ import ibeis import plottool as pt from ibeis.viz import viz_graph import networkx as nx pt.ensure_pylab_qt4() ibs = ibeis.opendb(defaultdb='PZ_Master1') nid2_aid = { #4880: [3690, 3696, 3703, 3706, 3712, 3721], 4880: [3690, 3696, 3703], 6537: [3739], 6653: [7671], 6610: [7566, 7408], #6612: [7664, 7462, 7522], #6624: [7465, 7360], #6625: [7746, 7383, 7390, 7477, 7376, 7579], 6630: [7586, 7377, 7464, 7478], #6677: [7500] } nid2_dbaids = { 4880: [33, 6120, 7164], 6537: [7017, 7206], 6653: [7660] } if ut.get_argflag('--small') or ut.get_argflag('--smaller'): del nid2_aid[6630] del nid2_aid[6537] del nid2_dbaids[6537] if ut.get_argflag('--smaller'): nid2_dbaids[4880].remove(33) nid2_aid[4880].remove(3690) nid2_aid[6610].remove(7408) #del nid2_aid[4880] #del nid2_dbaids[4880] aids = ut.flatten(nid2_aid.values()) temp_nids = [1] * len(aids) postcut = ut.get_argflag('--postcut') aids_list = ibs.group_annots_by_name(aids)[0] ensure_edges = 'all' if True or not postcut else None unlabeled_graph = viz_graph.make_netx_graph_from_aid_groups( ibs, aids_list, #invis_edges=invis_edges, ensure_edges=ensure_edges, temp_nids=temp_nids) viz_graph.color_by_nids(unlabeled_graph, unique_nids=[1] * len(list(unlabeled_graph.nodes()))) viz_graph.ensure_node_images(ibs, unlabeled_graph) nx.set_node_attributes(unlabeled_graph, 'shape', 'rect') #unlabeled_graph = unlabeled_graph.to_undirected() # Find the "database exemplars for these annots" if False: gt_aids = ibs.get_annot_groundtruth(aids) gt_aids = [ut.setdiff(s, aids) for s in gt_aids] dbaids = ut.unique(ut.flatten(gt_aids)) dbaids = ibs.filter_annots_general(dbaids, minqual='good') ibs.get_annot_quality_texts(dbaids) else: dbaids = ut.flatten(nid2_dbaids.values()) exemplars = nx.DiGraph() #graph = exemplars # NOQA exemplars.add_nodes_from(dbaids) def add_clique(graph, nodes, edgeattrs={}, nodeattrs={}): edge_list = ut.upper_diag_self_prodx(nodes) graph.add_edges_from(edge_list, **edgeattrs) return edge_list for aids_, nid in zip(*ibs.group_annots_by_name(dbaids)): add_clique(exemplars, aids_) viz_graph.ensure_node_images(ibs, exemplars) viz_graph.color_by_nids(exemplars, ibs=ibs) nx.set_node_attributes(unlabeled_graph, 'framewidth', False) nx.set_node_attributes(exemplars, 'framewidth', 4.0) nx.set_node_attributes(unlabeled_graph, 'group', 'unlab') nx.set_node_attributes(exemplars, 'group', 'exemp') #big_graph = nx.compose_all([unlabeled_graph]) big_graph = nx.compose_all([exemplars, unlabeled_graph]) # add sparse connections from unlabeled to exemplars import numpy as np rng = np.random.RandomState(0) if True or not postcut: for aid_ in unlabeled_graph.nodes(): flags = rng.rand(len(exemplars)) > .5 nid_ = ibs.get_annot_nids(aid_) exnids = np.array(ibs.get_annot_nids(list(exemplars.nodes()))) flags = np.logical_or(exnids == nid_, flags) exmatches = ut.compress(list(exemplars.nodes()), flags) big_graph.add_edges_from(list(ut.product([aid_], exmatches)), color=pt.ORANGE, implicit=True) else: for aid_ in unlabeled_graph.nodes(): flags = rng.rand(len(exemplars)) > .5 exmatches = ut.compress(list(exemplars.nodes()), flags) nid_ = ibs.get_annot_nids(aid_) exnids = np.array(ibs.get_annot_nids(exmatches)) exmatches = ut.compress(exmatches, exnids == nid_) big_graph.add_edges_from(list(ut.product([aid_], exmatches))) pass nx.set_node_attributes(big_graph, 'shape', 'rect') #if False and postcut: # ut.nx_delete_node_attr(big_graph, 'nid') # ut.nx_delete_edge_attr(big_graph, 'color') # viz_graph.ensure_graph_nid_labels(big_graph, ibs=ibs) # viz_graph.color_by_nids(big_graph, ibs=ibs) # big_graph = big_graph.to_undirected() layoutkw = { 'sep' : 1 / 5, 'prog': 'neato', 'overlap': 'false', #'splines': 'ortho', 'splines': 'spline', } as_directed = False #as_directed = True #hacknode = True hacknode = 0 graph = big_graph ut.nx_ensure_agraph_color(graph) if hacknode: nx.set_edge_attributes(graph, 'taillabel', {e: str(e[0]) for e in graph.edges()}) nx.set_edge_attributes(graph, 'headlabel', {e: str(e[1]) for e in graph.edges()}) explicit_graph = pt.get_explicit_graph(graph) _, layout_info = pt.nx_agraph_layout(explicit_graph, orig_graph=graph, inplace=True, **layoutkw) if ut.get_argflag('--smaller'): graph.node[7660]['pos'] = np.array([550, 350]) graph.node[6120]['pos'] = np.array([200, 600]) + np.array([350, -400]) graph.node[7164]['pos'] = np.array([200, 480]) + np.array([350, -400]) nx.set_node_attributes(graph, 'pin', 'true') _, layout_info = pt.nx_agraph_layout(graph, inplace=True, **layoutkw) elif ut.get_argflag('--small'): graph.node[7660]['pos'] = np.array([750, 350]) graph.node[33]['pos'] = np.array([300, 600]) + np.array([350, -400]) graph.node[6120]['pos'] = np.array([500, 600]) + np.array([350, -400]) graph.node[7164]['pos'] = np.array([410, 480]) + np.array([350, -400]) nx.set_node_attributes(graph, 'pin', 'true') _, layout_info = pt.nx_agraph_layout(graph, inplace=True, **layoutkw) if not postcut: #pt.show_nx(graph.to_undirected(), layout='agraph', layoutkw=layoutkw, # as_directed=False) #pt.show_nx(graph, layout='agraph', layoutkw=layoutkw, # as_directed=as_directed, hacknode=hacknode) pt.show_nx(graph, layout='custom', layoutkw=layoutkw, as_directed=as_directed, hacknode=hacknode) else: #explicit_graph = pt.get_explicit_graph(graph) #_, layout_info = pt.nx_agraph_layout(explicit_graph, orig_graph=graph, # **layoutkw) #layout_info['edge']['alpha'] = .8 #pt.apply_graph_layout_attrs(graph, layout_info) #graph_layout_attrs = layout_info['graph'] ##edge_layout_attrs = layout_info['edge'] ##node_layout_attrs = layout_info['node'] #for key, vals in layout_info['node'].items(): # #print('[special] key = %r' % (key,)) # nx.set_node_attributes(graph, key, vals) #for key, vals in layout_info['edge'].items(): # #print('[special] key = %r' % (key,)) # nx.set_edge_attributes(graph, key, vals) #nx.set_edge_attributes(graph, 'alpha', .8) #graph.graph['splines'] = graph_layout_attrs.get('splines', 'line') #graph.graph['splines'] = 'polyline' # graph_layout_attrs.get('splines', 'line') #graph.graph['splines'] = 'line' cut_graph = graph.copy() edge_list = list(cut_graph.edges()) edge_nids = np.array(ibs.unflat_map(ibs.get_annot_nids, edge_list)) cut_flags = edge_nids.T[0] != edge_nids.T[1] cut_edges = ut.compress(edge_list, cut_flags) cut_graph.remove_edges_from(cut_edges) ut.nx_delete_node_attr(cut_graph, 'nid') viz_graph.ensure_graph_nid_labels(cut_graph, ibs=ibs) #ut.nx_get_default_node_attributes(exemplars, 'color', None) ut.nx_delete_node_attr(cut_graph, 'color', nodes=unlabeled_graph.nodes()) aid2_color = ut.nx_get_default_node_attributes(cut_graph, 'color', None) nid2_colors = ut.group_items(aid2_color.values(), ibs.get_annot_nids(aid2_color.keys())) nid2_colors = ut.map_dict_vals(ut.filter_Nones, nid2_colors) nid2_colors = ut.map_dict_vals(ut.unique, nid2_colors) #for val in nid2_colors.values(): # assert len(val) <= 1 # Get initial colors nid2_color_ = {nid: colors_[0] for nid, colors_ in nid2_colors.items() if len(colors_) == 1} graph = cut_graph viz_graph.color_by_nids(cut_graph, ibs=ibs, nid2_color_=nid2_color_) nx.set_node_attributes(cut_graph, 'framewidth', 4) pt.show_nx(cut_graph, layout='custom', layoutkw=layoutkw, as_directed=as_directed, hacknode=hacknode) pt.zoom_factory()