1 | import time |
---|
2 | from abc import ABC |
---|
3 | from typing import List |
---|
4 | |
---|
5 | from ..structures.individual import Individual |
---|
6 | from ..structures.population import PopulationStructures |
---|
7 | from .experiment_abc import ExperimentABC |
---|
8 | |
---|
9 | |
---|
10 | class ExperimentIslands(ExperimentABC, ABC): |
---|
11 | |
---|
12 | def __init__(self, popsize, hof_size, number_of_populations, migration_interval, save_only_best) -> None: |
---|
13 | super().__init__(popsize=popsize, hof_size=hof_size, save_only_best=save_only_best) |
---|
14 | self.populations=[] |
---|
15 | self.number_of_populations=number_of_populations |
---|
16 | self.migration_interval=migration_interval |
---|
17 | |
---|
18 | def migrate_populations(self): |
---|
19 | print("Performing base migration") |
---|
20 | pool_of_all_individuals = [] |
---|
21 | for p in self.populations: |
---|
22 | pool_of_all_individuals.extend(p.population) |
---|
23 | print(f"Pool of individuals: {len(pool_of_all_individuals)}") |
---|
24 | sorted_individuals = sorted( |
---|
25 | pool_of_all_individuals, key=lambda x: x.rawfitness) |
---|
26 | print("Best individual for new islands:") |
---|
27 | for i in range(self.number_of_populations): |
---|
28 | shift = i*self.popsize |
---|
29 | self.populations[i].population = sorted_individuals[shift:shift+self.popsize] |
---|
30 | print(i, self.populations[i].population[-1].rawfitness) |
---|
31 | |
---|
32 | def initialize_evolution(self, initialgenotype): |
---|
33 | self.current_generation = 0 |
---|
34 | self.time_elapsed = 0 |
---|
35 | self.stats = [] # stores the best individuals, one from each generation |
---|
36 | initial_individual = Individual() |
---|
37 | initial_individual.set_and_evaluate(initialgenotype, self.evaluate) |
---|
38 | self.stats.append(initial_individual.rawfitness) |
---|
39 | [self.populations.append(PopulationStructures(initial_individual=initial_individual, |
---|
40 | popsize=self.popsize)) |
---|
41 | for _ in range(self.number_of_populations)] |
---|
42 | |
---|
43 | def get_state(self): |
---|
44 | return [self.time_elapsed, self.current_generation, self.populations, self.hof, self.stats] |
---|
45 | |
---|
46 | def set_state(self, state): |
---|
47 | self.time_elapsed, self.current_generation, self.populations, hof_, self.stats = state |
---|
48 | # sorting: ensure that we add from worst to best so all individuals are added to HOF |
---|
49 | for h in sorted(hof_, key=lambda x: x.rawfitness): |
---|
50 | self.hof.add(h) |
---|
51 | |
---|
52 | def evolve(self, hof_savefile, generations, initialgenotype, pmut, pxov, tournament_size): |
---|
53 | file_name = self.get_state_filename(hof_savefile) |
---|
54 | state = self.load_state(file_name) |
---|
55 | if state is not None: # loaded state from file |
---|
56 | # saved generation has been completed, start with the next one |
---|
57 | self.current_generation += 1 |
---|
58 | print("...Resuming from saved state: population size = %d, hof size = %d, stats size = %d, generation = %d/%d" % (len(self.populations[0].population), len( |
---|
59 | self.hof), len(self.stats), self.current_generation, generations)) # self.current_generation (and g) are 0-based, parsed_args.generations is 1-based |
---|
60 | else: |
---|
61 | self.initialize_evolution(initialgenotype) |
---|
62 | time0 = time.process_time() |
---|
63 | for g in range(self.current_generation, generations): |
---|
64 | for p in self.populations: |
---|
65 | p.population = self.make_new_population( |
---|
66 | p.population, pmut, pxov, tournament_size) |
---|
67 | |
---|
68 | if g % self.migration_interval == 0: |
---|
69 | print("---------Start of migration-------") |
---|
70 | self.migrate_populations() |
---|
71 | print("---------End of migration---------") |
---|
72 | |
---|
73 | pool_of_all_individuals = [] |
---|
74 | [pool_of_all_individuals.extend(p.population) |
---|
75 | for p in self.populations] |
---|
76 | self.update_stats(g, pool_of_all_individuals) |
---|
77 | if hof_savefile is not None: |
---|
78 | self.current_generation = g |
---|
79 | self.time_elapsed += time.process_time() - time0 |
---|
80 | self.save_state(file_name) |
---|
81 | |
---|
82 | if hof_savefile is not None: |
---|
83 | self.save_genotypes(hof_savefile) |
---|
84 | |
---|
85 | return self.hof, self.stats |
---|
86 | |
---|
87 | @staticmethod |
---|
88 | def get_args_for_parser(): |
---|
89 | parser = ExperimentABC.get_args_for_parser() |
---|
90 | |
---|
91 | parser.add_argument("-islands",type=int, default=5, |
---|
92 | help="Number of subpopulations (islands)") |
---|
93 | parser.add_argument("-generations_migration",type=int, default=10, |
---|
94 | help="Number of generations separating migration events when genotypes migrate between subpopulations (islands)") |
---|
95 | return parser |
---|