def mst_dfs_solve(list_of_locations, list_of_homes, starting_car_location, adjacency_matrix, existing_mst=None, params=[]): if not existing_mst: G, _ = adjacency_matrix_to_graph(adjacency_matrix) mst = nx.minimum_spanning_tree(G) else: mst = existing_mst seen = set() traversal = [] def dfs(u): if u not in seen: traversal.append(u) seen.add(u) for v in mst.neighbors(u): if v not in seen: dfs(v) traversal.append(u) dfs(list_of_locations.index(starting_car_location)) dropoffs = {home: [home] for home in list_of_homes} dropoffs = { list_of_locations.index(key): convert_locations_to_indices(dropoffs[key], list_of_locations) for key in dropoffs } return traversal, dropoffs
def __init__(self, adj_matrix, homes_arr, soda_loc, locs): self.graph = util170.adjacency_matrix_to_graph(adj_matrix)[ 0] ## maybe we want a graph object from network x instead mapping = dict(zip(self.graph, locs)) self.graph = netx.relabel_nodes(self.graph, mapping) self.distanceMemo = dict() self.start_loc = soda_loc self.homes = homes_arr
def __init__(self, num_of_locations, num_houses, list_of_locations, list_of_houses, starting_car_location, adjacency_matrix): self.number_of_locations = num_of_locations self.number_of_homes = num_houses self.list_of_locations = list_of_locations self.list_of_houses = list_of_houses self.starting_car_location = starting_car_location self.adjacency_matrix = adjacency_matrix self.G = adjacency_matrix_to_graph(self.adjacency_matrix)[0]
def __init__(self, adj_matrix, homes_arr, soda_loc, locs): self.graph = util170.adjacency_matrix_to_graph(adj_matrix)[0] mapping = dict(zip(self.graph, locs)) self.netx_graph = netx.relabel_nodes(self.graph, mapping) self.distanceMemo = dict() self.start_loc = soda_loc self.homes = homes_arr homes_to_visit = self.homes.copy() homes_to_visit.append(self.start_loc) self.steiner_tree = steiner_tree(self.netx_graph, homes_to_visit, weight='weight')
def solve(list_of_locations, list_of_homes, starting_car_location, adjacency_matrix, input_file, params=[]): """ Write your algorithm here. Input: list_of_locations: A list of locations such that node i of the graph corresponds to name at index i of the list list_of_homes: A list of homes starting_car_location: The name of the starting location for the car adjacency_matrix: The adjacency matrix from the input file Output: A list of locations representing the car path A dictionary mapping drop-off location to a list of homes of TAs that got off at that particular location NOTE: both outputs should be in terms of indices not the names of the locations themselves """ start_idx = list_of_locations.index(starting_car_location) homes_idx = [list_of_locations.index(h) for h in list_of_homes] new_homes_idx = convert_homes_idx(start_idx, homes_idx) graph, msg = student_utils.adjacency_matrix_to_graph(adjacency_matrix) #nn_output_directory = 'nearest_neighbor_algo/outputs' #nn_output_directory = 'local_search/outputs' #nn_output_directory = 'mst_2_approximation/outputs' #nn_output_directory = 'local_search2run/outputs' #nn_output_file = utils.input_to_output(input_file, nn_output_directory) #nn_output_data = utils.read_file(nn_output_file) #car_cycle = nn_output_data[0] #car_cycle_idx = student_utils.convert_locations_to_indices(car_cycle, list_of_locations) car_cycle_idx = new_homes_idx shortest_paths = nx.floyd_warshall(graph, weight='weight') new_car_cycle_idx = local_search(car_cycle_idx, shortest_paths) #final_path final_cycle = [] for i in range(len(new_car_cycle_idx) - 1): shortest_path = nx.shortest_path(graph, new_car_cycle_idx[i], new_car_cycle_idx[i + 1], weight='weight') shortest_path.pop() final_cycle.extend(shortest_path) final_cycle.append(new_car_cycle_idx[-1]) dropoffs = get_valid_dropoffs(final_cycle, homes_idx) return final_cycle, dropoffs
def __init__(self, adj_matrix, homes_arr, soda_loc, locs): self.graph = util170.adjacency_matrix_to_graph(adj_matrix)[ 0] ## maybe we want a graph object from network x instead mapping = dict(zip(self.graph, locs)) self.graph = netx.relabel_nodes(self.graph, mapping) self.start_loc = soda_loc self.homes = homes_arr self.locs = locs self.distanceMemo = dict() self.distancePathMemo = dict( netx.algorithms.shortest_paths.all_pairs_dijkstra_path(self.graph)) self.distanceLengthMemo = dict( netx.algorithms.shortest_paths.all_pairs_dijkstra_path_length( self.graph)) print("finished_memoizing_shortest_paths")
def findtour(inputfile): data = read_file(inputfile) numl, numh, listl, listh, start, matrix = data_parser(data) G, message = adjacency_matrix_to_graph(matrix) nodes = G.__iter__() nodedict = {} numdict = {} for i in range(numl): nextnode = next(nodes) nodedict[listl[i]] = nextnode numdict[nextnode] = listl[i] paths = [] graphs = [] distances = [] disttonode = {} nodetodist = {} list_home_nodes = [] for home in listh: homenode = nodedict.get(home) distance, shortestpath = nx.single_source_dijkstra( G, homenode, nodedict.get(start)) paths += [shortestpath] shortestgraph = nx.path_graph(shortestpath) graphs += [shortestgraph] distances.append(distance) if not distance in disttonode: disttonode[distance] = [homenode] else: disttonode[distance].append(homenode) nodetodist[homenode] = distance list_home_nodes += [homenode] distances.sort(reverse=False) mst = nx.compose_all(graphs) leaves = [x for x in mst.nodes() if mst.degree(x) == 1] visited = [] drive = [] adjlist = mst._adj startnode = nodedict.get(start) dropdict = {} tour, dropdict = traverse(startnode, visited, leaves, drive, distances, adjlist, nodedict, disttonode, nodetodist, G, startnode, mst, list_home_nodes, dropdict) tour = [ tour[i] for i in range(len(tour)) if (i == 0) or tour[i] != tour[i - 1] ] return tour, dropdict, numdict
def find_optimal_dropoff_within_cluster(list_of_homes, adjacency_matrix, community_mappings): # returns one optimal drop off within a cluster graph = student_utils.adjacency_matrix_to_graph(adjacency_matrix)[0] dropoffs = {} print(community_mappings) print(list_of_homes) for label in community_mappings.keys(): curr_homes = [] curr_locations = community_mappings[label] # find the homes within the current community for node in curr_locations: if str(node) in list_of_homes: curr_homes.append(node) neighbors = {} curr_neighbors = [] # create a mapping between nodes and their neighbors # nodes are mapped to a list of tuples that contains the neighboring node and the weight for node in curr_locations: for i in range(len(adjacency_matrix[node])): if type(adjacency_matrix[node][i]) != str: curr_neighbors.append((i, adjacency_matrix[node][i])) neighbors[node] = curr_neighbors # find the costs of dropping off at each location dropoff_costs = find_cost_of_dropoff_within_cluster( graph, curr_homes, curr_locations, neighbors) # find the minimum cost dropoff min_drop_cost = float("inf") min_drop = None for key in dropoff_costs.keys(): if dropoff_costs[key] < min_drop_cost: min_drop_cost = dropoff_costs[key] min_drop = key dropoffs[label] = min_drop return dropoffs
def solve_from_file_score(input_file, output_directory, params=[]): print('Processing', input_file) input_data = utils.read_file(input_file) num_of_locations, num_houses, list_locations, list_houses, starting_car_location, adjacency_matrix = data_parser( input_data) car_path, drop_offs = solve(list_locations, list_houses, starting_car_location, adjacency_matrix, params=params) basename, filename = os.path.split(input_file) if not os.path.exists(output_directory): os.makedirs(output_directory) output_file = utils.input_to_output(input_file, output_directory) G = student_utils.adjacency_matrix_to_graph(adjacency_matrix)[0] #convertToFile(car_path, drop_offs, output_file, list_locations) return G, car_path, drop_offs
def preProcess(list_of_locations, list_of_homes, starting_car_location, adjacency_matrix): G = student_utils.adjacency_matrix_to_graph(adjacency_matrix) location_dict = {} list_of_homes_int = [] list_of_locations_int = [_ for _ in range(len(list_of_locations))] for j in range(len(list_of_locations)): location_dict[j] = list_of_locations[j] if list_of_locations[j] == starting_car_location: car_start_int = j for _ in list_of_homes: if _ == list_of_locations[j]: list_of_homes_int += [j] for i in list_of_homes_int: if G[0].degree[i] == 1: list_of_homes_int[i] = G.edges[i][0] return list_of_locations, list_of_homes, starting_car_location, adjacency_matrix
def greedy_shortest_path_solve(list_of_locations, list_of_homes, starting_car_location, adjacency_matrix, verbose=False, params=[]): G, _ = adjacency_matrix_to_graph(adjacency_matrix) source_idx = list_of_locations.index(starting_car_location) num_locs = len(G) home_idxs = set([list_of_locations.index(h) for h in list_of_homes]) paths, all_pairs_dists = nx.floyd_warshall_predecessor_and_distance( G, weight='weight') def reconstruct_path(source, target, predecessors): if source == target: return [] prev = predecessors[source] curr = prev[target] path = [target, curr] while curr != source: curr = prev[curr] path.append(curr) return list(reversed(path)) traversal = [source_idx] # Find distances to previous node traversed dists = [(all_pairs_dists[traversal[-1]][h], h) for h in home_idxs] while dists: n = min(dists, key=lambda x: x[0]) dists.remove(n) n = n[1] traversal.extend(reconstruct_path(traversal[-1], n, paths)[1:]) dists = [(all_pairs_dists[traversal[-1]][r], r) for _, r in dists] traversal.extend(reconstruct_path(traversal[-1], source_idx, paths)[1:]) return traversal, assign_dropoffs(G, traversal, home_idxs, all_pairs_dists)
def findtour(inputfile): data = read_file(inputfile) # need this to create output file name = inputfile.name numl, numh, listl, listh, start, matrix = data_parser(data) G, message = adjacency_matrix_to_graph(matrix) nodes = G.__iter__() nodedict = {} for i in range(numl): nodedict[listl[i]] = next(nodes) paths = [] graphs = [] distances = [] disttonode = {} nodetodist = {} for home in listh: homenode = nodedict.get(home) distance, shortestpath = nx.single_source_dijkstra( G, homenode, nodedict.get(start)) paths += [shortestpath] shortestgraph = nx.path_graph(shortestpath) graphs += [shortestgraph] distances.append(distance) # fix duplicate keys if not distance in disttonode: disttonode[distance] = [homenode] else: disttonode[distance].append(homenode) nodetodist[homenode] = distance distances.sort(reverse=True) mst = nx.compose_all(graphs) leaves = [x for x in mst.nodes() if mst.degree(x) == 1] visited = [] drive = [] adjlist = mst._adj startnode = nodedict.get(start) tour = traverse(startnode, visited, leaves, drive, distances, adjlist, nodedict, disttonode, nodetodist, G, startnode, mst, [], listh) return tour
def visualize_communities_and_dropoffs(list_of_locations, list_of_homes, starting_car_location, adjacency_matrix): # draw each community in a different color with its dropoff node G = student_utils.adjacency_matrix_to_graph(adjacency_matrix)[0] comm_mappings = find_community_mappings(list_of_homes, adjacency_matrix) comm_dropoffs = find_dropoff_locations(list_of_homes, adjacency_matrix, starting_car_location, comm_mappings) color_map = [0 for n in range(len(list_of_locations))] current_color = 0 for comm_label in comm_mappings: locs = comm_mappings[comm_label] for loc in locs: color_map[loc] = current_color current_color += 1.0 / len(comm_mappings.keys()) color_map[int(starting_car_location)] = 1 labels = nx.get_edge_attributes(G, 'weight') nx.draw(G, node_color=color_map, with_labels=True, font_weight='bold') nx.draw_networkx_edge_labels(G, pos=nx.spring_layout(G), edge_labels=labels) plt.show()
def plot_graph(filepath, layout_style=nx.spring_layout, show_labels=True, show_edge_weights=False, edges_to_draw=None, directed=False): if isinstance(filepath, str): (num_locations, num_houses, location_names, house_names, source, adj) = parse_input(filepath) G, _ = adjacency_matrix_to_graph(adj) else: raise NotImplementedError if directed: G = G.to_directed() plt.figure(figsize=(10, 10)) pos = layout_style(G) colormap = [('yellow' if str(i) in house_names else 'red') for i in range(num_locations)] source_index = location_names.index(source) colormap[source_index] = 'blue' nx.draw(G, pos=pos, node_color=colormap, with_labels=show_labels) if show_edge_weights: labels = nx.get_edge_attributes(G, 'weight') nx.draw_networkx_edge_labels(G, pos=pos, edge_labels=labels) if edges_to_draw: nx.draw_networkx_edges(G, pos=pos, edgelist=edges_to_draw, edge_color='green') plt.show()
def solve(list_of_locations, list_of_homes, starting_car_location, adjacency_matrix, params=[]): """ Write your algorithm here. Input: list_of_locations: A list of locations such that node i of the graph corresponds to name at index i of the list list_of_homes: A list of homes starting_car_location: The name of the starting location for the car adjacency_matrix: The adjacency matrix from the input file Output: A list of locations representing the car path A dictionary mapping drop-off location to a list of homes of TAs that got off at that particular location NOTE: both outputs should be in terms of indices not the names of the locations themselves """ """list_of_locations, list_of_homes_int, starting_car_location, \ adjacency_matrix = preProcess(list_of_locations, list_of_homes, starting_car_location, adjacency_matrix)""" if len(list_of_locations) == 0: return [], {} #Do not delete next line # graphModifier.graphClusterer(list_of_locations, list_of_homes, starting_car_location, adjacency_matrix) location_dict = {} car_start_int = 0 list_of_homes_int = [] list_of_locations_int = [_ for _ in range(len(list_of_locations))] for j in range(len(list_of_locations)): location_dict[j] = list_of_locations[j] if list_of_locations[j] == starting_car_location: car_start_int = j for _ in list_of_homes: if _ == list_of_locations[j]: list_of_homes_int += [j] #preProcess(list_of_locations, list_of_homes, starting_car_location, adjacency_matrix) G = student_utils.adjacency_matrix_to_graph(adjacency_matrix)[0] B = clusterGraph(list_of_locations_int, list_of_homes_int, car_start_int, G) returner = practiceSolver.tspRepeats(B, car_start_int) if len(returner) == 0: returner = [car_start_int] finalList = [returner[0]] for i in range(len(returner) - 1): finalList += nx.shortest_path(G, returner[i], returner[i + 1], weight='weight')[1:] finalList += nx.shortest_path(G, returner[-1], returner[0], weight='weight')[1:] all_distances = dict(nx.floyd_warshall(G)) returnMapping = {} for i in list_of_homes_int: minDist = float('inf') home_map = 999 for j in finalList: if all_distances[i][j] < minDist: minDist = all_distances[i][j] home_map = j if home_map != 999: if not home_map in returnMapping: returnMapping[home_map] = [i] else: returnMapping[home_map] = returnMapping[home_map] + [i] #clustering_approach.visualize_communities_and_dropoffs(list_of_locations, list_of_homes, starting_car_location, adjacency_matrix) returnList = [] for i in finalList: returnList.append(location_dict[i]) print(cost_of_solution(G, [int(_) for _ in finalList], returnMapping)[0]) return [int(_) for _ in finalList], returnMapping
def trivial_output_solver(list_of_locations, list_of_homes, starting_car_location, adjacency_matrix, params=[]): G = student_utils.adjacency_matrix_to_graph(adjacency_matrix)[0] # helper function with access to G def distance_between_locations(a, b): path_dist = nx.shortest_path_length(G, a, b) # print("distance from " + str(a) + " to " + str(b) + ": ", path_dist) return path_dist # either proceed linearly through list of homes or through shuffled list of homes randomized_homes = np.random.shuffle(list_of_homes) homes = list_of_homes # using approximated average clustering for G: clustering_coeffs = nx.clustering(G) print("approximated average clustering coefficient:", clustering_coeffs) home_coeffs = {int(h): clustering_coeffs[int(h)] for h in homes} print("home clustering coeffs:", home_coeffs) # using the Girvan–Newman method to find communities of graphs # define custom function for how to select edges to remove in the algorithm def most_central_edge(G): centrality = nx.edge_betweenness_centrality(G, normalized=False, weight='weight') max_cent = max(centrality.values()) # Scale the centrality values so they are between 0 and 1, # and add some random noise. centrality = {e: c / max_cent for e, c in centrality.items()} # Add some random noise. centrality = {e: c + np.random.random() for e, c in centrality.items()} return max(centrality, key=centrality.get) # get only the first k tuples of communities k = 10 comp = algos.community.centrality.girvan_newman( G, most_valuable_edge=most_central_edge) for communities in itertools.islice(comp, k): print("communities: ", tuple(sorted(c) for c in communities)) # using A* from start to each node total_path = [] current_home = homes[0] total_path += nx.astar_path(G, int(starting_car_location), int(current_home), distance_between_locations) for next_home in homes[1:]: # print("finding path between " + str(current_home) + " and " + str(next_home)) path_between = nx.astar_path(G, int(starting_car_location), int(current_home), distance_between_locations) total_path += path_between current_home = next_home total_path += nx.astar_path(G, int(current_home), int(starting_car_location), distance_between_locations) print("total super shitty path: " + str(total_path)) # locs = [] # for i in list_of_locations: # locs.append(int(i)) # # homes = [] # for i in list_of_homes: # homes.append(int(i)) # # first, find a node that is a neighbor of the start # dropOffIndex = None # for i in range(len(adjacency_matrix[0])): # if adjacency_matrix[0][i] == 1: # dropOffIndex = i # # dropOffNode = list_of_locations[dropOffIndex] graph_maker.print_trivial_output(len(list_of_locations), starting_car_location, list_of_homes) start = list_of_locations[int(starting_car_location)] # dropOffNode = list_of_locations[int(dropOffNode)] start = int(start) # dropOffNode = int(dropOffNode) return [start], {start: homes}
if __name__ == '__main__': parser = argparse.ArgumentParser(description='Parsing arguments') parser.add_argument('locations', type=int) parser.add_argument('tas', type=int) args = parser.parse_args() while True: write_to_file( str(args.locations) + ".in", generate_input(args.locations, args.tas)) input_file = str(args.locations) + ".in" input_data = utils.read_file(input_file) num_of_locations, num_houses, list_locations, list_houses, starting_car_location, adjacency_matrix = data_parser( input_data) G, adj_message = adjacency_matrix_to_graph(adjacency_matrix) if not is_metric(G): print("not metric!") continue car_path, drop_offs = solve(list_locations, list_houses, starting_car_location, adjacency_matrix, params=[]) seen = [] duplicate_found = False for nxt in car_path[1:-1]: if nxt in seen: print("duplicate city found!", nxt)
def solve(self, list_of_locations, list_of_homes, starting_car_location, adjacency_matrix, params=[]): """ Write your algorithm here. Input: list_of_locations: A list of locations such that node i of the graph corresponds to name at index i of the list list_of_homes: A list of homes starting_car_location: The name of the starting location for the car adjacency_matrix: The adjacency matrix from the input file Output: A list of locations representing the car path A list of (location, [homes]) representing drop-offs """ adj = adjacency_matrix s = starting_car_location L = list_of_locations homes_numbered = [L.index(i) for i in list_of_homes] self.homes = homes_numbered G = student_utils.adjacency_matrix_to_graph(adj)[0] # show_graph(G) s = L.index(s) H = [L.index(i) for i in list_of_homes] + [s] G_steinertree = (steinertree.steiner_tree(G, H)) directed_steinertree = nx.dfs_tree(G_steinertree, s) self.prune_tree(directed_steinertree, s) drop_off_locs = set(self.drop_off.values()) pruned_homes = set(self.drop_off.keys()) for home in homes_numbered: if not (home in pruned_homes): drop_off_locs.add(home) p, d = nx.floyd_warshall_predecessor_and_distance(G) path = (self.nearest_neighbors(drop_off_locs, d, s, p)) # pos = hierarchy_pos(directed_steinertree, s) # nx.draw(directed_steinertree, pos=pos, with_labels=True) # plt.show() final_dict = {i: [] for i in drop_off_locs} for i in homes_numbered: if i in drop_off_locs: final_dict[i].append(i) else: final_dict[self.drop_off[i]].append(i) with_steiner = path, final_dict without_steiner_path = (self.nearest_neighbors(H, d, s, p)) without_steiner_dict = {i: [i] for i in homes_numbered} self.drop_off = {} self.prune_tree(directed_steinertree, s) # pos = hierarchy_pos(directed_steinertree, s) # nx.draw(directed_steinertree, pos=pos, with_labels=True) # plt.show() drop_off_locs = set(self.drop_off.values()) pruned_homes = set(self.drop_off.keys()) for home in homes_numbered: if not (home in pruned_homes): drop_off_locs.add(home) p, d = nx.floyd_warshall_predecessor_and_distance(G) path_double_pruned = (self.nearest_neighbors(drop_off_locs, d, s, p)) final_dict_double_pruned = {i: [] for i in drop_off_locs} for i in homes_numbered: if i in drop_off_locs: final_dict_double_pruned[i].append(i) else: final_dict_double_pruned[self.drop_off[i]].append(i) return min([ with_steiner, (without_steiner_path, without_steiner_dict), (path_double_pruned, final_dict_double_pruned) ], key=lambda x: cost_of_solution(G, x[0], x[1]))
def flp_solve(list_of_locations, list_of_homes, starting_car_location, adjacency_matrix, solve, params=[]): mapping = defaultdict(int) for i in range(len(list_of_locations)): mapping[list_of_locations[i]] = i G, _ = adjacency_matrix_to_graph(adjacency_matrix) U = list_of_homes facilities = list_of_locations start = starting_car_location paths, distances = nx.floyd_warshall_predecessor_and_distance(G, weight='weight') # print(distances) # print(paths) # print("ran all pairs") # Builds set S in polynomial time as opposed to powerset (exponential) def buildS(): res = [] for f in facilities: for s in range(1, len(U)): sorted_dist = sorted([(target, distances[mapping[f]][mapping[target]]) for target in U], key=lambda x: x[1]) sorted_dist = sorted_dist[:s] A = set([t for t, _ in sorted_dist]) cost = sum([s[1] for s in sorted_dist]) + (2/3) * distances[mapping[start]][mapping[f]] elem = {'facility': f, 'A': A, 'count': s, 'cost': cost } # facility, dictionary to see which elements are present, num elements, cost res.append(elem) return res S = buildS() # print("Built S") uncovered = set(U) lst = [(s['cost']/s['count'], hash(s['facility'] + str(s['count'])), s) for s in S] result = set([]) dropoff_mapping = defaultdict(list) while uncovered and lst: smallest = min(lst, key = lambda x: x[0]) lst.remove(smallest) smallest = smallest[2] result.add(smallest['facility']) dropoff_mapping[smallest['facility']] = list(smallest['A']) uncovered = uncovered.difference(smallest['A']) new_lst = [] for _, h, elem in lst: if solve == 1: if elem['facility'] in result: elem['cost'] -= distances[mapping[start]][mapping[elem['facility']]] if solve == 2: if elem['facility'] not in result: elem['cost'] = sum([distances[mapping[s]][mapping[elem['facility']]] for s in elem['A']]) + (1/4) * sum([distances[mapping[r]][mapping[elem['facility']]] for r in result if r != start]) intersect = len(elem['A'].intersection(uncovered)) new_cost = float('inf') if intersect > 0: new_cost = elem['cost']/intersect new_lst.append((new_cost, h, elem)) lst = new_lst # print("computed dropoffs") #finding path between all traversal = [mapping[start]] dists = [(distances[traversal[-1]][mapping[r]], mapping[r]) for r in list(result) if r != start] dropoffs = [mapping[start]] if start in result else [] # print("computing traversal") def reconstruct_path(source, target, predecessors): if source == target: return [] prev = predecessors[source] curr = prev[target] path = [target, curr] while curr != source: curr = prev[curr] path.append(curr) return list(reversed(path)) while dists: n = min(dists, key= lambda x: x[0]) dists.remove(n) n = n[1] dropoffs.append(n) traversal.extend(reconstruct_path(traversal[-1], n, paths)[1:]) dists = [(distances[traversal[-1]][r], r) for _, r in dists] traversal.extend(reconstruct_path(traversal[-1], mapping[start], paths)[1:]) # print("done") # dropoffs_dict = {key: dropoff_mapping[key] for key in dropoff_mapping} dropoffs_dict = defaultdict(list) for h in convert_locations_to_indices(list_of_homes, list_of_locations): minValue = float('inf') minVertex = mapping[start] for i in traversal: if distances[h][i] < minValue: minValue = distances[h][i] minVertex = i dropoffs_dict[minVertex].append(h) dropoffs_dict = {key: dropoffs_dict[key] for key in dropoffs_dict} return traversal, dropoffs_dict
f'Local search terminated with solution (cost={current_cost}): {current_solution}' ) evaluate(G, current_solution, home_idxs, verbose=True) return current_solution, assign_dropoffs(G, current_solution, home_idxs) i += 1 epsilon *= epsilon_decay_factor end_iter = time.time() # print(f'Iteration took {end_iter - start_iter} s') if __name__ == '__main__': # input_path = '../phase1/100.in' input_path = '../phase2/test_inputs/branching_20v_5h.in' (num_locations, num_houses, location_names, house_names, source, adj) = parse_input(input_path) G, _ = adjacency_matrix_to_graph(adj) # path = dijkstra_greedy_solve(location_names, house_names, source, adj) path, best_cost = local_search_solve(location_names, house_names, source, adj) edges_to_draw = [] for i in range(len(path) - 1): u, v = path[i], path[i + 1] edges_to_draw.append((u, v)) print(edges_to_draw) plot_graph(input_path, layout_style=nx.kamada_kawai_layout, show_edge_weights=True, edges_to_draw=edges_to_draw, directed=True)
def complete_home_graph_generator_from_file(input_file, params=[]): input_data = utils.read_file(input_file) old_num_of_locations, old_num_houses, old_list_locations, old_list_houses, old_starting_car_location, old_adjacency_matrix = su.data_parser( input_data) # make a old graph from file old_graph, old_adj_message = su.adjacency_matrix_to_graph(old_adjacency_matrix) new_num_of_locations = -1 new_list_locations = [] new_num_houses = old_num_houses new_list_houses = old_list_houses new_starting_car_location = old_starting_car_location new_adjacency_matrix = None # Flag shortest_paths_between_homes = None message = '' error = False old_starting_point_index = old_list_locations.index(old_starting_car_location) # Make complete_home_graph is_sp_home = None if old_starting_car_location in new_list_houses: is_sp_home = True if is_sp_home: print(" Sp is home, Complete Graph size = num_houses") complete_home_graph = nx.complete_graph(new_num_houses) new_num_of_locations = old_num_houses new_list_locations = old_list_houses else: # + 1 for the starting point print(" Sp is not home, Complete Graph size = num_houses + 1") complete_home_graph = nx.complete_graph(new_num_houses + 1) new_num_of_locations = old_num_houses + 1 new_list_locations = old_list_houses.copy() new_list_locations.insert(0, old_starting_car_location) # For debugging # nx.draw(complete_home_graph) # plt.show() give_nodes_names(is_sp_home, complete_home_graph, new_num_houses, new_list_houses, new_starting_car_location) shortest_paths_between_homes = give_edges_weights(complete_home_graph, old_graph, old_list_locations) new_adjacency_matrix = nx.to_numpy_array(complete_home_graph) # tests if not all(name.isalnum() and len(name) <= MAX_NAME_LENGTH for name in new_list_locations): message += f'One or more of the names of your locations are either not alphanumeric or are above the max length of {MAX_NAME_LENGTH}.\n' error = True # check counts if len(new_list_locations) != new_num_of_locations: message += f'The number of locations you listed ({len(new_list_locations)}) differs from the number you gave on the first line ({new_num_of_locations}).\n' error = True if len(new_list_houses) != new_num_houses: message += f'The number of homes you listed ({len(new_list_houses)}) differs from the number you gave on the first line ({new_num_houses}).\n' error = True # # no more needed because in the new complete_home_graph, the number of houses = the number of locatinos # if num_of_locations < num_houses: # message += f'The number of houses must be less than or equal to the number of locations.\n' # error = True # check containment if any(house not in new_list_locations for house in new_list_houses): message += f'You listed at least one house that is not an actual location. Ahh!\n' error = True if new_starting_car_location not in new_list_locations: message += f'You listed a starting car location that is not an actual location.\n' error = True # check distinct if not len(set(new_list_locations)) == len(new_list_locations): message += 'The names of your locations are not distinct.\n' error = True if not len(set(new_list_houses)) == len(new_list_houses): message += 'The names of your houses are not distinct.\n' error = True # check adjacency matrix if not len(new_adjacency_matrix) == len(new_adjacency_matrix[0]) == new_num_of_locations: message += f'The dimensions of your adjacency matrix do not match the number of locations you provided.\n' error = True if not all( entry == 'x' or (type(entry) is float and entry > 0 and entry <= 2e9 and su.decimal_digits_check(entry)) for row in new_adjacency_matrix for entry in row): message += f'Your adjacency matrix may only contain the character "x", or strictly positive integers less than 2e+9, or strictly positive floats with less than 5 decimal digits.\n' error = True # if not square, terminate if len(set(map(len, new_adjacency_matrix))) != 1 or len(new_adjacency_matrix[0]) != len(new_adjacency_matrix): message += f'Your adjacency matrix must be square.\n' error = True return message, error new_adjacency_matrix_numpy = np.matrix(new_adjacency_matrix) # check requirements on square matrix if not np.all(new_adjacency_matrix_numpy.T == new_adjacency_matrix_numpy): message += f'Your adjacency matrix is not symmetric.\n' error = True if not nx.is_connected(complete_home_graph): message += 'Your new complete_home_graph is not connected.\n' error = True if not su.is_metric(complete_home_graph): message += 'Your new complete_home_graph is not metric.\n' error = True if not message: message = "If you've received no other error messages, then your complete_home_graph is valid!\n\n\n" # print for debugging print("\n old_num_of_locations : " + str(old_num_of_locations)) print(" old_num_houses : " + str(old_num_houses)) print(" old_list_locations names: " + str(old_list_locations)) print(" old_list_houses names : " + str(old_list_houses)) print(" Old Starting Point name(Str) : " + old_starting_car_location) print(" Old Starting Point index : " + str(old_starting_point_index)) print("\n New Complete Graph Generated") print(" new_num_of_locations names: " + str(new_num_of_locations)) print(" new_num_houses names: " + str(new_num_houses)) print(" new_list_locations names: " + str(new_list_locations)) print(" new_list_houses: " + str(new_list_locations)) new_starting_point_index = 0 print(" New Starting Point name(Str) : " + old_starting_car_location) print(" ** New Starting Point index: " + str(new_starting_point_index)) # print(" new_adjacency_matrix:") # print(new_adjacency_matrix) # print(" shortest_paths_between_homes: \n" + str(shortest_paths_between_homes)) print("\n") # for edge in shortest_paths_between_homes: # print(edge, ':', shortest_paths_between_homes[edge]) # print("\n") return message, error, new_num_of_locations, new_num_houses, old_list_locations, new_list_locations, new_list_houses, \ new_starting_car_location, new_adjacency_matrix, shortest_paths_between_homes, complete_home_graph, old_graph, new_starting_point_index
def naiveSolve(list_of_locations, list_of_homes, starting_car_location, adjacency_matrix, params=[]): G = student_utils.adjacency_matrix_to_graph(adjacency_matrix) print(G) return 0
def local_search_solve(list_of_locations, list_of_homes, starting_car_location, adjacency_matrix, initial_solver=mst_dfs_solve, initial_solution=None, params=[]): start = time.time() # Generate initial solution (random or greedy algorithm) if not initial_solution: current_solution, _ = initial_solver(list_of_locations, list_of_homes, starting_car_location, adjacency_matrix, params=params) else: current_solution = initial_solution G, _ = adjacency_matrix_to_graph(adjacency_matrix) home_idxs = [list_of_locations.index(h) for h in list_of_homes] all_pairs_dists = nx.floyd_warshall(G, weight='weight') def get_neighbors(G, path): """ Returns "1-change" neighbors, which are all paths that: Either include a vertex that is not visited in path Include a vertex visited in path Swap a vertex that is visited with a vertex not visited """ visited = set(path) unvisited = set(range(len(G))) - visited occurences = collections.Counter(path) neighbors = [] # Exclude a vertex for vertex_to_exclude in visited - {0}: new_path = path.copy() # print(f'VERTEX TO EXCLUDE: {vertex_to_exclude}') for i in range(occurences[vertex_to_exclude]): idx = new_path.index(vertex_to_exclude) prev, nxt = new_path[idx - 1], new_path[idx + 1] if prev == nxt: # Able to skip over visiting new_path.pop(idx) new_path.pop(idx) else: two_shortest = k_shortest_paths(G, source=prev, target=nxt, k=2, weight='weight') for alt_path in two_shortest: if alt_path != path[ idx - 1:idx + 2] and vertex_to_exclude not in alt_path: new_path.pop(idx) new_path = new_path[:idx - 1] + alt_path + new_path[idx + 1:] break if vertex_to_exclude not in new_path: # print(f'REMOVING {vertex_to_exclude}: {new_path}') neighbors.append(new_path) # Include a vertex for vertex_to_include in unvisited: new_path = path.copy() one_away = [] for v in visited: if vertex_to_include in G.neighbors(v): one_away.append(v) if not one_away: continue # print(f'\nVERTEX TO INCLUDE: {vertex_to_include}') closest_neighbor = min( one_away, key=lambda n: G[vertex_to_include][n]['weight']) # Get the index of the closest neighbor, check if a path exists between # the vertex being added and the next vertex in the path. If so, a better # detour exists than the trivial path to the new vertex and directly back. closest_idx = new_path.index(closest_neighbor) # Make sure that the closest_idx is not the last node, otherwise this would # go out of bounds if closest_idx + 1 < len(new_path) and G.has_edge( vertex_to_include, new_path[closest_idx + 1]): new_path.insert(closest_idx + 1, vertex_to_include) else: new_path.insert(closest_idx + 1, closest_neighbor) new_path.insert(closest_idx + 1, vertex_to_include) # print(f'ADDING {vertex_to_include}: {new_path}') neighbors.append(new_path) # Handle cycles # Swap a vertex (maybe add this) return neighbors def assign_dropoffs(G, path, home_idxs): """ Returns the dictionary of all dropoffs along this path. """ locations_on_path = set(path) dropoffs = collections.defaultdict(list) # print(locations_on_path) for h in home_idxs: # print(f'DISTANCES FOR {h}', all_pairs_dists[h]) closest_loc_on_path = min(locations_on_path, key=lambda loc: all_pairs_dists[h][loc]) dropoffs[closest_loc_on_path].append(h) return dropoffs def evaluate(G, path, home_idxs, verbose=False): """ Assigns optimal dropoff locations for each TA, given a path that the car is going to take, and calculate the total energy expended. This is the metric that neighbors are going to be evaluated on. """ dropoffs = assign_dropoffs(G, path, home_idxs) cost, msg = cost_of_solution(G, path, dropoffs, shortest=all_pairs_dists) if verbose: print(msg) return cost # print(get_neighbors(G, current_solution)) # print(assign_dropoffs(G, current_solution, home_idxs)) i = 0 current_cost = evaluate(G, current_solution, home_idxs) # epsilon = 0.1 epsilon = 0 epsilon_decay_factor = 0.95 while True: # TODO(justinvyu): Add a timeout start_iter = time.time() neighbors = get_neighbors(G, current_solution.copy()) # Take a random action with epsilon probability, decaying over time if np.random.rand() < epsilon: current_solution = neighbors[np.random.randint(len(neighbors))] current_cost = evaluate(G, current_solution, home_idxs) print(f'=== Iteration #{i}, Epsilon = {epsilon} ===' f'\n Picked a RANDOM neighbor, Cost = {current_cost}') epsilon *= epsilon_decay_factor # print(f'\nSearching over {len(neighbors)} neighbors...') best_neighbor = min( neighbors, key=(lambda neighbor_sol: evaluate(G, neighbor_sol, home_idxs))) best_cost = evaluate(G, best_neighbor, home_idxs, verbose=False) if best_cost < current_cost: # print(f'=== Iteration #{i}, Epsilon = {epsilon} === \n Improved Cost = {best_cost}') current_solution = best_neighbor current_cost = best_cost else: print(f'Local search took: {time.time() - start} seconds') print( f'Local search terminated with solution (cost={current_cost}): {current_solution}' ) evaluate(G, current_solution, home_idxs, verbose=True) return current_solution, assign_dropoffs(G, current_solution, home_idxs) i += 1 epsilon *= epsilon_decay_factor end_iter = time.time()
['point146', 62, 11, False], ['point147', 63, 0, False], ['point148', 63, 7, True], ['point149', 65, 7, True], ['point150', 65, 10, False], ['point151', 66, 6, True], ['point152', 66, 8, True], ['point153', 66, 10, True], ['point154', 67, 4, False], ['point155', 60, 23, True], ['point156', 60, 31, False], ['point157', 61, 24, True], ['point158', 62, 21, True], ['point159', 62, 25, False], ['point160', 64, 31, True], ['point161', 64, 33, False], ['point162', 65, 20, True], ['point163', 65, 23, True], ['point164', 67, 23, False], ['point165', 68, 25, False], ['point166', 69, 30, False], ['point167', 62, 49, False], ['point168', 63, 52, False], ['point169', 64, 48, False], ['point170', 65, 40, False], ['point171', 65, 44, False], ['point172', 65, 48, True], ['point173', 66, 43, False], ['point174', 66, 47, False], ['point175', 67, 40, False], ['point176', 67, 48, False], ['point177', 69, 40, True], ['point178', 69, 43, False], ['point179', 60, 64, True], ['point180', 60, 67, True], ['point181', 61, 63, False], ['point182', 61, 71, False], ['point183', 62, 62, True], ['point184', 63, 70, True], ['point185', 64, 66, True], ['point186', 64, 70, False], ['point187', 66, 71, True], ['point188', 67, 60, False], ['point189', 67, 65, False], ['point190', 67, 66, False]] matrix = connectNodes(nodeLarge) graph = student_utils.adjacency_matrix_to_graph(matrix)[0] # nx.draw(graph) # plt.show() print(student_utils.is_metric(graph)) writeToInput(nodeLarge, matrix)