Skip to content

Commit

Permalink
begun adding tests and bug fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
100 committed Jun 19, 2017
1 parent e5a130e commit e2f8667
Show file tree
Hide file tree
Showing 11 changed files with 117 additions and 37 deletions.
32 changes: 19 additions & 13 deletions library/EvolutionaryAlgorithm.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from abc import ABCMeta, abstractmethod
from copy import deepcopy
from random import randint, random, shuffle
from random import random, shuffle


class EvolutionaryAlgorithm:
Expand Down Expand Up @@ -131,15 +131,19 @@ def _select_n(self, n):
"""
shuffle(self.population)
total_fitness = sum(self.fitnesses)
probs = list([self._fitness(x) / total_fitness for x in self.population])
if total_fitness != 0:
probs = list([self._fitness(x) / total_fitness for x in self.population])
else:
return self.population[0:n]
res = []
for _ in probs:
for _ in range(n):
r = random()
sum = 0
sum_ = 0
for i, x in enumerate(probs):
sum += probs[i]
if r < sum:
res.add(deepcopy(self.population[i]))
sum_ += probs[i]
if r <= sum_:
res.append(deepcopy(self.population[i]))
break
return res

@abstractmethod
Expand All @@ -163,31 +167,33 @@ def _mutate(self, member):
"""
pass

def evolutionary_algorithm(self, verbose=True):
def run(self, verbose=True):
"""
Conducts evolutionary algorithm
:param verbose: indicates whether or not to print progress regularly
:return: best state and best objective function value
"""
num_copy = int((1 - self.crossover_rate) * len(self.population))
num_crossover = len(self.population) - num_copy
self._clear()
self.population = self._initial_population()
self._populate_fitness()
self.best_member, self.best_fitness = self._most_fit()
num_copy = max(int((1 - self.crossover_rate) * len(self.population)), 2)
num_crossover = len(self.population) - num_copy
for i in range(self.max_steps):
self.cur_steps += 1

if (i % 100 == 0) and verbose:
if ((i + 1) % 100 == 0) and verbose:
print self

self._populate_fitness()
self.population = self._select_n(num_copy)
self._populate_fitness()

parents = self._select_n(2)
for _ in range(num_crossover):
self.population.append(self._crossover(*parents))

self.population = list([self.mutate(x) for x in self.population])
self.population = list([self._mutate(x) for x in self.population])
self._populate_fitness()

best_member, best_fitness = self._most_fit()
Expand Down
33 changes: 20 additions & 13 deletions library/GeneticAlgorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,19 @@ def _select_n(self, n):
"""
shuffle(self.population)
total_fitness = sum(self.fitnesses)
probs = list([self._fitness(x) / total_fitness for x in self.population])
if total_fitness != 0:
probs = list([self._fitness(x) / total_fitness for x in self.population])
else:
return self.population[0:n]
res = []
for _ in probs:
for _ in range(n):
r = random()
sum = 0
sum_ = 0
for i, x in enumerate(probs):
sum += probs[i]
if r < sum:
res.add(deepcopy(self.population[i]))
sum_ += probs[i]
if r <= sum_:
res.append(deepcopy(self.population[i]))
break
return res

def _crossover(self, parent1, parent2):
Expand All @@ -151,7 +155,7 @@ def _crossover(self, parent1, parent2):
:param parent2: a member
:return: member made by combining elements of both parents
"""
partition = randint(0, len(self.population[0] - 1))
partition = randint(0, len(self.population[0]) - 1)
return parent1[0:partition] + parent2[partition:]

def _mutate(self, member):
Expand All @@ -164,32 +168,35 @@ def _mutate(self, member):
if self.mutation_rate >= random():
idx = randint(0, len(member) - 1)
member[idx] = 1 if member[idx] == 0 else 1
return member

def genetic_algorithm(self, verbose=True):
def run(self, verbose=True):
"""
Conducts genetic algorithm
:param verbose: indicates whether or not to print progress regularly
:return: best state and best objective function value
"""
num_copy = int((1 - self.crossover_rate) * len(self.population))
num_crossover = len(self.population) - num_copy
self._clear()
self.population = self._initial_population()
self._populate_fitness()
self.best_member, self.best_fitness = self._most_fit()
num_copy = max(int((1 - self.crossover_rate) * len(self.population)), 2)
num_crossover = len(self.population) - num_copy
for i in range(self.max_steps):
self.cur_steps += 1

if (i % 100 == 0) and verbose:
if ((i + 1) % 100 == 0) and verbose:
print self

self._populate_fitness()
self.population = self._select_n(num_copy)
self._populate_fitness()

parents = self._select_n(2)
for _ in range(num_crossover):
self.population.append(self._crossover(*parents))

self.population = list([self.mutate(x) for x in self.population])
self.population = list([self._mutate(x) for x in self.population])
self._populate_fitness()

best_member, best_fitness = self._most_fit()
Expand Down
13 changes: 7 additions & 6 deletions library/HarmonySearch.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def _clear(self):
@abstractmethod
def _random_harmony(self):
"""
Generates a list of random harmonies, each represented as a list of floats
Generates a random harmony, represented as a list of floats
:return: list of harmonies
"""
Expand Down Expand Up @@ -127,33 +127,34 @@ def _best_score(self):
"""
return argmax(self.scores)

def harmony_search(self, verbose=True):
def run(self, verbose=True):
"""
Conducts harmony search
:param verbose: indicates whether or not to print progress regularly
:return: best state and objective function value of best state
"""
self._clear()
self._score_all()
for i in range(self.max_steps):
self.cur_steps += 1

if (i % 100 == 0) and verbose:
if ((i + 1) % 100 == 0) and verbose:
print self

self._score_all()

selected = [] * len(self.memory[0])
selected = [0.] * len(self.memory[0])
for i in range(len(selected)):
if self.hmcr >= random():
selected_component = choice(self.memory)[i]
if self.par >= random():
selected_component += uniform(-1, 1) * self.fw
else:
selected_component = self._random_harmony()[i]
selected.append(selected_component)
selected[i] = selected_component

if self._score(selected_component) > self.score(self._worst_score()):
if self._score(selected) > self._score(self.memory[self._worst_score()]):
self.memory[self._worst_score()] = selected
self.scores[self._worst_score()] = self._score(selected)

Expand Down
2 changes: 1 addition & 1 deletion library/ParticleSwarm.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def _global_best(self):
if min(self.scores) < self.global_best[0][0]:
self.global_best = array([self.pos[argmin(self.scores)],] * self.swarm_size)

def swarm(self, verbose=True):
def run(self, verbose=True):
"""
Conducts particle swarm optimization
Expand Down
2 changes: 1 addition & 1 deletion library/SimulatedAnnealing.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def _accept_neighbor(self, neighbor):
p = exp(self._energy(self.current_state) - self._energy(neighbor)) / self.current_temp
return True if p >= 1 else p >= random()

def anneal(self, verbose=True):
def run(self, verbose=True):
"""
Conducts simulated annealing
Expand Down
4 changes: 2 additions & 2 deletions library/StochasticHillClimb.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ def _accept_neighbor(self, neighbor):
p = 1. / (1 + (exp(self._objective(self.current_state) - self._objective(neighbor)) / self.temp))
return True if p >= 1 else p >= random()

def anneal(self, verbose=True):
def run(self, verbose=True):
"""
Conducts simulated annealing
Conducts hill climb
:param verbose: indicates whether or not to print progress regularly
:return: best state and best objective function value
Expand Down
2 changes: 1 addition & 1 deletion library/TabuSearch.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def _best(self, neighborhood):
"""
return neighborhood[argmax([self._score(x) for x in neighborhood])]

def tabu_search(self, verbose=True):
def run(self, verbose=True):
"""
Conducts tabu search
Expand Down
File renamed without changes.
30 changes: 30 additions & 0 deletions tests/test_evolutionary_algorithm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from random import choice, randint, random
from string import lowercase
from library.EvolutionaryAlgorithm import EvolutionaryAlgorithm


class Algorithm(EvolutionaryAlgorithm):
"""
Tries to get a randomly-generated string to match string "clout"
"""
def _initial_population(self):
return list(''.join([choice(lowercase) for _ in range(5)]) for _ in range(50))

def _fitness(self, member):
return float(sum(member[i] == "clout"[i] for i in range(5)))

def _crossover(self, parent1, parent2):
partition = randint(0, len(self.population[0]) - 1)
return parent1[0:partition] + parent2[partition:]

def _mutate(self, member):
if self.mutation_rate >= random():
member = list(member)
member[randint(0,4)] = choice(lowercase)
member = ''.join(member)
return member


def test_algorithm():
algorithm = Algorithm(.5, .7, 500, max_fitness=None)
algorithm.run()
18 changes: 18 additions & 0 deletions tests/test_genetic_algorithm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from random import choice
from library.GeneticAlgorithm import GeneticAlgorithm


class Algorithm(GeneticAlgorithm):
"""
Tries to get a randomly-generated string to match 000111
"""
def _initial_population(self):
return list(list([choice([0, 1]) for _ in range(6)]) for _ in range(50))

def _fitness(self, member):
return float(sum(member[i] == [0,0,0,1,1,1][i] for i in range(6)))


def test_algorithm():
algorithm = Algorithm(.5, .7, 500, max_fitness=None)
algorithm.run()
18 changes: 18 additions & 0 deletions tests/test_harmony_search.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from random import uniform
from library.HarmonySearch import HarmonySearch


class Algorithm(HarmonySearch):
"""
Tries to get a randomly-generated list to match [.1, .2, .3, .2, .1]
"""
def _random_harmony(self):
return list([uniform(0, 1) for _ in range(5)])

def _score(self, member):
return 1./ sum(abs(member[i] - [.1, .2, .3, .2, .1][i]) for i in range(5))


def test_algorithm():
algorithm = Algorithm(50, .5, .3, .01, 2000, max_score=None)
algorithm.run()

0 comments on commit e2f8667

Please sign in to comment.