def solve(initial_graph): """Create ajd list graph for later""" euler_graph = multi_graph.MultiGraph(edges=initial_graph.edge_list()) """Find odd degree nodes""" odd_degree_nodes = euler_graph.odd_degree_nodes() print(f'{len(odd_degree_nodes)} odd_degree_nodes =', odd_degree_nodes) """Compute all possible odd node pairs""" odd_node_pairs = list(itertools.combinations(odd_degree_nodes, 2)) odd_node_pairs = [utils.normalize_pair(*p) for p in odd_node_pairs] print(f'{len(odd_node_pairs)} odd node pairs') """Compute the minimum distance for every pair""" print('computing odd node pairs shortest paths...') tstart = datetime.now() csr_mat = scipy.sparse.csr_matrix(initial_graph.mat) dist_matrix = shortest_path(csr_mat, directed=False, return_predecessors=False) # Dist matrix of the whole graph tend = datetime.now() print('done, took', tend - tstart) odd_node_pairs_distance_list = [] # (u, v, d) for pair in odd_node_pairs: u, v = pair odd_node_pairs_distance_list.append((u, v, dist_matrix[u, v])) """Create a complete graph of those pairs with the minimum distance as weight""" print('creating euler graph') graph_odd_complete = Graph(odd_node_pairs_distance_list) """Compute the minimum weight matching""" print('computing minimum weight matching...') tstart = datetime.now() min_weight_pairs = edmonds.min_weight_matching(graph_odd_complete) tend = datetime.now() print('done, took', tend - tstart) print(f'{len(min_weight_pairs)} min_weight_pairs =', min_weight_pairs) """Built up the euleur graph (initial graph + "fake" edges between the pairs found in previous computation)""" for pair in min_weight_pairs: u, v = utils.normalize_pair(*pair) dist = dist_matrix[u, v] euler_graph.add_edge(*pair, dist) """Get a naive path (naive because it uses the previous fakes edges)""" naive_euler_circuit = list(euler.eulerian_circuit(euler_graph)) print(naive_euler_circuit) """Create the real final path by replacing the fake edges with the shortest possible path""" final_path = [] for edge in naive_euler_circuit: if initial_graph.has_edge(*edge): final_path.append(edge) else: real_path, _ = initial_graph.shortest_path(csr_mat, *edge) final_path += real_path return final_path
def remove_node(self, node_id): node = self.nodes.get(node_id) if node is None: return for neighbour_id, neighbour in node.neighbours.items(): del neighbour.node.neighbours[ node_id] # Remove neighbours ref to this node self.edges.pop(utils.normalize_pair(node_id, neighbour_id), None) # Remove the edge from the graph self.degree -= 1 del self.nodes[node_id]
def get_edge_color(path, u, v, highlight_last=False): if highlight_last and ( (u, v) == path[-1] or (v, u) == path[-1]): # Highlight last visited edge return 'yellow' if visit_times.get(utils.normalize_pair(u, v), 0) > 1: # Already visited edge return 'blue' if (u, v) in path or (v, u) in path: # Visited edge return 'red' return 'w'
def add_edge(self, u, v, weight): # Undirected for now # Get (or create if necessary) the two nodes start_node = self.add_node(u) end_node = self.add_node(v) # Link the two nodes start_node.add_neighbour(end_node) end_node.add_neighbour(start_node) key = utils.normalize_pair(u, v) self.edges[key] = weight
def remove_edge(self, u, v): # Check if edge actually exists key = utils.normalize_pair(u, v) edge = self.edges.pop(key, None) if edge is None: return # If so, remove link between each node del self.nodes[u].neighbours[v] # Remove neighbouring ref u -> v del self.nodes[v].neighbours[u] # Remove neighbouring ref v -> u # Remove the edge from the graph del self.edges[key]
def get_edge(self, u, v): # No checks! return self.edges[utils.normalize_pair(u, v)]
def has_edge(self, u, v): return utils.normalize_pair(u, v) in self.edges
def render_path_as_gif(initial_graph_edges, path, img_size=1000, duration_between_steps=0.8, edge_batch_size=1): """ Render final path as a gif (optional) """ ga = gvanim.Animation() for edge in initial_graph_edges: ga.add_edge(edge[0], edge[1]) ga.next_step() visit_times = {} for edge in path: edge = utils.normalize_pair(*edge) visit_times[edge] = 0 drawn_path = [] def draw_path(): for edge in drawn_path: if visit_times[edge] > 1: ga.highlight_edge(*edge, color='blue') else: ga.highlight_edge(*edge, color='red') # for raw_edge in path: # new_edge = utils.normalize_pair(*raw_edge) # draw_path() # ga.highlight_edge(*new_edge, color='red') # drawn_path.append(new_edge) # visit_times[new_edge] += 1 # ga.next_step() cs = edge_batch_size # Chunk size to process paths by for chunk in [ path[cs * i:cs * (i + 1)] for i in range(len(path) // cs + 1) ]: for raw_edge in chunk: new_edge = utils.normalize_pair(*raw_edge) drawn_path.append(new_edge) visit_times[new_edge] += 1 draw_path() ga.highlight_edge(*drawn_path[-1], color='yellow') # Mark last visited edge in red ga.next_step() # Final result draw_path() ga.next_step() render_directory = 'render_files' os.makedirs(render_directory) ga_graphs = ga.graphs() files = gvanim.render(ga_graphs, f'{render_directory}/step', 'png', size=img_size) render_filename = 'render.gif' with imageio.get_writer(render_filename, mode='I', duration=duration_between_steps) as writer: for file in files: image = imageio.imread(file) writer.append_data(image) print( f'Final path rendering available at "src/theoric_app/{render_filename}"' )
def render_osmnx_path(osmnx_graph, edge_path, duration_between_steps=0.5, step_size=1, edge_width=1.0): time_stamp = int(time.time()) output_dir = f'render_files_{time_stamp}' os.makedirs(output_dir) visit_times = {} for edge in edge_path: visit_times[utils.normalize_pair(*edge)] = 0 def get_edge_color(path, u, v, highlight_last=False): if highlight_last and ( (u, v) == path[-1] or (v, u) == path[-1]): # Highlight last visited edge return 'yellow' if visit_times.get(utils.normalize_pair(u, v), 0) > 1: # Already visited edge return 'blue' if (u, v) in path or (v, u) in path: # Visited edge return 'red' return 'w' def get_edge_colors(path, highlight_last=False): return [ get_edge_color(path, u, v, highlight_last) for (u, v) in osmnx_graph.edges() ] files = [] def render_path(path, filename, highlight_last=False): edge_colors = get_edge_colors(path, highlight_last) ox.plot_graph(osmnx_graph, node_color='w', node_edgecolor='k', node_size=0, node_zorder=3, edge_color=edge_colors, edge_linewidth=edge_width, show=False, close=True, save=True, filepath=filename) files.append(filename) # # Render step by step # for i in range(len(edge_path)): # current_path = edge_path[:(i + 1)] # render_path(current_path, f'{output_dir}/step_{i:05}.png', highlight_last=True) # visit_times[utils.normalize_pair(*edge_path[i])] += 1 # Update last visited pair # Render by big steps for i in range(len(edge_path) // step_size + 1): start_i, end_i = step_size * i, step_size * (i + 1) current_path = edge_path[:end_i] render_path(current_path, f'{output_dir}/step_{i:05}.png', highlight_last=True) for edge in edge_path[start_i:end_i]: visit_times[utils.normalize_pair(*edge)] += 1 # Render final step render_path(edge_path, f'{output_dir}/step_{len(edge_path)}.png') print(files) # Render final gif with imageio.get_writer(f'osmnx_render_{time_stamp}.gif', mode='I', duration=duration_between_steps) as writer: for file in files: image = imageio.imread(file) writer.append_data(image) print(output_dir)