class MelhornTwoApprox: def __init__(self, instance): dists, paths, closest_sources, limits = voronoi( instance.g, instance.terms, instance.weights) self.instance = instance self.dists = dists self.paths = paths self.closest_sources = closest_sources self.limits = limits self.gc = UndirectedGraph() self.treec = None self.nontree_edges = SortedListWithKey(key=lambda ec: self.weights[ec]) self.sources = set(self.instance.terms) self.nodes = {x: self.gc.add_node() for x in self.instance.terms} self.nodesback = {v: x for x, v in self.nodes.items()} self.weights = {} self.pathslinks = {} for x, limit_nodes in self.limits.items(): xc = self.nodes[x] for u, edges in limit_nodes.items(): for e in edges: v = e.neighbor(u) y = self.closest_sources[v] yc = self.nodes[y] wc = self.dists[x][u] + self.instance.weights[ e] + self.dists[y][v] try: ec = xc.get_incident_edge(yc) if self.weights[ec] > wc: self.weights[ec] = wc self.pathslinks[ec] = (u, v, e) except NodeError: ec = self.gc.add_edge(xc, yc) self.weights[ec] = wc self.pathslinks[ec] = (u, v, e) self.treec = kruskal(self.gc, self.weights) edges = set(self.treec) for ec in self.gc.edges: if ec not in edges: self.nontree_edges.add(ec) def compute(self): return self.current_tree() def current_tree(self): tree = Tree(self.instance.g, self.instance.weights) for ec in self.treec: vuc, vvc = ec.extremities xu = self.nodesback[vuc] xv = self.nodesback[vvc] u, v, e = self.pathslinks[ec] try: pathu = self.paths[xu][u] pathv = self.paths[xv][v] except KeyError: pathu = self.paths[xu][v] pathv = self.paths[xv][u] for f in pathu: tree.add_edge(f) for f in pathv: tree.add_edge(f) tree.add_edge(e) tree.simplify(self.instance.terms) return tree def current_cost(self): return self.treec.cost def _add_edge(self, ec): fc = self.treec.add_edge(ec) if fc is not None: self.nontree_edges.add(fc) def _decrease_edge_key(self, ec, wc, pathlink): if ec in self.nontree_edges: self.nontree_edges.remove(ec) self.weights[ec] = wc self.nontree_edges.add(ec) else: self.weights[ec] = wc self.pathslinks[ec] = pathlink def _remove_edges(self, rem_edges): for ec in rem_edges: try: self.nontree_edges.remove(ec) except ValueError: self.treec.remove_edge(ec) del self.weights[ec] del self.pathslinks[ec] to_remove_from_non_tree = [] for ec in self.nontree_edges: remove_edge = self.treec.add_edge(ec, handle_conflict=False) if remove_edge is None: to_remove_from_non_tree.append(ec) if len(self.treec) == len(self.gc) - 1: break for ec in to_remove_from_non_tree: self.nontree_edges.remove(ec) def add_sources(self, new_terms): incremental_voronoi(self.instance.g, self.sources, self.instance.weights, self.dists, self.paths, self.closest_sources, self.limits, new_terms) rem_edges = [] for ec in self.gc.edges: vuc, vvc = ec.extremities xu = self.nodesback[vuc] xv = self.nodesback[vvc] u, v, e = self.pathslinks[ec] if self.closest_sources[u] != xu and self.closest_sources[u] != xv or \ self.closest_sources[v] != xu and self.closest_sources[v] != xv: rem_edges.append(ec) self.sources |= set(new_terms) nodes = {x: self.gc.add_node() for x in new_terms} self.nodes.update(nodes) self.nodesback.update({v: x for x, v in nodes.items()}) add_edges = [] for x in new_terms: xc = self.nodes[x] limit_nodes = self.limits[x] for u, edges in limit_nodes.items(): for e in edges: v = e.neighbor(u) y = self.closest_sources[v] yc = self.nodes[y] wc = self.dists[x][u] + self.instance.weights[ e] + self.dists[y][v] try: ec = xc.get_incident_edge(yc) if self.weights[ec] > wc: self.weights[ec] = wc self.pathslinks[ec] = (u, v, e) except NodeError: ec = self.gc.add_edge(xc, yc) self.weights[ec] = wc self.pathslinks[ec] = (u, v, e) add_edges.append(ec) for ec in add_edges: self._add_edge(ec) self._remove_edges(rem_edges) for ec in rem_edges: self.gc.remove_edge(ec) def rem_sources(self, rem_terms): neighbor_sources = decremental_voronoi(self.instance.g, self.sources, self.instance.weights, self.dists, self.paths, self.closest_sources, self.limits, rem_terms) self.sources -= set(rem_terms) rem_nodes = set() for x in rem_terms: xc = self.nodes.pop(x) del self.nodesback[xc] rem_nodes.add(xc) add_edges = set() decrease_key_edges = {} for x in neighbor_sources: xc = self.nodes[x] limit_nodes = self.limits[x] for u, edges in limit_nodes.items(): for e in edges: v = e.neighbor(u) y = self.closest_sources[v] if y not in neighbor_sources: continue yc = self.nodes[y] wc = self.dists[x][u] + self.instance.weights[ e] + self.dists[y][v] try: ec = xc.get_incident_edge(yc) if self.weights[ec] > wc: if ec not in add_edges: decrease_key_edges[ec] = (wc, (u, v, e)) else: self.weights[ec] = wc self.pathslinks[ec] = (u, v, e) except NodeError: ec = self.gc.add_edge(xc, yc) self.weights[ec] = wc self.pathslinks[ec] = (u, v, e) add_edges.add(ec) for ec, val in decrease_key_edges.items(): wc, pathlink = val self._decrease_edge_key(ec, wc, pathlink) for ec in add_edges: self._add_edge(ec) for xc in rem_nodes: self._remove_edges(set(xc.incident_edges)) self.gc.remove_node(xc)
class TestUndirectedNode(unittest.TestCase): def setUp(self): self.g = UndirectedGraph() v1 = self.g.add_node() v2 = self.g.add_node() v3 = self.g.add_node() v4 = self.g.add_node() v5 = self.g.add_node() v6 = self.g.add_node() v7 = self.g.add_node() v8 = self.g.add_node() self.nodes = [v1, v2, v3, v4, v5, v6, v7, v8] self.couples = [(v1, v5), (v2, v6), (v3, v7), (v4, v8), (v1, v2), (v2, v3), (v3, v4), (v4, v1)] self.edges = [self.g.add_edge(u, v) for u, v in self.couples] def test_add_node_increase_index(self): v1, v2, v3, v4, v5, v6, v7, v8 = self.nodes self.assertEqual(v1.index + 1, v2.index) self.assertEqual(v2.index + 1, v3.index) self.assertEqual(v3.index + 1, v4.index) self.assertEqual(v4.index + 1, v5.index) self.assertEqual(v5.index + 1, v6.index) self.assertEqual(v6.index + 1, v7.index) self.assertEqual(v7.index + 1, v8.index) def test_add_edge_increase_nb_neighbors(self): sizes = [3, 3, 3, 3, 1, 1, 1, 1] for v, size in zip(self.nodes, sizes): self.assertEqual(len(v), size) self.assertEqual(v.nb_neighbors, size) def test_add_node_do_not_increase_nb_neighbors(self): self.g.add_node() self.g.add_node() self.g.add_node() sizes = [3, 3, 3, 3, 1, 1, 1, 1] for v, size in zip(self.nodes, sizes): self.assertEqual(len(v), size) self.assertEqual(v.nb_neighbors, size) def test_remove_node_decrease_nb_neighbors_of_neighbors(self): self.g.remove_node(self.nodes[0]) sizes = [2, 3, 2, 0, 1, 1, 1] for v, size in zip(self.nodes[1:], sizes): self.assertEqual(len(v), size) self.assertEqual(v.nb_neighbors, size) self.g.remove_node(self.nodes[5]) sizes = [1, 3, 2, 0, 1, 1] for v, size in zip(self.nodes[1:5] + self.nodes[6:], sizes): self.assertEqual(len(v), size) self.assertEqual(v.nb_neighbors, size) def test_remove_edge_decrease_nb_neighbors_of_extremities(self): self.g.remove_edge(self.edges[0]) sizes = [2, 3, 3, 3, 0, 1, 1, 1] for v, size in zip(self.nodes, sizes): self.assertEqual(len(v), size) self.assertEqual(v.nb_neighbors, size) self.g.remove_edge(self.edges[4]) sizes = [1, 2, 3, 3, 0, 1, 1, 1] for v, size in zip(self.nodes, sizes): self.assertEqual(len(v), size) self.assertEqual(v.nb_neighbors, size) for i, e in enumerate(self.edges): if i != 0 and i != 4: self.g.remove_edge(e) for v in self.nodes: self.assertEqual(len(v), 0) self.assertEqual(v.nb_neighbors, 0) def test_add_edge_add_neighbors(self): for u in self.g: for v in self.g: if u == v: continue if (u, v) in self.couples: self.assertTrue(v.is_neighbor_of(u)) self.assertTrue(u.is_neighbor_of(v)) elif (v, u) not in self.couples: self.assertFalse(v.is_neighbor_of(u)) self.assertFalse(u.is_neighbor_of(v)) def test_add_node_do_not_add_neighbors(self): u = self.g.add_node() v = self.g.add_node() w = self.g.add_node() for v2 in self.nodes: self.assertFalse(u.is_neighbor_of(v2)) self.assertFalse(v.is_neighbor_of(v2)) self.assertFalse(w.is_neighbor_of(v2)) self.assertFalse(v2.is_neighbor_of(u)) self.assertFalse(v2.is_neighbor_of(v)) self.assertFalse(v2.is_neighbor_of(w)) def test_remove_node_remove_neighbor_of_neighbors(self): v1, v2, v3, v4, v5, v6, v7, v8 = self.nodes self.g.remove_node(v1) self.assertFalse(v2.is_neighbor_of(v1)) self.assertFalse(v4.is_neighbor_of(v1)) self.assertFalse(v5.is_neighbor_of(v1)) self.assertFalse(v1.is_neighbor_of(v2)) self.assertFalse(v1.is_neighbor_of(v4)) self.assertFalse(v1.is_neighbor_of(v5)) def test_remove_edge_remove_neighbors_of_extremities(self): v1, v2, v3, v4, v5, v6, v7, v8 = self.nodes self.g.remove_edge(self.edges[0]) self.assertFalse(v1.is_neighbor_of(v5)) self.assertFalse(v5.is_neighbor_of(v1)) self.g.remove_edge(self.edges[7]) self.assertFalse(v1.is_neighbor_of(v4)) self.assertFalse(v4.is_neighbor_of(v1)) def test_add_edge_add_neighbors_2(self): for u in self.g: for v in self.g: if u == v: continue if (u, v) in self.couples: self.assertIn(u, list(v.neighbors)) self.assertIn(v, list(u.neighbors)) elif (v, u) not in self.couples: self.assertNotIn(u, list(v.neighbors)) self.assertNotIn(v, list(u.neighbors)) def test_add_node_do_not_add_neighbors_2(self): u = self.g.add_node() v = self.g.add_node() w = self.g.add_node() for v2 in self.nodes: self.assertNotIn(v2, list(u.neighbors)) self.assertNotIn(v2, list(v.neighbors)) self.assertNotIn(v2, list(w.neighbors)) self.assertNotIn(u, list(v2.neighbors)) self.assertNotIn(v, list(v2.neighbors)) self.assertNotIn(w, list(v2.neighbors)) def test_remove_node_remove_neighbor_of_neighbors_2(self): v1, v2, v3, v4, v5, v6, v7, v8 = self.nodes self.g.remove_node(v1) self.assertNotIn(v1, list(v2.neighbors)) self.assertNotIn(v1, list(v4.neighbors)) self.assertNotIn(v1, list(v5.neighbors)) self.assertNotIn(v2, list(v1.neighbors)) self.assertNotIn(v4, list(v1.neighbors)) self.assertNotIn(v5, list(v1.neighbors)) def test_remove_edge_remove_neighbors_of_extremities_2(self): v1, v2, v3, v4, v5, v6, v7, v8 = self.nodes self.g.remove_edge(self.edges[0]) self.assertNotIn(v5, list(v1.neighbors)) self.assertNotIn(v1, list(v5.neighbors)) self.g.remove_edge(self.edges[7]) self.assertNotIn(v4, list(v1.neighbors)) self.assertNotIn(v1, list(v4.neighbors)) def test_nb_neighbors_equal_nb_incident_edges(self): for v in self.nodes: self.assertEqual(v.nb_neighbors, len(list(v.incident_edges))) def test_add_edge_add_incident_edge(self): for e, couple in zip(self.edges, self.couples): u, v = couple for w in self.nodes: if w != u and w != v: self.assertFalse(w.is_incident_to(e)) else: self.assertTrue(w.is_incident_to(e)) def test_new_node_are_not_incident_to_previous_edges(self): u = self.g.add_node() v = self.g.add_node() w = self.g.add_node() for e in self.edges: self.assertFalse(u.is_incident_to(e)) self.assertFalse(v.is_incident_to(e)) self.assertFalse(w.is_incident_to(e)) def test_remove_node_remove_incident_edges_of_neighbors(self): v1, v2, v3, v4, v5, v6, v7, v8 = self.nodes e1, e2, e3, e4, e5, e6, e7, e8 = self.edges self.g.remove_node(v1) self.assertFalse(v2.is_incident_to(e5)) self.assertFalse(v5.is_incident_to(e1)) self.assertFalse(v4.is_incident_to(e8)) def test_remove_edge_remove_incident_edges_extremities(self): v1, v2, v3, v4, v5, v6, v7, v8 = self.nodes e1, e2, e3, e4, e5, e6, e7, e8 = self.edges self.g.remove_edge(e1) self.assertFalse(v1.is_incident_to(e1)) self.assertFalse(v5.is_incident_to(e1)) self.g.remove_edge(e8) self.assertFalse(v1.is_incident_to(e8)) self.assertFalse(v4.is_incident_to(e8)) def test_add_edge_add_incident_edge_2(self): for e, couple in zip(self.edges, self.couples): u, v = couple for w in self.nodes: if w != u and w != v: self.assertNotIn(e, list(w.incident_edges)) else: self.assertIn(e, list(w.incident_edges)) def test_new_node_are_not_incident_to_previous_edges_2(self): u = self.g.add_node() v = self.g.add_node() w = self.g.add_node() for e in self.edges: self.assertNotIn(e, list(u.incident_edges)) self.assertNotIn(e, list(v.incident_edges)) self.assertNotIn(e, list(w.incident_edges)) def test_remove_node_remove_incident_edges_of_neighbors_2(self): v1, v2, v3, v4, v5, v6, v7, v8 = self.nodes e1, e2, e3, e4, e5, e6, e7, e8 = self.edges self.g.remove_node(v1) self.assertNotIn(e5, list(v2.incident_edges)) self.assertNotIn(e1, list(v5.incident_edges)) self.assertNotIn(e8, list(v4.incident_edges)) def test_remove_edge_remove_incident_edges_extremities_2(self): v1, v2, v3, v4, v5, v6, v7, v8 = self.nodes e1, e2, e3, e4, e5, e6, e7, e8 = self.edges self.g.remove_edge(e1) self.assertNotIn(e1, list(v1.incident_edges)) self.assertNotIn(e1, list(v5.incident_edges)) self.g.remove_edge(e8) self.assertNotIn(e8, list(v1.incident_edges)) self.assertNotIn(e8, list(v4.incident_edges)) def test_add_edge_add_incident_edge_3(self): for e, couple in zip(self.edges, self.couples): u, v = couple self.assertEqual(e, u.get_incident_edge(v)) self.assertEqual(e, v.get_incident_edge(u)) def test_get_incident_edge_raise_TypeError_with_not_node(self): v1, v2, v3, v4, v5, v6, v7, v8 = self.nodes e1, e2, e3, e4, e5, e6, e7, e8 = self.edges g2 = DirectedGraph() v = g2.add_node() with self.assertRaises(TypeError): v1.get_incident_edge(1) with self.assertRaises(TypeError): v1.get_incident_edge('abc') with self.assertRaises(TypeError): v1.get_incident_edge((v1, v2)) with self.assertRaises(TypeError): v1.get_incident_edge(e5) with self.assertRaises(TypeError): v1.get_incident_edge(None) with self.assertRaises(TypeError): v1.get_incident_edge(v) def test_get_incident_edge_raise_NodeError_with_not_neighbor(self): v1, v2, v3, v4, v5, v6, v7, v8 = self.nodes g2 = UndirectedGraph() v = g2.add_node() with self.assertRaises(NodeError): v1.get_incident_edge(v3) with self.assertRaises(NodeError): v1.get_incident_edge(v) def test_get_incident_edge_raise_NodeError_with_not_neighbor_due_to_remove_edge( self): v1, v2, v3, v4, v5, v6, v7, v8 = self.nodes e1, e2, e3, e4, e5, e6, e7, e8 = self.edges self.g.remove_edge(e1) with self.assertRaises(NodeError): v1.get_incident_edge(v5) with self.assertRaises(NodeError): v5.get_incident_edge(v1) def test_get_incident_edge_raise_NodeError_with_not_neighbor_due_to_remove_node( self): v1, v2, v3, v4, v5, v6, v7, v8 = self.nodes self.g.remove_node(v5) with self.assertRaises(NodeError): v1.get_incident_edge(v5)