def create_random_instance(cls, point_amount, task, bounds=(0, 500), generation=0, edge_chance=0.5): points = np.random.random_integers(bounds[0], bounds[1], size=(point_amount, 2)) ad_matrix = np.zeros((len(points), len(points))) tril_indices = np.tril_indices_from(ad_matrix, -1) tril_edges = np.array([ 1 if i < edge_chance else 0 for i in np.random.random(size=len(tril_indices[0])) ]) while tril_edges.max() == 0: tril_edges = np.array([ 1 if i < edge_chance else 0 for i in np.random.random(size=len(tril_indices[0])) ]) ad_matrix[tril_indices] = tril_edges ad_matrix = ad_matrix + ad_matrix.T graph = Graph(points, ad_matrix=ad_matrix) return GraphGenome(graph, bounds=bounds, task=task, generation=generation)
def create_random_bipartite(point_amount, bounds=(0, 500), edge_chance=0.5, max_cone=np.inf): points = np.random.random_integers(bounds[0], bounds[1], size=(point_amount, 2)) association = np.random.random_integers(0, 1, size=(point_amount)) while association.max() == 0 or association.min() == 1: association = np.random.random_integers(0, 1, size=(point_amount)) v_1 = [] v_2 = [] for i, a in enumerate(association): v_1.append(i) if not a else v_2.append(i) ad_matrix = np.zeros((len(points), len(points))) while ad_matrix.max() == 0: for i, j in itertools.product(v_1, v_2): if np.random.random() < edge_chance: ad_matrix[i, j] = ad_matrix[j, i] = 1 lb = max(get_lower_bounds(points, ad_matrix)) if lb > max_cone: ad_matrix[i, j] = ad_matrix[j, i] = 0 graph = Graph(points, ad_matrix=ad_matrix) return graph, association
def test_build_paths(): ip = AngularGraphScanMakespanHamilton() n = 3 ad_vert = np.zeros([n * n], dtype=tuple) ad_vert[:] = [(i, j) for i in range(3) for j in range(3)] edges = set([(v1, v2) for v1 in ad_vert for v2 in ad_vert if v1 != v2]) g = Graph(ad_vert, edges) model = gurobipy.Model() ip._build_hamilton_path(0)
def solve(self, graph: Graph, **kwargs): start_time = time.time() times = None error_message = None try: coloring = self.coloring_solver(graph) max_color = max(coloring) index_colorings = [] log_max_color = math.ceil(math.log2(max_color)) color_bit_vector = np.array([[(color >> (log_max_color - 1 - i)) & 1 for i in range(log_max_color)] for color in range(max_color)]) for vec in color_bit_vector.T: # Get indices of the vertices where in one of the sets to_color = np.arange(len(vec))[vec == 1] isin = np.isin(coloring, to_color) both_sets_indices = (*np.where(isin == 0), *np.where(isin == 1)) index_colorings.append(both_sets_indices) solutions = [] used_edges = set() for set_one, set_two in index_colorings: subgraph = graph.get_bipartite_subgraph( set_one, set_two, forbidden_edges=used_edges) if subgraph.edge_amount > 0: sol = self.sub_solver.solve( subgraph, colors=[ int(i in set_one) for i in range(graph.vert_amount) ]) solutions.append(sol) # If wanted collect the already used edges to maybe get a better solution if self.no_used_edges: used_edges.update({(u) for u in product(set_one, set_two)}) times = self._calculate_order(solutions, graph) if len(times) != graph.edge_amount: times = None raise Exception("Edge order does not contain all edges!") except Exception as e: error_message = str(e) raise e return AngularGraphSolution(graph, time.time() - start_time, self.__class__.__name__, self.sub_solver.solution_type, times=times, error_message=error_message)
def test_ip_solver(): ip = AngularGraphScanMakespanAbsoluteReduced() n = 3 ad_vert = np.zeros([n * n], dtype=tuple) ad_vert[:] = [(i, j) for i in range(3) for j in range(3)] e_arr = np.triu_indices(n*n, 1) edges = {(ad_vert[e_arr[0][i]], ad_vert[e_arr[1][i]]) for i in range(len(e_arr[0]))} g = Graph(ad_vert, edges) solution = ip.build_ip_and_optimize(g) from utils.visualization import visualize_solution_2d, visualize_graph_2d #visualize_graph_2d(solution.graph) visualize_solution_2d(solution)
def test_ip_solver_not_fully_connected(): ip = AngularGraphScanMakespanAbsoluteReduced() n = 3 ad_vert = np.zeros([n * n - 1], dtype=tuple) ad_vert[:] = [(i, j) for i in range(3) for j in range(3) if i != 1 or i != j] e_arr = np.triu_indices(n*n-1, 1) edges = [(ad_vert[e_arr[0][i]], ad_vert[e_arr[1][i]]) for i in range(len(e_arr[0]))] random.seed(0) chosen_edges = random.sample(edges, k=math.ceil(len(edges)/2)) g = Graph(ad_vert, chosen_edges) solution = ip.build_ip_and_optimize(g) from utils.visualization import visualize_solution_2d, visualize_graph_2d #visualize_graph_2d(solution.graph) visualize_solution_2d(solution)
def create_random_instance(cls, point_amount, task, bounds=(0, 500), generation=0): points = np.random.random_integers(bounds[0], bounds[1], size=(point_amount, 2)) ad_matrix = np.ones((len(points), len(points))) ad_matrix[np.diag_indices_from(ad_matrix)] = 0 graph = Graph(points, ad_matrix=ad_matrix) return CompleteGraphGenome(graph, bounds=bounds, task=task, generation=generation)
def create_random_instance_fixed_edges(point_amount, edge_amount, bounds=(0, 500), seed=None): gen = np.random.default_rng(seed) points = gen.integers(bounds[0], bounds[1], size=(point_amount, 2)) ad_matrix = np.zeros((len(points), len(points))) tril_indices = np.tril_indices_from(ad_matrix, -1) l = len(tril_indices[0]) chosen = gen.choice(range(l), size=edge_amount, replace=False) chosen_indices = np.zeros(l) chosen_indices[chosen] = 1 ad_matrix[tril_indices] = chosen_indices ad_matrix = ad_matrix + ad_matrix.T graph = Graph(points, ad_matrix=ad_matrix) return graph
def _visualize_edges_2d(graph: Graph, taken_edges=None, already_used=None): if graph.vertices.dtype == np.dtype('O'): graph.vertices = np.array([p for p in graph.vertices]) for edge in graph.edges: plt.plot(graph.vertices[edge][:, 0], graph.vertices[edge][:, 1], color='black', marker=',', alpha=0.3) if already_used: for indices in already_used: edge = np.array([graph.vertices[i] for i in indices]) plt.plot(edge[:, 0], edge[:, 1], "y-") if taken_edges: for indices in taken_edges: edge = np.array([graph.vertices[i] for i in indices]) plt.plot(edge[:, 0], edge[:, 1], "r-")
def convert_graph_to_angular_abstract_graph(graph: Graph, simple_graph=True, return_tripel_edges=False ) -> Graph: """Converts a graph into an abstract angular graph Can be used to calculate a path tsp Arguments: graph {Graph} -- Graph to be converted simple_graph {bool} -- Indicates if graph is simple return_tripel_edges {bool} -- Also return translation for original edges to abstract Returns: Graph -- Converted abstract graph """ # create a vertex for every edge in the original graph # For geometric instances, only one direction of edges is needed vertices = np.array([[u, v] for u, v in graph.edges if u < v]) edges = {} tripel_edges = {} for i, vertex in enumerate(vertices): ran = range(i + 1, len(vertices)) if simple_graph else range(len(vertices)) for j in ran: if j == i: continue other = vertices[j] if np.intersect1d(vertex, other).size > 0: shared_vertex = np.intersect1d(vertex, other) non_shared = np.setdiff1d(np.hstack([vertex, other]), shared_vertex) edges[(i, j)] = get_angle(graph.vertices[shared_vertex], graph.vertices[non_shared[0]], graph.vertices[non_shared[1]]) if return_tripel_edges: from_vertex = np.intersect1d(vertex, non_shared) to_vertex = np.intersect1d(other, non_shared) edge = (*from_vertex, *to_vertex) tripel_edges[(*shared_vertex, *edge)] = (i, j) graph = Graph(vertices, edges.keys(), c=edges) if return_tripel_edges: return (tripel_edges, graph) return graph
def create_children(self, data, mother=None, **kwargs): generation = self.generation try: generation = max(generation, mother.generation) except AttributeError: pass generation = kwargs.pop("generation", generation + 1) vertices, ad_matrix = self._translate_chromosome(data) graph = Graph(V=vertices, ad_matrix=ad_matrix, i_type=self.graph.i_type) return GraphGenome(graph, parents=[self, mother], generation=generation, bounds=self.bounds, task=self.task)
def create_random_instance(point_amount, bounds=(0, 500), edge_chance=0.5, seed=None): gen = np.random.default_rng(seed) points = gen.integers(bounds[0], bounds[1], size=(point_amount, 2)) ad_matrix = np.zeros((len(points), len(points))) tril_indices = np.tril_indices_from(ad_matrix, -1) tril_edges = np.array([ 1 if i < edge_chance else 0 for i in gen.random(size=len(tril_indices[0])) ]) while tril_edges.max() == 0: tril_edges = np.array([ 1 if i < edge_chance else 0 for i in gen.random(size=len(tril_indices[0])) ]) ad_matrix[tril_indices] = tril_edges ad_matrix = ad_matrix + ad_matrix.T graph = Graph(points, ad_matrix=ad_matrix) return graph
def _greedy_order(orders, graph: Graph, solver: Solver, slice, timeout): start_time = time.time() results = [] for orig_order in orders: if time.time() - start_time > timeout: return results, slice arg_ordered = np.argsort(orig_order) ordered = orig_order[arg_ordered] inner_graph = Graph(graph.vertices, graph.edges[arg_ordered]) order, cost = solver.solve(inner_graph, no_sol_return=True) edge_dict = {(o[0], o[1]): i for i, o in enumerate(graph.edges)} order_translate = { edge_dict[(e1, e2)]: i for i, (e1, e2) in enumerate(order) } new_order = np.array( [ordered[order_translate[key]] for key in range(len(orig_order))]) cost = _calculate_cost(solver, graph, new_order) results.append((new_order, -cost)) return results, slice
def _greedy_order(orders, graph: Graph, solver: Solver): results = [] for orig_order in orders: arg_ordered = np.argsort(orig_order) ordered = orig_order[arg_ordered] inner_graph = Graph(graph.vertices, graph.edges[arg_ordered]) order, cost = solver.solve(inner_graph, no_sol_return=True) edge_dict = {(o[0], o[1]): i for i, o in enumerate(graph.edges)} order_translate = { edge_dict[(e1, e2)]: i for i, (e1, e2) in enumerate(order) } new_order = np.array( [ordered[order_translate[key]] for key in range(len(orig_order))]) times, heads = calculate_times(graph.edges[np.argsort(new_order)], graph, return_angle_sum=True) try: assert (graph.edges[np.argsort(new_order)] == order ).all(), "Not all edges are sorted the same" except AssertionError as ar: print(ar) results.append((new_order, -cost)) return results
def create_children(self, data, mother=None, **kwargs): # If no graph creation function is passed just create a fully connected graph generation = self.generation try: generation = max(generation, mother.generation) except AttributeError: pass generation = kwargs.pop("generation", generation + 1) if self.graph_creation_func is None: ad_matrix = np.ones((len(data), len(data))) ad_matrix[np.diag_indices_from(ad_matrix)] = 0 self.graph_creation_func = None graph = Graph(data, ad_matrix=ad_matrix) else: graph = self.graph_creation_func(data) return CompleteGraphGenome( graph, parents=[self, mother], graph_creation_func=self.graph_creation_func, generation=generation, bounds=self.bounds, task=self.task)
test_graphs = [ Graph(V=np.array([[345, 53], [482, 51], [333, 455], [425, 128], [417, 316], [417, 382], [ 65, 101], [235, 207], [ 96, 229], [470, 60], [ 74, 253], [ 76, 478], [491, 473], [166, 176]]), ad_matrix=np.array([[0., 0., 1., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 1., 1., 0., 1., 0., 0., 0., 0., 0.], [1., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 1., 0.], [0., 0., 1., 0., 0., 1., 0., 0., 0., 0., 0., 0., 1., 0.], [0., 1., 0., 0., 1., 0., 0., 1., 0., 0., 0., 0., 0., 1.], [0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 1., 0.], [1., 1., 0., 1., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0.], [0., 0., 1., 0., 0., 0., 0., 0., 1., 0., 1., 0., 1., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 1., 1., 0., 0., 1., 0., 1., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.]]) ), Graph(
def _decode_graph(self, graph): return Graph(np.array(graph["vertices"]), ad_matrix=np.array(graph["ad_matrix"]))
def solve(self, graph: Graph, **kwargs): if graph.costs is None: graph.costs = get_graph_angles(graph) error_message = None self.time_limit = kwargs.pop("time_limit", self.max_time) self.graph = graph try: self.start_time = time.time() genomes = np.zeros(self.pop_size, dtype=object) genomes[:] = [ EdgeOrderGraphGenome(graph, skip_graph_calc=True) for i in range(self.pop_size) ] with concurrent.futures.ThreadPoolExecutor( max_workers=cpu_count()) as executor: self.executor = executor if self.greedy_start: slicings = self._get_slicing(genomes) futures = [ executor.submit( _greedy_order, [ genome.order_numbers for genome in genomes[slicing] ], self.graph, self.sub_solvers[np.random.randint( 0, len(self.sub_solvers))] #self._greedy_order, genomes[slicing],\ #self.sub_solvers[np.random.randint(0, len(self.sub_solvers))] ) for slicing in slicings ] timeout_time = self.time_limit - (time.time() - self.start_time) concurrent.futures.wait(futures, timeout=timeout_time) for future in futures: future.exception() # Start solver genetic_algorithm = GeneticAlgorithm( genomes=genomes, selection=self.selection, mutation=self._mutation, fitness=self._fitness, crossover=self.crossover, callback=self.callback, termCon=self.termination_condition, elitism=self.elitism, mutationChance=self.mutation_chance_genome, mutationChanceGene=self.mutation_chance_gene) last_gen = genetic_algorithm.evolve() except concurrent.futures.TimeoutError as time_error: error_message = str(time_error) last_gen = genetic_algorithm.genomes finally: self.executor = None self.graph = None max_index = np.argmax([gen.solution for gen in last_gen]) arg_sorted = np.argsort(last_gen[max_index].order_numbers) order = graph.edges[arg_sorted].tolist() sol = AngularGraphSolution(graph, time.time() - self.start_time, self.__class__.__name__, self.solution_type, is_optimal=False, order=order, error_message=error_message) return sol
def solve(self, graph: Graph, start_solution=None, **kwargs): time_limit = float(kwargs.pop("time_limit", self.time_limit)) start_time = time.time() if graph.costs is None: angles = get_graph_angles(graph) graph.costs = angles else: angles = [i.copy() for i in graph.costs] solution = self.sub_solver( graph) if start_solution is None else start_solution improvement = True # solution can be AngularGraphSolution or simple edge order best_order = np.array(solution.order) if isinstance( solution, AngularGraphSolution) else np.array(solution) best_cost = self._get_cost(solution, angles) with concurrent.futures.ThreadPoolExecutor( max_workers=cpu_count()) as executor: try: error_message = None with tqdm.tqdm( total=self.max_iterations, desc= f"Iterating local neighborhood. Best Sol: {best_cost}" ) as progressbar: iteration = 0 while (self.max_iterations is None or iteration < self.max_iterations) and\ (time_limit or time.time() - start_time < time_limit) and\ (improvement): improvement = False candidate_order = None candidate_cost = None futures = [ executor.submit(self._inner_solve, [i.copy() for i in angles], i, best_order, best_cost, time_limit, start_time) for i in range(graph.edge_amount) ] timeout = time_limit - (time.time() - start_time) + 10 concurrent.futures.wait(futures, timeout=timeout) for future in futures: if future.done(): order, cost = future.result() if candidate_cost is None or cost < candidate_cost: candidate_cost = cost candidate_order = order if candidate_cost is not None and candidate_cost < best_cost: improvement = True best_cost = candidate_cost best_order = candidate_order progressbar.set_description( f"Iterating local neighborhood. Best Sol: {best_cost}" ) if not improvement: break progressbar.update() iteration += 1 except concurrent.futures.TimeoutError as time_error: error_message = str(time_error) + " in iteration " + str( iteration) sol = AngularGraphSolution(graph, time.time() - start_time, self.__class__.__name__, self.solution_type, is_optimal=False, order=best_order.tolist(), error_message=error_message) return sol
def calculate_graph(self): """(Re-)calculate the graph with new sorted order_numbers """ sorted_indices = np.argsort(self.order_numbers) edges = self.orig_graph.edges[sorted_indices] self.graph = Graph(self.orig_graph.vertices, edges)