def s2(g: gt.Graph, weight_prop: gt.EdgePropertyMap, labels, budget=20):
    L = set()

    n = g.num_vertices()

    known_labels = -np.ones(n) * np.inf

    W = gt.topology.shortest_distance(g, weights=weight_prop).get_2d_array(
        range(n))  #original distance map

    x = np.random.choice(list(set(range(n)).difference(L)))
    while budget > 0:
        known_labels[x] = labels[x]
        L.add(x)
        if len(L) == n:
            break
        budget -= 1
        to_remove = []
        for e in g.get_out_edges(x):
            if known_labels[e[1]] >= 0 and known_labels[
                    e[1]] != known_labels[x]:
                to_remove.append(e)

        for e in to_remove:
            g.remove_edge(g.edge(e[0], e[1]))

        #mid_point = mssp(g, weight_prop, L, known_labels)

        if False:
            x = int(mid_point)
        else:
            x = np.random.choice(list(set(range(n)).difference(L)))

    return label_propagation(W, known_labels, np.unique(labels))
Пример #2
0
 def create_true_graph(self):
     final_workflow = Graph()
     final_workflow.add_vertex(self.network_size)
     i = 0
     while max([
             shortest_distance(final_workflow, x, (self.network_size - 1))
             for x in final_workflow.get_vertices()
     ]) > 100:
         i += 1
         if i > 10000:
             raise RuntimeError('generating graph took too long')
         valid_source_nodes = [
             index for index, in_degree in enumerate(
                 final_workflow.get_in_degrees(
                     final_workflow.get_vertices()))
             if ((in_degree > 0 or index < self.input_nodes) and index <
                 (self.network_size - 1))
         ]
         valid_to_nodes = [
             index for index in final_workflow.get_vertices()
             if (index >= self.input_nodes)
         ]
         new_edge = final_workflow.add_edge(
             self.np_random.choice(valid_source_nodes),
             self.np_random.choice(valid_to_nodes))
         if not is_DAG(final_workflow):
             final_workflow.remove_edge(new_edge)
         observation = adjacency(final_workflow).toarray().astype(int)
         if not self.observation_space.contains(observation):
             final_workflow.remove_edge(new_edge)
     return final_workflow
Пример #3
0
def ring(num_vtx=100, k=2, p=0.0):
    g = Graph(directed=False)
    vtx = list(g.add_vertex(num_vtx))

    # connect neighbors
    for i in vtx:
        for j in xrange(1, k + 1):
            dest = g.vertex((g.vertex_index[i] - j) % num_vtx)
            if g.edge(i, dest) is None:
                g.add_edge(i, dest)

    # redirect edges
    # old_edges = list(g.edges())
    old_edges = [(x.source(), x.target()) for x in g.edges()]
    for i in old_edges:
        n = random.random()
        if n < p:  # redirect edge; choose random vertex as new destination
            vtx_tmp = vtx[:]
            vtx_tmp.remove(i[1])
            if i[0] in vtx_tmp:
                vtx_tmp.remove(i[0])
            dest = random.choice(vtx_tmp)
            while g.edge(i[0], dest) is not None:
                vtx_tmp.remove(dest)
                dest = random.choice(vtx_tmp)

            g.remove_edge(g.edge(i[0], i[1]))
            g.add_edge(i[0], dest)

    return g
Пример #4
0
    def _filter_short_branch(self, filter=False, short=30):
        """
        filter out very short branches: do this maybe not right for some models, for models with flat part, it is right
        I will test how this effect the final matching results
        need to delete nodes, switch with the last one then delete last
        """
        if filter == False:
            self.verts = self.verts_init
            self.edges = self.edges_init
        else:
            init_graph = Graph(directed=False)
            init_graph.add_vertex(len(self.verts_init))
            for edge in self.edges_init:
                init_graph.add_edge(init_graph.vertex(edge[0]), init_graph.vertex(edge[1]))

            terminal_node = []
            for v in init_graph.vertices():
                if v.out_degree() == 1:
                    terminal_node.append(v)

            visitor = DepthVisitor()
            short_nodes = []
            for tn in terminal_node:
                search.dfs_search(init_graph, tn, visitor)
                tmp_node = visitor.get_short_branch(min_length=short)
                visitor.reset()
                for n in tmp_node:
                    short_nodes.append(n)

            ## get edges on the short paths
            short_nodes = list(set(short_nodes))
            short_edges = []
            temp_verts = self.verts_init[:]
            v_num = len(self.verts_init)
            if len(short_nodes):
                for v in reversed(sorted(short_nodes)):
                    for ve in init_graph.vertex(v).out_edges():
                        short_edges.append(ve)

                ## delete edges first, then vertex
                short_edges = list(set(short_edges))
                for e in short_edges:
                    init_graph.remove_edge(e)

                print 'deleting vertex',
                for v in reversed(sorted(short_nodes)):
                    print v,
                    temp_verts[int(v)] = temp_verts[v_num-1]
                    init_graph.remove_vertex(v, fast=True)
                    v_num -= 1
                print '\ndeleting related edges' # already done above, just info user
            else:
                print 'no short branches'

            ######## new vertices and edges ########
            self.verts = temp_verts[:v_num]
            self.edges = []
            for e in init_graph.edges():
                self.edges.append([int(e.source()), int(e.target())])
Пример #5
0
def shortest_path_cover_logn_apx(g: gt.Graph, weight: gt.EdgePropertyMap):
    started_with_directed = g.is_directed()
    if not g.is_directed():
        reversed_edges = np.fliplr(g.get_edges())
        g.set_directed(True)
        g.add_edge_list(reversed_edges)
        weight.a[-reversed_edges.shape[0]:] = weight.a[:reversed_edges.
                                                       shape[0]]

    if weight.value_type() not in [
            "bool", "int", "int16_t", "int32_t", "int64_t"
    ]:
        #min = np.min(weight.a)
        #min_second = np.min(weight.a[weight.a > min])

        eps = 1  #min_second - min
        scaled_weight = (np.ceil(weight.a / eps) *
                         (g.num_vertices() + 1)).astype(np.int)  # ints >= 1
    else:
        scaled_weight = weight.a * (g.num_vertices() + 1)

    summed_edge_weight = np.sum(scaled_weight)

    adjusted_weight = g.new_edge_property("long", vals=scaled_weight - 1)

    paths = []

    covered_vertices = set()

    while len(covered_vertices) != g.num_vertices():
        curr_paths = shortest_path_visiting_most_nodes(g, adjusted_weight,
                                                       covered_vertices,
                                                       summed_edge_weight)

        for path in curr_paths:
            paths.append(path)

            #if len(path) <= 2 switch to fast mode and just add single edges/vertices until done.
            path_vertices = set(path)
            for v in path_vertices.difference(covered_vertices):
                for w in g.get_in_neighbors(v):
                    adjusted_weight[g.edge(w, v)] += 1  #.a[list()] -= 1
                    if adjusted_weight[g.edge(
                            w, v)] % (g.num_vertices() + 1) != 0:
                        exit(5)

            new_covered = path_vertices.difference(covered_vertices)
            covered_vertices = covered_vertices.union(path_vertices)
            print(len(new_covered), len(path), len(covered_vertices), path)
    if not started_with_directed:
        g.set_directed(False)
        for e in reversed_edges:
            g.remove_edge(g.edge(e[0], e[1]))
    return paths
def s2(g: graph_tool.Graph,
       weight_prop: graph_tool.EdgePropertyMap,
       labels,
       budget=20,
       use_adjacency=False):
    L = set()

    n = g.num_vertices()

    known_labels = -np.ones(n) * np.inf

    W = graph_tool.topology.shortest_distance(
        g, weights=weight_prop).get_2d_array(range(n))  #original distance map

    x = np.random.choice(list(set(range(n)).difference(L)))
    while budget > 0:
        known_labels[x] = labels[x]
        L.add(x)
        if len(L) == n:
            break
        budget -= 1
        to_remove = []
        for e in g.get_out_edges(x):
            if known_labels[e[1]] > -np.inf and known_labels[
                    e[1]] != known_labels[x]:
                to_remove.append(e)

        for e in to_remove:
            g.remove_edge(g.edge(e[0], e[1]))

        mid_point = mssp(g, weight_prop, L, known_labels)

        if mid_point is not None:
            x = int(mid_point)
        else:
            x = np.random.choice(list(set(range(n)).difference(L)))
        prediction = label_propagation(W,
                                       known_labels,
                                       labels,
                                       use_adjacency=use_adjacency)

        print("accuracy", np.sum(prediction == labels) / labels.size)
Пример #7
0
class ob_viz(QWidget):
    def __init__(self, bg_color):
        QWidget.__init__(self)

        self.background_color = bg_color
        self.c = 0

        # K = 0.5
        # how many iterations the realignment is supposed to take
        self.step = 15
        self.rwr_c = 0

        # dumper([qt_coords])
        dumper(['obv viz init'])
        # self.show()

        # with open("/tmp/eaf3.csv", "a") as fo:
        #     wr = csv.writer(fo)
        #     wr.writerow([self.c, "runs4"])
        # dumper([self.c, "runs4"])

        # self.node_names [g_id[i] for i in g.vertices()]

    def init2(self, emacs_var_dict):
        self.emacs_var_dict = emacs_var_dict

        self.link_str = self.emacs_var_dict['links']
        self.g = Graph()
        self.label_ep = self.g.new_edge_property("string")
        self.links = self.link_str.split(";")

        link_tpls = [i.split(" -- ") for i in self.links]
        dumper([str(i) for i in link_tpls])

        self.g_id = self.g.add_edge_list(link_tpls,
                                         hashed=True,
                                         string_vals=True,
                                         eprops=[self.label_ep])

        self.adj = np.array([(int(i.source()), int(i.target()))
                             for i in self.g.edges()])
        self.node_names = [self.g_id[i] for i in self.g.vertices()]

        self.vd = {}
        for i in self.g.vertices():
            self.vd[self.g_id[i]] = int(i)

        # self.pos_vp = sfdp_layout(self.g, K=0.5)
        self.pos_vp = fruchterman_reingold_layout(self.g)
        self.base_pos_ar = self.pos_vp.get_2d_array((0, 1)).T
        self.qt_coords = self.nolz_pos_ar(self.base_pos_ar)

        dumper([str(self.qt_coords)])

        # dumper([link_str])

    def update_graph(self, emacs_var_dict):
        """set new links and nodes"""
        new_link_str = emacs_var_dict['links']
        new_links = new_link_str.split(";")
        new_link_tpls = [i.split(" -- ") for i in new_links]

        links_to_add = list(set(new_links) - set(self.links))
        links_to_del = list(set(self.links) - set(new_links))

        # setting new stuff
        self.links = new_links

        new_nodes = []
        for tpl in new_link_tpls:
            new_nodes.append(tpl[0])
            new_nodes.append(tpl[1])

        new_nodes_unique = list(set(new_nodes))

        nodes_to_del = list(set(self.node_names) - set(new_nodes_unique))
        nodes_to_add = list(set(new_nodes_unique) - set(self.node_names))

        dumper([
            "nodes_to_add: ", nodes_to_add, "nodes_to_del: ", nodes_to_del,
            "links_to_add: ", links_to_add, "links_to_del: ", links_to_del
        ])

        # first add nodes + index them, but not there yet (first links)

        for n in nodes_to_add:
            dumper(['adding node'])
            v = self.g.add_vertex()
            # how to new nodes pos to parents? separate loop afterwards
            self.vd[n] = int(v)
            self.g_id[v] = n

        del_node_ids = [self.vd[i] for i in nodes_to_del]
        self.g.remove_vertex(del_node_ids)

        # have to reindex after deletion
        self.vd = {}
        for i in self.g.vertices():
            self.vd[self.g_id[i]] = int(i)

        dumper(['node deleted'])
        # nodes_to_del_id =

        # dumper(['old nodes deleted, add new links'])

        for l in links_to_add:
            tpl = l.split(" -- ")
            n0, n1 = tpl[0], tpl[1]
            self.g.add_edge(self.vd[n0], self.vd[n1])

        # dumper(['new links added, delete old links'])

        for l in links_to_del:
            tpl = l.split(" -- ")
            n0 = tpl[0]
            n1 = tpl[1]
            dumper([list(self.vd.keys())])
            # only remove edge when neither of nodes removed
            if n0 in self.vd.keys() and n1 in self.vd.keys():
                self.g.remove_edge(self.g.edge(self.vd[n0], self.vd[n1]))

        # dumper(['graph modifications done'])

        # set positions of new nodes to parent nodes
        for n in nodes_to_add:
            v = self.g.vertex(self.vd[n])
            v_prnt = list(v.all_neighbors())[0]
            self.pos_vp[v] = self.pos_vp[v_prnt]

        # dumper(['node positions adjusted'])

        self.adj = np.array([(int(i.source()), int(i.target()))
                             for i in self.g.edges()])
        self.node_names = [self.g_id[i] for i in self.g.vertices()]

        # dumper(['storage objects updated'])

        # dumper(["nbr_edges new: ", str(len([i for i in self.g.edges()]))])
        # dumper(['nodes_to_add'] + nodes_to_add)
        # seems to work
        dumper(['to here'])

        self.recalculate_layout()
        dumper(['to here2'])

    def recalculate_layout(self):
        """calculate new change_array, set rwr_c counter"""
        dumper(['recalculating starting'])
        self.base_pos_ar = self.pos_vp.get_2d_array((0, 1)).T

        # set_dict = {'p': 2, 'max_level': 20, 'adaptive_cooling': False,
        #             'gamma': 1, 'theta': 1, 'cooling_step': 0.3, 'C': 0.6, 'mu_p': 1.2}

        # self.goal_vp = sfdp_layout(self.g, K=0.5, pos=self.pos_vp, **set_dict)
        self.goal_vp = fruchterman_reingold_layout(self.g, pos=self.pos_vp)

        goal_ar = self.goal_vp.get_2d_array([0, 1]).T
        self.chng_ar = (goal_ar - self.base_pos_ar) / self.step

        self.rwr_c = self.step
        dumper(["base_pos_ar: ", self.base_pos_ar])
        dumper(["goal_ar: ", goal_ar])
        dumper(["chng_ar: ", self.chng_ar])
        dumper(['recalculating done'])

    def redraw_layout(self):
        """actually do the drawing, run multiple (step (rwr_c)) times"""
        self.cur_pos_ar = np.round(
            self.base_pos_ar + self.chng_ar * (self.step - self.rwr_c), 3)
        self.qt_coords = self.nolz_pos_ar(self.cur_pos_ar)

        self.rwr_c -= 1
        self.update()
        # dumper(['redrawing'])

    # def draw_arrow(qp, p1x, p1y, p2x, p2y):
    def draw_arrow(self, qp, p1x, p1y, p2x, p2y, node_width):
        """draw arrow from p1 to rad units before p2"""
        # get arrow angle, counterclockwise from center -> east line

        # dumper(['painting time'])
        angle = degrees(atan2((p1y - p2y), (p1x - p2x)))

        # calculate attach point
        arw_goal_x = p2x + node_width * cos(radians(angle))
        arw_goal_y = p2y + node_width * sin(radians(angle))

        # calculate start point: idk how trig works but does
        start_px = p1x - node_width * cos(radians(angle))
        start_py = p1y - node_width * sin(radians(angle))

        # arrow stuff: +/- 30 deg
        ar1 = angle + 25
        ar2 = angle - 25

        arw_len = 10

        # need to focus on vector from p2 to p1
        ar1_x = arw_goal_x + arw_len * cos(radians(ar1))
        ar1_y = arw_goal_y + arw_len * sin(radians(ar1))

        ar2_x = arw_goal_x + arw_len * cos(radians(ar2))
        ar2_y = arw_goal_y + arw_len * sin(radians(ar2))

        # qp.drawLine(p1x, p1y, p2x, p2y)
        # qp.drawLine(p1x, p1y, arw_goal_x, arw_goal_y)
        qp.drawLine(start_px, start_py, arw_goal_x, arw_goal_y)
        qp.drawLine(ar1_x, ar1_y, arw_goal_x, arw_goal_y)
        qp.drawLine(ar2_x, ar2_y, arw_goal_x, arw_goal_y)

    def paintEvent(self, event):
        # dumper(['start painting'])
        node_width = 10
        qp = QPainter(self)
        edges = [(self.qt_coords[i[0]], self.qt_coords[i[1]])
                 for i in self.adj]
        # dumper([str(i) for i in edges])

        qp.setPen(QPen(Qt.green, 2, Qt.SolidLine))

        # [qp.drawLine(e[0][0], e[0][1], e[1][0], e[1][1]) for e in edges]
        [
            self.draw_arrow(qp, e[0][0], e[0][1], e[1][0], e[1][1],
                            (node_width / 2) + 5) for e in edges
        ]

        qp.setPen(QColor(168, 34, 3))
        # qp.setPen(Qt.green)
        qp.setFont(QFont('Decorative', 10))
        [
            qp.drawText(t[0][0] + node_width, t[0][1], t[1])
            for t in zip(self.qt_coords, self.node_names)
        ]
        # dumper(['done painting'])

        qp.setPen(QPen(Qt.black, 3, Qt.SolidLine))
        # qp.setBrush(QBrush(Qt.green, Qt.SolidPattern))

        dumper(['painting nodes'])

        for i in zip(self.qt_coords, self.node_names):
            if self.emacs_var_dict['cur_node'] == i[1]:
                qp.setPen(QPen(Qt.black, 4, Qt.SolidLine))
                qp.drawEllipse(i[0][0] - (node_width / 2),
                               i[0][1] - (node_width / 2), node_width,
                               node_width)
                qp.setPen(QPen(Qt.black, 3, Qt.SolidLine))

            else:
                qp.drawEllipse(i[0][0] - (node_width / 2),
                               i[0][1] - (node_width / 2), node_width,
                               node_width)

        # qp.drawEllipse(self.c, self.c, 7, 7)
        # qp.end()

    def nolz_pos_ar(self, pos_ar_org):
        """normalize pos ar to window limits"""

        # pos_ar_org = goal_ar

        size = self.size()

        limits = [[20, size.width() - 50], [20, size.height() - 20]]

        x_max = max(pos_ar_org[:, 0])
        x_min = min(pos_ar_org[:, 0])
        y_max = max(pos_ar_org[:, 1])
        y_min = min(pos_ar_org[:, 1])

        # need linear maping function again
        pos_ar2 = pos_ar_org

        pos_ar2[:, 0] = (((pos_ar2[:, 0] - x_min) / (x_max - x_min)) *
                         (limits[0][1] - limits[0][0])) + limits[0][0]
        pos_ar2[:, 1] = (((pos_ar2[:, 1] - y_min) / (y_max - y_min)) *
                         (limits[1][1] - limits[1][0])) + limits[1][0]

        return (pos_ar2)
Пример #8
0
class graphRL(gym.Env):
    """
    will have fixed action space, but not all actions are valid within each state
    step function should have a function that tests if the chosen action is valid
    the observation returned will be the graph_tool graph, but the state will just be
    the adjacency matrix (? maybe, currently have obs space as the matrix)
    maybe step function just alters the given graph
    """
    metadata = {'render.modes': ['human', 'graph', 'interactive']}

    def __init__(self, network_size=10, input_nodes=3):
        self.network_size = network_size
        self.input_nodes = input_nodes
        self.graph = Graph()
        self.graph.set_fast_edge_removal(True)
        self.graph.add_vertex(self.network_size)

        self.action_space = spaces.Tuple((spaces.Discrete(self.network_size),
                                          spaces.Discrete(self.network_size)))
        self.observation_space = spaces.MultiDiscrete(
            np.full((self.network_size, self.network_size), 2))
        self.time_step = 0
        self.observation = adjacency(self.graph).toarray().astype(int)
        self.seed_value = self.seed()
        self.true_graph = self.create_true_graph()
        self.reset()

    def create_true_graph(self):
        final_workflow = Graph()
        final_workflow.add_vertex(self.network_size)
        i = 0
        while max([
                shortest_distance(final_workflow, x, (self.network_size - 1))
                for x in final_workflow.get_vertices()
        ]) > 100:
            i += 1
            if i > 10000:
                raise RuntimeError('generating graph took too long')
            valid_source_nodes = [
                index for index, in_degree in enumerate(
                    final_workflow.get_in_degrees(
                        final_workflow.get_vertices()))
                if ((in_degree > 0 or index < self.input_nodes) and index <
                    (self.network_size - 1))
            ]
            valid_to_nodes = [
                index for index in final_workflow.get_vertices()
                if (index >= self.input_nodes)
            ]
            new_edge = final_workflow.add_edge(
                self.np_random.choice(valid_source_nodes),
                self.np_random.choice(valid_to_nodes))
            if not is_DAG(final_workflow):
                final_workflow.remove_edge(new_edge)
            observation = adjacency(final_workflow).toarray().astype(int)
            if not self.observation_space.contains(observation):
                final_workflow.remove_edge(new_edge)
        return final_workflow

    def render(self, mode='human'):
        if mode == 'graph':
            # return graphtools graph object
            return self.graph
        elif mode == 'interactive':
            interactive_window(self.graph)
        elif mode == 'human':
            filename = "./renders/render" + str(self.time_step) + ".png"
            graph_draw(self.graph,
                       vertex_text=self.graph.vertex_index,
                       vertex_font_size=18,
                       output_size=(1000, 1000),
                       output=filename)

    def render_truth(self, mode='human'):
        if mode == 'graph':
            # return graphtools graph object
            return self.true_graph
        elif mode == 'interactive':
            interactive_window(self.true_graph)
        elif mode == 'human':
            filename = "./renders/TrueGraphSeed" + str(
                self.seed_value) + ".png"
            graph_draw(self.true_graph,
                       vertex_text=self.true_graph.vertex_index,
                       vertex_font_size=18,
                       output_size=(1000, 1000),
                       output=filename)

    def seed(self, seed=None):
        self.np_random, seed = seeding.np_random(seed)
        return [seed]

    def step(self, action):
        done = 0
        reward = 0
        assert self.action_space.contains(action)
        valid_source_nodes = [
            index for index, in_degree in enumerate(
                self.graph.get_in_degrees(self.graph.get_vertices()))
            if ((in_degree > 0 or index < self.input_nodes) and index <
                (self.network_size - 1))
        ]
        if action[0] not in valid_source_nodes:
            raise ValueError('this action does not have a valid from node')
        new_edge = self.graph.add_edge(action[0], action[1])
        if not is_DAG(self.graph):
            self.graph.remove_edge(new_edge)
            raise ValueError('this action violates the DAG property')
        self.observation = adjacency(self.graph).toarray().astype(int)
        if not self.observation_space.contains(self.observation):
            print(self.observation)
            self.graph.remove_edge(new_edge)
            self.observation = adjacency(self.graph).toarray().astype(int)
            raise ValueError('this action makes a duplicate edge')
        if isomorphism(self.graph, self.true_graph):
            reward = 1
            done = 1
        self.time_step += 1
        return self.observation, reward, done, {"time_step": self.time_step}

    def reset(self):
        self.graph.clear_edges()
        self.time_step = 0
        self.observation = adjacency(self.graph).toarray()
        return self.observation