def test_initialize(self): """ Tests initialize() method. """ # Double initialization. param_values = deepcopy(self.default_param_values) brkga = BrkgaMpIpr(**param_values) # 1st initialization. brkga.initialize() with self.assertRaises(RuntimeError) as context: # 2nd initialization. brkga.initialize() self.assertEqual( str(context.exception).strip(), "The algorithm is already initialized. " "Please call 'reset()' instead.") # Custom function is not defined. filename = os.path.join(CONFIG_DIR, "custom_bias_function.conf") brkga_params, _ = load_configuration(filename) param_values = deepcopy(self.default_param_values) param_values["params"] = brkga_params brkga = BrkgaMpIpr(**param_values) with self.assertRaises(ValueError) as context: brkga.initialize() self.assertEqual( str(context.exception).strip(), "The bias function is not defined. Call " "set_bias_custom_function() before call initialize().") ######################## # Test without warmstart ######################## param_values = deepcopy(self.default_param_values) param_values["sense"] = Sense.MAXIMIZE params = param_values["params"] brkga = BrkgaMpIpr(**param_values) brkga.initialize() self.assertTrue(brkga._initialized, "Flag 'brkga._initialized' is supposed to be 'True'") self.assertFalse( brkga._reset_phase, "Flag 'brkga._reset_phase' is supposed to be 'False'") self.assertEqual(len(brkga._current_populations), params.num_independent_populations) self.assertEqual(len(brkga._previous_populations), params.num_independent_populations) for i in range(params.num_independent_populations): self.assertEqual(len(brkga._current_populations[i].chromosomes), params.population_size) self.assertEqual(len(brkga._current_populations[i].fitness), params.population_size) self.assertEqual(len(brkga._previous_populations[i].chromosomes), params.population_size) self.assertEqual(len(brkga._previous_populations[i].fitness), params.population_size) self.assertEqual(brkga._current_populations[i].chromosomes, brkga._previous_populations[i].chromosomes) self.assertIsNot(brkga._current_populations[i].chromosomes, brkga._previous_populations[i].chromosomes) correct_order = True for j in range(1, brkga.params.population_size): correct_order &= \ brkga._current_populations[i].fitness[j - 1][0] >= \ brkga._current_populations[i].fitness[j][0] # end for self.assertTrue(correct_order, "incorrect chromosome order") # end for param_values = deepcopy(self.default_param_values) param_values["sense"] = Sense.MINIMIZE params = param_values["params"] brkga = BrkgaMpIpr(**param_values) brkga.initialize() for i in range(params.num_independent_populations): correct_order = True for j in range(1, brkga.params.population_size): correct_order &= \ brkga._current_populations[i].fitness[j - 1][0] <= \ brkga._current_populations[i].fitness[j][0] # end for self.assertTrue(correct_order, "incorrect chromosome order") # end for ######################## # Test with warmstart ######################## local_rng = Random(param_values["seed"]) chromosomes = [ BaseChromosome([ local_rng.random() for _ in range(param_values["chromosome_size"]) ]), BaseChromosome([ local_rng.random() for _ in range(param_values["chromosome_size"]) ]), BaseChromosome([ local_rng.random() for _ in range(param_values["chromosome_size"]) ]) ] param_values = deepcopy(self.default_param_values) param_values["sense"] = Sense.MINIMIZE params = param_values["params"] brkga = BrkgaMpIpr(**param_values) brkga.set_initial_population(chromosomes) brkga.initialize() for i in range(params.num_independent_populations): self.assertEqual(len(brkga._current_populations[i].chromosomes), params.population_size) self.assertEqual(len(brkga._current_populations[i].fitness), params.population_size) self.assertEqual(len(brkga._previous_populations[i].chromosomes), params.population_size) self.assertEqual(len(brkga._previous_populations[i].fitness), params.population_size) self.assertEqual(brkga._current_populations[i].chromosomes, brkga._previous_populations[i].chromosomes) self.assertIsNot(brkga._current_populations[i].chromosomes, brkga._previous_populations[i].chromosomes) # end for old_chr = deepcopy(chromosomes[0]) param_values["decoder"].decode(chromosomes[0], rewrite=True) self.assertNotEqual(chromosomes[0], old_chr) self.assertEqual(brkga._current_populations[0].chromosomes[0], chromosomes[0]) # Create a local chromosome and applied the decoder on it. local_rng = Random(param_values["seed"]) for _ in range(1000): local_rng.random() local_chr = BaseChromosome([ local_rng.random() for _ in range(param_values["chromosome_size"]) ]) param_values["decoder"].decode(local_chr, rewrite=True) # 4th chromosome must be the 1st generated one due to the warmstart. self.assertEqual(brkga._current_populations[0].chromosomes[3], local_chr) ######################## # Test reset phase ######################## param_values = deepcopy(self.default_param_values) params = param_values["params"] brkga = BrkgaMpIpr(**param_values) brkga.initialize() # Create a local RNG and advance it until the same state as the # internal BRKGA RNG after initialization. local_rng = Random(param_values["seed"]) skip = 1000 + params.num_independent_populations * \ params.population_size * brkga.chromosome_size for _ in range(skip): local_rng.random() self.assertEqual(brkga._rng.getstate(), local_rng.getstate()) brkga._reset_phase = True brkga.initialize() # Create a local chromosome and applied the decoder on it. local_chr = BaseChromosome([ local_rng.random() for _ in range(param_values["chromosome_size"]) ]) param_values["decoder"].decode(local_chr, rewrite=True) self.assertEqual(brkga._current_populations[0].chromosomes[0], local_chr)
def main() -> None: """ Proceeds with the optimization. Create to avoid spread `global` keywords around the code. """ args = docopt.docopt(__doc__) # print(args) configuration_file = args["--config_file"] instance_file = args["--instance_file"] seed = int(args["--seed"]) stop_rule = StopRule(args["--stop_rule"]) if stop_rule == StopRule.TARGET: stop_argument = float(args["--stop_arg"]) else: stop_argument = int(args["--stop_arg"]) maximum_time = float(args["--max_time"]) if maximum_time <= 0.0: raise RuntimeError(f"Maximum time must be larger than 0.0. " f"Given {maximum_time}.") perform_evolution = not args["--no_evolution"] ######################################## # Load config file and show basic info. ######################################## brkga_params, control_params = load_configuration(configuration_file) print(f"""------------------------------------------------------ > Experiment started at {datetime.now()} > Instance: {instance_file} > Configuration: {configuration_file} > Algorithm Parameters:""", end="") if not perform_evolution: print("> - Simple multi-start: on (no evolutionary operators)") else: output_string = "" for name, value in vars(brkga_params).items(): output_string += f"\n> -{name} {value}" for name, value in vars(control_params).items(): output_string += f"\n> -{name} {value}" print(output_string) print(f"""> Seed: {seed} > Stop rule: {stop_rule} > Stop argument: {stop_argument} > Maximum time (s): {maximum_time} ------------------------------------------------------""") ######################################## # Load instance and adjust BRKGA parameters ######################################## print(f"\n[{datetime.now()}] Reading TSP data...") instance = TSPInstance(instance_file) print(f"Number of nodes: {instance.num_nodes}") print(f"\n[{datetime.now()}] Generating initial tour...") # Generate a greedy solution to be used as warm start for BRKGA. initial_cost, initial_tour = greedy_tour(instance) print(f"Initial cost: {initial_cost}") ######################################## # Build the BRKGA data structures and initialize ######################################## print(f"\n[{datetime.now()}] Building BRKGA data...") # Usually, it is a good idea to set the population size # proportional to the instance size. brkga_params.population_size = min(brkga_params.population_size, 10 * instance.num_nodes) print(f"New population size: {brkga_params.population_size}") # Build a decoder object. decoder = TSPDecoder(instance) # Chromosome size is the number of nodes. # Each chromosome represents a permutation of nodes. brkga = BrkgaMpIpr(decoder=decoder, sense=Sense.MINIMIZE, seed=seed, chromosome_size=instance.num_nodes, params=brkga_params, evolutionary_mechanism_on=perform_evolution) # To inject the initial tour, we need to create chromosome representing # that solution. First, we create a set of keys to be used in the # chromosome. random.seed(seed) keys = sorted([random.random() for _ in range(instance.num_nodes)]) # Then, we visit each node in the tour and assign to it a key. initial_chromosome = [0] * instance.num_nodes for i in range(instance.num_nodes): initial_chromosome[initial_tour[i]] = keys[i] # Inject the warm start solution in the initial population. brkga.set_initial_population([initial_chromosome]) # NOTE: don't forget to initialize the algorithm. print(f"\n[{datetime.now()}] Initializing BRKGA data...") brkga.initialize() ######################################## # Warm up the script/code ######################################## # To make sure we are timing the runs correctly, we run some warmup # iterations with bogus data. Warmup is always recommended for script # languages. Here, we call the most used methods. print(f"\n[{datetime.now()}] Warming up...") bogus_alg = deepcopy(brkga) bogus_alg.evolve(2) # TODO (ceandrade): warm up path relink functions. # bogus_alg.path_relink(brkga_params.pr_type, brkga_params.pr_selection, # (x, y) -> 1.0, (x, y) -> true, 0, 0.5, 1, 10.0, 1.0) bogus_alg.get_best_fitness() bogus_alg.get_best_chromosome() bogus_alg = None ######################################## # Evolving ######################################## print(f"\n[{datetime.now()}] Evolving...") print("* Iteration | Cost | CurrentTime") best_cost = initial_cost * 2 best_chromosome = initial_chromosome iteration = 0 last_update_time = 0.0 last_update_iteration = 0 large_offset = 0 # TODO (ceandrade): enable the following when path relink is ready. # path_relink_time = 0.0 # num_path_relink_calls = 0 # num_homogenities = 0 # num_best_improvements = 0 # num_elite_improvements = 0 run = True # Main optimization loop. We evolve one generation at time, # keeping track of all changes during such process. start_time = time.time() while run: iteration += 1 # Evolves one iteration. brkga.evolve() # Checks the current results and holds the best. fitness = brkga.get_best_fitness() if fitness < best_cost: last_update_time = time.time() - start_time update_offset = iteration - last_update_iteration if large_offset < update_offset: large_offset = update_offset last_update_iteration = iteration best_cost = fitness best_chromosome = brkga.get_best_chromosome() print(f"* {iteration} | {best_cost:.0f} | {last_update_time:.2f}") # end if # TODO (ceandrade): implement path relink calls here. # Please, see Julia version for that. iter_without_improvement = iteration - last_update_iteration # Check stop criteria. run = not ( (time.time() - start_time > maximum_time) or (stop_rule == StopRule.GENERATIONS and iteration == stop_argument) or (stop_rule == StopRule.IMPROVEMENT and iter_without_improvement >= stop_argument) or (stop_rule == StopRule.TARGET and best_cost <= stop_argument)) # end while total_elapsed_time = time.time() - start_time total_num_iterations = iteration print(f"[{datetime.now()}] End of optimization\n") print(f"Total number of iterations: {total_num_iterations}") print(f"Last update iteration: {last_update_iteration}") print(f"Total optimization time: {total_elapsed_time:.2f}") print(f"Last update time: {last_update_time:.2f}") print(f"Large number of iterations between improvements: {large_offset}") # TODO (ceandrade): enable when path relink is ready. # print(f"\nTotal path relink time: {path_relink_time:.2f}") # print(f"\nTotal path relink calls: {num_path_relink_calls}") # print(f"\nNumber of homogenities: {num_homogenities}") # print(f"\nImprovements in the elite set: {num_elite_improvements}") # print(f"\nBest individual improvements: {num_best_improvements}") ######################################## # Extracting the best tour ######################################## tour = [] for (index, key) in enumerate(best_chromosome): tour.append((key, index)) tour.sort() print(f"\n% Best tour cost: {best_cost:.2f}") print("% Best tour: ", end="") for _, node in tour: print(node, end=" ") print( "\n\nInstance,Seed,NumNodes,TotalIterations,TotalTime," #"TotalPRTime,PRCalls,NumHomogenities,NumPRImprovElite," #"NumPrImprovBest," "LargeOffset,LastUpdateIteration,LastUpdateTime," "Cost") print( f"{basename(instance_file)}," f"{seed},{instance.num_nodes},{total_num_iterations}," f"{total_elapsed_time:.2f}," # f"{path_relink_time:.2f},{num_path_relink_calls}," # f"{num_homogenities},{num_elite_improvements},{num_best_improvements}," f"{large_offset},{last_update_iteration}," f"{last_update_time:.2f},{best_cost:.0f}")
def test_set_initial_population(self): """ Tests set_initial_population() method. """ param_values = deepcopy(self.default_param_values) param_values["chromosome_size"] = 3 param_values["params"].num_independent_populations = 2 brkga = BrkgaMpIpr(**param_values) local_rng = Random(param_values["seed"]) chromosomes = [ BaseChromosome() for _ in range(brkga.params.population_size + 1) ] with self.assertRaises(ValueError) as context: brkga.set_initial_population(chromosomes) self.assertEqual( str(context.exception).strip(), "Number of given chromosomes (11) is large than " "the population size (10)") chromosomes = [ BaseChromosome([ local_rng.random() for _ in range(param_values["chromosome_size"] + 1) ]) ] with self.assertRaises(ValueError) as context: brkga.set_initial_population(chromosomes) self.assertEqual( str(context.exception).strip(), "Error on setting initial population: chromosome 0 " "does not have the required dimension " "(actual size: 4, required size: 3)") chromosomes = [ BaseChromosome([ local_rng.random() for _ in range(param_values["chromosome_size"] - 1) ]) ] with self.assertRaises(ValueError) as context: brkga.set_initial_population(chromosomes) self.assertEqual( str(context.exception).strip(), "Error on setting initial population: chromosome 0 " "does not have the required dimension " "(actual size: 2, required size: 3)") chromosomes = [ BaseChromosome([ local_rng.random() for _ in range(param_values["chromosome_size"] + 1) ]), BaseChromosome([ local_rng.random() for _ in range(param_values["chromosome_size"]) ]), BaseChromosome([ local_rng.random() for _ in range(param_values["chromosome_size"]) ]) ] with self.assertRaises(ValueError) as context: brkga.set_initial_population(chromosomes) self.assertEqual( str(context.exception).strip(), "Error on setting initial population: chromosome 0 " "does not have the required dimension " "(actual size: 4, required size: 3)") chromosomes = [ BaseChromosome([ local_rng.random() for _ in range(param_values["chromosome_size"]) ]), BaseChromosome([ local_rng.random() for _ in range(param_values["chromosome_size"] + 1) ]), BaseChromosome([ local_rng.random() for _ in range(param_values["chromosome_size"]) ]) ] with self.assertRaises(ValueError) as context: brkga.set_initial_population(chromosomes) self.assertEqual( str(context.exception).strip(), "Error on setting initial population: chromosome 1 " "does not have the required dimension " "(actual size: 4, required size: 3)") chromosomes = [ BaseChromosome([ local_rng.random() for _ in range(param_values["chromosome_size"]) ]), BaseChromosome([ local_rng.random() for _ in range(param_values["chromosome_size"]) ]), BaseChromosome([ local_rng.random() for _ in range(param_values["chromosome_size"] + 1) ]) ] with self.assertRaises(ValueError) as context: brkga.set_initial_population(chromosomes) self.assertEqual( str(context.exception).strip(), "Error on setting initial population: chromosome 2 " "does not have the required dimension " "(actual size: 4, required size: 3)") chromosomes = [ BaseChromosome([ local_rng.random() for _ in range(param_values["chromosome_size"]) ]) ] brkga.set_initial_population(chromosomes) self.assertEqual(brkga._current_populations[0].chromosomes[0], chromosomes[0]) self.assertIsNot(brkga._current_populations[0].chromosomes[0], chromosomes[0]) chromosomes[0] = BaseChromosome([0.1111, 0.2222, 0.3333]) self.assertNotEqual(brkga._current_populations[0].chromosomes[0], chromosomes[0]) chromosomes = [ BaseChromosome([ local_rng.random() for _ in range(param_values["chromosome_size"]) ]), BaseChromosome([ local_rng.random() for _ in range(param_values["chromosome_size"]) ]), BaseChromosome([ local_rng.random() for _ in range(param_values["chromosome_size"]) ]) ] brkga.set_initial_population(chromosomes) self.assertEqual(len(brkga._current_populations[0].chromosomes), len(chromosomes)) self.assertEqual(brkga._current_populations[0].chromosomes, chromosomes) self.assertIsNot(brkga._current_populations[0].chromosomes, chromosomes) self.assertEqual(brkga._initial_population, True)