def __init__(self, graph, starting_vertex_id=1, reset_heuristic=False): self.route = Route(graph) self.starting_vertex_id = starting_vertex_id self.route.goto( self.route.graph.get_vertex_by_id(starting_vertex_id)) self.done = False self.attempted_starting_vertex_ids = list([starting_vertex_id]) self.remaining_starting_vertex_ids = self.get_remaining_starting_vertex_ids( ) self.reset_heuristic = reset_heuristic self.crosses = 0
def crossover(self, other_chromosome, every_other=True): new_path = list([]) if every_other: # Combines chromosome by alternating allels. self_index = 0 other_index = 1 my_turn = True while len(new_path) < len(self.route.vertices) - 1: if my_turn and self_index < len( self.route.vertices) - 1: if self.route.vertices[ self_index].vertex_id not in new_path: new_path.append( self.route.vertices[self_index].vertex_id) my_turn = False self_index += 1 else: if other_chromosome.route.vertices[ other_index].vertex_id not in new_path: new_path.append( other_chromosome.route. vertices[other_index].vertex_id) my_turn = True if other_index >= len( other_chromosome.route.vertices) - 2: my_turn = True else: other_index += 1 else: # Splits the two chromosomes down the middle index = 0 while len(new_path) < len(self.route.vertices) - 1: if index < len(self.route.vertices) // 2: if self.route.vertices[ index].vertex_id not in new_path: new_path.append( self.route.vertices[index].vertex_id) index += 1 else: remaining_vertices = [ vertex for vertex in self.route.vertices if vertex.vertex_id not in new_path ] for remainining_vertex in remaining_vertices: new_path.append(remainining_vertex.vertex_id) new_route = Route(self.route.graph) new_route.walk_complete_path(new_path) resultant_chromosome = TravelingSalesman.GeneticAlgorithm.Chromosome( None, new_route) return resultant_chromosome
def initialize_population(self): city_range = list(range(1, 1 + len(self.graph.vertices))) for chromosome_index in range(self.population_size): random_city_order = deepcopy(city_range) random.shuffle(random_city_order) random_route = Route(graph) random_route.walk_complete_path(random_city_order) chromosome = TravelingSalesman.GeneticAlgorithm.Chromosome( chromosome_index, random_route) self.population.append(chromosome) self.population = np.array(self.population)
def depth_first_search(graph, source_vertex_id=1, target_vertex_id=11): dfs_stack = DepthFirstSearchStack() def search_deeper(current_vertex, current_route): # Get unfinished remaining adjacent vertices remaining_adjacent_vertices = dfs_stack.get_unfinished_adjacent_vertices( current_vertex.adjacent_vertices) # Update the route with the new vertex current_route.goto(current_vertex.vertex_id) # Push the current vertex ontop of the dfs stack dfs_stack.push(current_vertex, current_route) # If there are no remaining adjacent vertices if len(remaining_adjacent_vertices) == 0: # Pop the finished node off the stack finished_node = dfs_stack.pop() # walk back from the route since no longer part of it current_route.walk_back() # Mark the node complete. Update lists. dfs_stack.node_complete(finished_node) # If there are still items on the stack. if len(dfs_stack.node_stack) > 0: # Search deeper using the previous item as guide. search_deeper(dfs_stack.node_stack[-1].vertex, current_route) else: # Search the first adjacent vertex search_deeper(remaining_adjacent_vertices[0], current_route) source_vertex = graph.get_vertex_by_id(source_vertex_id) route = Route([], graph) search_deeper(source_vertex, route) return dfs_stack.get_path_to_finished_vertex_id(target_vertex_id)
def initialize_population(self): city_range = list(range(1, 1 + len(self.graph.vertices))) for chromosome_index in range(self.population_size): random_city_order = deepcopy(city_range, memo={}) random.shuffle(random_city_order) random_route = Route(self.graph) random_route.walk_complete_path(random_city_order) chromosome = GeneticAlgorithm.Chromosome( chromosome_index, random_route, crossover_method=self.crossover_method, mutation_method=self.mutation_method) self.population.append(chromosome) self.population = np.array(self.population)
def breadth_first_search(graph, source_vertex_id=1, target_vertex_id=11): bfs_tree = BreadthFirstSearchTree() current_vertex = graph.get_vertex_by_id(source_vertex_id) route = Route([current_vertex.vertex_id], graph) current_layer = 0 current_node = BreadthFirstSearchTree.Node("source", current_vertex, str(current_layer), route) node_index = 1 bfs_tree.add_node(current_node) # Iterate over each layer in the bfs tree and create the next layer while str(current_layer) in bfs_tree.nodes.keys(): # Iterate over all nodes for node in bfs_tree.nodes[str(current_layer)]: # Loop over its adjacent vertices for adjacent_vertex in node.vertex.adjacent_vertices: # Copy the route of the current node current_route = deepcopy(node.minimum_route) # Update the current route with the new vertex goto current_route.goto(adjacent_vertex.vertex_id) # Create a node representation of the vertex/route adjacent_node = BreadthFirstSearchTree.Node( str(node_index), adjacent_vertex, str(current_layer + 1), current_route) # Try to add the node if bfs_tree.add_node(adjacent_node) is True: # If added, append the node and increment the node_index node.adjacent_nodes.append(adjacent_node) node_index += 1 print("=== DISPLAYING UPDATED TREE === ") bfs_tree.display() # Iterate to the next layer to be done current_layer += 1 # Iterate over the final bfs_tree looking for the target_vertex_id for current_layer in bfs_tree.nodes.keys(): for node in bfs_tree.nodes[current_layer]: if node.vertex.vertex_id == target_vertex_id: # Adjust indices within minimum_route to match initial representation for vertex_id in node.minimum_route.vertex_order: vertex_id += 1 # Return minimum route. return node.minimum_route
def __init__(self, graph): self.graph = graph self.edge_dictionary = {} self.route = Route(graph) self.max_edge_count = 0
class CrowdSolution(object): def __init__(self, graph): self.graph = graph self.edge_dictionary = {} self.route = Route(graph) self.max_edge_count = 0 def log(self, log_path): log = open(log_path, "w+") log.write("edge_key" + ", " + "edge_count" + "\n") for edge_key, edge_entry in self.edge_dictionary.items(): log.write(str(edge_entry.edge_key) + ", " + str(edge_entry.edge_count) + "\n") log.close() def add_edge_entry(self, new_edge_entry): ## EDGE CANNOT BE REMOVED HERE. NEEDS TO BE CONSERVATIVE ## ISSUE spot_taken = False keys_to_be_deleted = list([]) self.display() for edge_key, edge_entry in self.edge_dictionary.items(): if edge_entry.edge.vertices[0].vertex_id == new_edge_entry.edge.vertices[0].vertex_id or edge_entry.edge.vertices[0].vertex_id == new_edge_entry.edge.vertices[0].vertex_id: if new_edge_entry.edge_count >= edge_entry.edge.distance: # Entries have same starting vertex if new_edge_entry.edge.distance <= edge_entry.edge.distance: keys_to_be_deleted.append(edge_entry.edge_key) break else: spot_taken = True else: spot_taken = True for key in keys_to_be_deleted: del self.edge_dictionary[key] if not spot_taken: self.edge_dictionary[new_edge_entry.edge_key] = new_edge_entry def load(self, log_path): with open(log_path, "r") as log: data_line = log.readline() while True: data_line = log.readline() if len(data_line) == 0: break edge_key, edge_count = data_line.split(", ") edge_count = int(edge_count) if edge_count > self.max_edge_count: self.max_edge_count = edge_count vertices = re.findall(r'\d+', edge_key) vertex_start = self.graph.get_vertex_by_id(int(vertices[0])) vertex_end = self.graph.get_vertex_by_id(int(vertices[1])) edge_entry = CrowdSolution.EdgeEntry(edge_key=edge_key, edge_count=edge_count, edge=Edge(vertex_start, vertex_end)) self.edge_dictionary[edge_entry.edge_key] = edge_entry # self.display() def display(self): for edge_key, edge_entry in self.edge_dictionary.items(): print(str(edge_entry.edge_key) + ", " + str(edge_entry.edge_count)) def generate_heat_map(self, superiority_tolerance=0.8): graph = self.graph x = list([]) y = list([]) plots = list([]) arrow_plots = list([]) arrow_labels = list([]) # Iterate over vertices, retrieving x and y coordinates for vertex in graph.vertices: x.append(vertex.x) y.append(vertex.y) # Plot the vertices vertex_plot = plt.scatter(x, y, label="Vertices") plots.append(vertex_plot) for edge_key, edge_entry in self.edge_dictionary.items(): if edge_entry.edge_count >= (self.max_edge_count * superiority_tolerance): vertices = re.findall(r'\d+', edge_entry.edge_key) vertex_start = graph.get_vertex_by_id(int(vertices[0])) vertex_end = graph.get_vertex_by_id(int(vertices[1])) arrow_label = "Edge {}->{}".format(vertices[0], vertices[1]) arrow_plot = plt.arrow(vertex_start.x, vertex_start.y, vertex_end.x-vertex_start.x, vertex_end.y-vertex_start.y, head_width=1, head_length=1, color='#{}{}{}'.format(Math.normalize_rgb(self.max_edge_count - edge_entry.edge_count, 0, self.max_edge_count), "00", Math.normalize_rgb(edge_entry.edge_count, 0, self.max_edge_count)), label=arrow_label) plots.append(arrow_plot) arrow_plots.append(arrow_plot) arrow_labels.append(arrow_label) # Show the graph with a legend plt.legend(arrow_plots, arrow_labels, loc=2, fontsize='small') plt.show() def get_unvisited_vertices_and_ending_vertices(self): last_visited_vertex = None unvisited_vertices = list([]) ending_vertices = list([]) for vertex in self.route.vertices: if not vertex.visited: unvisited_vertices.append(vertex) if last_visited_vertex is not None: ending_vertices.append(last_visited_vertex) last_visited_vertex = vertex ending_vertices.append(self.route.vertices[-1]) return unvisited_vertices, ending_vertices def edge_create_circular_path(self, edge): starting_vertex = edge.vertices[0] ending_vertex = edge.vertices[1] initial_ending_vertex = ending_vertex while True: # print(starting_vertex) edge_matching_starting_vertex = self.route.get_edge_by_vertex_id(starting_vertex.vertex_id, 1) if edge_matching_starting_vertex is None: break else: if edge_matching_starting_vertex.vertices[0].vertex_id == initial_ending_vertex.vertex_id: return True starting_vertex = edge_matching_starting_vertex.vertices[0] ending_vertex = edge_matching_starting_vertex.vertices[1] return False def complete_graph_greedy_heuristic(self, superiority_tolerance=0.8): self.route.reset_route() starting_vertex = None # Update route to match current representation given superiority_tolerance superiority_edges = [(edge_key, edge_entry) for (edge_key, edge_entry) in self.edge_dictionary.items() if edge_entry.edge_count >= (self.max_edge_count * superiority_tolerance)] for edge_key, edge_entry in superiority_edges: better_edge = False for edge_key_1, edge_entry_1 in superiority_edges: if edge_entry.edge.vertices[0].vertex_id == edge_entry_1.edge.vertices[0].vertex_id or edge_entry.edge.vertices[1].vertex_id == edge_entry_1.edge.vertices[1].vertex_id: if edge_entry.edge_count == edge_entry_1.edge_count: if edge_entry.edge.distance > edge_entry_1.edge.distance: better_edge = True elif edge_entry.edge_count < edge_entry_1.edge_count: better_edge = True if not better_edge: if self.route.edges is None: self.route.add_edge(edge_entry.edge) else: if not self.edge_create_circular_path(edge_entry.edge): self.route.add_edge(edge_entry.edge) self.route.distance_traveled = self.route.recount_distance() def choose_next_vertex(): closest_item_next_to_closest_vertex = None r_type_of_closest_item = None closest_vertex = None closest_distance = None starting_vertex = self.route.vertices[0] for vertex in self.route.get_vertices_not_in_route(): closest_item_next_to_vertex, item_distance = self.route.get_shortest_distance_to_route(vertex) if closest_vertex is None: closest_vertex = vertex closest_distance = item_distance closest_item_next_to_closest_vertex = closest_item_next_to_vertex else: if item_distance < closest_distance: closest_distance = item_distance closest_vertex = vertex closest_item_next_to_closest_vertex = closest_item_next_to_vertex if len(self.route.get_unvisited_vertices()) == 0: return self.route.vertices[0], self.route.vertices[1] else: return closest_vertex, closest_item_next_to_closest_vertex while len(self.route.vertices) < len(self.route.graph.vertices): next_vertex, closest_item_next_to_vertex = choose_next_vertex() self.route.lasso(next_vertex, closest_item_next_to_vertex) self.route.greedy_recombine() return self.route class EdgeEntry(object): def __init__(self, edge_key, edge_count, edge): self.edge_key = edge_key self.edge_count = edge_count self.edge = edge def __eq__(self, other): return self.edge_key == other.edge_key def __lt__(self, other): return self.edge_count < other.edge_count def __le__(self, other): return self.edge_count <= other.edge_count def __gt__(self, other): return self.edge_count > other.edge_count def __ge__(self, other): return self.edge_count >= other.edge_count def __str__(self): return "[edge_key: " + str(self.edge_key) + ", edge_count: " + str(self.edge_count) + ", edge: " + str(self.edge) + "]" def increment(self): self.edge_count += 1
def brute_force_solution(graph, current_vertex_id=1, reduce_ram_usage=False): # Generate route log path if reduce_ram_usage: route_log_path =os.getcwd() + os.path.sep + ".." + os.path.sep + "logs" + os.path.sep + \ "RouteLog_{0}_{1}".format(len(graph.vertices), str(time.time())[:9]) # Recursive function for trying all adjacent vertices. def try_all_open_routes_from_current_route(route, reduce_ram_usage=False): # Initialize Routes to keep track of all attempted routes. routes = np.array([]) # Start at the current vertex id location current_vertex = route.graph.vertices[current_vertex_id] # For each adjacent vertex that has not been visited for adjacent_vertex in current_vertex.get_unvisited_adjacent_vertex_ids( ): # copy the route so far new_route = deepcopy(route) # goto the current adjacent_vertex new_route.goto(adjacent_vertex.vertex_id) # if all vertices have been visisted if new_route.graph.finished(): # goto the current vertex id new_route.goto(current_vertex_id) if reduce_ram_usage: # Log finished route to hard disk FileHandler.log_route(new_route, route_log_path) # Delete from RAM del new_route else: # append the route to the list of completed routes routes = np.concatenate((routes, new_route), axis=None) else: # if not, if reduce_ram_usage: try_all_open_routes_from_current_route( new_route, reduce_ram_usage) else: # Recall the recursive function using the updated route. routes = np.concatenate(( routes, try_all_open_routes_from_current_route(new_route)), axis=None) # After all adjacent vertices have been visisted recursively, return the list of routes return routes # Initialize the route route = Route(list([]), graph) # goto the current vertex id route.goto(current_vertex_id) # Initialize a list of routes routes = np.array([]) # Recursively try all open routes from the current route, advancing when possible. routes = np.concatenate( (routes, try_all_open_routes_from_current_route( route, reduce_ram_usage=reduce_ram_usage)), axis=None) if reduce_ram_usage: del routes # Sift file located at route_log_path for the shortest route return FileHandler.find_minimum_route(route_log_path) else: # Identify the route with minimum distance traveled return min(routes)
class GreedyAlgorithm(object): def __init__(self, graph, starting_vertex_id=1, reset_heuristic=False): self.route = Route(graph) self.starting_vertex_id = starting_vertex_id self.route.goto( self.route.graph.get_vertex_by_id(starting_vertex_id)) self.done = False self.attempted_starting_vertex_ids = list([starting_vertex_id]) self.remaining_starting_vertex_ids = self.get_remaining_starting_vertex_ids( ) self.reset_heuristic = reset_heuristic self.crosses = 0 def __eq__(self, other): return self.route.distance_traveled == other.route.distance_traveled def __lt__(self, other): return self.route.distance_traveled < other.route.distance_traveled def __le__(self, other): return self.route.distance_traveled <= other.route.distance_traveled def __gt__(self, other): return self.route.distance_traveled > other.route.distance_traveled def __ge__(self, other): return self.route.distance_traveled >= other.route.distance_traveled def __str__(self): string = "Starting vertex id: " + str( self.starting_vertex_id) + "\n" string += "Route: " + str(self.route) + "\n" string += "reset_heuristic: " + str(self.reset_heuristic) + "\n" string += "Crosses: " + str(self.crosses) return string def complete(self): while not self.done: self.step_forward() return self.route def step_forward(self): next_vertex, closest_item_next_to_vertex = self.choose_next_vertex( ) self.route.lasso(next_vertex, closest_item_next_to_vertex) if len(self.route.vertices) > len(self.route.graph.vertices): self.done = True def step_backward(self): if len(self.route.vertices) > 0: self.route.walk_back() self.attempted_starting_vertex_ids.pop() self.remaining_starting_vertex_ids = self.get_remaining_starting_vertex_ids( ) if self.done: self.done = False def choose_next_vertex(self): closest_item_next_to_closest_vertex = None r_type_of_closest_item = None closest_vertex = None closest_distance = None for vertex in self.route.get_unvisited_vertices(): closest_item_next_to_vertex, item_distance = self.route.get_shortest_distance_to_route( vertex) if closest_vertex is None: closest_vertex = vertex closest_distance = item_distance closest_item_next_to_closest_vertex = closest_item_next_to_vertex else: if item_distance < closest_distance: closest_distance = item_distance closest_vertex = vertex closest_item_next_to_closest_vertex = closest_item_next_to_vertex if len(self.route.get_unvisited_vertices()) == 0: return self.route.vertices[0], self.route.vertices[1] else: return closest_vertex, closest_item_next_to_closest_vertex def get_remaining_starting_vertex_ids(self): return [ vertex_id for vertex_id in list( range(1, len(self.route.graph.vertices) + 1)) if vertex_id not in self.attempted_starting_vertex_ids ]
def build_chromosome_from_path_and_graph(chromosome_id, path, graph, crossover_method, mutation_method): route = Route(graph) route.walk_complete_path(path) return GeneticAlgorithm.Chromosome(chromosome_id, route, crossover_method, mutation_method)
def crossover(self, other_chromosome): new_path = list([]) if self.crossover_method == GeneticAlgorithm.Chromosome.CrossoverMethods.UNIFORM: self_turn = True while len(new_path) < len(self.route.vertices) - 1: if self_turn: remaining_vertex_ids = [ vertex.vertex_id for vertex in self.route.vertices if vertex.vertex_id not in new_path ] if len(remaining_vertex_ids) > 0: new_path.append( random.choice(remaining_vertex_ids)) self_turn = False else: remaining_vertex_ids = [ vertex.vertex_id for vertex in other_chromosome.route.vertices if vertex.vertex_id not in new_path ] if len(remaining_vertex_ids) > 0: new_path.append( random.choice(remaining_vertex_ids)) self_turn = True elif self.crossover_method == GeneticAlgorithm.Chromosome.CrossoverMethods.PARTIALLY_MAPPED: p1 = random.randint(1, len(self.route.vertices) - 3) p2 = random.randint(p1 + 1, len(self.route.vertices) - 2) self_ids = [ vertex.vertex_id for vertex in self.route.vertices ][:-1] self_s1 = self_ids[:p1] self_s2 = self_ids[p1:p2] self_s3 = self_ids[p2:] other_ids = [ vertex.vertex_id for vertex in other_chromosome.route.vertices ][:-1] other_s1 = other_ids[:p1] other_s2 = other_ids[p1:p2] other_s3 = other_ids[p2:] new_path = self_s1 s2_left = list([]) for vertex_id in other_s2: if vertex_id not in self_s1 and vertex_id not in self_s3: s2_left.append(vertex_id) s3_left = list([]) for vertex_id in other_s3: if vertex_id not in self_s1 and vertex_id not in self_s3: s3_left.append(vertex_id) s1_left = list([]) for vertex_id in other_s1: if vertex_id not in self_s1 and vertex_id not in self_s3: s1_left.append(vertex_id) remaining_vertex_ids = s2_left + s3_left + s1_left new_path += remaining_vertex_ids[:p2 - p1] new_path += self_s3 elif self.crossover_method == GeneticAlgorithm.Chromosome.CrossoverMethods.ORDERED_CROSSOVER: p1 = random.randint(1, len(self.route.vertices) - 3) p2 = random.randint(p1 + 1, len(self.route.vertices) - 2) j_1 = p1 + 1 j_2 = j_1 k = j_1 to_p1 = self.route.vertices[:p1] from_p1 = self.route.vertices[p1:] mid = other_chromosome.route.vertices[p1:p2 + 1] for vertex in to_p1: if vertex not in mid: new_path.append(vertex.vertex_id) for vertex in mid: new_path.append(vertex.vertex_id) for vertex in from_p1: if vertex.vertex_id not in new_path: new_path.append(vertex.vertex_id) else: # Splits the two chromosomes down the middle index = 0 while len(new_path) < len(self.route.vertices) - 1: if index < len(self.route.vertices) // 2: if self.route.vertices[ index].vertex_id not in new_path: new_path.append( self.route.vertices[index].vertex_id) index += 1 else: remaining_vertices = [ vertex for vertex in self.route.vertices if vertex.vertex_id not in new_path ] for remainining_vertex in remaining_vertices: new_path.append(remainining_vertex.vertex_id) new_route = Route(self.route.graph) new_route.walk_complete_path(new_path) resultant_chromosome = GeneticAlgorithm.Chromosome( None, new_route, self.crossover_method, self.mutation_method) return resultant_chromosome