Пример #1
0
    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)
Пример #2
0
    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)
Пример #3
0
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
Пример #4
0
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