From 985fac54a29a8b3f56affe0c7d183e3ce1127021 Mon Sep 17 00:00:00 2001 From: George Lacey Date: Sun, 1 Oct 2017 20:45:49 +0100 Subject: [PATCH] Revamp algorithm - Implement variable elitism and crossover - Don't crossover all members --- src/individual.py | 21 ++++++++++++- src/main.py | 12 +++++--- src/population.py | 75 +++++++++++++++++++++++++++++++---------------- 3 files changed, 77 insertions(+), 31 deletions(-) diff --git a/src/individual.py b/src/individual.py index 5e27443..571a210 100644 --- a/src/individual.py +++ b/src/individual.py @@ -1,14 +1,23 @@ -from math import pow, sin, pi, cos +from math import pow, sin, pi from random import Random +from enum import Enum rand = Random() +class Flag(Enum): + UNSET = 1, + PARENT = 2, + ELITE = 3, + PERSIST = 4 + + class Individual(object): def __init__(self): self.x = self.get_rand_param() self.y = self.get_rand_param() + self.flag = Flag(Flag.UNSET) @classmethod def from_params(cls, x, y): @@ -42,3 +51,13 @@ class Individual(object): @staticmethod def get_rand_param(): return rand.uniform(0, 1) + + def set_flag(self, flag): + if self.flag is Flag.UNSET: + self.flag = flag + return True + else: + return False + + def reset_flag(self): + self.flag = Flag.UNSET diff --git a/src/main.py b/src/main.py index 3ef1b6c..b19beb7 100644 --- a/src/main.py +++ b/src/main.py @@ -22,13 +22,17 @@ pop = Population(args.pop) for i in range(0, args.iter): cls() print("Epoch: %d " % i) - pop.advance_generation() - best = pop.best_fitness() - best_fit.append(pop.avg_fitness()) + elite = round(args.pop * .1) + crossover = round(args.pop * .8) + pop.advance_generation(elite, crossover) + best_fit.append(pop.best_fitness().fitness()) #input("...") sleep(args.wait) +print("Best: ") +print(pop.best_fitness()) + plt.plot(best_fit) plt.xlabel("Epoch") plt.ylabel("Fitness") -plt.savefig("fig.png") +plt.show() diff --git a/src/population.py b/src/population.py index 19a9ebf..2325f31 100644 --- a/src/population.py +++ b/src/population.py @@ -1,4 +1,4 @@ -from individual import Individual +from individual import Individual, Flag from random import Random rand = Random() @@ -24,14 +24,15 @@ class Population(object): (best.x, best.y, best.fitness()) return return_string - def fitness_function(self): - for member in self.members: - member.fitness() - - def total_fitness(self): + def total_fitness(self, members=None): total = 0 - for member in self.members: + + if members is None: + members = self.members + + for member in members: total += member.fitness() + return total def avg_fitness(self): @@ -45,33 +46,55 @@ class Population(object): best_member = member return best_member - def roulette(self, divisor=1): - total = self.total_fitness() / divisor + def roulette(self, members=None): + total = self.total_fitness(members=members) position = rand.uniform(0, total) - for member in self.members: - position -= member.fitness() / divisor + if members is None: + members = self.members + + for member in members: + position -= member.fitness() if position <= 0: - #print(member) - return member + return member.set_flag(Flag.PARENT) def mutate(self, chance): for member in self.members: if rand.random() < chance/100: member.mutate() - def advance_generation(self): - # todo: elitism - # todo: don't crossover all - self.fitness_function() - parents = list() - for i in range(0, len(self.members)): - parents.append(self.roulette()) + def elite(self, amount): + return sorted(self.members, key=lambda x: x.fitness(), + reverse=True)[:amount] + + def advance_generation(self, n_elite, n_crossover): + + for member in self.members: + member.reset_flag() + + # elitism + for member in self.elite(n_elite): + member.flag = Flag.ELITE + + potential_parents = [member for member in self.members if member.flag + is Flag.UNSET] + # parent + for ind in range(0, n_crossover): + while not self.roulette(members=potential_parents): + pass + + # persist + [member.set_flag(Flag.PERSIST) for member in self.members + if member.flag is Flag.UNSET] + + parents = [member for member in self.members if member.flag is + Flag.PARENT] + + for ind in range(0, len(parents), 2): + if ind is len(parents) - 1: + parents[ind].set_flag(Flag.PERSIST) + else: + parents[ind], parents[ind + 1] = parents[ind]\ + .crossover(parents[ind + 1]) - children = list() - for i in range(0, len(parents) - 1, 2): - one, two = self.members[i].crossover(parents[i + 1]) - children.append(one) - children.append(two) - self.members = children self.mutate(2)