Revamp algorithm

- Implement variable elitism and crossover
- Don't crossover all members
This commit is contained in:
George Lacey 2017-10-01 20:45:49 +01:00
parent 8956dfefed
commit 985fac54a2
3 changed files with 77 additions and 31 deletions

View File

@ -1,14 +1,23 @@
from math import pow, sin, pi, cos from math import pow, sin, pi
from random import Random from random import Random
from enum import Enum
rand = Random() rand = Random()
class Flag(Enum):
UNSET = 1,
PARENT = 2,
ELITE = 3,
PERSIST = 4
class Individual(object): class Individual(object):
def __init__(self): def __init__(self):
self.x = self.get_rand_param() self.x = self.get_rand_param()
self.y = self.get_rand_param() self.y = self.get_rand_param()
self.flag = Flag(Flag.UNSET)
@classmethod @classmethod
def from_params(cls, x, y): def from_params(cls, x, y):
@ -42,3 +51,13 @@ class Individual(object):
@staticmethod @staticmethod
def get_rand_param(): def get_rand_param():
return rand.uniform(0, 1) 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

View File

@ -22,13 +22,17 @@ pop = Population(args.pop)
for i in range(0, args.iter): for i in range(0, args.iter):
cls() cls()
print("Epoch: %d " % i) print("Epoch: %d " % i)
pop.advance_generation() elite = round(args.pop * .1)
best = pop.best_fitness() crossover = round(args.pop * .8)
best_fit.append(pop.avg_fitness()) pop.advance_generation(elite, crossover)
best_fit.append(pop.best_fitness().fitness())
#input("...") #input("...")
sleep(args.wait) sleep(args.wait)
print("Best: ")
print(pop.best_fitness())
plt.plot(best_fit) plt.plot(best_fit)
plt.xlabel("Epoch") plt.xlabel("Epoch")
plt.ylabel("Fitness") plt.ylabel("Fitness")
plt.savefig("fig.png") plt.show()

View File

@ -1,4 +1,4 @@
from individual import Individual from individual import Individual, Flag
from random import Random from random import Random
rand = Random() rand = Random()
@ -24,14 +24,15 @@ class Population(object):
(best.x, best.y, best.fitness()) (best.x, best.y, best.fitness())
return return_string return return_string
def fitness_function(self): def total_fitness(self, members=None):
for member in self.members:
member.fitness()
def total_fitness(self):
total = 0 total = 0
for member in self.members:
if members is None:
members = self.members
for member in members:
total += member.fitness() total += member.fitness()
return total return total
def avg_fitness(self): def avg_fitness(self):
@ -45,33 +46,55 @@ class Population(object):
best_member = member best_member = member
return best_member return best_member
def roulette(self, divisor=1): def roulette(self, members=None):
total = self.total_fitness() / divisor total = self.total_fitness(members=members)
position = rand.uniform(0, total) position = rand.uniform(0, total)
for member in self.members: if members is None:
position -= member.fitness() / divisor members = self.members
for member in members:
position -= member.fitness()
if position <= 0: if position <= 0:
#print(member) return member.set_flag(Flag.PARENT)
return member
def mutate(self, chance): def mutate(self, chance):
for member in self.members: for member in self.members:
if rand.random() < chance/100: if rand.random() < chance/100:
member.mutate() member.mutate()
def advance_generation(self): def elite(self, amount):
# todo: elitism return sorted(self.members, key=lambda x: x.fitness(),
# todo: don't crossover all reverse=True)[:amount]
self.fitness_function()
parents = list() def advance_generation(self, n_elite, n_crossover):
for i in range(0, len(self.members)):
parents.append(self.roulette()) 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) self.mutate(2)