From 300678156cb960c5f93bcc618aad273ff3db7790 Mon Sep 17 00:00:00 2001 From: George Lacey Date: Fri, 13 Oct 2017 16:20:58 +0100 Subject: [PATCH] Implement tournament selection --- src/individual.py | 3 +-- src/lifecycle.py | 14 +++++++++++--- src/main.py | 15 +++++++-------- src/population.py | 38 +++++++++++++++++++++++++------------- 4 files changed, 44 insertions(+), 26 deletions(-) diff --git a/src/individual.py b/src/individual.py index c1e35b2..9beb145 100644 --- a/src/individual.py +++ b/src/individual.py @@ -33,8 +33,7 @@ class Individual(object): return one, two def mutate(self): - rand.randint(1, 10) - if rand.randint(1, 10) % 2 == 0: + if rand.random() < 0.5: self.x = self.get_rand_param() else: self.y = self.get_rand_param() diff --git a/src/lifecycle.py b/src/lifecycle.py index 0f71a25..d59805f 100644 --- a/src/lifecycle.py +++ b/src/lifecycle.py @@ -9,14 +9,21 @@ class Lifecycle(object): self.population = Population(self.params["population_size"]) self.best_fit = list() self.average_fit = list() + self.iterations = 0 def start(self): - for epoch in range(0, self.params["iter"]): + for epoch in range(0, self.params["epochs"]): + self.iterations += 1 elite = self.params["elite"] crossover = self.params["crossover"] - self.population.advance_generation(elite, crossover) + self.population.advance_generation(elite, crossover_rate=crossover, n_arena=4) self.best_fit.append(self.population.best_fitness()) self.average_fit.append(self.population.avg_fitness()) + if epoch > 50: + recent_best = self.best_fit[-50:] + if max(recent_best) - min(recent_best) < 0.02: + break + def best_fitness(self): return self.population.best_fitness() @@ -33,4 +40,5 @@ class Lifecycle(object): def get_csv(self): id = {'id': self.id} best_fitness = {'best_fit': self.best_fitness()} - return {**id, **best_fitness, **self.params} + iter = {'iter': self.iterations} + return {**id, **best_fitness, **self.params, **iter} diff --git a/src/main.py b/src/main.py index d98859d..aadcda6 100644 --- a/src/main.py +++ b/src/main.py @@ -8,14 +8,15 @@ import time param_example = {'population_size': 10, 'elite': round(0.1 * 10), 'crossover': round(0.6 * 10), - 'iter': 1000} + 'epochs': 1000, + 'iter': 0} -def gen_param(pop, elite, crossover, iter): +def gen_param(pop, elite, crossover, epochs): return {'population_size': pop, 'elite': round(elite * pop), 'crossover': round(crossover * pop), - 'iter': iter} + 'epochs': epochs} outputs = Queue() @@ -39,9 +40,9 @@ instances = list() id = 0 -for i in [x * 0.01 for x in range(20, 80, 5)]: +for i in [x * 1 for x in range(0, 100)]: instances.append(Process(name="Thread-%d" % i, target=run_instance, - args=[Lifecycle(id, gen_param(20, 0.1, i, 50)), outputs])) + args=[Lifecycle(id, gen_param(50, 0, 0.5, 500)), outputs])) id += 1 start_time = time.time() @@ -50,7 +51,7 @@ for instance in instances: instance.start() while len([t for t in instances if t.is_alive()]) != 0: - print("here") + print("Processes left: %d" % len([t for t in instances if t.is_alive()])) sleep(1) end_time = time.time() @@ -61,5 +62,3 @@ while not outputs.empty(): output_file.close() print("Execution took %f seconds" % (end_time - start_time)) - -#ga.generate_graph(show=True) diff --git a/src/population.py b/src/population.py index 1589df7..cf7ad8e 100644 --- a/src/population.py +++ b/src/population.py @@ -72,7 +72,7 @@ class Population(object): return sorted_members - def advance_generation(self, n_elite, n_crossover): + def advance_generation(self, n_elite, crossover_rate=0.5, n_arena=4): new_generation = list() # elitism @@ -80,25 +80,37 @@ class Population(object): new_generation.append(member) # parent - for ind in range(0, n_crossover): - parent_one = self.roulette() - parent_two = self.roulette() - x, y = parent_one.crossover(parent_two) - new_generation.append(x) - new_generation.append(y) + # for ind in range(0, n_crossover): + # self.roulette_crossover(new_generation) while len(new_generation) < len(self.members): - new_generation.append(self.roulette()) + if n_arena > 0: + x, y = self.tournament_selection(n_arena, crossover_rate) + else: + x, y = self.roulette_crossover() + + new_generation.append(x) + new_generation.append(y) self.members = new_generation self.mutate(10) - self.check_termination() - def remove_member(self, member): self.members.remove(member) - def check_termination(self): - # todo: complete - pass + def roulette_crossover(self): + parent_one = self.roulette() + parent_two = self.roulette() + return parent_one.crossover(parent_two) + + def tournament_selection(self, arena_size, rate): + parents = list() + for i in range(arena_size): + parents.append(self.members[rand.randint(0, len(self.members) - 1)]) + parents = sorted(parents, key=lambda x: x.fitness(), reverse=True) + + if rand.random() < rate: + return parents[0].crossover(parents[1]) + else: + return parents[0], parents[1]