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 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

View File

@ -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()

View File

@ -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)