Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Implementation: Genetic Algorithm (Fixing Build) #501

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Apr 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 77 additions & 100 deletions search.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,18 @@
then create problem instances and solve them with calls to the various search
functions."""

import bisect
import math
import random
import string
import sys
from collections import defaultdict

from fuzzywuzzy import fuzz

from grid import distance
from utils import (
is_in, argmin, argmax_random_tie, probability,
memoize, print_table, DataFile, Stack,
is_in, argmin, argmax, argmax_random_tie, probability, weighted_sampler,
weighted_sample_with_replacement, memoize, print_table, DataFile, Stack,
FIFOQueue, PriorityQueue, name
)
from grid import distance

from collections import defaultdict
import math
import random
import sys
import bisect

infinity = float('inf')

Expand Down Expand Up @@ -572,94 +569,74 @@ def LRTA_cost(self, s, a, s1, H):
# Genetic Algorithm


class GAState:
def __init__(self, length):
self.string = ''.join(random.choice(string.ascii_letters)
for _ in range(length))
self.fitness = -1


def ga(in_str=None, population=20, generations=10000):
in_str_len = len(in_str)
individuals = init_individual(population, in_str_len)

for generation in range(generations):

individuals = fitness(individuals, in_str)
individuals = selection(individuals)
individuals = crossover(individuals, population, in_str_len)

if any(individual.fitness >= 90 for individual in individuals):
"""
individuals[0] is the individual with the highest fitness,
because individuals is sorted in the selection function.
Thus we return the individual with the highest fitness value,
among the individuals whose fitness is equal to or greater
than 90.
"""

return individuals[0]

individuals = mutation(individuals, in_str_len)

"""
sufficient number of generations have passed and the individuals
could not evolve to match the desired fitness value.
thus we return the fittest individual among the individuals.
Since individuals are sorted according to their fitness
individuals[0] is the fittest.
"""
return individuals[0]


def init_individual(population, length):
return [GAState(length) for _ in range(population)]


def fitness(individuals, in_str):
for individual in individuals:
individual.fitness = fuzz.ratio(individual.string, in_str) # noqa

return individuals


def selection(individuals):
individuals = sorted(
individuals, key=lambda individual: individual.fitness, reverse=True)

individuals = individuals[:int(0.2 * len(individuals))]
return individuals


def crossover(individuals, population, in_str_len):
offspring = []
for _ in range(int((population - len(individuals)) / 2)):
parent1 = random.choice(individuals)
parent2 = random.choice(individuals)
child1 = GAState(in_str_len)
child2 = GAState(in_str_len)
split = random.randint(0, in_str_len)
child1.string = parent1.string[0:split] + parent2.string[
split:in_str_len]
child2.string = parent2.string[0:split] + parent1.string[
split:in_str_len]
offspring.append(child1)
offspring.append(child2)

individuals.extend(offspring)
return individuals


def mutation(individuals, in_str_len):
for individual in individuals:

for idx, param in enumerate(individual.string):
if random.uniform(0.0, 1.0) <= 0.1:
individual.string = individual.string[0:idx] \
+ random.choice(string.ascii_letters) \
+ individual.string[idx + 1:in_str_len]

return individuals
def genetic_search(problem, fitness_fn, ngen=1000, pmut=0.1, n=20):
"""Call genetic_algorithm on the appropriate parts of a problem.
This requires the problem to have states that can mate and mutate,
plus a value method that scores states."""

# NOTE: This is not tested and might not work.
# TODO: Use this function to make Problems work with genetic_algorithm.

s = problem.initial_state
states = [problem.result(s, a) for a in problem.actions(s)]
random.shuffle(states)
return genetic_algorithm(states[:n], problem.value, ngen, pmut)


def genetic_algorithm(population, fitness_fn, gene_pool=['0', '1'], f_thres=None, ngen=1000, pmut=0.1):
"""[Figure 4.8]"""
for i in range(ngen):
new_population = []
fitnesses = map(fitness_fn, population)
random_selection = weighted_sampler(population, fitnesses)
for j in range(len(population)):
x = random_selection()
y = random_selection()
child = reproduce(x, y)
if random.uniform(0, 1) < pmut:
child = mutate(child, gene_pool)
new_population.append(child)

population = new_population

if f_thres:
fittest_individual = argmax(population, key=fitness_fn)
if fitness_fn(fittest_individual) >= f_thres:
return fittest_individual

return argmax(population, key=fitness_fn)


def init_population(pop_number, gene_pool, state_length):
"""Initializes population for genetic algorithm
pop_number : Number of individuals in population
gene_pool : List of possible values for individuals
(char only)
state_length: The length of each individual"""
g = len(gene_pool)
population = []
for i in range(pop_number):
new_individual = ''.join([gene_pool[random.randrange(0, g)]
for j in range(state_length)])
population.append(new_individual)

return population


def reproduce(x, y):
n = len(x)
c = random.randrange(1, n)
return x[:c] + y[c:]


def mutate(x, gene_pool):
n = len(x)
g = len(gene_pool)
c = random.randrange(0, n)
r = random.randrange(0, g)

new_gene = gene_pool[r]
return x[:c] + new_gene + x[c+1:]

# _____________________________________________________________________________
# The remainder of this file implements examples for the search algorithms.
Expand Down
39 changes: 39 additions & 0 deletions tests/test_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,45 @@ def test_LRTAStarAgent():
assert my_agent('State_5') is None


def test_genetic_algorithm():
# Graph coloring
edges = {
'A': [0, 1],
'B': [0, 3],
'C': [1, 2],
'D': [2, 3]
}

population = init_population(8, ['0', '1'], 4)

def fitness(c):
return sum(c[n1] != c[n2] for (n1, n2) in edges.values())

solution = genetic_algorithm(population, fitness)
assert solution == "0101" or solution == "1010"

# Queens Problem
population = init_population(100, [str(i) for i in range(8)], 8)

def fitness(q):
non_attacking = 0
for row1 in range(len(q)):
for row2 in range(row1+1, len(q)):
col1 = int(q[row1])
col2 = int(q[row2])
row_diff = row1 - row2
col_diff = col1 - col2

if col1 != col2 and row_diff != col_diff and row_diff != -col_diff:
non_attacking += 1

return non_attacking


solution = genetic_algorithm(population, fitness, f_thres=25)
assert fitness(solution) >= 25


# TODO: for .ipynb:
"""
>>> compare_graph_searchers()
Expand Down