class When_we_want_to_find_the_shortest_path_with_the_same_origin_and_destination: def given_a_graph(self): """ (A)--1-->(B)--1-->(C) \ / <-------1------- """ self.graph = Graph() self.vertexA = Vertex(element="Vertex A") self.vertexB = Vertex(element="Vertex B") self.vertexC = Vertex(element="Vertex C") self.edgeAB = Edge(self.vertexA, self.vertexB, element=1) self.edgeBC = Edge(self.vertexB, self.vertexC, element=1) self.edgeCA = Edge(self.vertexC, self.vertexA, element=1) self.graph.add_edge(self.edgeAB) self.graph.add_edge(self.edgeBC) self.graph.add_edge(self.edgeCA) def because_we_calculate_the_most_cost_effective_path(self): self.path, self.cost = shortest_path(self.graph, self.vertexA, self.vertexA) def it_should_return_the_shortest_path(self): expected_path = [self.vertexA, self.vertexB, self.vertexC, self.vertexA] expect(self.path).to(equal(expected_path)) def it_should_return_the_correct_cost_of_the_shortest_path(self): expect(self.cost).to(equal(3))
class When_we_want_to_find_the_path_with_the_smaller_cost: def given_a_graph(self): """ (A)--3-->(B)--1-->(C)--1-->(D) \ / -------1-------> """ self.graph = Graph() self.vertexA = Vertex(element="Vertex A") self.vertexB = Vertex(element="Vertex B") self.vertexC = Vertex(element="Vertex C") self.vertexD = Vertex(element="Vertex D") self.edgeAB = Edge(self.vertexA, self.vertexB, element=3) self.edgeBC = Edge(self.vertexB, self.vertexC, element=1) self.edgeCD = Edge(self.vertexC, self.vertexD, element=1) self.edgeBD = Edge(self.vertexB, self.vertexD, element=1) self.graph.add_edge(self.edgeAB) self.graph.add_edge(self.edgeBC) self.graph.add_edge(self.edgeCD) self.graph.add_edge(self.edgeBD) def because_we_calculate_the_most_cost_effective_path(self): self.path, self.cost = shortest_path(self.graph, self.vertexA, self.vertexD) def it_should_return_the_shortest_path(self): expected_path = [self.vertexA, self.vertexB, self.vertexD] expect(self.path).to(equal(expected_path)) def it_should_return_the_correct_cost_of_the_shortest_path(self): expect(self.cost).to(equal(4))
class When_we_want_to_work_with_a_simple_graph: def given_an_empty_graph(self): self.graph = Graph() def because_we_configure_the_graph_with_vertices_and_edges(self): self.vertex1 = Vertex(element="Vertex 1") self.vertex2 = Vertex(element="Vertex 2") self.vertex3 = Vertex(element="Vertex 3") self.edge12 = Edge(self.vertex1, self.vertex2, element="6") self.edge23 = Edge(self.vertex2, self.vertex3, element="4") self.graph.add_edge(self.edge12) self.graph.add_edge(self.edge23) def it_should_have_the_correct_number_of_vertices(self): expect(self.graph.vertices).to(have_len(3)) def it_should_have_the_correct_number_of_edges(self): expect(self.graph.edges).to(have_len(2)) def it_should_have_the_correct_configuration(self): expect(self.graph).to(equal({ self.vertex1: {self.vertex2: self.edge12}, self.vertex2: {self.vertex3: self.edge23}, self.vertex3: {}}))
class When_we_want_to_find_the_path_when_it_is_unreachable: def given_a_graph(self): """ (A)--3-->(B)--1-->(C)<--1--(D) \ / <-------1------- """ self.graph = Graph() self.vertexA = Vertex(element="Vertex A") self.vertexB = Vertex(element="Vertex B") self.vertexC = Vertex(element="Vertex C") self.vertexD = Vertex(element="Vertex D") self.edgeAB = Edge(self.vertexA, self.vertexB, element=3) self.edgeBC = Edge(self.vertexB, self.vertexC, element=1) self.edgeDC = Edge(self.vertexD, self.vertexC, element=1) self.edgeDB = Edge(self.vertexD, self.vertexB, element=1) self.graph.add_edge(self.edgeAB) self.graph.add_edge(self.edgeBC) self.graph.add_edge(self.edgeDC) self.graph.add_edge(self.edgeDB) def because_we_calculate_the_shortest_path_to_an_unreachable_vertex(self): self.path, self.cost = shortest_path(self.graph, self.vertexA, self.vertexD) def it_should_return_the_shortest_path(self): expect(self.path).to(equal([])) def it_should_return_the_correct_cost_of_the_shortest_path(self): expect(self.cost).to(equal(float("inf")))
def given_a_graph(self): """ 1 / \ \ / (A)--3-->(B)--1-->(C)--2-->(D) \ / -------10------> """ self.graph = Graph() self.vertexA = Vertex(element="Vertex A") self.vertexB = Vertex(element="Vertex B") self.vertexC = Vertex(element="Vertex C") self.vertexD = Vertex(element="Vertex D") self.edgeAB = Edge(self.vertexA, self.vertexB, element=3) self.edgeBC = Edge(self.vertexB, self.vertexC, element=1) self.edgeCD = Edge(self.vertexC, self.vertexD, element=2) self.edgeBD = Edge(self.vertexB, self.vertexD, element=10) # Add special case of edge that is looping from vertexB to itself self.edgeBB = Edge(self.vertexB, self.vertexB, element=1) self.graph.add_edge(self.edgeAB) self.graph.add_edge(self.edgeBC) self.graph.add_edge(self.edgeCD) self.graph.add_edge(self.edgeBD) self.graph.add_edge(self.edgeBB)
def given_a_graph(self): """ -----------5----------> / <--2--- \ / / \ \ (A)--3-->(B)--1-->(C)--1-->(D) \ \ / / \ <-----/--3------ -----2------>/ """ self.graph = Graph() self.vertexA = Vertex(element="Vertex A") self.vertexB = Vertex(element="Vertex B") self.vertexC = Vertex(element="Vertex C") self.vertexD = Vertex(element="Vertex D") self.edgeAB = Edge(self.vertexA, self.vertexB, element=3) self.edgeAD = Edge(self.vertexA, self.vertexD, element=5) self.edgeAC = Edge(self.vertexA, self.vertexC, element=2) self.edgeBC = Edge(self.vertexB, self.vertexC, element=1) self.edgeCD = Edge(self.vertexC, self.vertexD, element=1) self.edgeDB = Edge(self.vertexD, self.vertexB, element=3) self.graph.add_edge(self.edgeAB) self.graph.add_edge(self.edgeAD) self.graph.add_edge(self.edgeAC) self.graph.add_edge(self.edgeBC) self.graph.add_edge(self.edgeCD) self.graph.add_edge(self.edgeDB)
def given_a_graph(self): """ (A)--1-->(B)--1-->(C) \ / <-------1------- """ self.graph = Graph() self.vertexA = Vertex(element="Vertex A") self.vertexB = Vertex(element="Vertex B") self.vertexC = Vertex(element="Vertex C") self.edgeAB = Edge(self.vertexA, self.vertexB, element=1) self.edgeBC = Edge(self.vertexB, self.vertexC, element=1) self.edgeCA = Edge(self.vertexC, self.vertexA, element=1) self.graph.add_edge(self.edgeAB) self.graph.add_edge(self.edgeBC) self.graph.add_edge(self.edgeCA)
def given_a_graph(self): """ (A)--3-->(B)--1-->(C)--1-->(D) \ / -------1-------> """ self.graph = Graph() self.vertexA = Vertex(element="Vertex A") self.vertexB = Vertex(element="Vertex B") self.vertexC = Vertex(element="Vertex C") self.vertexD = Vertex(element="Vertex D") self.edgeAB = Edge(self.vertexA, self.vertexB, element=3) self.edgeBC = Edge(self.vertexB, self.vertexC, element=1) self.edgeCD = Edge(self.vertexC, self.vertexD, element=1) self.edgeBD = Edge(self.vertexB, self.vertexD, element=1) self.graph.add_edge(self.edgeAB) self.graph.add_edge(self.edgeBC) self.graph.add_edge(self.edgeCD) self.graph.add_edge(self.edgeBD)
class When_we_want_to_find_the_number_of_paths_between_two_vertices_in_a_cyclic_graph: def given_a_graph(self): """ -----------5----------> / <--2--- \ / / \ \ (A)--3-->(B)--1-->(C)--1-->(D) \ \ / / \ <-----/--3------ -----2------>/ """ self.graph = Graph() self.vertexA = Vertex(element="Vertex A") self.vertexB = Vertex(element="Vertex B") self.vertexC = Vertex(element="Vertex C") self.vertexD = Vertex(element="Vertex D") self.edgeAB = Edge(self.vertexA, self.vertexB, element=3) self.edgeAD = Edge(self.vertexA, self.vertexD, element=5) self.edgeAC = Edge(self.vertexA, self.vertexC, element=2) self.edgeBC = Edge(self.vertexB, self.vertexC, element=1) self.edgeCD = Edge(self.vertexC, self.vertexD, element=1) self.edgeDB = Edge(self.vertexD, self.vertexB, element=3) self.graph.add_edge(self.edgeAB) self.graph.add_edge(self.edgeAD) self.graph.add_edge(self.edgeAC) self.graph.add_edge(self.edgeBC) self.graph.add_edge(self.edgeCD) self.graph.add_edge(self.edgeDB) def because_we_want_all_available_paths_from_A_to_B(self): self.paths = list(find_paths(self.graph, self.vertexA, self.vertexD, 5)) def it_should_return_the_correct_list_of_available_paths(self): expected_paths = [ [self.vertexA, self.vertexD], [self.vertexA, self.vertexC, self.vertexD], [self.vertexA, self.vertexB, self.vertexC, self.vertexD] ] expect(self.paths).to(have_len(3)) for path in expected_paths: expect(self.paths).to(contain(path))
class When_we_want_to_find_the_path_with_the_smaller_cost_on_a_more_compicated_graph: def given_a_graph(self): """ 1 / \ \ / (A)--3-->(B)--1-->(C)--2-->(D) \ / -------10------> """ self.graph = Graph() self.vertexA = Vertex(element="Vertex A") self.vertexB = Vertex(element="Vertex B") self.vertexC = Vertex(element="Vertex C") self.vertexD = Vertex(element="Vertex D") self.edgeAB = Edge(self.vertexA, self.vertexB, element=3) self.edgeBC = Edge(self.vertexB, self.vertexC, element=1) self.edgeCD = Edge(self.vertexC, self.vertexD, element=2) self.edgeBD = Edge(self.vertexB, self.vertexD, element=10) # Add special case of edge that is looping from vertexB to itself self.edgeBB = Edge(self.vertexB, self.vertexB, element=1) self.graph.add_edge(self.edgeAB) self.graph.add_edge(self.edgeBC) self.graph.add_edge(self.edgeCD) self.graph.add_edge(self.edgeBD) self.graph.add_edge(self.edgeBB) def because_we_calculate_the_most_cost_effective_path(self): self.path, self.cost = shortest_path(self.graph, self.vertexA, self.vertexD) def it_should_return_the_shortest_path(self): expected_path = [self.vertexA, self.vertexB, self.vertexC, self.vertexD] expect(self.path).to(equal(expected_path)) def it_should_return_the_correct_cost_of_the_shortest_path(self): expect(self.cost).to(equal(6))
class When_we_configure_a_graph_by_passing_only_the_elements: def given_an_empty_graph(self): self.graph = Graph() def because_we_configure_the_graph_by_passing_only_the_elements(self): self.vertex1 = Vertex(element="Vertex 1") self.vertex2 = Vertex(element="Vertex 2") self.vertex3 = Vertex(element="Vertex 3") self.vertex4 = Vertex(element="Vertex 4") self.edge12 = Edge(self.vertex1, self.vertex2, element=1) self.edge23 = Edge(self.vertex2, self.vertex3, element=2) self.edge34 = Edge(self.vertex3, self.vertex4, element=3) self.edge43 = Edge(self.vertex4, self.vertex3, element=4) self.edge42 = Edge(self.vertex4, self.vertex2, element=5) self.graph.create_edge("Vertex 1", "Vertex 2", 1) self.graph.create_edge("Vertex 2", "Vertex 3", 2) self.graph.create_edge("Vertex 3", "Vertex 4", 3) self.graph.create_edge("Vertex 4", "Vertex 3", 4) self.graph.create_edge("Vertex 4", "Vertex 2", 5) def it_should_have_the_correct_number_of_vertices(self): expect(self.graph.vertices).to(have_len(4)) def it_should_have_the_correct_number_of_edges(self): expect(self.graph.edges).to(have_len(5)) def it_should_have_the_correct_configuration(self): expect(self.graph).to(equal({ self.vertex1: {self.vertex2: self.edge12}, self.vertex2: {self.vertex3: self.edge23}, self.vertex3: {self.vertex4: self.edge34}, self.vertex4: {self.vertex3: self.edge43, self.vertex2: self.edge42}, }))
class When_we_want_to_work_with_a_graph_with_cycles: def given_an_empty_graph(self): self.graph = Graph() def because_we_configure_the_graph_with_vertices_and_edges(self): self.vertex1 = Vertex(element="Vertex 1") self.vertex2 = Vertex(element="Vertex 2") self.vertex3 = Vertex(element="Vertex 3") self.vertex4 = Vertex(element="Vertex 4") self.edge12 = Edge(self.vertex1, self.vertex2, element=1) self.edge23 = Edge(self.vertex2, self.vertex3, element=2) self.edge34 = Edge(self.vertex3, self.vertex4, element=3) self.edge43 = Edge(self.vertex4, self.vertex3, element=4) self.edge42 = Edge(self.vertex4, self.vertex2, element=5) self.graph.add_edge(self.edge12) self.graph.add_edge(self.edge23) self.graph.add_edge(self.edge34) self.graph.add_edge(self.edge43) self.graph.add_edge(self.edge42) def it_should_have_the_correct_number_of_vertices(self): expect(self.graph.vertices).to(have_len(4)) def it_should_have_the_correct_number_of_edges(self): expect(self.graph.edges).to(have_len(5)) def it_should_have_the_correct_configuration(self): expect(self.graph).to(equal({ self.vertex1: {self.vertex2: self.edge12}, self.vertex2: {self.vertex3: self.edge23}, self.vertex3: {self.vertex4: self.edge34}, self.vertex4: {self.vertex3: self.edge43, self.vertex2: self.edge42}, }))
class When_we_want_to_find_the_number_of_paths_between_two_vertices: def given_a_graph(self): """ ------------5----------> / \ (A)--3-->(B)--1-->(C)--1-->(D) \ / -------3-------> """ self.graph = Graph() self.vertexA = Vertex(element="Vertex A") self.vertexB = Vertex(element="Vertex B") self.vertexC = Vertex(element="Vertex C") self.vertexD = Vertex(element="Vertex D") self.edgeAB = Edge(self.vertexA, self.vertexB, element=3) self.edgeAD = Edge(self.vertexA, self.vertexD, element=5) self.edgeBC = Edge(self.vertexB, self.vertexC, element=1) self.edgeCD = Edge(self.vertexC, self.vertexD, element=1) self.edgeBD = Edge(self.vertexB, self.vertexD, element=3) self.graph.add_edge(self.edgeAB) self.graph.add_edge(self.edgeAD) self.graph.add_edge(self.edgeBC) self.graph.add_edge(self.edgeCD) self.graph.add_edge(self.edgeBD) def because_we_want_all_available_paths_from_A_to_B_and_those_smaller_than_2(self): self.paths = list(find_paths(self.graph, self.vertexA, self.vertexD, 5)) self.paths_less_than_cutoff = list(find_paths(self.graph, self.vertexA, self.vertexD, 2)) self.paths_equal_2_size = list(find_paths_equal_to_size(self.graph, self.vertexA, self.vertexD, 2)) self.paths_equal_3_size = list(find_paths_equal_to_size(self.graph, self.vertexA, self.vertexD, 3)) self.paths_equal_4_size = list(find_paths_equal_to_size(self.graph, self.vertexA, self.vertexD, 4)) def it_should_return_the_correct_list_of_available_paths(self): expected_paths = [ [self.vertexA, self.vertexD], [self.vertexA, self.vertexB, self.vertexD], [self.vertexA, self.vertexB, self.vertexC, self.vertexD] ] expect(self.paths).to(have_len(3)) for path in expected_paths: expect(self.paths).to(contain(path)) def it_should_return_the_correct_list_of_available_paths_smaller_than_2_size(self): expected_paths = [ [self.vertexA, self.vertexD], [self.vertexA, self.vertexB, self.vertexD] ] expect(self.paths_less_than_cutoff).to(have_len(2)) for path in expected_paths: expect(self.paths_less_than_cutoff).to(contain(path)) def it_should_return_the_correct_list_of_available_paths_equal_to_2_size(self): expected_paths = [ [self.vertexA, self.vertexD] ] expect(self.paths_equal_2_size).to(have_len(1)) for path in expected_paths: expect(self.paths_equal_2_size).to(contain(path)) def it_should_return_the_correct_list_of_available_paths_equal_to_3_size(self): expected_paths = [ [self.vertexA, self.vertexB, self.vertexD] ] expect(self.paths_equal_3_size).to(have_len(1)) for path in expected_paths: expect(self.paths_equal_3_size).to(contain(path)) def it_should_return_the_correct_list_of_available_paths_equal_to_4_size(self): expected_paths = [ [self.vertexA, self.vertexB, self.vertexC, self.vertexD] ] expect(self.paths_equal_4_size).to(have_len(1)) for path in expected_paths: expect(self.paths_equal_4_size).to(contain(path))
def given_an_empty_graph(self): self.graph = Graph()
#!/usr/bin/env python3 from hermes import Graph, Edge, Vertex from hermes.algorithms import total_path_cost, shortest_path, find_paths, find_paths_equal_to_size, find_paths_lt_hours from hermes.exceptions import PathNotFoundException def total_path_cost_days(*args, **kwargs): try: return "{} days".format(total_path_cost(*args, **kwargs)) except PathNotFoundException: return "not possible" shipping_graph = Graph() new_york = Vertex(element="New York") liverpool = Vertex(element="Liverpool") casablanca = Vertex(element="Casablanca") buenos_aires = Vertex(element="Buenos Aires") cape_town = Vertex(element="Cape Town") shipping_graph.add_edge(Edge(buenos_aires, new_york, element=6)) shipping_graph.add_edge(Edge(buenos_aires, casablanca, element=5)) shipping_graph.add_edge(Edge(buenos_aires, cape_town, element=4)) shipping_graph.add_edge(Edge(new_york, liverpool, element=4)) shipping_graph.add_edge(Edge(liverpool, casablanca, element=3)) shipping_graph.add_edge(Edge(liverpool, cape_town, element=6)) shipping_graph.add_edge(Edge(casablanca, liverpool, element=3)) shipping_graph.add_edge(Edge(casablanca, cape_town, element=6))