evotraveller / src /algorithm.py
jismartin's picture
Create algorithm.py
771cdea verified
Raw
History Blame Contribute Delete
5.04 kB
import numpy as np
import pandas as pd
import random
import copy
# Be carefull, in Python lists are passed by reference; a list of list is a list of references to lists
# TSP hyperparameters
grid_size = 10
# Set seed
def set_seed(seed):
np.random.seed(seed)
random.seed(seed)
# Places to visit (return a list of locations of the TSP)
def places(grid_size,n_places):
points = list(zip(np.random.randint(0,grid_size,n_places),np.random.randint(0,grid_size,n_places)))
while (0,0) in points:
points = list(zip(np.random.randint(0,grid_size,n_places),np.random.randint(0,grid_size,n_places)))
return points
# Add the origin and destination to the sequence-list
def add_extremes(sequence):
return [(0,0)] + sequence + [(0,0)]
# Fitness function (works for one sequence-list)
def distance(sequence):
path=add_extremes(sequence)
return sum(np.sqrt((path[i][0] - path[i+1][0])**2 + (path[i][1] - path[i+1][1])**2)
for i in range(len(path)-1))
def fitness(solution):
return 1 / distance(solution) # shorter distence higher fitness
# Intial Population (return a list of lists!)
def initial_population(places,population_size):
return [random.sample(places, len(places)) for _ in range(population_size)]
# Selection (random tournament)
def random_tournament(population, tournament_size):
selected_for_next_round = []
for _ in range(len(population)):
tournament = random.sample(population, tournament_size)
winner = max(tournament, key=fitness)
selected_for_next_round.append(winner.copy()) # Be careful, a copy of a list to get a new solution
return selected_for_next_round
# Crossover (ox crossover) returns two offspring solutions from two parents
# Ox ccopy
def ox_copy(parent1,parent2,start,end):
size = min(len(parent1), len(parent2))
child=parent1.copy() # Lists are always passed by reference in Phyton!
tmp=parent2.copy()
tmp.reverse()
for place in child[start:end]:
tmp.remove(place)
for j in range(end,size):
child[j] = tmp.pop()
for j in range(0,start):
child[j] = tmp.pop()
return child
# Generate offsprings
def ox_crossover(parent1, parent2):
size = min(len(parent1), len(parent2))
start, end = sorted(random.sample(range(size), 2))
child1=ox_copy(parent1,parent2,start,end)
child2=ox_copy(parent2,parent1,start,end)
return child1, child2
# Mutation
def mutate(solution, mutation_rate):
size=len(solution)
if random.random() < mutation_rate:
# swap two random positions
i,j = random.sample(range(size), 2)
tmp=solution[i]
solution[i]=solution[j]
solution[j]=tmp
return solution
# Genetic algortihm
def genetic_algorithm(targets, population_size, generations, tournament_size, mutation_rate, elitism,crossover):
# Initialize the population
population = initial_population(targets,population_size)
best_solution=max(population, key=fitness)
fitness_evolution=[fitness(best_solution)]
# Track simulating generations
df_simulation = pd.DataFrame({'c%i'%i:[add_extremes(population[i])] for i in range(population_size)})
e = max(1, int(elitism*population_size))
e = e if (e % 2 == 0) else e + 1 # size of elitism: even number
nc = int(crossover*population_size)
nc = nc if (nc % 2 == 0) else nc + 1 # size of crossover: even number
for t in range(generations):
# Sort population by fitness in descending order
population = sorted(population, key=fitness, reverse=True)
# Create next generation list
# (1) elite (no mutation)
next_generation = copy.deepcopy(population[:e]) # Keep elite individuals (best solutions)
# Selection
selection = random_tournament(population, tournament_size)
parents=random.sample(selection, len(selection)) # shuffle the selected parents
# (2) direct descendants with mutation
for i in range(population_size - nc - e):
parent = parents.pop()
next_generation.append(mutate(parent,mutation_rate))
# (3) descendants with croosover and mutation
for i in range(int(nc/2)):
parent1 = parents.pop()
parent2 = parents.pop()
child1, child2 = ox_crossover(parent1, parent2)
next_generation.append(mutate(child1,mutation_rate))
next_generation.append(mutate(child2,mutation_rate))
# Update population
population = copy.deepcopy(sorted(next_generation, key=fitness, reverse=True))
fitness_evolution.append(fitness(max(population, key=fitness)))
# Track simulation data
df_simulation=pd.concat([df_simulation,
pd.DataFrame({'c%i'%i:[add_extremes(population[i])] for i in range(population_size)})],ignore_index=True)
best_solution=max(population, key=fitness)
return add_extremes(best_solution), fitness_evolution, df_simulation