def test_reconstruct_path(self): with pytest.raises(KeyError): XG = nx.DiGraph() XG.add_weighted_edges_from([ ("s", "u", 10), ("s", "x", 5), ("u", "v", 1), ("u", "x", 2), ("v", "y", 1), ("x", "u", 3), ("x", "v", 5), ("x", "y", 2), ("y", "s", 7), ("y", "v", 6), ]) predecessors, _ = nx.floyd_warshall_predecessor_and_distance(XG) path = nx.reconstruct_path("s", "v", predecessors) assert path == ["s", "x", "u", "v"] path = nx.reconstruct_path("s", "s", predecessors) assert path == [] # this part raises the keyError nx.reconstruct_path("1", "2", predecessors)
def steiner_find(list_of_locations, list_of_homes, starting_car_location, adjacency_matrix): G = nx.Graph(incoming_graph_data=adjacency_matrix, cutoff=1000) predecessors, distances = nx.floyd_warshall_predecessor_and_distance(G) homeIndices = [] for h in list_of_homes: homeIndices.append(list_of_locations.index(h)) # homeIndices.append(list_of_locations.index(starting_car_location)) tree = apxa.steiner_tree( G, homeIndices + [list_of_locations.index(starting_car_location)]) # print(list(tree.nodes)) # print(homeIndices) # print(starting_car_location) # treematrix = nx.adjacency_matrix(tree) # return greedyAllPairs(list(tree.nodes), homeIndices, int(starting_car_location), treematrix) nodelist = list(tree.nodes()) currIndex = list_of_locations.index(starting_car_location) visitedNodes = [] path = [list_of_locations.index(starting_car_location)] dropoff_mapping = {} added = False # print(currIndex) nodelist = nodelist[ nodelist.index(currIndex):] + nodelist[:nodelist.index(currIndex)] # print(nodelist) dropped = False allHomes = set(homeIndices) homeSet = set(homeIndices) length = len(nodelist) - 1 i = 0 while i in range(0, length): dropped = False homeNeighbors = list( homeSet.intersection(list(tree.neighbors(nodelist[i])))) if len(homeNeighbors) >= 3: dropped = True homeNeighbors = [j for j in homeNeighbors if j in homeSet] if (nodelist[i] in homeSet): homeNeighbors = homeNeighbors + [nodelist[i]] dropoff_mapping[nodelist[i]] = homeNeighbors homeSet = set([j for j in homeSet if j not in homeNeighbors]) n = 1 while i + n in range(0, length) and nodelist[ i + n] in allHomes and nodelist[i + n] not in homeSet: n = n + 1 path_to_next = nx.reconstruct_path(nodelist[i], nodelist[i + n], predecessors) path.extend(path_to_next[1:]) if nodelist[i] in homeSet and not dropped: dropoff_mapping[nodelist[i]] = [nodelist[i]] homeSet.remove(nodelist[i]) i = i + n if nodelist[-1] in homeSet: dropoff_mapping[nodelist[-1]] = [nodelist[-1]] path_to_start = nx.reconstruct_path(nodelist[-1], nodelist[0], predecessors) path.extend(path_to_start[1:]) return path, dropoff_mapping
def path_generate_for_truck_tsp(self, truck: Truck): """ 生成一个车辆的回路 数学模型:d[i][status]表示从编号i城市出发,经过status中为1的顶点并回到配送中心的最小花费。其中状态status用二进制数来表示 右起第i位表示第i个城市的状态,0为未拜访,1为已拜访。设i, j为顶点映射到整数的编号,u, v为对应的真实的顶点编号,则状态转移方程为: ```d[i][status] = min(dists[u][v] + d[j][status^(1<<j)]) for j=0 to n-1``` 其中v是u的所有邻接顶点,依次取值来得到最小值。dists[u][v]表示城市到城市u到v的最短路程。 时间复杂度:Floyd预处理为O(n^3),枚举各种状态的时间复杂度为O(2^n*n^2),总的时间复杂度即为O(2^n*n^2) :param truck: 待生成回路的车辆 :return: None """ # 取子图,求Floyd subgraph = truck.subgraph.copy() predecessors, dists = nx.floyd_warshall_predecessor_and_distance(subgraph) subgraph.remove_node(0) # 去除原图中的顶点0 # 初始化动态规划数组及顶点映射 n = len(subgraph) status_max = 1 << n d = {u: {} for u in subgraph} # d[i][status]表示从编号i城市出发,经过status中为1的顶点并回到配送中心的最小花费 path = {u: {} for u in subgraph} # path[i][status]表示上述最小花费所走的路径 mapping = {u: i for i, u in enumerate(subgraph)} # 顶点到顶点编号的映射(因为子图的顶点并不是从0开始连续增1) for u in subgraph: d[u][0] = dists[u][0] # 状态为0说明直接返回配送中心,因此距离即为当前顶点到配送中心的距离 path[u][0] = nx.reconstruct_path(u, 0, predecessors) # 并且上述距离对应的路径即为u直接到0 # 动态规划配送网点 for status in range(1, status_max - 1): for u in subgraph: dist_opt = np.inf path_opt = [] for v in subgraph[u]: status_v = 1 << mapping[v] if status_v | status == status: # 如果v属于当前的状态中 dist = dists[u][v] + d[v][status ^ status_v] # 那么可将v去除 if dist < dist_opt: dist_opt = dist path_opt = nx.reconstruct_path(u, v, predecessors)[:-1] + path[v][status ^ status_v] if not np.isinf(dist_opt): d[u][status] = dist_opt path[u][status] = path_opt # 动态规划配送中心 status = status_max - 1 dist_opt = np.inf path_opt = [] for v in truck.subgraph[0]: status_v = 1 << mapping[v] dist = dists[0][v] + d[v][status ^ status_v] if dist < dist_opt: dist_opt = dist path_opt = nx.reconstruct_path(0, v, predecessors)[:-1] + path[v][status ^ status_v] # 更新车辆路径 truck.d = dist_opt truck.path = path_opt
def get_next_step(self, currPos, destPos, router_type, weight): if str.lower(router_type) == 'dijkstra' and weight == 'delay': return nx.dijkstra_path(self.dynetwork._network, currPos, destPos, weight='edge_delay')[1] elif str.lower(router_type) == 'dijkstra': return nx.dijkstra_path(self.dynetwork._network, currPos, destPos)[1] else: return nx.reconstruct_path(currPos, destPos, self.preds)[1]
def path_generate_for_truck_greedy(self, truck: Truck): """ 通过贪心算法,生成一个车辆的次优回路,时间复杂度为O(n^2) :param truck: 待生成回路的车辆 :return: None """ # Floyd预处理 subgraph = truck.subgraph predecessors, dists = nx.floyd_warshall_predecessor_and_distance(subgraph) d = 0 path = [] visited = {u: False for u in subgraph} visited[0] = True # 从配送中心出发开始贪心 u = 0 for i in range(len(subgraph) - 1): v_opt = 0 dist_opt = np.inf for v in subgraph: if not visited[v] and dists[u][v] < dist_opt: v_opt = v dist_opt = dists[u][v] visited[v_opt] = True d += dists[u][v_opt] path += nx.reconstruct_path(u, v_opt, predecessors)[:-1] u = v_opt # 更新车辆路径 truck.d = d + dists[u][0] truck.path = path + [u, 0]
def findPath(graph, src, dest): # finds node predecessors using a function imported from networkx predecessors, _ = nx.floyd_warshall_predecessor_and_distance(graph) try: # finds the shortest path through the Floyd-Warshall algorithm by using a function imported from networkx path = nx.reconstruct_path(src, dest, predecessors) dpath = nx.dijkstra_path(graph, src, dest) bfpath = nx.bellman_ford_path(graph, src, dest) # prints the shortest path print("Path:", path) # print("Dijkstra's Path:", dpath) # print("Bellman-Ford Path:", bfpath) # prints the number of hops from source to destination print("Number of Hops:", len(path) - 1) # print("Dijkstra's Number of Hops:", len(dpath)-1) # print("Bellman-Ford Number of Hops:", len(bfpath)-1) # gets the total cost edge = 0 for i in range(1, len(path)): edge += graph.edges[path[i - 1], path[i]]['weight'] dpathcost = nx.dijkstra_path_length(graph, src, dest) bfpathcost = nx.bellman_ford_path_length(graph, src, dest) print("Total Cost:", edge) # print("Dijkstra's Total Cost:", dpathcost) # print("Bellman-Ford Total Cost:", bfpathcost) print() except: # Print to the terminal if no path exists. print("ERROR: No available path from source: node", src, "to destination: node", dest)
def greedyAllPairs(list_of_locations, list_of_homes, starting_car_location, adjacency_matrix): #print(adjacency_matrix) G = nx.Graph(incoming_graph_data=adjacency_matrix, cutoff=1000) predecessors, distances = nx.floyd_warshall_predecessor_and_distance(G) list_of_homes = list_of_homes[:] def find_closest_home_to_location(location): distance = float('inf') closest_home = list_of_homes[0] for h in list_of_homes: home_idx = list_of_locations.index(h) location_idx = list_of_locations.index(location) new_dist = list(distances.items())[home_idx][1][location_idx] if new_dist < distance: distance = new_dist closest_home = h return closest_home, distance current = starting_car_location # print(list_of_locations) # print(list_of_homes) # print(starting_car_location) total_path = [list_of_locations.index(starting_car_location)] dropoff_mapping = {} for _ in range(len(list_of_homes)): closest, distance = find_closest_home_to_location(current) curr_idx = list_of_locations.index(current) next_idx = list_of_locations.index(closest) path_to_closest = nx.reconstruct_path(curr_idx, next_idx, predecessors) dropoff_mapping[next_idx] = [next_idx] total_path.extend(path_to_closest[1:]) list_of_homes.remove(closest) current = closest start_idx = list_of_locations.index(starting_car_location) path_to_start = nx.reconstruct_path(next_idx, start_idx, predecessors) total_path.extend(path_to_start[1:]) # print(dropoff_mapping) # print(total_path) return total_path, dropoff_mapping
def test_reconstruct_path(self): XG = nx.DiGraph() XG.add_weighted_edges_from([('s', 'u', 10), ('s', 'x', 5), ('u', 'v', 1), ('u', 'x', 2), ('v', 'y', 1), ('x', 'u', 3), ('x', 'v', 5), ('x', 'y', 2), ('y', 's', 7), ('y', 'v', 6)]) predecessors, _ = nx.floyd_warshall_predecessor_and_distance(XG) path = nx.reconstruct_path('s', 'v', predecessors) assert_equal(path, ['s', 'x', 'u', 'v']) path = nx.reconstruct_path('s', 's', predecessors) assert_equal(path, []) # this part raises the keyError nx.reconstruct_path('1', '2', predecessors)
def give_edges_weights(new_graph, old_graph, old_list_locations): # output: shortest_paths_between_homes predecessor, shortest = nx.floyd_warshall_predecessor_and_distance(old_graph) shortest_paths_between_homes = {} for (u, v) in new_graph.edges(): # print("u,v : " + str(u) + " , " + str(v)) left = old_list_locations.index(new_graph.nodes[u]['name']) right = old_list_locations.index(new_graph.nodes[v]['name']) # print("(left,right): " + str(left) + " ," + str(right)) new_graph[u][v]['weight'] = shortest[left][right] # print(new_graph[u][v]['weight']) # u,v here is the index of newly created complete graph # left,right here is the index of old graph. # u <-> left, v <-> right shortest_paths_between_homes[(u, v)] = nx.reconstruct_path(left, right, predecessor) shortest_paths_between_homes[(v, u)] = nx.reconstruct_path(right, left, predecessor) return shortest_paths_between_homes
def test_reconstruct_path(self): XG = nx.DiGraph() XG.add_weighted_edges_from([ ('s', 'u', 10), ('s', 'x', 5), ('u', 'v', 1), ('u', 'x', 2), ('v', 'y', 1), ('x', 'u', 3), ('x', 'v', 5), ('x', 'y', 2), ('y', 's', 7), ('y', 'v', 6) ]) predecessors, _ = nx.floyd_warshall_predecessor_and_distance(XG) path = nx.reconstruct_path('s', 'v', predecessors) assert_equal(path, ['s', 'x', 'u', 'v']) path = nx.reconstruct_path('s', 's', predecessors) assert_equal(path, []) # this part raises the keyError nx.reconstruct_path('1', '2', predecessors)
def all_pairs_shortest_paths(graph): """Returns a dictionary of dictionaries. Each inner dictionary is indexed by a node and value is distance between outer key and inner key.""" predecessors, lengths = nx.floyd_warshall_predecessor_and_distance(graph) paths = dict() for i in range(len(graph.nodes)): paths[i] = dict() for j in range(len(graph.nodes)): paths[i][j] = nx.reconstruct_path(i, j, predecessors) return lengths, paths
def RutaCorta(ciudad1,ciudad2,g): dist=0 predecessors, _ = nx.floyd_warshall_predecessor_and_distance(g) ruta=nx.reconstruct_path(ciudad1, ciudad2, predecessors) for i in ruta: if i==ciudad1: j=i elif i==ciudad2: dist=g.get_edge_data(j,i)['weight']+dist return(ruta,dist) else: dist=g.get_edge_data(j,i)['weight']+dist j=i
def floyd_warshall(adj): try: import networkx as nx except ImportError: print("please install networkx first") else: # consider disconnected nodes as connected with np.inf weight, or disconnected graph causing error G = nx.convert_matrix.from_numpy_matrix(A = adj, create_using = nx.DiGraph) predecessors, distance = nx.floyd_warshall_predecessor_and_distance(G) N = adj.shape[0] paths = {} for i in range(N): for j in range(N): paths[str(i)+"_"+str(j)] = nx.reconstruct_path(i, j, predecessors) return paths, distance
def find_path(self, graph, src, dest): # finds node predecessors using a function imported from networkx predecessors, _ = nx.floyd_warshall_predecessor_and_distance(graph) try: # finds the shortest path through the Floyd-Warshall algorithm by using a function imported from networkx self.path = nx.reconstruct_path(src, dest, predecessors) self.dpath = nx.dijkstra_path(graph, src, dest) self.bfpath = nx.bellman_ford_path(graph, src, dest) except: # Print to the terminal if no path exists. self.path = [] self.dpath = [] self.bfpath = []
def find_main_routes(self): if self.v: print('Searching for main routes in graph') routes, lens = nx.floyd_warshall_predecessor_and_distance(self.graph) for src in sorted(self.nodes): for dst in sorted(self.nodes): if dst <= src or math.isinf(lens[src][dst]): continue route = nx.reconstruct_path(src, dst, routes) self.main_routes[(src, dst)] = { 'route': route.copy(), 'len': lens[src][dst], 'delay': lens[src][dst] * self.delay_mks } if self.v: print(f'Found {len(self.main_routes)} main routes')
def path(u, v): return nx.reconstruct_path(u, v, predecessors)
def get_route(source, target): predecessors = get_value('predecessors') route = nx.reconstruct_path(source, target, predecessors) return route
def path_generate(self, truck: Truck): """ 生成一个卡车的回路 :param truck: 待生成回路的卡车 :return: None 数学模型:dp[i][v]表示在状态v下,到达城市i所用的最小花费。其中状态v用二进制数来表示 右起第i位表示第i个城市的状态,0为未拜访,1为已拜访。则状态转移方程为: dp[i][v]=min(dp[i][v],dp[k][v^(1<<(i-1)]+dist[k][i]) 其中k从1到n依次取值来得到最小值,dist[k][i]表示城市k到城市i的路程,^为异或运算,v^(1<<i-1)将v中第i位置0 即dp[k][v^(1<<(i-1)]表示未访问城市i的状态下,到达城市k的花费。 时间复杂度:Floyd预处理为O(n^3),枚举各种状态的时间复杂度为O(2^n*n^2),总的时间复杂度即为O(2^n*n^2) """ predecessors, dists = nx.floyd_warshall_predecessor_and_distance( truck.subgraph) n = len(truck.subgraph) mapping = np.zeros(n) for i, u in enumerate(truck.subgraph): mapping[i] = u dp = np.zeros((n, 1 << (n - 1))) path = dict() for i in range(1, n): u = int(mapping[i]) dp[i, 0] = dists[u][0] path[u] = {0: u} path[0] = dict() for status in range(1 << (n - 1) - 1): for i in range(1, n): u = int(mapping[i]) dist_opt = np.inf path_opt = [] for v in truck.subgraph[u]: if v != 0 and (1 << (v - 1)) | status == status: dist = dp[v, status ^ (1 << (v - 1))] + dists[u][v] if dist < dist_opt: dist_opt = dist path_opt = nx.reconstruct_path( u, v, predecessors).pop() + path[v][status ^ (1 << (v - 1))] if not np.isinf(dist_opt): dp[i, status] = dist_opt path[u][status] = path_opt i = 0 status = 1 << (n - 1) - 1 u = int(mapping[i]) dist_opt = np.inf path_opt = [] for v in truck.subgraph[u]: if v != 0 and (1 << (v - 1)) | status == status: dist = dp[v, status ^ (1 << (v - 1))] + dists[u][v] if dist < dist_opt: dist_opt = dist path_opt = nx.reconstruct_path( u, v, predecessors) + path[v][status ^ (1 << (v - 1))] if not np.isinf(dist_opt): dp[i, status] = dist_opt path[u][status] = path_opt else: raise nx.NetworkXError print(dp[i, 0]) print(path[u][0])
'B': (1.25, 1.75), 'C': (2.5, 1), 'D': (2, 0), 'E': (.75, 0), } predecessores, distancias = nx.floyd_warshall_predecessor_and_distance(G) predecessores_od = collections.OrderedDict(sorted(predecessores.items())) distancias_od = collections.OrderedDict(sorted(distancias.items())) resultados_np = nx.floyd_warshall_numpy(G) print(resultados_np) print('\nDistâncias:') for chave, valor in distancias_od.items(): od = collections.OrderedDict(sorted(valor.items())) print(f'Distâncias a partir de {chave}:', json.dumps(od)) print("\nCamihos:") for source, valor in predecessores_od.items(): valor_od = collections.OrderedDict(sorted(valor.items())) for target, caminho in valor_od.items(): print(f'Caminhos a partir de {source} até {target}') nx.reconstruct_path(source, target, predecessores_od) print("") rotulos = nx.get_edge_attributes(G, 'weight') nx.draw(G, posicoes, with_labels=True, connectionstyle='arc3, rad=0.1') nx.draw_networkx_edge_labels(G, posicoes, edge_labels=rotulos, label_pos=0.3)
continuar = True while (continuar): print(""" MENU 1. Ruta mas corta 2. Centro de grafo 3. Modificar grafo 4. Ver grafo 5. Salir""") opcion = input("Que opcion desea?: ") if (opcion == "1"): ciudad = input("Cual es la ciudad de origen?: ") destino = input("Cual es la ciudad de destino?: ") predecessors, _ = nx.floyd_warshall_predecessor_and_distance(DG) print(nx.reconstruct_path(ciudad, destino, predecessors)) elif (opcion == "2"): centro = nx.center(DG) print("El centro del grafo esta en:" + centro[0]) elif (opcion == "3"): print(""" 1. Reportar trafico 2. Crear nueva conexion""") option = input("Que desea hacer?: ") if (option == "1"): ciudad1 = input("Indique la ciudad de origen: ") ciudad2 = input("Indique la ciudad de destino: ") trafico = int( input("Simule con un numero que tanto trafico hay: "))
def floyd_warshall_explored(G, source, destination): predecessors, distance = nx.floyd_warshall_predecessor_and_distance(G) visited = {} for key in predecessors.keys(): visited[key] = nx.reconstruct_path(source, key, predecessors) return " -> ".join(visited)
def floyd_warshall(G, source, destination): predecessors, distance = nx.floyd_warshall_predecessor_and_distance(G) return " -> ".join(nx.reconstruct_path(source, destination, predecessors))
def greedyAllPairs2(list_of_locations, list_of_homes, starting_car_location, adjacency_matrix): #print(adjacency_matrix) G = nx.Graph(incoming_graph_data=adjacency_matrix, cutoff=1000) predecessors, distances = nx.floyd_warshall_predecessor_and_distance(G) homeIndices = [] for h in list_of_homes: homeIndices.append(list_of_locations.index(h)) homeSet = set(homeIndices) homeList = list_of_homes[:] def get_distance(a, b): return list(distances.items())[a][1][b] def find_closest_home_to_location(location, homeList=homeList): distance = float('inf') closest_home = homeList[0] for h in homeList: if h in list_of_locations: home_idx = list_of_locations.index(h) else: home_idx = h if location in list_of_locations: location_idx = list_of_locations.index(location) else: location_idx = location #new_dist = list(distances.items())[home_idx][1][location_idx] new_dist = get_distance(home_idx, location_idx) if new_dist < distance: distance = new_dist closest_home = h return closest_home, distance current = starting_car_location # print(list_of_locations) # print(list_of_homes) # print(starting_car_location) path = [list_of_locations.index(starting_car_location)] dropoff_mapping = {} length = len(homeSet) + 1 i = 0 closest, distance = find_closest_home_to_location(current) while i in range(length): dropped = False node = list_of_locations.index(current) homeNeighbors = set(homeSet.intersection(list(G.neighbors(node)))) shlong = 0 for node2 in list(G.neighbors(node)): for node3 in list(G.neighbors(node2)): if node3 != node and node3 in homeSet: homeNeighbors.add(node2) shlong += get_distance(node, node3) dong = 0 curr = current dong_dropoffs = [] homeNeighbors = [j for j in homeNeighbors if j in homeSet] n = 1 homeNeighborsCopy = list(homeNeighbors) for _ in range(len(homeNeighbors)): closest, distance = find_closest_home_to_location( curr, homeNeighborsCopy) # curr_idx = list_of_locations.index(curr) # next_idx = list_of_locations.index(closest) homeNeighborsCopy.remove(closest) dong_dropoffs.append(closest) curr = closest dong += distance if shlong < dong: if node not in dropoff_mapping: dropoff_mapping[node] = [] if node in homeSet: dong_dropoffs.append(node) for d in dong_dropoffs: dropoff_mapping[node].append(d) n = len(dong_dropoffs) dropped = True homeSet = set([j for j in homeSet if j not in dong_dropoffs]) homeList = [ j for j in homeList if list_of_locations.index(j) in homeSet ] #drop it like its hot # print(homeNeighbors) # if len(homeNeighbors) >= 3 : # dropped = True # node = list_of_locations.index(current) # homeNeighbors = [j for j in homeNeighbors if j in homeSet] # if (node in homeSet): # homeNeighbors = homeNeighbors + [node] # dropoff_mapping[node]= homeNeighbors # homeSet = set([j for j in homeSet if j not in homeNeighbors]) # homeList = [j for j in homeList if list_of_locations.index(j) in homeSet] # n = len(homeNeighbors) if node in homeSet and not dropped: dropoff_mapping[node] = [node] homeSet.remove(node) homeList.remove(current) if len(homeList) != 0: closest, distance = find_closest_home_to_location( current, homeList) path_to_next = nx.reconstruct_path( node, list_of_locations.index(closest), predecessors) path.extend(path_to_next[1:]) current = closest i = i + n start_idx = list_of_locations.index(starting_car_location) path_to_start = nx.reconstruct_path(list_of_locations.index(current), start_idx, predecessors) path.extend(path_to_start[1:]) return path, dropoff_mapping
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 """ # pass home_list = convert_locations_to_indices(list_of_homes, list_of_locations) location_list = convert_locations_to_indices(list_of_locations, list_of_locations) G, message = adjacency_matrix_to_graph(adjacency_matrix) #Dropoff mappings dictionary dropoff_mappings = {} # for home in home_list: # dropoff_mapping[home] = [home] # Create a minimum spanning_tree of G #T = nx.minimum_spanning_tree(G) # Predecessors, distances of the minimum spanning tree predecessors, distances = nx.floyd_warshall_predecessor_and_distance(G) #list_of_edges = sorted(T.edges(data=False)) starting_location = list_of_locations.index(starting_car_location) curr_location = starting_location car_path = [curr_location] visited_homes = [] ##aka car_path closest_home_path = [] # visited_homes.append(curr_location) not_yet_visited_homes = home_list[:] while len(not_yet_visited_homes) != 0: closest_home, closest_home_path = find_closest_home_to_current_location( predecessors, distances, visited_homes, not_yet_visited_homes, curr_location) visited_homes.append(closest_home) not_yet_visited_homes.remove(closest_home) curr_location = closest_home car_path += closest_home_path # Return home path_home = nx.reconstruct_path(curr_location, starting_location, predecessors) car_path += path_home clean_path = [] clean_path.append(car_path[0]) for i in range(1, len(car_path)): if car_path[i] != clean_path[-1]: clean_path.append(car_path[i]) car_path = clean_path[:] node_list = car_path[:] ## Optimization gadget palindromic_windows = [] covered_indices = set() for center in range(len(car_path)): left_bound = center right_bound = center dist = 1 while (center - dist >= 0) and (center + dist < len(car_path)) and ( car_path[center - dist] == car_path[center + dist]): left_bound -= 1 right_bound += 1 dist += 1 contained = True for i in range(left_bound, right_bound + 1): if i not in covered_indices: contained = False if not contained: palindromic_windows.append((left_bound, right_bound)) for i in range(left_bound, right_bound + 1): covered_indices.add(i) clean_windows = [] for window1 in palindromic_windows: contained = False for window2 in palindromic_windows: if window1 != window2: if (window1[0] >= window2[0]) and (window1[1] <= window2[1]): contained = True break if not contained: clean_windows.append(window1) clean_path = [car_path[clean_windows[0][0]]] home_set = set(home_list) for window in clean_windows: if clean_path[-1] != car_path[window[0]]: clean_path.append(car_path[window[0]]) for i in range(window[0] + 1 + ((window[1] - window[0]) // 2), window[1]): if car_path[i] in home_set: #print(i) dropoff_mappings[car_path[i]] = [ car_path[window[0] + ((window[1] - window[0]) // 2)] ] for j in range(window[1] - 1, window[0] + 1 + ((window[1] - window[0]) // 2), -1): clean_path.append(car_path[j]) for j in range(window[0] + 1 + ((window[1] - window[0]) // 2), window[1]): clean_path.append(car_path[j]) clean_path.append(car_path[window[0]]) break for home in clean_path: if home in home_list: if home in dropoff_mappings: dropoff_mappings[home].append(home) home_list.remove(home) else: dropoff_mappings[home] = [home] home_list.remove(home) node_list = clean_path return node_list, dropoff_mappings