Trashmaster/genetic_algorithm/TSP.py

236 lines
7.7 KiB
Python
Raw Normal View History

2022-06-09 23:53:44 +02:00
import numpy as np, random, operator, pandas as pd, matplotlib.pyplot as plt
from path_search_algorthms.a_star import get_cost
2022-06-10 01:14:08 +02:00
from decision_tree import decisionTree
from settings import *
import math
2022-06-09 23:53:44 +02:00
# klasa tworząca miasta czy też śmietniki
class City:
2022-06-10 02:16:56 +02:00
def __init__(self, x, y):
2022-06-09 23:53:44 +02:00
self.x = x
self.y = y
#self.array = array
2022-06-10 01:14:08 +02:00
# self.dist = distance
#dystans to d = sqrt(x^2 + y^2)
2022-06-09 23:53:44 +02:00
def distance(self, city):
#getting distance by astar gives wrong final distance (intial = final)
#return get_cost(math.floor(self.x / TILESIZE), math.floor(self.y / TILESIZE), math.floor(city.x / TILESIZE), math.floor(city.y / TILESIZE), self.array)
2022-06-09 23:53:44 +02:00
xDis = abs(self.x - city.x)
yDis = abs(self.y - city.y)
distance = np.sqrt((xDis ** 2) + (yDis ** 2))
return distance
2022-06-09 23:53:44 +02:00
def __repr__(self):
return "(" + str(self.x) + "," + str(self.y) + ")"
2022-06-09 23:53:44 +02:00
# fitness function,
# inverse of route distance
# we want to minimize distance so the larger the fitness the better
class Fitness:
def __init__(self, route):
self.route = route
self.distance = 0
self.fitness = 0.0
2022-06-09 23:53:44 +02:00
def routeDistance(self):
if self.distance == 0:
2022-06-09 23:53:44 +02:00
pathDistance = 0
for i in range(0, len(self.route)):
fromCity = self.route[i]
toCity = None
if i + 1 < len(self.route): # for returning to point 0?
2022-06-09 23:53:44 +02:00
toCity = self.route[i + 1]
else:
toCity = self.route[0]
pathDistance += fromCity.distance(toCity)
self.distance = pathDistance
return self.distance
2022-06-09 23:53:44 +02:00
def routeFitness(self):
if self.fitness == 0:
self.fitness = 1 / float(self.routeDistance())
return self.fitness
2022-06-09 23:53:44 +02:00
# creating one individual - single route from city to city (trash to trash)
def createRoute(cityList):
route = random.sample(cityList, len(cityList))
return route
2022-06-09 23:53:44 +02:00
# creating initial population of given size
def initialPopulation(popSize, cityList):
population = []
for i in range(0, popSize):
population.append(createRoute(cityList))
return population
2022-06-09 23:53:44 +02:00
# ranking fitness of given route, output is ordered list with route id and its fitness score
def rankRoutes(population):
fitnessResults = {}
for i in range(0, len(population)):
2022-06-09 23:53:44 +02:00
fitnessResults[i] = Fitness(population[i]).routeFitness()
return sorted(fitnessResults.items(), key=operator.itemgetter(1), reverse=True)
2022-06-09 23:53:44 +02:00
# selecting "mating pool"
# we are using here "Firness proportionate selection", its fitness-weighted probability of being selected
# moreover we are using elitism to ensure that the best of the best will preserve
def selection(popRanked, eliteSize):
selectionResults = []
# roulette wheel
df = pd.DataFrame(np.array(popRanked), columns=["Index", "Fitness"])
2022-06-09 23:53:44 +02:00
df['cum_sum'] = df.Fitness.cumsum()
df['cum_perc'] = 100 * df.cum_sum / df.Fitness.sum()
for i in range(0, eliteSize): # elitism
2022-06-09 23:53:44 +02:00
selectionResults.append(popRanked[i][0])
for i in range(0,
len(popRanked) - eliteSize): # comparing randomly drawn number to weights for selection for mating pool
pick = 100 * random.random()
2022-06-09 23:53:44 +02:00
for i in range(0, len(popRanked)):
if pick <= df.iat[i, 3]:
2022-06-09 23:53:44 +02:00
selectionResults.append(popRanked[i][0])
break
return selectionResults # returns list of route IDs
2022-06-09 23:53:44 +02:00
# creating mating pool from list of routes IDs from "selection"
def matingPool(population, selectionResults):
matingpool = []
for i in range(0, len(selectionResults)):
index = selectionResults[i]
matingpool.append(population[index])
return matingpool
2022-06-09 23:53:44 +02:00
# creating new generation
# ordered crossover bc we need to include all locations exactly one time
# randomly selecting a subset of the first parent string and then filling the remainder of route
# with genes from the second parent in the order in which they appear, without duplicating any genes from the first parent
def breed(parent1, parent2):
child = []
childP1 = []
childP2 = []
2022-06-09 23:53:44 +02:00
geneA = int(random.random() * len(parent1))
geneB = int(random.random() * len(parent1))
2022-06-09 23:53:44 +02:00
startGene = min(geneA, geneB)
endGene = max(geneA, geneB)
for i in range(startGene, endGene): # ordered crossover
2022-06-09 23:53:44 +02:00
childP1.append(parent1[i])
2022-06-09 23:53:44 +02:00
childP2 = [item for item in parent2 if item not in childP1]
child = childP1 + childP2
return child
2022-06-09 23:53:44 +02:00
# creating whole offspring population
def breedPopulation(matingpool, eliteSize):
children = []
length = len(matingpool) - eliteSize
pool = random.sample(matingpool, len(matingpool))
2022-06-09 23:53:44 +02:00
# using elitism to retain best genes (routes)
for i in range(0, eliteSize):
2022-06-09 23:53:44 +02:00
children.append(matingpool[i])
2022-06-09 23:53:44 +02:00
# filling rest generation
for i in range(0, length):
child = breed(pool[i], pool[len(matingpool) - i - 1])
2022-06-09 23:53:44 +02:00
children.append(child)
return children
2022-06-09 23:53:44 +02:00
# using swap mutation
# with specified low prob we swap two cities in route
def mutate(individual, mutationRate):
for swapped in range(len(individual)):
if (random.random() < mutationRate):
2022-06-09 23:53:44 +02:00
swapWith = int(random.random() * len(individual))
2022-06-09 23:53:44 +02:00
city1 = individual[swapped]
city2 = individual[swapWith]
2022-06-09 23:53:44 +02:00
individual[swapped] = city2
individual[swapWith] = city1
return individual
2022-06-09 23:53:44 +02:00
# extending mutate function to run through new pop
def mutatePopulation(population, mutationRate):
mutatedPop = []
2022-06-09 23:53:44 +02:00
for ind in range(0, len(population)):
mutatedInd = mutate(population[ind], mutationRate)
mutatedPop.append(mutatedInd)
return mutatedPop
# creating new generation
2022-06-09 23:53:44 +02:00
def nextGeneration(currentGen, eliteSize, mutationRate):
popRanked = rankRoutes(currentGen) # rank routes in current gen
selectionResults = selection(popRanked, eliteSize) # determining potential parents
matingpool = matingPool(currentGen, selectionResults) # creating mating pool
children = breedPopulation(matingpool, eliteSize) # creating new gen
nextGeneration = mutatePopulation(children, mutationRate) # applying mutation to new gen
2022-06-09 23:53:44 +02:00
return nextGeneration
def geneticAlgorithm(population, popSize, eliteSize, mutationRate, generations):
pop = initialPopulation(popSize, population)
print("Initial distance: " + str(1 / rankRoutes(pop)[0][1]))
2022-06-09 23:53:44 +02:00
for i in range(0, generations):
pop = nextGeneration(pop, eliteSize, mutationRate)
2022-06-09 23:53:44 +02:00
print("Final distance: " + str(1 / rankRoutes(pop)[0][1]))
bestRouteIndex = rankRoutes(pop)[0][0]
bestRoute = pop[bestRouteIndex]
return bestRoute
2022-06-09 23:53:44 +02:00
# tutaj ma być lista kordów potencjalnych śmietników z drzewa decyzyjnego
cityList = []
2022-06-10 02:12:10 +02:00
# for i in range(0,25):
# cityList.append(City(x=int(random.random() * 200), y=int(random.random() * 200)))
2022-06-09 23:53:44 +02:00
2022-06-10 02:12:10 +02:00
# geneticAlgorithm(population=cityList, popSize=100, eliteSize=20, mutationRate=0.01, generations=1000)
2022-06-09 23:53:44 +02:00
# plotting the progress
def geneticAlgorithmPlot(population, popSize, eliteSize, mutationRate, generations):
pop = initialPopulation(popSize, population)
progress = []
progress.append(1 / rankRoutes(pop)[0][1])
2022-06-10 02:12:10 +02:00
print("Initial distance: " + str(1 / rankRoutes(pop)[0][1]))
2022-06-09 23:53:44 +02:00
for i in range(0, generations):
pop = nextGeneration(pop, eliteSize, mutationRate)
progress.append(1 / rankRoutes(pop)[0][1])
2022-06-10 02:12:10 +02:00
print("Final distance: " + str(1 / rankRoutes(pop)[0][1]))
bestRouteIndex = rankRoutes(pop)[0][0]
bestRoute = pop[bestRouteIndex]
2022-06-09 23:53:44 +02:00
plt.plot(progress)
plt.ylabel('Distance')
plt.xlabel('Generation')
plt.show()
2022-06-10 02:12:10 +02:00
return bestRoute
2022-06-09 23:53:44 +02:00
# geneticAlgorithmPlot(population=cityList, popSize=100, eliteSize=20, mutationRate=0.01, generations=1000)