def __init__(self, state: SampleState, graph: Graph, old_true_block_assignment: np.ndarray) -> None: """Creates a new Sample object. Contains information about the sampled vertices and edges, the mapping of sampled vertices to the original graph vertices, and the true block membership for the sampled vertices. Parameters ---------- state : SampleState contains the sampled vertices graph : Graph the graph from which the sample is taken old_true_block_assignment : np.ndarray[int] the vertex-to-community assignment array. Currently assumes that community assignment is non-overlapping. """ self.state = state sampled_vertices = sorted(state.sample_idx[-state.sample_size:]) self.vertex_mapping = dict([(v, k) for k, v in enumerate(sampled_vertices)]) binary_filter = np.zeros(graph.num_vertices()) binary_filter[sampled_vertices] = 1 graph.set_vertex_filter( graph.new_vertex_property("bool", binary_filter)) self.graph = Graph( graph, prune=True ) # If ordering is wacky, may need to play around with vorder graph.clear_filters() true_block_assignment = old_true_block_assignment[sampled_vertices] # Assuming the sample doesn't capture all the blocks, the block numbers in the sample may not be consecutive # The true_blocks_mapping ensures that they are consecutive true_blocks = list(set(true_block_assignment)) self.true_blocks_mapping = dict([(v, k) for k, v in enumerate(true_blocks)]) self.true_block_assignment = np.asarray( [self.true_blocks_mapping[b] for b in true_block_assignment]) self.sample_num = len(self.vertex_mapping)
def create_sample(graph: Graph, old_true_block_assignment: np.ndarray, args: argparse.Namespace, prev_state: SampleState) -> 'Sample': """Performs sampling according to the sample type in args. TODO: either re-write how this method is used, or get rid of it - it seems to be a code smell. """ # get rid of 1-degree vertices degrees = graph.get_total_degrees(np.arange(graph.num_vertices())) degree_filter = degrees > 2 mapping = np.where(degrees > 2)[0] graph.set_vertex_filter( graph.new_vertex_property("bool", degree_filter)) filtered_graph = Graph(graph, prune=True) print(filtered_graph.num_vertices()) graph.clear_filters() # TODO: keep track of the mapping to original graph # TODO: below methods can return a SampleState, which we map back to original vertices here, then create the # sample before return. This is brilliant! I am genius! if args.sample_type == "degree_weighted": state = Sample.degree_weighted_sample(filtered_graph, graph.num_vertices(), prev_state, args) elif args.sample_type == "expansion_snowball": state = Sample.expansion_snowball_sample(filtered_graph, graph.num_vertices(), prev_state, args) elif args.sample_type == "forest_fire": state = Sample.forest_fire_sample(filtered_graph, graph.num_vertices(), prev_state, args) elif args.sample_type == "max_degree": state = Sample.max_degree_sample(filtered_graph, graph.num_vertices(), prev_state, args) elif args.sample_type == "random_jump": state = Sample.random_jump_sample(filtered_graph, graph.num_vertices(), prev_state, args) elif args.sample_type == "random_node_neighbor": state = Sample.random_node_neighbor_sample(filtered_graph, graph.num_vertices(), prev_state, args) elif args.sample_type == "random_walk": state = Sample.random_walk_sample(filtered_graph, graph.num_vertices(), prev_state, args) elif args.sample_type == "uniform_random": state = Sample.uniform_random_sample(filtered_graph, graph.num_vertices(), prev_state, args) else: raise NotImplementedError( "Sample type: {} is not implemented!".format(args.sample_type)) state.sample_idx = mapping[state.sample_idx] return Sample(state, graph, old_true_block_assignment)
class graph: def __init__(self, mol): """ instantiate a graph object which will be attached to the parent mol :Parameter: - mol : a mol type object (can be a derived type like bb or topo as well) """ self._mol = mol logger.debug("generated the graph addon") return def make_graph(self, idx=None, hashes=True): """ generate a graph for the mol object (atoms should be typed) we use the atomtype name with the "_" and everything after it (rule=2) truncated. in other words the vertex property is the element plus the coordination number """ if idx == None: idx = range(self._mol.natoms) self.molg = Graph(directed=False) # now add vertices self.molg.vp.type = self.molg.new_vertex_property("string") self.vert2atom = [ ] # this list maps vertex indices to the real atoms becasue we omit the hydrogens in the graph ig = 0 for i in idx: if self._mol.elems[i] != "x": self.molg.add_vertex() self.vert2atom.append(i) vtype = self._mol.atypes[i] # extract element and coordination number if "_" in vtype: vtype = vtype.split("_")[0] # if the coordination number is one replace the element by a # if hashes: if vtype[-1] == "1": vtype = "#" self.molg.vp.type[ig] = vtype ig += 1 self.nvertices = len(self.vert2atom) logger.info("generated a graph for a mol object with %d vertices" % self.nvertices) # now add edges ... only bonds between vertices for i in range(self.nvertices): ia = self.vert2atom[i] for ja in self._mol.conn[ia]: if ja >= ia: #we need a .le. here for those atoms/vertices connected to itself twice in different boxes if ja in self.vert2atom: # print("bond from %d to %d" % (ia, ja)) # print(self._mol.atypes[ia], self._mol.atypes[ja]) self.molg.add_edge( self.molg.vertex(i), self.molg.vertex(self.vert2atom.index(ja))) #self.molg.add_edge( self.molg.vertex(self.vert2atom.index(ja)),self.molg.vertex(i)) return def plot_graph(self, fname, g=None, size=1000, fsize=16, vsize=8, ptype="pdf", method='arf'): """ plot the grap (needs more tuning options :Parameter: - fname : filename (will write filename.pdf) - size : outputsize will be (size, size) in px [default 800] - fsize : font size [default 10] - method : placement method to draw graph, can be one of arf frucht radtree sfdp random """ if g: draw_g = g else: draw_g = self.molg import graph_tool.draw import graph_tool.draw as gt g = draw_g if method == 'arf': pos = graph_tool.draw.arf_layout(draw_g, max_iter=0) elif method == 'frucht': pos = graph_tool.draw.fruchterman_reingold_layout(draw_g, n_iter=1000) elif method == 'radtree': pos = gt.radial_tree_layout(g, g.vertex(0)) elif method == 'sfdp': pos = gt.sfdp_layout(g) elif method == 'sfdp': pos = gt.random_layout(g) else: pos = None from graph_tool.draw import graph_draw graph_draw(draw_g,pos=pos, vertex_text=draw_g.vp.type, vertex_font_size=fsize, vertex_size=vsize, \ output_size=(size, size), output=fname+"."+ptype, bg_color=[1,1,1,1]) return def find_subgraph(self, graph, subg): """ use graph_tools subgraph_isomorphism tool to find substructures :Parameter: - graph : parent graph to be searched - subg : graph to be found :Returns: a list of lists with the (sorted) vertex indices of the substructure """ maps = subgraph_isomorphism(subg, graph, vertex_label=(subg.vp.type, graph.vp.type)) subs = [] subs_check = [] for m in maps: sl = list(m) sl_check = copy.deepcopy(sl) sl_check.sort() if sl_check not in subs_check: subs.append(sl) subs_check.append(sl_check) return subs def find_sub(self, subg): """ use graph_tools subgraph_isomorphism tool to find substructures :Parameter: - subg : graph object (from another molsys) to be searched :Returns: a list of lists with the (sorted) vertex indices of the substructure """ subs = self.find_subgraph(self.molg, subg.molg) return subs def find_fragment(self, frag, add_hydrogen=False): """ find a complete fragment (including the hydrogen atoms not included in the graph) Note that the fragment found can be different from the fragment by the number of hydrogen atoms!! :Parameter: - frag : mol object with graph addon to be found :Returns: a list of lists with the atom indices of the fragment in the full system """ subs = self.find_sub(frag.graph) frags = [] for s in subs: # loop over all vertices f = [] for v in s: a = self.vert2atom[v] f.append(a) # check all atoms connected to this atom if they are hydrogen if add_hydrogen: for ca in self._mol.conn[a]: if self._mol.elems[ca] == "h": f.append(ca) frags.append(f) return frags def util_graph(self, vertices, conn): """ generate a generate a graph with vertices and connectivity in conn """ g = Graph(directed=False) # now add vertices g.vp.type = g.new_vertex_property("string") for i, v in enumerate(vertices): g.add_vertex() g.vp.type[i] = v # now add edges ... for i, v in enumerate(vertices): for j in conn[i]: if j >= i: g.add_edge(g.vertex(i), g.vertex(j)) return g def filter_graph(self, idx): """ filters all atoms besides the given out of the graph :Parameters: - idx (list): indices of atoms to keep """ # TODO use vert2atom assert type(idx) == list self.molg.clear_filters() filter = self.molg.new_vertex_property("bool") filter.set_value(False) for i in idx: filter[self.molg.vertex(i)] = True self.molg.set_vertex_filter(filter) return
class graphtool(): def get_edges(self): self.edges = [] for dev in Device.objects: port = dev['ports'] for port in dev['ports']: if not port['acc']: self.edges.append([int(port['dev']), int(dev['devid'])]) for edge in self.edges: if edge[::-1] in self.edges: self.edges.remove(edge) def create_graph(self): self.get_edges() self.g = Graph(directed=False) self.g.add_edge_list(self.edges) def load_graph(self): self.g = pickle.loads(System.objects.first().graph.read()) def shortestpath(self, source, dest): if source == dest: return ('нужны разные пипишники') #ip to id source = Device.objects(uri=source) dest = Device.objects(uri=dest) if len(source) > 0 and len(dest) > 0: source = self.g.vertex(source[0].devid) dest = self.g.vertex(dest[0].devid) result = graph_tool.topology.shortest_path(self.g, source, dest) path = [self.g.vertex_index[x] for x in result[0]] filteredge = self.g.new_edge_property('bool') filteredge[result[1][0]] = True self.g.set_edge_filter(filteredge, inverted=True) result = graph_tool.topology.shortest_path(self.g, source, dest) second_path = [self.g.vertex_index[x] for x in result[0]] self.g.clear_filters() another_paths = [] all_shortest = graph_tool.topology.all_shortest_paths( self.g, source, dest) for i in all_shortest: another_paths.append([self.g.vertex_index[j] for j in i]) self.all_paths = [path] + [second_path] + another_paths self.all_paths = [tuple(t) for t in self.all_paths] self.all_paths = [t for t in self.all_paths if len(t) > 0] self.all_paths = list(set(self.all_paths)) self.all_paths = [list(t) for t in self.all_paths] dev_from_stp = [] count = 0 for path in self.all_paths: for dev in path: dev = Device.objects(devid=dev).first().uri if Stpdomins.objects(devices__=dev): count += 1 [ dev_from_stp.append(x) for x in Stpdomins.objects( devices__=dev).first().devices if x not in dev_from_stp ] if len(dev_from_stp) > 0 and count > 1: print('stp domains') filtevertex = self.g.new_vertex_property('bool') for x in dev_from_stp: filtevertex[self.g.vertex( Device.objects(uri=x).first().devid)] = True self.g.set_vertex_filter(filtevertex) source = self.g.vertex( Device.objects(uri=dev_from_stp[0]).first().devid) dest = self.g.vertex( Device.objects(uri=dev_from_stp[-1]).first().devid) result = graph_tool.topology.all_paths(self.g, source, dest) for x in result: self.all_paths.append([int(self.g.vertex(i)) for i in x]) self.g.clear_filters() self.all_paths.sort() self.all_paths = list( self.all_paths for self.all_paths, _ in itertools.groupby(self.all_paths)) self.all_paths = [ path for path in self.all_paths if len(path) > 0 ] return self.all_paths def fancy_shortest(self): self.fancy_paths = [] for path in self.all_paths: fancy = [] for i in path: d = Device.objects(devid=i).first() if d.devtype not in passive: fancy.append([d.uri, d.addr, dev_type_dict[d.devtype]]) self.fancy_paths.append(fancy) return self.fancy_paths def paths_ports(self): output = [] for path in self.all_paths: for i, j in zip(path, path[1:]): dev = Device.objects(devid=i).first() if dev.devtype in supported: ports = [x['num'] for x in dev.ports if x['dev'] == j] if len(ports) == 0: ports = 0 else: ports = ports[0] output.append([dev.uri, dev.devtype, ports]) dev = Device.objects(devid=j).first() if dev.devtype in supported: ports = [x['num'] for x in dev.ports if x['dev'] == i] if len(ports) == 0: ports = 0 else: ports = ports[0] output.append([dev.uri, dev.devtype, ports]) g_fancy_output = dict() g_output = dict() for key, group in groupby(output, lambda x: x[0]): ports = [] for i in group: ports.append(i[2]) if key in g_output: # print (g_output[key]['ports'], ports) g_output[key]['ports'] = g_output[key]['ports'] + ports else: g_output[key] = {'type': i[1], 'ports': ports} for key in g_output: g_output[key]['ports'] = list(set(g_output[key]['ports'])) g_fancy_output = copy.deepcopy(g_output) for i in g_fancy_output: g_fancy_output[i]['type'] = dev_type_dict[g_fancy_output[i] ['type']] return g_fancy_output, g_output