def test_edge_weight(self): g = Graph() g.add_edge(0, 1, 5) self.assertTrue(g.get_edge_weight(0, 1) == 5) g.increase_edge_weight(0, 1, 1) self.assertTrue(g.get_edge_weight(0, 1) == 6) g.increase_edge_weight(0, 2, 3) self.assertTrue(g.get_edge_weight(0, 2) == 3)
def test_remove_node(self): g = Graph() g.add_edge(0, 1) g.add_edge(1, 2) g.add_edge(2, 3) g.remove_vertex(1) self.assertTrue((0, 1) not in g) self.assertTrue((1, 2) not in g) self.assertTrue((1, 2) not in g) self.assertTrue((2, 3) in g)
class TestGraph(unittest.TestCase): def setUp(self): # A brand new graph. self.new = Graph() # A disconnected graph self.disconnected = Graph() self.disconnected.add_edge(1, 2) self.disconnected.add_edge(3, 4) # A complete graph self.complete = Graph() self.complete.add_edge(1, 2, 3) self.complete.add_edge(2, 3, 5) self.complete.add_edge(3, 4, 7) self.complete.add_edge(4, 1, 5) self.complete.add_edge(1, 3, 4) self.complete.add_edge(2, 4, 6) def test_has_edge(self): self.assertFalse(self.new.has_edge(1, 2)) self.assertFalse(self.disconnected.has_edge(2, 3)) self.assertFalse(self.disconnected.has_edge(1, 1)) self.assertFalse(self.disconnected.has_edge(5, 1)) self.assertTrue(self.disconnected.has_edge(2, 1)) def test_unweighted_edges(self): self.assertEqual(self.new.unweighted_edges(), set()) self.assertEqual(len(self.disconnected.unweighted_edges()), 2) self.assertEqual(len(self.complete.unweighted_edges()), 6) for graph in self.disconnected, self.complete: edges = graph.unweighted_edges() for (source, target) in edges: self.assertNotIn((target, source), edges) def test_weighted_edges(self): self.assertEqual(self.new.weighted_edges(), set()) self.assertEqual(len(self.disconnected.weighted_edges()), 2) self.assertEqual(len(self.complete.weighted_edges()), 6) edges = self.disconnected.weighted_edges() for (source, target, weight) in edges: self.assertEqual(weight, 1) self.assertNotIn((target, source, weight), edges) edges = self.complete.weighted_edges() for (source, target, weight) in edges: self.assertEqual(weight, source + target) self.assertNotIn((target, source, weight), edges) def test_topological_sort(self): for graph in self.new, self.disconnected, self.complete: self.assertEqual(graph.topological_sort(), []) def test_best_tour(self): self.assertIn(self.complete.best_tour(), [[1, 2, 3, 4, 1], [2, 3, 4, 1, 2], [3, 4, 1, 2, 3], [4, 1, 2, 3, 4]]) def test_minimum_spanning_tree(self): mst = self.complete.minimum_spanning_tree(4) self.assertEqual(mst.nodes(), self.complete.nodes()) self.assertEqual(mst.unweighted_edges(), {(4, 1), (1, 2), (1, 3)})
def test_add_edge(self): g = Graph() g.add_edge(0, 1) self.assertTrue((0, 1) in g) self.assertTrue((1, 0) not in g)
else: for v in graph.adj[u]: if v not in visited: visited.append(v) queue.append(v) return bfs_len, visited if __name__ == '__main__': filename = "../tests/DiameterGraph.txt" with open(filename) as fd: V = int(fd.readline()) E = int(fd.readline()) g = Graph(V) for lines in range(E): u, v = tuple(map(int, fd.readline().split())) g.add_edge(u, v) maxd = 0 for u in g.adj.iterkeys(): d, vertices = find_diameter(g, u) maxd = max(d, maxd) print "Max diameter {} vertices {}".format(maxd, vertices)
>>> cities.weight(cities.shortest_path(start, end)) 284 >>> cities.weighted_distance(start, end) 284 Although all cities are directly connected to each other, the shortest path is not necessarily the direct path. >>> cities.shortest_path(start, end) ['Luton', 'Nuneaton', 'Penzance'] """ from lib.graph import Graph cities = Graph() cities.add_edge("Scunthorpe", "Bridlington", 31) cities.add_edge("Scunthorpe", "Wick", 514) cities.add_edge("Scunthorpe", "Bognor", 252) cities.add_edge("Scunthorpe", "Nuneaton", 111) cities.add_edge("Scunthorpe", "Luton", 117) cities.add_edge("Scunthorpe", "Wrexham", 142) cities.add_edge("Scunthorpe", "Penzance", 318) cities.add_edge("Bridlington", "Luton", 142) cities.add_edge("Bridlington", "Nuneaton", 115) cities.add_edge("Bridlington", "Bognor", 209) cities.add_edge("Bridlington", "Penzance", 426) cities.add_edge("Bridlington", "Wrexham", 162) cities.add_edge("Bridlington", "Wick", 512) cities.add_edge("Luton", "Wick", 627) cities.add_edge("Luton", "Bognor", 112) cities.add_edge("Luton", "Wrexham", 151)
class Graph(gtk.ScrolledWindow): def __init__(self, builder, changed_method): gtk.ScrolledWindow.__init__(self) self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.builder = builder self.event_box = EventBox() self.event_box.add_events(gtk.gdk.BUTTON_PRESS_MASK) self.event_box.add_events(gtk.gdk.BUTTON_RELEASE_MASK) self.event_box.add_events(gtk.gdk.MOTION_NOTIFY) self.event_box.add_events(gtk.gdk.BUTTON1_MOTION_MASK) self.event_box.add_events(gtk.gdk.KEY_PRESS_MASK) self.event_box.set_events(gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK) self.event_box.connect('motion-notify-event', self.mouse_motion) self.event_box.connect('button-press-event', self.mouse_press) self.event_box.connect('button-release-event', self.mouse_release) self.event_box.connect('scroll-event', self.mouse_scroll) self.action = None self.menu = None self.box = None self.last_vertex_clicked = None self.last_position_clicked = None self.box_selecting = None self.changed = False self.changed_method = changed_method self.graph = GraphController() self.area = GraphArea(self.graph) self.event_box.add(self.area) self.add_with_viewport(self.event_box) self.area.show() self.event_box.show() self.show() # To UNDO and REDO actions self.states = [] self.state_index = None self.add_state() # Algorithm stuff self.algorithm_runner = None self.algorithm_playing = None self.algorithm_paused = None self.algorithm_states = [] def centralize_scroll(self, position=None): """Put both scrolls in center""" vadj = self.get_vadjustment() hadj = self.get_hadjustment() if not position: position = [(vadj.upper / 2), (hadj.upper / 2)] print([int(x) for x in position]) hadj.set_value(position[0] - (hadj.page_size / 2)) vadj.set_value(position[1] - (vadj.page_size / 2)) def zoom_in(self, center=None): if self.area.zoom < 1.7: self.area.zoom *= 1.1 self.do_zoom(lambda v: v * 1.1, center) def zoom_out(self, center=None): if self.area.zoom > 0.2: self.area.zoom /= 1.1 self.do_zoom(lambda v: v / 1.1, center) def zoom_default(self): old = self.area.zoom self.area.zoom = 1 self.do_zoom(lambda v: v / old) def do_zoom(self, op, center=None): width, height = self.area.get_size_request() vadj = self.get_vadjustment() hadj = self.get_hadjustment() width *= self.area.zoom height *= self.area.zoom if not center: offsetx, offsety = self.event_box.get_size_request() x = (hadj.get_value() + offsetx / 2) * self.area.zoom y = (vadj.get_value() + offsety / 2) * self.area.zoom center = [x, y] hadj.set_upper(width) vadj.set_upper(height) # self.centralize_scroll(center) hadj.set_value(op(hadj.get_value())) vadj.set_value(op(vadj.get_value())) self.area.queue_draw() def mouse_scroll(self, widget, event): width, height = self.area.get_size_request() vadj = self.get_vadjustment() hadj = self.get_hadjustment() if not (event.state & gtk.gdk.CONTROL_MASK): return center = [v / self.area.zoom for v in event.get_coords()] if event.direction == gtk.gdk.SCROLL_UP: self.zoom_in(center) elif event.direction == gtk.gdk.SCROLL_DOWN: self.zoom_out(center) width, height = self.area.get_size_request() vadj = self.get_vadjustment() hadj = self.get_hadjustment() def set_changed(self, value): if self.changed != value: self.changed = value self.changed_method(self) def add_vertex(self): self.graph.add_vertex(self.last_position_clicked) self.add_state() self.action = None self.area.queue_draw() def remove_vertex(self): to_be_removed = list(self.graph.selected_vertices()) list(map(lambda vertex: self.graph.remove_vertex(vertex), to_be_removed)) self.add_state() self.action = None self.area.queue_draw() def edit_vertex(self): if len(self.graph.selected_vertices()) == 1: vertex = self.graph.selected_vertices()[0] self.graph.deselect_vertex(vertex) vertex_edit = Vertex(self, vertex) def add_edge(self): if len(self.graph.selected_vertices()) == 1: vertex = self.graph.find_by_position(self.last_position_clicked) if vertex != None and vertex != self.graph.selected_vertices()[0]: self.graph.add_edge(self.graph.selected_vertices()[0], vertex) self.graph.deselect_vertex(self.graph.selected_vertices()[0]) self.add_state() self.action = None self.area.queue_draw() return True if len(self.graph.selected_vertices()) > 1: for i in range(len(self.graph.selected_vertices())): for j in range(i, len(self.graph.selected_vertices())): vertex1 = self.graph.selected_vertices()[i] vertex2 = self.graph.selected_vertices()[j] if vertex1 != vertex2: self.graph.add_edge(vertex1, vertex2) if self.graph.directed: self.graph.add_edge(vertex2, vertex1) selected_vertices = list(self.graph.selected_vertices()) if len(selected_vertices): self.graph.deselect_all() self.add_state() for vertex in selected_vertices: self.graph.select_vertex(vertex) self.action = None self.area.queue_draw() return True self.action = "add_edge" return False def remove_edge(self): # TODO - Handle multiple edges if len(self.graph.selected_vertices()) == 1: vertex = self.graph.find_by_position(self.last_position_clicked) if vertex != None: edge = self.graph.find_edge(self.graph.selected_vertices()[0], vertex) if len(edge) > 0: self.graph.remove_edge(edge[0]) self.graph.deselect_vertex(self.graph.selected_vertices()[0]) self.add_state() self.action = None self.area.queue_draw() return True elif len(self.graph.selected_vertices()) > 1: for vertex1 in self.graph.selected_vertices(): for vertex2 in self.graph.selected_vertices(): if vertex1 != vertex2: edge = self.graph.find_edge(vertex1, vertex2) if len(edge) > 0: self.graph.remove_edge(edge[0]) self.graph.deselect_all() self.add_state() self.action = None self.area.queue_draw() return True return False def edit_edge(self): # TODO - Handle multiple edges if len(self.graph.selected_vertices()) == 2: v1 = self.graph.selected_vertices()[0] v2 = self.graph.selected_vertices()[1] edge = self.graph.find_edge(v1, v2) self.graph.deselect_vertex(v1) self.graph.deselect_vertex(v2) # TODO: This only works for 1 edge. 0 or more than 1 edges: fail. edge_edit = Edge(self, edge[0]) def select_area(self, event, area): if not area: return x, y, w, h = area vertices = self.graph.find_in_area(x, y, w, h) from gtk.gdk import CONTROL_MASK, SHIFT_MASK if not (event.state & CONTROL_MASK or event.state & SHIFT_MASK): self.graph.deselect_all() method = self.graph.select_vertex if (event.state & CONTROL_MASK): method = self.graph.toggle_vertex_selection list(map(lambda vertex: method(vertex), vertices)) def select_vertex(self, event): vertex = self.graph.find_by_position(self.last_position_clicked) from gtk.gdk import CONTROL_MASK, SHIFT_MASK if not (event.state & CONTROL_MASK or event.state & SHIFT_MASK) and (len(self.graph.selected_vertices()) > 0): if not vertex or not vertex.selected: self.graph.deselect_all() if vertex: if vertex.selected and (event.state & CONTROL_MASK or event.state & SHIFT_MASK): self.graph.deselect_vertex(vertex) else: self.graph.select_vertex(vertex) self.last_vertex_clicked = vertex else: self.box_selecting = self.last_position_clicked self.action = None def __block_event_box(self): if self.algorithm_runner: self.event_box.handler_block_by_func(self.mouse_motion) self.event_box.handler_block_by_func(self.mouse_press) self.event_box.handler_block_by_func(self.mouse_release) self.event_box.handler_block_by_func(self.mouse_scroll) else: self.event_box.handler_unblock_by_func(self.mouse_motion) self.event_box.handler_unblock_by_func(self.mouse_press) self.event_box.handler_unblock_by_func(self.mouse_release) self.event_box.handler_unblock_by_func(self.mouse_scroll) def algorithm_play(self): if self.algorithm_runner: self.graph.deselect_all() self.algorithm_playing = True if self.algorithm_paused: self.algorithm_paused = False else: self.algorithm_runner.play() self.queue_draw() gobject.timeout_add(500, self.algorithm_next, True) def algorithm_pause(self): if self.algorithm_runner: if self.algorithm_playing: self.algorithm_runner.pause() self.algorithm_playing = False self.algorithm_paused = True def algorithm_load(self, Algorithm): if self.algorithm_runner: self.algorithm_runner.stop() self.algorithm_runner = Algorithm(self) self.__block_event_box() def algorithm_next(self, auto=False): if self.algorithm_runner: if auto and not self.algorithm_paused: next(self.algorithm_runner) self.queue_draw() if self.algorithm_playing and self.algorithm_runner.is_alive(): return True else: if not self.algorithm_playing or not self.algorithm_runner.is_alive(): next(self.algorithm_runner) self.queue_draw() self.algorithm_playing = False return False def algorithm_prev(self): if self.algorithm_runner: self.algorithm_runner.prev() self.queue_draw() if self.algorithm_playing: self.algorithm_playing = False self.algorithm_prev() def algorithm_stop(self): if self.algorithm_runner: self.algorithm_runner.stop() self.algorithm_runner = None self.queue_draw() self.__block_event_box() def algorithm_layout(self): self.graph.layout_graph(50) self.queue_draw() def add_state(self): state = pickle.dumps(self.graph) if not self.state_index: self.state_index = 0 for place in ["menu_edit_", "toolbutton_"]: self.builder.get_object(place + "undo").set_sensitive(True) if (self.state_index < len(self.states) - 1): self.states = self.states[:self.state_index + 1] for place in ["menu_edit_", "toolbutton_"]: self.builder.get_object(place + "redo").set_sensitive(False) self.states.append(state) if len(self.states) == 1: self.state_index = 0 else: self.state_index += 1 def prev_state(self): if (self.state_index > 0 and len(self.states) > 0): self.state_index -= 1 graph = self.states[self.state_index] state = pickle.loads(graph) if self.state_index == 0: for place in ["menu_edit_", "toolbutton_"]: self.builder.get_object(place + "undo").set_sensitive(False) if len(self.states) > 1: for place in ["menu_edit_", "toolbutton_"]: self.builder.get_object(place + "redo").set_sensitive(True) return state return None def next_state(self): if (self.state_index < len(self.states) - 1): self.state_index += 1 graph = self.states[self.state_index] state = pickle.loads(graph) if self.state_index == len(self.states) - 1: for place in ["menu_edit_", "toolbutton_"]: self.builder.get_object(place + "redo").set_sensitive(False) if len(self.states) > 1: for place in ["menu_edit_", "toolbutton_"]: self.builder.get_object(place + "undo").set_sensitive(True) return state return None def undo(self): graph = self.prev_state() if graph: graph.path = self.graph.path graph.title = self.graph.title self.area.graph = graph self.graph = graph self.set_changed(True) self.queue_draw() def redo(self): graph = self.next_state() if graph: self.area.graph = graph self.graph = graph self.set_changed(True) self.queue_draw() def mouse_press(self, widget, event): self.last_position_clicked = [v / self.area.zoom for v in event.get_coords()] if event.button == 1: if self.action != None: self.set_changed(True) if self.action == None: self.select_vertex(event) if event.type == gtk.gdk._2BUTTON_PRESS: self.edit_vertex() elif self.action == "add_vertex": self.add_vertex() elif self.action == "remove_vertex": self.remove_vertex() elif self.action == "add_edge": if not self.add_edge(): self.select_vertex(event) elif self.action == "remove_edge": if not self.remove_edge(): self.select_vertex(event) elif event.button == 2: if self.action != None: self.set_changed(True) if self.action == None: vertex = self.graph.find_by_position(self.last_position_clicked) if vertex: self.select_vertex(event) self.action = "add_edge" else: self.add_vertex() elif self.action == "add_edge": if not self.add_edge(): self.select_vertex(event) elif event.button == 3: self.right_click_menu(event) self.area.queue_draw() self.mouse_motion(widget, event) def right_click_menu(self, event): vertex = self.graph.find_by_position(self.last_position_clicked) if len(self.graph.selected_vertices()) == 0 and vertex: self.graph.select_vertex(vertex) def execute_action(event, action): action() if not self.menu: self.menu = gtk.Menu() self.menu_add_edge = gtk.MenuItem(_("_Add edge")) self.menu_remove_edge = gtk.MenuItem(_("_Remove edge")) self.menu_add_vertex = gtk.MenuItem(_("_Add vertex")) self.menu_remove_vertex = gtk.MenuItem(_("_Remove vertex")) self.menu_edit_vertex = gtk.MenuItem(_("_Edit vertex settings")) self.menu_edit_edge = gtk.MenuItem(_("_Edit edge settings")) self.menu_add_edge.connect("activate", execute_action, self.add_edge) self.menu_remove_edge.connect("activate", execute_action, self.remove_edge) self.menu_add_vertex.connect("activate", execute_action, self.add_vertex) self.menu_remove_vertex.connect("activate", execute_action, self.remove_vertex) self.menu_edit_vertex.connect("activate", execute_action, self.edit_vertex) self.menu_edit_edge.connect("activate", execute_action, self.edit_edge) self.menu.append(self.menu_add_vertex) self.menu.append(self.menu_remove_vertex) self.menu.append(gtk.SeparatorMenuItem()) self.menu.append(self.menu_add_edge) self.menu.append(self.menu_remove_edge) self.menu.append(gtk.SeparatorMenuItem()) self.menu.append(self.menu_edit_vertex) self.menu.append(self.menu_edit_edge) if vertex: self.menu_add_vertex.set_sensitive(False) self.menu_edit_vertex.set_sensitive(True) self.menu_remove_vertex.set_sensitive(True) self.menu_add_edge.set_sensitive(True) self.menu_remove_edge.set_sensitive(True) self.menu_edit_edge.set_sensitive(False) if len(self.graph.selected_vertices()) == 2: self.menu_edit_edge.set_sensitive(True) else: self.menu_add_vertex.set_sensitive(True) self.menu_edit_vertex.set_sensitive(False) self.menu_remove_vertex.set_sensitive(False) self.menu_add_edge.set_sensitive(len(self.graph.selected_vertices()) > 0) self.menu_remove_edge.set_sensitive(False) self.menu_edit_edge.set_sensitive(False) self.menu.show_all() self.menu.popup(None, None, None, event.button, event.time) def mouse_release(self, widget, event): selected_vertices = list(self.graph.selected_vertices()) if len(selected_vertices) > 0 and self.last_vertex_clicked: self.graph.deselect_all() self.add_state() for vertex in selected_vertices: self.graph.select_vertex(vertex) self.last_vertex_clicked = None self.box_selecting = None self.select_area(event, self.area.selected_area) self.area.selected_area = None self.area.queue_draw() def mouse_motion(self, widget, event): coords = [v / self.area.zoom for v in event.get_coords()] if self.box_selecting: x, y = self.box_selecting w, h = [e - s for e, s in zip(coords, self.box_selecting)] self.area.selected_area = (x, y, w, h) self.area.queue_draw() return selected_vertices = self.graph.selected_vertices() if self.action == "add_edge" and len(selected_vertices) == 1: start = selected_vertices[0].position end = coords self.area.adding_edge = (start, end) self.area.queue_draw() else: self.area.adding_edge = None if len(selected_vertices) > 0 and self.last_vertex_clicked: end_position = coords start_position = self.last_vertex_clicked.position delta_x = end_position[0] - start_position[0] delta_y = end_position[1] - start_position[1] self.set_changed(True) for vertex in selected_vertices: new_position = [vertex.position[0] + delta_x, vertex.position[1] + delta_y] vertex.position = new_position self.area.queue_draw()